From 65211d2105860c10061f86b4b6d67965c2c3c425 Mon Sep 17 00:00:00 2001 From: devloglogan Date: Mon, 29 Jul 2024 13:55:24 -0500 Subject: [PATCH] Implement motion vectors in compatibility renderer --- drivers/gles3/rasterizer_canvas_gles3.cpp | 4 +- drivers/gles3/rasterizer_scene_gles3.cpp | 272 +++++++------ drivers/gles3/rasterizer_scene_gles3.h | 13 +- drivers/gles3/shaders/scene.glsl | 363 ++++++++++++++---- drivers/gles3/storage/config.cpp | 1 + drivers/gles3/storage/config.h | 9 +- drivers/gles3/storage/material_storage.cpp | 20 +- drivers/gles3/storage/mesh_storage.cpp | 265 +++++++++---- drivers/gles3/storage/mesh_storage.h | 57 ++- drivers/gles3/storage/particles_storage.cpp | 1 + drivers/gles3/storage/particles_storage.h | 16 + .../storage/render_scene_buffers_gles3.cpp | 7 - drivers/gles3/storage/texture_storage.cpp | 127 +++++- drivers/gles3/storage/texture_storage.h | 14 + .../extensions/openxr_extension_wrapper.h | 1 + ...openxr_fb_space_warp_extension_wrapper.cpp | 190 +++++++++ .../openxr_fb_space_warp_extension_wrapper.h | 85 ++++ modules/openxr/openxr_api.cpp | 54 +++ modules/openxr/openxr_api.h | 24 +- modules/openxr/openxr_interface.cpp | 28 ++ modules/openxr/openxr_interface.h | 4 + modules/openxr/register_types.cpp | 6 + .../rendering/dummy/storage/texture_storage.h | 5 + .../renderer_rd/storage_rd/texture_storage.h | 5 + servers/rendering/renderer_viewport.cpp | 6 + servers/rendering/storage/texture_storage.h | 6 + servers/xr/xr_interface.cpp | 12 + servers/xr/xr_interface.h | 3 + 28 files changed, 1292 insertions(+), 306 deletions(-) create mode 100644 modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.cpp create mode 100644 modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.h diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 3c959f014388..c8665f99430b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1448,9 +1448,9 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index, Ren uint64_t vertex_input_mask = state.canvas_instance_batches[p_index].vertex_input_mask; if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, vertex_input_mask, false, vertex_array_gl); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, vertex_input_mask, false, vertex_array_gl); } index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index c2d17849580a..06d0754d9e93 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1457,147 +1457,147 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da correction.set_depth_correction(p_flip_y, true, false); Projection projection = correction * p_render_data->cam_projection; //store camera into ubo - GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix); - GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix); - 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); - GLES3::MaterialStorage::store_transform(p_render_data->main_cam_transform, scene_state.ubo.main_cam_inv_view_matrix); - scene_state.ubo.camera_visible_layers = p_render_data->camera_visible_layers; + GLES3::MaterialStorage::store_camera(projection, scene_state.data.projection_matrix); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.data.inv_projection_matrix); + GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.data.inv_view_matrix); + GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.data.view_matrix); + GLES3::MaterialStorage::store_transform(p_render_data->main_cam_transform, scene_state.data.main_cam_inv_view_matrix); + scene_state.data.camera_visible_layers = p_render_data->camera_visible_layers; 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]); + GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_data.projection_matrix_view[v]); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_data.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.multiview_data.eye_offset[v][0] = p_render_data->view_eye_offset[v].x; + scene_state.multiview_data.eye_offset[v][1] = p_render_data->view_eye_offset[v].y; + scene_state.multiview_data.eye_offset[v][2] = p_render_data->view_eye_offset[v].z; + scene_state.multiview_data.eye_offset[v][3] = 0.0; } } // Only render the lights without shadows in the base pass. - scene_state.ubo.directional_light_count = p_render_data->directional_light_count - p_render_data->directional_shadow_count; + scene_state.data.directional_light_count = p_render_data->directional_light_count - p_render_data->directional_shadow_count; - scene_state.ubo.z_far = p_render_data->z_far; - scene_state.ubo.z_near = p_render_data->z_near; + scene_state.data.z_far = p_render_data->z_far; + scene_state.data.z_near = p_render_data->z_near; - scene_state.ubo.viewport_size[0] = p_screen_size.x; - scene_state.ubo.viewport_size[1] = p_screen_size.y; + scene_state.data.viewport_size[0] = p_screen_size.x; + scene_state.data.viewport_size[1] = p_screen_size.y; Size2 screen_pixel_size = Vector2(1.0, 1.0) / Size2(p_screen_size); - scene_state.ubo.screen_pixel_size[0] = screen_pixel_size.x; - scene_state.ubo.screen_pixel_size[1] = screen_pixel_size.y; + scene_state.data.screen_pixel_size[0] = screen_pixel_size.x; + scene_state.data.screen_pixel_size[1] = screen_pixel_size.y; - scene_state.ubo.luminance_multiplier = p_render_data->luminance_multiplier; + scene_state.data.luminance_multiplier = p_render_data->luminance_multiplier; - scene_state.ubo.shadow_bias = p_shadow_bias; - scene_state.ubo.pancake_shadows = p_pancake_shadows; + scene_state.data.shadow_bias = p_shadow_bias; + scene_state.data.pancake_shadows = p_pancake_shadows; //time global variables - scene_state.ubo.time = time; + scene_state.data.time = time; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { - scene_state.ubo.use_ambient_light = true; - scene_state.ubo.ambient_light_color_energy[0] = 1; - scene_state.ubo.ambient_light_color_energy[1] = 1; - scene_state.ubo.ambient_light_color_energy[2] = 1; - scene_state.ubo.ambient_light_color_energy[3] = 1.0; - scene_state.ubo.use_ambient_cubemap = false; - scene_state.ubo.use_reflection_cubemap = false; + scene_state.data.use_ambient_light = true; + scene_state.data.ambient_light_color_energy[0] = 1; + scene_state.data.ambient_light_color_energy[1] = 1; + scene_state.data.ambient_light_color_energy[2] = 1; + scene_state.data.ambient_light_color_energy[3] = 1.0; + scene_state.data.use_ambient_cubemap = false; + scene_state.data.use_reflection_cubemap = false; } else if (is_environment(p_render_data->environment)) { RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; + scene_state.data.ambient_light_color_energy[3] = bg_energy_multiplier; - scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); + scene_state.data.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); //ambient if (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && (env_bg == RS::ENV_BG_CLEAR_COLOR || env_bg == RS::ENV_BG_COLOR)) { Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; - scene_state.ubo.use_ambient_light = true; - scene_state.ubo.use_ambient_cubemap = false; + scene_state.data.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.data.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.data.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; + scene_state.data.use_ambient_light = true; + scene_state.data.use_ambient_cubemap = false; } else { float energy = environment_get_ambient_light_energy(p_render_data->environment); Color color = environment_get_ambient_light(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * energy; + scene_state.data.ambient_light_color_energy[0] = color.r * energy; + scene_state.data.ambient_light_color_energy[1] = color.g * energy; + scene_state.data.ambient_light_color_energy[2] = color.b * energy; Basis sky_transform = environment_get_sky_orientation(p_render_data->environment); sky_transform = sky_transform.inverse() * p_render_data->cam_transform.basis; - GLES3::MaterialStorage::store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform); - scene_state.ubo.use_ambient_cubemap = (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ambient_src == RS::ENV_AMBIENT_SOURCE_SKY; - scene_state.ubo.use_ambient_light = scene_state.ubo.use_ambient_cubemap || ambient_src == RS::ENV_AMBIENT_SOURCE_COLOR; + GLES3::MaterialStorage::store_transform_3x3(sky_transform, scene_state.data.radiance_inverse_xform); + scene_state.data.use_ambient_cubemap = (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ambient_src == RS::ENV_AMBIENT_SOURCE_SKY; + scene_state.data.use_ambient_light = scene_state.data.use_ambient_cubemap || ambient_src == RS::ENV_AMBIENT_SOURCE_COLOR; } //specular RS::EnvironmentReflectionSource ref_src = environment_get_reflection_source(p_render_data->environment); if ((ref_src == RS::ENV_REFLECTION_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ref_src == RS::ENV_REFLECTION_SOURCE_SKY) { - scene_state.ubo.use_reflection_cubemap = true; + scene_state.data.use_reflection_cubemap = true; } else { - scene_state.ubo.use_reflection_cubemap = false; + scene_state.data.use_reflection_cubemap = false; } - scene_state.ubo.fog_enabled = environment_get_fog_enabled(p_render_data->environment); - scene_state.ubo.fog_mode = environment_get_fog_mode(p_render_data->environment); - scene_state.ubo.fog_density = environment_get_fog_density(p_render_data->environment); - scene_state.ubo.fog_height = environment_get_fog_height(p_render_data->environment); - scene_state.ubo.fog_depth_curve = environment_get_fog_depth_curve(p_render_data->environment); - scene_state.ubo.fog_depth_end = environment_get_fog_depth_end(p_render_data->environment) > 0.0 ? environment_get_fog_depth_end(p_render_data->environment) : scene_state.ubo.z_far; - scene_state.ubo.fog_depth_begin = MIN(environment_get_fog_depth_begin(p_render_data->environment), scene_state.ubo.fog_depth_end - 0.001); - scene_state.ubo.fog_height_density = environment_get_fog_height_density(p_render_data->environment); - scene_state.ubo.fog_aerial_perspective = environment_get_fog_aerial_perspective(p_render_data->environment); + scene_state.data.fog_enabled = environment_get_fog_enabled(p_render_data->environment); + scene_state.data.fog_mode = environment_get_fog_mode(p_render_data->environment); + scene_state.data.fog_density = environment_get_fog_density(p_render_data->environment); + scene_state.data.fog_height = environment_get_fog_height(p_render_data->environment); + scene_state.data.fog_depth_curve = environment_get_fog_depth_curve(p_render_data->environment); + scene_state.data.fog_depth_end = environment_get_fog_depth_end(p_render_data->environment) > 0.0 ? environment_get_fog_depth_end(p_render_data->environment) : scene_state.data.z_far; + scene_state.data.fog_depth_begin = MIN(environment_get_fog_depth_begin(p_render_data->environment), scene_state.data.fog_depth_end - 0.001); + scene_state.data.fog_height_density = environment_get_fog_height_density(p_render_data->environment); + scene_state.data.fog_aerial_perspective = environment_get_fog_aerial_perspective(p_render_data->environment); Color fog_color = environment_get_fog_light_color(p_render_data->environment).srgb_to_linear(); float fog_energy = environment_get_fog_light_energy(p_render_data->environment); - scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy; - scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy; - scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy; + scene_state.data.fog_light_color[0] = fog_color.r * fog_energy; + scene_state.data.fog_light_color[1] = fog_color.g * fog_energy; + scene_state.data.fog_light_color[2] = fog_color.b * fog_energy; - scene_state.ubo.fog_sun_scatter = environment_get_fog_sun_scatter(p_render_data->environment); + scene_state.data.fog_sun_scatter = environment_get_fog_sun_scatter(p_render_data->environment); } else { } if (p_render_data->camera_attributes.is_valid()) { - scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); - scene_state.ubo.IBL_exposure_normalization = 1.0; + scene_state.data.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.data.IBL_exposure_normalization = 1.0; if (is_environment(p_render_data->environment)) { RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment); - scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky_get_baked_exposure(sky_rid)); + scene_state.data.IBL_exposure_normalization = current_exposure / MAX(0.001, sky_get_baked_exposure(sky_rid)); } } - } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + } else if (scene_state.data.emissive_exposure_normalization > 0.0) { // This branch is triggered when using render_material(). // Emissive is set outside the function, so don't set it. // IBL isn't used don't set it. } else { - scene_state.ubo.emissive_exposure_normalization = 1.0; - scene_state.ubo.IBL_exposure_normalization = 1.0; + scene_state.data.emissive_exposure_normalization = 1.0; + scene_state.data.IBL_exposure_normalization = 1.0; } if (scene_state.ubo_buffer == 0) { glGenBuffers(1, &scene_state.ubo_buffer); glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_UNIFORM_BUFFER, scene_state.ubo_buffer, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW, "Scene state UBO"); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_UNIFORM_BUFFER, scene_state.ubo_buffer, sizeof(SceneState::UBO) * 2, &scene_state.data, GL_STREAM_DRAW, "Scene state UBO"); glBindBuffer(GL_UNIFORM_BUFFER, 0); } else { 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); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO) * 2, &scene_state.data, GL_STREAM_DRAW); } glBindBuffer(GL_UNIFORM_BUFFER, 0); @@ -1606,10 +1606,10 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da if (scene_state.multiview_buffer == 0) { glGenBuffers(1, &scene_state.multiview_buffer); glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_UNIFORM_BUFFER, scene_state.multiview_buffer, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW, "Multiview UBO"); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_UNIFORM_BUFFER, scene_state.multiview_buffer, sizeof(SceneState::MultiviewUBO) * 2, &scene_state.multiview_data, GL_STREAM_DRAW, "Multiview UBO"); } else { 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); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO) * 2, &scene_state.multiview_data, GL_STREAM_DRAW); } glBindBuffer(GL_UNIFORM_BUFFER, 0); @@ -2357,7 +2357,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glBindBuffer(GL_UNIFORM_BUFFER, 0); - scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization. + scene_state.data.emissive_exposure_normalization = -1.0; // Use default exposure normalization. bool flip_y = !is_reflection_probe; @@ -2462,6 +2462,46 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } } + scene_state.reset_gl_state(); + + GLuint motion_vector_fbo = rt->motion_vector_fbo; + if (motion_vector_fbo != 0 && GLES3::Config::get_singleton()->max_vertex_attribs >= 22) { + RENDER_TIMESTAMP("Motion Vector Pass"); + glBindFramebuffer(GL_FRAMEBUFFER, motion_vector_fbo); + + Size2i motion_vector_target_size = rt->motion_vector_target_size; + glViewport(0, 0, motion_vector_target_size.x, motion_vector_target_size.y); + + if (!scene_state.is_prev_data_stored) { + scene_state.prev_data = scene_state.data; + scene_state.prev_multiview_data = scene_state.multiview_data; + scene_state.is_prev_data_stored = true; + } + + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_depth_draw(true); + scene_state.enable_gl_blend(false); + glDepthFunc(GL_GEQUAL); + scene_state.enable_gl_scissor_test(false); + + glColorMask(1, 1, 1, 1); + RasterizerGLES3::clear_depth(0.0); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLuint db = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &db); + + uint64_t spec_constant = SceneShaderGLES3::DISABLE_FOG | SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL | + SceneShaderGLES3::DISABLE_LIGHTMAP | SceneShaderGLES3::DISABLE_LIGHT_OMNI | + SceneShaderGLES3::DISABLE_LIGHT_SPOT; + + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant, use_wireframe); + _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); + + scene_state.prev_data = scene_state.data; + scene_state.prev_multiview_data = scene_state.multiview_data; + } + GLuint fbo = 0; if (is_reflection_probe) { fbo = GLES3::LightStorage::get_singleton()->reflection_probe_instance_get_framebuffer(render_data.reflection_probe, render_data.reflection_probe_pass); @@ -2473,8 +2513,6 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glBindFramebuffer(GL_FRAMEBUFFER, fbo); glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); - scene_state.reset_gl_state(); - // Do depth prepass if it's explicitly enabled bool use_depth_prepass = config->use_depth_prepass; @@ -2908,6 +2946,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } else if constexpr (p_pass_mode == PASS_MODE_DEPTH || p_pass_mode == PASS_MODE_SHADOW) { shader_variant = SceneShaderGLES3::MODE_DEPTH; + } else if constexpr (p_pass_mode == PASS_MODE_MOTION_VECTORS) { + base_spec_constants |= SceneShaderGLES3::RENDER_MOTION_VECTORS; } if (p_render_data->view_count > 1) { @@ -2915,8 +2955,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } bool should_request_redraw = false; - if constexpr (p_pass_mode != PASS_MODE_DEPTH) { - // Don't count elements during depth pre-pass to match the RD renderers. + if constexpr (p_pass_mode != PASS_MODE_DEPTH && p_pass_mode != PASS_MODE_MOTION_VECTORS) { + // Don't count elements during depth pre-pass or motion vector pass to match the RD renderers. if (p_render_data->render_info) { p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] += p_to_element - p_from_element; } @@ -2971,7 +3011,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if constexpr (p_pass_mode != PASS_MODE_SHADOW) { if (shader->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE) { - scene_state.enable_gl_depth_draw((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || p_pass_mode == PASS_MODE_DEPTH); + scene_state.enable_gl_depth_draw((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || p_pass_mode == PASS_MODE_DEPTH || p_pass_mode == PASS_MODE_MOTION_VECTORS); } else { scene_state.enable_gl_depth_draw(shader->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS); } @@ -2987,9 +3027,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, */ for (int32_t pass = 0; pass < MAX(1, int32_t(inst->light_passes.size() + p_render_data->directional_shadow_count)); pass++) { - if constexpr (p_pass_mode == PASS_MODE_DEPTH || p_pass_mode == PASS_MODE_SHADOW) { + if constexpr (p_pass_mode == PASS_MODE_DEPTH || p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_MOTION_VECTORS) { if (pass > 0) { - // Don't render shadow passes when doing depth or shadow pass. + // Don't render shadow passes when doing depth, shadow, or motion vector pass. break; } } @@ -3104,9 +3144,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Skeleton and blend shapes. if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, p_pass_mode == PASS_MODE_MOTION_VECTORS, vertex_array_gl); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, p_pass_mode == PASS_MODE_MOTION_VECTORS, vertex_array_gl); } index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); @@ -3272,7 +3312,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } float opaque_prepass_threshold = 0.0; - if constexpr (p_pass_mode == PASS_MODE_DEPTH) { + if constexpr (p_pass_mode == PASS_MODE_DEPTH || p_pass_mode == PASS_MODE_MOTION_VECTORS) { opaque_prepass_threshold = 0.99; } else if constexpr (p_pass_mode == PASS_MODE_SHADOW) { opaque_prepass_threshold = 0.1; @@ -3428,6 +3468,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + if constexpr (p_pass_mode == PASS_MODE_MOTION_VECTORS) { + if (unlikely(!inst->is_prev_transform_stored)) { + inst->prev_transform = world_transform; + inst->is_prev_transform_stored = true; + } + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::PREV_WORLD_TRANSFORM, inst->prev_transform, shader->version, instance_variant, spec_constants); + inst->prev_transform = world_transform; + } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, spec_constants); { GLES3::Mesh::Surface *s = reinterpret_cast(surf->surface); @@ -3461,8 +3511,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, count = count * 2; } - if constexpr (p_pass_mode != PASS_MODE_DEPTH) { - // Don't count draw calls during depth pre-pass to match the RD renderers. + if constexpr (p_pass_mode != PASS_MODE_DEPTH && p_pass_mode != PASS_MODE_MOTION_VECTORS) { + // Don't count draw calls during depth pre-pass or motion vector pass to match the RD renderers. if (p_render_data->render_info) { p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]++; } @@ -3487,32 +3537,40 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, break; } - glBindBuffer(GL_ARRAY_BUFFER, instance_buffer); - - glEnableVertexAttribArray(12); - glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); - glVertexAttribDivisor(12, 1); - glEnableVertexAttribArray(13); - glVertexAttribPointer(13, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4)); - glVertexAttribDivisor(13, 1); - if (!(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D)) { - glEnableVertexAttribArray(14); - glVertexAttribPointer(14, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 8)); - glVertexAttribDivisor(14, 1); - } + bool uses_format_2d = inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; + bool has_color_or_custom_data = (inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR) || (inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA); + // Current data multimesh vertex attrib data begins at index 12. + mesh_storage->multimesh_vertex_attrib_setup(instance_buffer, stride, uses_format_2d, has_color_or_custom_data, 12); - if ((inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR) || (inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA)) { - uint32_t color_custom_offset = inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D ? 8 : 12; - glEnableVertexAttribArray(15); - glVertexAttribIPointer(15, 4, GL_UNSIGNED_INT, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(color_custom_offset * sizeof(float))); - glVertexAttribDivisor(15, 1); - } else { - // Set all default instance color and custom data values to 1.0 or 0.0 using a compressed format. - uint16_t zero = Math::make_half_float(0.0f); - uint16_t one = Math::make_half_float(1.0f); - GLuint default_color = (uint32_t(one) << 16) | one; - GLuint default_custom = (uint32_t(zero) << 16) | zero; - glVertexAttribI4ui(15, default_color, default_color, default_custom, default_custom); + if (p_pass_mode == PASS_MODE_MOTION_VECTORS) { + GLuint prev_instance_buffer = 0; + if (inst->flags_cache & INSTANCE_DATA_FLAG_PARTICLES) { + prev_instance_buffer = particles_storage->particles_get_prev_gl_buffer(inst->data->base); + } else { + prev_instance_buffer = mesh_storage->multimesh_get_prev_gl_buffer(inst->data->base); + } + + if (prev_instance_buffer == 0) { + break; + } + + GLuint secondary_instance_buffer = 0; + if (inst->flags_cache & INSTANCE_DATA_FLAG_PARTICLES) { + if (particles_storage->particles_get_last_change(inst->data->base) == RSG::rasterizer->get_frame_number()) { + secondary_instance_buffer = prev_instance_buffer; + } else { + secondary_instance_buffer = instance_buffer; + } + } else { + if (mesh_storage->multimesh_get_last_change(inst->data->base) == RSG::rasterizer->get_frame_number()) { + secondary_instance_buffer = prev_instance_buffer; + } else { + secondary_instance_buffer = instance_buffer; + } + } + + // Previous data multimesh vertex attrib data begins at index 18. + mesh_storage->multimesh_vertex_attrib_setup(secondary_instance_buffer, stride, uses_format_2d, has_color_or_custom_data, 18); } if (use_wireframe) { @@ -3636,7 +3694,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray 0.5 ? 1.0 : -1.0; // 0.5 does not exist in UNORM16, so values are either greater or smaller. angle = abs(angle * 2.0 - 1.0) * M_PI; // 0.5 is basically zero, allowing to encode both signs reliably. vec3 axis = normal; @@ -537,29 +598,29 @@ void main() { #endif #if defined(COLOR_USED) - color_interp = color_attrib; + color_interp = color_attrib_input; #ifdef USE_INSTANCING vec4 instance_color; - instance_color.xy = unpackHalf2x16(instance_color_custom_data.x); - instance_color.zw = unpackHalf2x16(instance_color_custom_data.y); + instance_color.xy = unpackHalf2x16(instance_color_custom_data_input.x); + instance_color.zw = unpackHalf2x16(instance_color_custom_data_input.y); color_interp *= instance_color; #endif #endif #if defined(UV_USED) - uv_interp = uv_attrib; + uv_interp = uv_attrib_input; #endif #if defined(UV2_USED) || defined(USE_LIGHTMAP) - uv2_interp = uv2_attrib; + uv2_interp = uv2_attrib_input; #endif - if (uv_scale != vec4(0.0)) { // Compression enabled + if (uv_scale_input != vec4(0.0)) { // Compression enabled #ifdef UV_USED - uv_interp = (uv_interp - 0.5) * uv_scale.xy; + uv_interp = (uv_interp - 0.5) * uv_scale_input.xy; #endif #if defined(UV2_USED) || defined(USE_LIGHTMAP) - uv2_interp = (uv2_interp - 0.5) * uv_scale.zw; + uv2_interp = (uv2_interp - 0.5) * uv_scale_input.zw; #endif } @@ -568,19 +629,28 @@ void main() { #endif #ifdef USE_MULTIVIEW - mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; - mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; - vec3 eye_offset = multiview_data.eye_offset[ViewIndex].xyz; + mat4 projection_matrix; + mat4 inv_projection_matrix; + vec3 eye_offset; + if (use_prev_multiview_data) { + projection_matrix = multiview_data_block.prev_data.projection_matrix_view[ViewIndex]; + inv_projection_matrix = multiview_data_block.prev_data.inv_projection_matrix_view[ViewIndex]; + eye_offset = multiview_data_block.prev_data.eye_offset[ViewIndex].xyz; + } else { + projection_matrix = multiview_data_block.data.projection_matrix_view[ViewIndex]; + inv_projection_matrix = multiview_data_block.data.inv_projection_matrix_view[ViewIndex]; + eye_offset = multiview_data_block.data.eye_offset[ViewIndex].xyz; + } #else - mat4 projection_matrix = scene_data.projection_matrix; - mat4 inv_projection_matrix = scene_data.inv_projection_matrix; + mat4 projection_matrix = scene_data_input.projection_matrix; + mat4 inv_projection_matrix = scene_data_input.inv_projection_matrix; vec3 eye_offset = vec3(0.0, 0.0, 0.0); #endif //USE_MULTIVIEW #ifdef USE_INSTANCING vec4 instance_custom; - instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z); - instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w); + instance_custom.xy = unpackHalf2x16(instance_color_custom_data_input.z); + instance_custom.zw = unpackHalf2x16(instance_color_custom_data_input.w); #else vec4 instance_custom = vec4(0.0); #endif @@ -604,8 +674,8 @@ void main() { float roughness = 1.0; - highp mat4 modelview = scene_data.view_matrix * model_matrix; - highp mat3 modelview_normal = mat3(scene_data.view_matrix) * model_normal_matrix; + highp mat4 modelview = scene_data_input.view_matrix * model_matrix; + highp mat3 modelview_normal = mat3(scene_data_input.view_matrix) * model_normal_matrix; float point_size = 1.0; @@ -633,14 +703,14 @@ void main() { // Using world coordinates #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - vertex = (scene_data.view_matrix * vec4(vertex, 1.0)).xyz; + vertex = (scene_data_input.view_matrix * vec4(vertex, 1.0)).xyz; #ifdef NORMAL_USED - normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz; + normal = (scene_data_input.view_matrix * vec4(normal, 0.0)).xyz; #endif #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz; - tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz; + binormal = (scene_data_input.view_matrix * vec4(binormal, 0.0)).xyz; + tangent = (scene_data_input.view_matrix * vec4(tangent, 0.0)).xyz; #endif #endif @@ -654,6 +724,7 @@ void main() { binormal_interp = binormal; #endif +#ifndef RENDER_MOTION_VECTORS // Calculate shadows. #ifdef USE_ADDITIVE_LIGHTING #if defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) @@ -693,23 +764,25 @@ void main() { #endif // USE_ADDITIVE_LIGHTING #if defined(RENDER_SHADOWS) && !defined(RENDER_SHADOWS_LINEAR) - // This is an optimized version of normalize(vertex_interp) * scene_data.shadow_bias / length(vertex_interp). + // This is an optimized version of normalize(vertex_interp) * scene_data_input.shadow_bias / length(vertex_interp). float light_length_sq = dot(vertex_interp, vertex_interp); - vertex_interp += vertex_interp * scene_data.shadow_bias / light_length_sq; + vertex_interp += vertex_interp * scene_data_input.shadow_bias / light_length_sq; #endif +#endif // RENDER_MOTION_VECTORS #if defined(OVERRIDE_POSITION) - gl_Position = position; + clip_position_output = position; #else - gl_Position = projection_matrix * vec4(vertex_interp, 1.0); + clip_position_output = projection_matrix * vec4(vertex_interp, 1.0); #endif #ifdef RENDER_MATERIAL - gl_Position.xy = (uv2_attrib.xy + uv_offset) * 2.0 - 1.0; - gl_Position.z = 0.00001; - gl_Position.w = 1.0; + clip_position_output.xy = (uv2_attrib_input.xy + uv_offset) * 2.0 - 1.0; + clip_position_output.z = 0.00001; + clip_position_output.w = 1.0; #endif +#ifndef RENDER_MOTION_VECTORS #ifdef USE_VERTEX_LIGHTING #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #ifdef USE_MULTIVIEW @@ -721,7 +794,7 @@ void main() { specular_light_interp = vec3(0.0); #ifdef BASE_PASS #ifndef DISABLE_LIGHT_DIRECTIONAL - for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + for (uint i = uint(0); i < scene_data_input.directional_light_count; i++) { light_compute(normal_interp, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].color * directional_lights[i].energy, true, roughness, diffuse_light_interp.rgb, specular_light_interp.rgb); @@ -767,7 +840,92 @@ void main() { #endif // USE_ADDITIVE_LIGHTING #endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #endif // USE_VERTEX_LIGHTING +#endif // RENDER_MOTION_VECTORS } +void main() { +#if defined(RENDER_MOTION_VECTORS) + +#ifdef USE_INSTANCING + // Check for inactive particle instances. + highp vec4 input_instance_xform0; + highp vec4 input_instance_xform1; + highp vec4 input_instance_xform2; + highp uvec4 input_instance_color_custom_data; + if (prev_instance_xform0.xyz == vec3(0.0, 0.0, 0.0)) { + input_instance_xform0 = instance_xform0; + input_instance_xform1 = instance_xform1; + input_instance_xform2 = instance_xform2; + input_instance_color_custom_data = instance_color_custom_data; + } else { + input_instance_xform0 = prev_instance_xform0; + input_instance_xform1 = prev_instance_xform1; + input_instance_xform2 = prev_instance_xform2; + input_instance_color_custom_data = prev_instance_color_custom_data; + } +#endif + + vertex_shader(prev_vertex_attrib, + compressed_aabb_size, + compressed_aabb_position, + prev_world_transform, + model_flags, + scene_data_block.prev_data, +#ifdef USE_INSTANCING + input_instance_xform0, input_instance_xform1, input_instance_xform2, + input_instance_color_custom_data, +#endif +#ifdef NORMAL_USED + prev_normal_attrib, +#endif +#if defined(COLOR_USED) + color_attrib, +#endif +#if defined(UV_USED) + uv_attrib, +#endif +#if defined(UV2_USED) || defined(USE_LIGHTMAP) + uv2_attrib, +#endif +#ifdef USE_MULTIVIEW + true, +#endif + uv_scale, + prev_clip_position); +#else + vec4 clip_position; +#endif + + vertex_shader(vertex_angle_attrib, + compressed_aabb_size, + compressed_aabb_position, + world_transform, + model_flags, + scene_data_block.data, +#ifdef USE_INSTANCING + instance_xform0, instance_xform1, instance_xform2, + instance_color_custom_data, +#endif +#ifdef NORMAL_USED + axis_tangent_attrib, +#endif +#if defined(COLOR_USED) + color_attrib, +#endif +#if defined(UV_USED) + uv_attrib, +#endif +#if defined(UV2_USED) || defined(USE_LIGHTMAP) + uv2_attrib, +#endif +#ifdef USE_MULTIVIEW + false, +#endif + uv_scale, + clip_position); + + gl_Position = clip_position; +} + /* clang-format off */ #[fragment] @@ -816,6 +974,12 @@ void main() { /* Varyings */ +#if defined(RENDER_MOTION_VECTORS) +in highp vec4 clip_position; +in highp vec4 prev_clip_position; +#endif + +#ifndef RENDER_MOTION_VECTORS #if defined(COLOR_USED) in vec4 color_interp; #endif @@ -913,7 +1077,7 @@ layout(std140) uniform MaterialUniforms { // ubo:3 #endif -layout(std140) uniform SceneData { // ubo:2 +struct SceneData { highp mat4 projection_matrix; highp mat4 inv_projection_matrix; highp mat4 inv_view_matrix; @@ -961,16 +1125,26 @@ layout(std140) uniform SceneData { // ubo:2 float luminance_multiplier; uint camera_visible_layers; bool pancake_shadows; +}; + +layout(std140) uniform SceneDataBlock { // ubo:2 + SceneData data; + SceneData prev_data; } -scene_data; +scene_data_block; #ifdef USE_MULTIVIEW -layout(std140) uniform MultiviewData { // ubo:8 +struct MultiviewData { highp mat4 projection_matrix_view[MAX_VIEWS]; highp mat4 inv_projection_matrix_view[MAX_VIEWS]; highp vec4 eye_offset[MAX_VIEWS]; +}; + +layout(std140) uniform MultiviewDataBlock { // ubo:8 + MultiviewData data; + MultiviewData prev_data; } -multiview_data; +multiview_data_block; #endif /* clang-format off */ @@ -1201,6 +1375,7 @@ ivec2 multiview_uv(ivec2 uv) { uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; uniform highp uint model_flags; +#endif // !RENDER_MOTION_VECTORS #if defined(RENDER_MATERIAL) layout(location = 0) out vec4 albedo_output_buffer; @@ -1209,11 +1384,17 @@ layout(location = 2) out vec4 orm_output_buffer; layout(location = 3) out vec4 emission_output_buffer; #else // !RENDER_MATERIAL + +#ifndef RENDER_MOTION_VECTORS // Normal color rendering. layout(location = 0) out vec4 frag_color; +#else +layout(location = 0) out vec4 motion_vector; +#endif // !RENDER_MOTION_VECTORS #endif // !RENDER_MATERIAL +#ifndef RENDER_MOTION_VECTORS vec3 F0(float metallic, float specular, vec3 albedo) { float dielectric = 0.16 * specular * specular; // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials; @@ -1276,8 +1457,8 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di // light is written by the light shader highp mat4 model_matrix = world_transform; - mat4 projection_matrix = scene_data.projection_matrix; - mat4 inv_projection_matrix = scene_data.inv_projection_matrix; + mat4 projection_matrix = scene_data_block.data.projection_matrix; + mat4 inv_projection_matrix = scene_data_block.data.inv_projection_matrix; vec3 normal = N; vec3 light = L; @@ -1525,32 +1706,32 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f #endif // !USE_VERTEX_LIGHTING vec4 fog_process(vec3 vertex) { - vec3 fog_color = scene_data.fog_light_color; + vec3 fog_color = scene_data_block.data.fog_light_color; #ifdef USE_RADIANCE_MAP /* - if (scene_data.fog_aerial_perspective > 0.0) { + if (scene_data_block.data.fog_aerial_perspective > 0.0) { vec3 sky_fog_color = vec3(0.0); - vec3 cube_view = scene_data.radiance_inverse_xform * vertex; + vec3 cube_view = scene_data_block.data.radiance_inverse_xform * vertex; // mip_level always reads from the second mipmap and higher so the fog is always slightly blurred - float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)); + float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data_block.data.z_near) / (scene_data_block.data.z_far - scene_data_block.data.z_near)); sky_fog_color = textureLod(radiance_map, cube_view, mip_level * RADIANCE_MAX_LOD).rgb; - fog_color = mix(fog_color, sky_fog_color, scene_data.fog_aerial_perspective); + fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective); } */ #endif #ifndef DISABLE_LIGHT_DIRECTIONAL - if (scene_data.fog_sun_scatter > 0.001) { + if (scene_data_block.data.fog_sun_scatter > 0.001) { vec4 sun_scatter = vec4(0.0); float sun_total = 0.0; vec3 view = normalize(vertex); - for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + for (uint i = uint(0); i < scene_data_block.data.directional_light_count; i++) { vec3 light_color = directional_lights[i].color * directional_lights[i].energy; float light_amount = pow(max(dot(view, directional_lights[i].direction), 0.0), 8.0); - fog_color += light_color * light_amount * scene_data.fog_sun_scatter; + fog_color += light_color * light_amount * scene_data_block.data.fog_sun_scatter; } } #endif // !DISABLE_LIGHT_DIRECTIONAL @@ -1558,18 +1739,18 @@ vec4 fog_process(vec3 vertex) { float fog_amount = 0.0; #ifdef USE_DEPTH_FOG - float fog_z = smoothstep(scene_data.fog_depth_begin, scene_data.fog_depth_end, length(vertex)); - fog_amount = pow(fog_z, scene_data.fog_depth_curve) * scene_data.fog_density; + float fog_z = smoothstep(scene_data_block.data.fog_depth_begin, scene_data_block.data.fog_depth_end, length(vertex)); + fog_amount = pow(fog_z, scene_data_block.data.fog_depth_curve) * scene_data_block.data.fog_density; #else - fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density)); + fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data_block.data.fog_density)); #endif // USE_DEPTH_FOG - if (abs(scene_data.fog_height_density) >= 0.0001) { - float y = (scene_data.inv_view_matrix * vec4(vertex, 1.0)).y; + if (abs(scene_data_block.data.fog_height_density) >= 0.0001) { + float y = (scene_data_block.data.inv_view_matrix * vec4(vertex, 1.0)).y; - float y_dist = y - scene_data.fog_height; + float y_dist = y - scene_data_block.data.fog_height; - float vfog_amount = 1.0 - exp(min(0.0, y_dist * scene_data.fog_height_density)); + float vfog_amount = 1.0 - exp(min(0.0, y_dist * scene_data_block.data.fog_height_density)); fog_amount = max(vfog_amount, fog_amount); } @@ -1723,20 +1904,27 @@ vec4 textureArray_bicubic(sampler2DArray tex, vec3 uv, vec2 texture_size) { (g1(fuv.y) * (g0x * texture(tex, vec3(p2, uv.z)) + g1x * texture(tex, vec3(p3, uv.z)))); } #endif //LIGHTMAP_BICUBIC_FILTER +#endif // RENDER_MOTION_VECTORS void main() { +#ifndef RENDER_MOTION_VECTORS + // #if defined(RENDER_MOTION_VECTORS) + // // Not a real output in the motion vector pass. + // vec4 frag_color; + // #endif + //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; #ifdef USE_MULTIVIEW - vec3 eye_offset = multiview_data.eye_offset[ViewIndex].xyz; + vec3 eye_offset = multiview_data_block.data.eye_offset[ViewIndex].xyz; vec3 view = -normalize(vertex_interp - eye_offset); - mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; - mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; + mat4 projection_matrix = multiview_data_block.data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data_block.data.inv_projection_matrix_view[ViewIndex]; #else vec3 eye_offset = vec3(0.0, 0.0, 0.0); vec3 view = -normalize(vertex_interp); - mat4 projection_matrix = scene_data.projection_matrix; - mat4 inv_projection_matrix = scene_data.inv_projection_matrix; + mat4 projection_matrix = scene_data_block.data.projection_matrix; + mat4 inv_projection_matrix = scene_data_block.data.inv_projection_matrix; #endif highp mat4 model_matrix = world_transform; vec3 albedo = vec3(1.0); @@ -1810,7 +1998,7 @@ void main() { float normal_map_depth = 1.0; - vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size; + vec2 screen_uv = gl_FragCoord.xy * scene_data_block.data.screen_pixel_size; float sss_strength = 0.0; @@ -1903,7 +2091,7 @@ void main() { // fog must be processed as early as possible and then packed. // to maximize VGPR usage - if (scene_data.fog_enabled) { + if (scene_data_block.data.fog_enabled) { fog = fog_process(vertex); } #endif // !DISABLE_FOG @@ -1932,7 +2120,7 @@ void main() { vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); #ifdef USE_RADIANCE_MAP - if (scene_data.use_reflection_cubemap) { + if (scene_data_block.data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; @@ -1945,11 +2133,11 @@ void main() { #endif ref_vec = mix(ref_vec, normal, roughness * roughness); float horizon = min(1.0 + dot(ref_vec, normal), 1.0); - ref_vec = scene_data.radiance_inverse_xform * ref_vec; + ref_vec = scene_data_block.data.radiance_inverse_xform * ref_vec; specular_light = textureLod(radiance_map, ref_vec, sqrt(roughness) * RADIANCE_MAX_LOD).rgb; specular_light = srgb_to_linear(specular_light); specular_light *= horizon * horizon; - specular_light *= scene_data.ambient_light_color_energy.a; + specular_light *= scene_data_block.data.ambient_light_color_energy.a; } #endif // USE_RADIANCE_MAP @@ -1985,21 +2173,21 @@ void main() { #ifndef USE_LIGHTMAP //lightmap overrides everything - if (scene_data.use_ambient_light) { - ambient_light = scene_data.ambient_light_color_energy.rgb; + if (scene_data_block.data.use_ambient_light) { + ambient_light = scene_data_block.data.ambient_light_color_energy.rgb; #ifdef USE_RADIANCE_MAP - if (scene_data.use_ambient_cubemap) { - vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; + if (scene_data_block.data.use_ambient_cubemap) { + vec3 ambient_dir = scene_data_block.data.radiance_inverse_xform * normal; vec3 cubemap_ambient = textureLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).rgb; cubemap_ambient = srgb_to_linear(cubemap_ambient); - ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); + ambient_light = mix(ambient_light, cubemap_ambient * scene_data_block.data.ambient_light_color_energy.a, scene_data_block.data.ambient_color_sky_mix); } #endif // USE_RADIANCE_MAP #ifndef DISABLE_REFLECTION_PROBE if (ambient_accum.a > 0.0) { - ambient_light = mix(ambient_light, (ambient_accum.rgb / ambient_accum.a) * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); + ambient_light = mix(ambient_light, (ambient_accum.rgb / ambient_accum.a) * scene_data_block.data.ambient_light_color_energy.a, scene_data_block.data.ambient_color_sky_mix); } #endif // DISABLE_REFLECTION_PROBE } @@ -2012,7 +2200,7 @@ void main() { #ifndef DISABLE_LIGHTMAP #ifdef USE_LIGHTMAP_CAPTURE { - vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal; + vec3 wnormal = mat3(scene_data_block.data.inv_view_matrix) * normal; const float c1 = 0.429043; const float c2 = 0.511664; const float c3 = 0.743125; @@ -2028,7 +2216,7 @@ void main() { 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * - scene_data.emissive_exposure_normalization; + scene_data_block.data.emissive_exposure_normalization; } #else #ifdef USE_LIGHTMAP @@ -2106,7 +2294,7 @@ void main() { #else #ifndef DISABLE_LIGHT_DIRECTIONAL - for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + for (uint i = uint(0); i < scene_data_block.data.directional_light_count; i++) { #if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) if (directional_lights[i].bake_mode == LIGHT_BAKE_STATIC) { continue; @@ -2209,7 +2397,7 @@ void main() { #ifdef MODE_RENDER_DEPTH #ifdef RENDER_SHADOWS_LINEAR // Linearize the depth buffer if rendering cubemap shadows. - gl_FragDepth = (scene_data.z_far - (length(vertex) + scene_data.shadow_bias)) / scene_data.z_far; + gl_FragDepth = (scene_data_block.data.z_far - (length(vertex) + scene_data_block.data.shadow_bias)) / scene_data_block.data.z_far; #endif // Nothing happens, so a tree-ssa optimizer will result in no fragment shader :) @@ -2493,7 +2681,7 @@ void main() { frag_color.rgb += additive_light_color; #endif // USE_ADDITIVE_LIGHTING - frag_color.rgb *= scene_data.luminance_multiplier; + frag_color.rgb *= scene_data_block.data.luminance_multiplier; #endif // !RENDER_MATERIAL #endif // !MODE_RENDER_DEPTH @@ -2501,4 +2689,11 @@ void main() { #ifdef PREMUL_ALPHA_USED frag_color.rgb *= premul_alpha; #endif // PREMUL_ALPHA_USED +#endif // !RENDER_MOTION_VECTORS + +#if defined(RENDER_MOTION_VECTORS) + vec3 ndc = clip_position.xyz / clip_position.w; + vec3 prev_ndc = prev_clip_position.xyz / prev_clip_position.w; + motion_vector = vec4(ndc - prev_ndc, 0.0); +#endif // RENDER_MOTION_VECTORS } diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 209b7dd7d266..a42917f6b100 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -108,6 +108,7 @@ Config::Config() { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); // sanity clamp buffer size to 16K..1MB diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index d60f295d6629..7888548f48cf 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -62,6 +62,7 @@ class Config { GLint max_texture_image_units = 0; GLint max_texture_size = 0; GLint max_viewport_size[2] = { 0, 0 }; + GLint max_vertex_attribs = 0; GLint64 max_uniform_buffer_size = 0; int64_t max_renderable_elements = 0; @@ -108,7 +109,13 @@ class Config { PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr; PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr; PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr; -#endif + +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample +#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT +#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR +#define glEGLImageTargetTexture2DOES GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES +#endif // ANDROID_ENABLED static Config *get_singleton() { return singleton; }; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 5dca149d99a0..63fffa16ca78 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1205,13 +1205,13 @@ MaterialStorage::MaterialStorage() { actions.renames["MODEL_MATRIX"] = "model_matrix"; actions.renames["MODEL_NORMAL_MATRIX"] = "model_normal_matrix"; - actions.renames["VIEW_MATRIX"] = "scene_data.view_matrix"; - actions.renames["INV_VIEW_MATRIX"] = "scene_data.inv_view_matrix"; + actions.renames["VIEW_MATRIX"] = "scene_data_block.data.view_matrix"; + actions.renames["INV_VIEW_MATRIX"] = "scene_data_block.data.inv_view_matrix"; actions.renames["PROJECTION_MATRIX"] = "projection_matrix"; actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; actions.renames["MODELVIEW_MATRIX"] = "modelview"; actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal"; - actions.renames["MAIN_CAM_INV_VIEW_MATRIX"] = "scene_data.main_cam_inv_view_matrix"; + actions.renames["MAIN_CAM_INV_VIEW_MATRIX"] = "scene_data_block.data.main_cam_inv_view_matrix"; actions.renames["VERTEX"] = "vertex"; actions.renames["NORMAL"] = "normal"; @@ -1232,12 +1232,12 @@ MaterialStorage::MaterialStorage() { //builtins - actions.renames["TIME"] = "scene_data.time"; - actions.renames["EXPOSURE"] = "(1.0 / scene_data.emissive_exposure_normalization)"; + actions.renames["TIME"] = "scene_data_block.data.time"; + actions.renames["EXPOSURE"] = "(1.0 / scene_data_block.data.emissive_exposure_normalization)"; actions.renames["PI"] = _MKSTR(Math_PI); actions.renames["TAU"] = _MKSTR(Math_TAU); actions.renames["E"] = _MKSTR(Math_E); - actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size"; + actions.renames["VIEWPORT_SIZE"] = "scene_data_block.data.viewport_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; actions.renames["FRONT_FACING"] = "gl_FrontFacing"; @@ -1281,10 +1281,10 @@ MaterialStorage::MaterialStorage() { actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz"; - actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; - actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz"; - actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers"; - actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * model_matrix)[3].xyz"; + actions.renames["CAMERA_POSITION_WORLD"] = "scene_data_block.data.inv_view_matrix[3].xyz"; + actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data_block.data.inv_view_matrix[2].xyz"; + actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data_block.data.camera_visible_layers"; + actions.renames["NODE_POSITION_VIEW"] = "(scene_data_block.data.view_matrix * model_matrix)[3].xyz"; actions.renames["VIEW_INDEX"] = "ViewIndex"; actions.renames["VIEW_MONO_LEFT"] = "uint(0)"; diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 505855465907..a5a0fd8ad92f 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -853,7 +853,7 @@ void MeshStorage::mesh_clear(RID p_mesh) { } } -void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis) { +void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_uses_motion_vectors, MeshInstance::Surface *mis, int p_current_vertex_buffer, int p_prev_vertex_buffer) { Mesh::Surface::Attrib attribs[RS::ARRAY_MAX]; int position_stride = 0; // Vertex position only. @@ -1005,7 +1005,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V if (i <= RS::ARRAY_TANGENT) { attribs[i].stride = (i == RS::ARRAY_VERTEX) ? position_stride : normal_tangent_stride; if (mis) { - glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffers[p_current_vertex_buffer]); } else { glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); } @@ -1025,12 +1025,28 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V glEnableVertexAttribArray(i); } + if (p_uses_motion_vectors) { + for (int i = 0; i < RS::ARRAY_TANGENT; i++) { + if (mis) { + glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffers[mis->prev_vertex_buffer]); + } else { + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); + } + + glVertexAttribPointer(i + 16, attribs[i].size, attribs[i].type, attribs[i].normalized, attribs[i].stride, CAST_INT_TO_UCHAR_PTR(attribs[i].offset)); + glEnableVertexAttribArray(i + 16); + } + } + // Do not bind index here as we want to switch between index buffers for LOD glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); v.input_mask = p_input_mask; + v.uses_motion_vectors = p_uses_motion_vectors; + v.current_vertex_buffer = p_current_vertex_buffer; + v.prev_vertex_buffer = p_prev_vertex_buffer; } /* MESH INSTANCE API */ @@ -1092,16 +1108,18 @@ void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { memfree(mi->surfaces[i].versions); } - if (mi->surfaces[i].vertex_buffers[0] != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[0]); - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[1]); - mi->surfaces[i].vertex_buffers[0] = 0; - mi->surfaces[i].vertex_buffers[1] = 0; + if (mi->surfaces[i].blend_shape_vertex_buffers[0] != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].blend_shape_vertex_buffers[0]); + GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].blend_shape_vertex_buffers[1]); + mi->surfaces[i].blend_shape_vertex_buffers[0] = 0; + mi->surfaces[i].blend_shape_vertex_buffers[1] = 0; } - if (mi->surfaces[i].vertex_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffer); - mi->surfaces[i].vertex_buffer = 0; + for (int j = 0; j < 2; j++) { + if (mi->surfaces[i].vertex_buffers[j] != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[j]); + mi->surfaces[i].vertex_buffers[j] = 0; + } } } mi->surfaces.clear(); @@ -1140,16 +1158,17 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3 int buffer_size = s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count; - // Buffer to be used for rendering. Final output of skeleton and blend shapes. - glGenBuffers(1, &s.vertex_buffer); - glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffer, buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance vertex buffer"); + // First buffer to be used for rendering. Final output of skeleton and blend shapes. + // If motion vectors are enabled, a second buffer will be created on demand, and they'll be swapped every frame. + glGenBuffers(1, &s.vertex_buffers[0]); + glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[0]); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffers[0], buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance vertex buffer"); if (mesh->blend_shape_count > 0) { // Ping-Pong buffers for processing blendshapes. - glGenBuffers(2, s.vertex_buffers); + glGenBuffers(2, s.blend_shape_vertex_buffers); for (uint32_t i = 0; i < 2; i++) { - glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[i]); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffers[i], buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance process buffer[" + itos(i) + "]"); + glBindBuffer(GL_ARRAY_BUFFER, s.blend_shape_vertex_buffers[i]); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.blend_shape_vertex_buffers[i], buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance process buffer[" + itos(i) + "]"); } } glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind @@ -1186,7 +1205,7 @@ void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, c } void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) { - glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]); + glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].blend_shape_vertex_buffers[0]); if ((p_mi->surfaces[p_surface].format_cache & (1ULL << RS::ARRAY_VERTEX))) { glEnableVertexAttribArray(RS::ARRAY_VERTEX); @@ -1228,7 +1247,7 @@ void MeshStorage::_compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_UNSIGNED_SHORT, GL_TRUE, skin_stride, CAST_INT_TO_UCHAR_PTR(4 * sizeof(uint16_t))); } - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_mi->surfaces[p_surface].vertex_buffer); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_mi->surfaces[p_surface].vertex_buffers[p_mi->surfaces[p_surface].current_vertex_buffer]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, p_sk->transforms_texture); @@ -1255,6 +1274,36 @@ void MeshStorage::update_mesh_instances() { while (dirty_mesh_instance_arrays.first()) { MeshInstance *mi = dirty_mesh_instance_arrays.first()->self(); + bool uses_motion_vectors = GLES3::TextureStorage::get_singleton()->get_num_render_targets_with_motion_vectors() > 0; + int frame = RSG::rasterizer->get_frame_number(); + if (uses_motion_vectors) { + for (uint32_t i = 0; i < mi->surfaces.size(); i++) { + mi->surfaces[i].prev_vertex_buffer = mi->surfaces[i].current_vertex_buffer; + + if (frame - mi->surfaces[i].last_change == 1) { + // Previous buffer's data can only be one frame old to be able to use motion vectors. + uint32_t new_buffer_index = mi->surfaces[i].current_vertex_buffer ^ 1; + + if (mi->surfaces[i].vertex_buffers[new_buffer_index] == 0) { + // Create the new vertex buffer on demand where the result for the current frame will be stored. + GLuint new_vertex_buffer = 0; + GLES3::Mesh::Surface *surface = mi->mesh->surfaces[i]; + int buffer_size = mi->surfaces[i].vertex_stride_cache * surface->vertex_count; + glGenBuffers(1, &new_vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, new_vertex_buffer); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, new_vertex_buffer, buffer_size, nullptr, (surface->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Secondary mesh vertex buffer"); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + mi->surfaces[i].vertex_buffers[new_buffer_index] = new_vertex_buffer; + } + + mi->surfaces[i].current_vertex_buffer = new_buffer_index; + } + + mi->surfaces[i].last_change = frame; + } + } + Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); // Precompute base weight if using blend shapes. @@ -1266,7 +1315,7 @@ void MeshStorage::update_mesh_instances() { } for (uint32_t i = 0; i < mi->surfaces.size(); i++) { - if (mi->surfaces[i].vertex_buffer == 0) { + if (mi->surfaces[i].vertex_buffers[mi->surfaces[i].current_vertex_buffer] == 0) { continue; } @@ -1301,9 +1350,9 @@ void MeshStorage::update_mesh_instances() { GLuint vertex_array_gl = 0; uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). - mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); + mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, false, vertex_array_gl); glBindVertexArray(vertex_array_gl); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].blend_shape_vertex_buffers[0]); glBeginTransformFeedback(GL_POINTS); glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); glEndTransformFeedback(); @@ -1325,15 +1374,17 @@ void MeshStorage::update_mesh_instances() { skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_WEIGHT, weight, skeleton_shader.shader_version, variant, specialization); skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization); + // Ensure the skeleton shader outputs to the correct (current) VBO. + glBindVertexArray(mi->mesh->surfaces[i]->blend_shapes[bs].vertex_array); _blend_shape_bind_mesh_instance_buffer(mi, i); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[1]); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].blend_shape_vertex_buffers[1]); glBeginTransformFeedback(GL_POINTS); glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); glEndTransformFeedback(); - SWAP(mi->surfaces[i].vertex_buffers[0], mi->surfaces[i].vertex_buffers[1]); + SWAP(mi->surfaces[i].blend_shape_vertex_buffers[0], mi->surfaces[i].blend_shape_vertex_buffers[1]); } uint32_t bs = mi->mesh->blend_shape_count - 1; @@ -1369,7 +1420,7 @@ void MeshStorage::update_mesh_instances() { can_use_skeleton = false; } else { // Do last blendshape by itself and prepare vertex data for use by the renderer. - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffer); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[mi->surfaces[i].current_vertex_buffer]); glBeginTransformFeedback(GL_POINTS); glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count); @@ -1415,7 +1466,7 @@ void MeshStorage::update_mesh_instances() { GLuint vertex_array_gl = 0; uint64_t mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_VERTEX; uint64_t format = mi->mesh->surfaces[i]->format & mask; // Format should only have vertex, normal, tangent (as necessary). - mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl); + mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, false, vertex_array_gl); glBindVertexArray(vertex_array_gl); _compute_skeleton(mi, sk, i); } @@ -1459,9 +1510,11 @@ void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS: return; } - if (multimesh->buffer) { - GLES3::Utilities::get_singleton()->buffer_free_data(multimesh->buffer); - multimesh->buffer = 0; + for (int i = 0; i < 2; i++) { + if (multimesh->buffer[i] != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(multimesh->buffer[i]); + multimesh->buffer[i] = 0; + } } if (multimesh->data_cache_dirty_regions) { @@ -1489,9 +1542,9 @@ void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS: multimesh->visible_instances = MIN(multimesh->visible_instances, multimesh->instances); if (multimesh->instances) { - glGenBuffers(1, &multimesh->buffer); - glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); - GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float), nullptr, GL_STATIC_DRAW, "MultiMesh buffer"); + glGenBuffers(1, &multimesh->buffer[0]); + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer[0]); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, multimesh->buffer[0], multimesh->instances * multimesh->stride_cache * sizeof(float), nullptr, GL_STATIC_DRAW, "MultiMesh buffer"); glBindBuffer(GL_ARRAY_BUFFER, 0); } @@ -1522,7 +1575,7 @@ void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) { } else if (multimesh->instances) { // Need to re-create AABB. Unfortunately, calling this has a penalty. if (multimesh->buffer_set) { - Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); + Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer[multimesh->current_buffer], multimesh->instances * multimesh->stride_cache * sizeof(float)); const uint8_t *r = buffer.ptr(); const float *data = (const float *)r; _multimesh_re_create_aabb(multimesh, data, multimesh->instances); @@ -1546,7 +1599,7 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { float *w = multimesh->data_cache.ptrw(); if (multimesh->buffer_set) { - Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); + Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer[multimesh->current_buffer], multimesh->instances * multimesh->stride_cache * sizeof(float)); { const uint8_t *r = buffer.ptr(); @@ -1889,6 +1942,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector &p_ MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); + // Assign data to previous buffer if motion vectors are used, that data will be made current in _update_dirty_multimeshes(). + bool uses_motion_vectors = GLES3::TextureStorage::get_singleton()->get_num_render_targets_with_motion_vectors() > 0; + int buffer_index = uses_motion_vectors ? multimesh->prev_buffer : multimesh->current_buffer; + if (multimesh->uses_colors || multimesh->uses_custom_data) { // Color and custom need to be packed so copy buffer to data_cache and pack. @@ -1934,7 +1991,7 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector &p_ multimesh->data_cache.resize(multimesh->instances * (int)multimesh->stride_cache); const float *r = multimesh->data_cache.ptr(); - glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer[buffer_index]); glBufferData(GL_ARRAY_BUFFER, multimesh->data_cache.size() * sizeof(float), r, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -1947,7 +2004,7 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector &p_ // Only Transform is being used, so we can upload directly. ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); const float *r = p_buffer.ptr(); - glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer[buffer_index]); glBufferData(GL_ARRAY_BUFFER, p_buffer.size() * sizeof(float), r, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } @@ -1978,14 +2035,14 @@ Vector MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector()); Vector ret; - if (multimesh->buffer == 0 || multimesh->instances == 0) { + if (multimesh->buffer[multimesh->current_buffer] == 0 || multimesh->instances == 0) { return Vector(); } else if (multimesh->data_cache.size()) { ret = multimesh->data_cache; } else { // Buffer not cached, so fetch from GPU memory. This can be a stalling operation, avoid whenever possible. - Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); + Vector buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer[multimesh->current_buffer], multimesh->instances * multimesh->stride_cache * sizeof(float)); ret.resize(multimesh->instances * multimesh->stride_cache); { float *w = ret.ptrw(); @@ -2085,60 +2142,114 @@ void MeshStorage::_update_dirty_multimeshes() { while (multimesh_dirty_list) { MultiMesh *multimesh = multimesh_dirty_list; - if (multimesh->data_cache.size()) { //may have been cleared, so only process if it exists - const float *data = multimesh->data_cache.ptr(); + bool uses_motion_vectors = GLES3::TextureStorage::get_singleton()->get_num_render_targets_with_motion_vectors() > 0; + if (uses_motion_vectors) { + multimesh->prev_buffer = multimesh->current_buffer; + uint32_t new_buffer_index = multimesh->current_buffer ^ 1; + + // Generate secondary buffer if it doesn't exist. + if (multimesh->buffer[new_buffer_index] == 0 && multimesh->instances) { + GLuint new_buffer = 0; + glGenBuffers(1, &new_buffer); + glBindBuffer(GL_ARRAY_BUFFER, new_buffer); + GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, new_buffer, multimesh->instances * multimesh->stride_cache * sizeof(float), nullptr, GL_STATIC_DRAW, "MultiMesh secondary buffer"); + glBindBuffer(GL_ARRAY_BUFFER, 0); + multimesh->buffer[new_buffer_index] = new_buffer; + } - uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; + multimesh->current_buffer = new_buffer_index; + multimesh->last_change = RSG::rasterizer->get_frame_number(); + } - if (multimesh->data_cache_used_dirty_regions) { - uint32_t data_cache_dirty_region_count = Math::division_round_up(multimesh->instances, (int)MULTIMESH_DIRTY_REGION_SIZE); - uint32_t visible_region_count = visible_instances == 0 ? 0 : Math::division_round_up(visible_instances, (uint32_t)MULTIMESH_DIRTY_REGION_SIZE); + _update_dirty_multimesh(multimesh, uses_motion_vectors); - GLint region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); + multimesh_dirty_list = multimesh->dirty_list; - if (multimesh->data_cache_used_dirty_regions > 32 || multimesh->data_cache_used_dirty_regions > visible_region_count / 2) { - // If there too many dirty regions, or represent the majority of regions, just copy all, else transfer cost piles up too much - glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, MIN(visible_region_count * region_size, multimesh->instances * multimesh->stride_cache * sizeof(float)), data); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } else { - // Not that many regions? update them all - // TODO: profile the performance cost on low end - glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); - for (uint32_t i = 0; i < visible_region_count; i++) { - if (multimesh->data_cache_dirty_regions[i]) { - GLint offset = i * region_size; - GLint size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); - uint32_t region_start_index = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; - glBufferSubData(GL_ARRAY_BUFFER, offset, MIN(region_size, size - offset), &data[region_start_index]); - } - } - glBindBuffer(GL_ARRAY_BUFFER, 0); - } + multimesh->dirty_list = nullptr; + multimesh->dirty = false; + } + + multimesh_dirty_list = nullptr; +} + +void MeshStorage::_update_dirty_multimesh(MultiMesh *p_multimesh, bool p_uses_motion_vectors) { + if (p_multimesh->data_cache.size()) { // May have been cleared, so only process if it exists. + const float *data = p_multimesh->data_cache.ptr(); - for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { - multimesh->data_cache_dirty_regions[i] = false; + uint32_t visible_instances = p_multimesh->visible_instances >= 0 ? p_multimesh->visible_instances : p_multimesh->instances; + + if (p_multimesh->data_cache_used_dirty_regions) { + uint32_t data_cache_dirty_region_count = Math::division_round_up(p_multimesh->instances, (int)MULTIMESH_DIRTY_REGION_SIZE); + uint32_t visible_region_count = visible_instances == 0 ? 0 : Math::division_round_up(visible_instances, (uint32_t)MULTIMESH_DIRTY_REGION_SIZE); + + GLint region_size = p_multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); + + if ((p_multimesh->data_cache_used_dirty_regions > 32 || p_multimesh->data_cache_used_dirty_regions > visible_region_count / 2) || p_uses_motion_vectors) { + // If there are too many dirty regions, the dirty regions represent the majority of visible regions, or motion vectors are used: + // Just copy all, else transfer cost piles up too much. + glBindBuffer(GL_ARRAY_BUFFER, p_multimesh->buffer[p_multimesh->current_buffer]); + glBufferSubData(GL_ARRAY_BUFFER, 0, MIN(visible_region_count * region_size, p_multimesh->instances * p_multimesh->stride_cache * sizeof(float)), data); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } else { + // Not that many regions? Update them all. + // TODO: profile the performance cost on low end + glBindBuffer(GL_ARRAY_BUFFER, p_multimesh->buffer[p_multimesh->current_buffer]); + for (uint32_t i = 0; i < visible_region_count; i++) { + if (p_multimesh->data_cache_dirty_regions[i]) { + GLint offset = i * region_size; + GLint size = p_multimesh->stride_cache * (uint32_t)p_multimesh->instances * (uint32_t)sizeof(float); + uint32_t region_start_index = p_multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; + glBufferSubData(GL_ARRAY_BUFFER, offset, MIN(region_size, size - offset), &data[region_start_index]); + } } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } - multimesh->data_cache_used_dirty_regions = 0; + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + p_multimesh->data_cache_dirty_regions[i] = false; } - if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) { - multimesh->aabb_dirty = false; - if (multimesh->custom_aabb == AABB()) { - _multimesh_re_create_aabb(multimesh, data, visible_instances); - multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); - } + p_multimesh->data_cache_used_dirty_regions = 0; + } + + if (p_multimesh->aabb_dirty && p_multimesh->mesh.is_valid()) { + p_multimesh->aabb_dirty = false; + if (p_multimesh->custom_aabb == AABB()) { + _multimesh_re_create_aabb(p_multimesh, data, visible_instances); + p_multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } } + } +} - multimesh_dirty_list = multimesh->dirty_list; +void GLES3::MeshStorage::multimesh_vertex_attrib_setup(GLuint p_instance_buffer, uint32_t p_stride, bool p_uses_format_2d, bool p_has_color_or_custom_data, int p_attrib_base_index) { + glBindBuffer(GL_ARRAY_BUFFER, p_instance_buffer); - multimesh->dirty_list = nullptr; - multimesh->dirty = false; + glEnableVertexAttribArray(p_attrib_base_index + 0); + glVertexAttribPointer(p_attrib_base_index + 0, 4, GL_FLOAT, GL_FALSE, p_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribDivisor(p_attrib_base_index + 0, 1); + glEnableVertexAttribArray(p_attrib_base_index + 1); + glVertexAttribPointer(p_attrib_base_index + 1, 4, GL_FLOAT, GL_FALSE, p_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4)); + glVertexAttribDivisor(p_attrib_base_index + 1, 1); + if (!p_uses_format_2d) { + glEnableVertexAttribArray(p_attrib_base_index + 2); + glVertexAttribPointer(p_attrib_base_index + 2, 4, GL_FLOAT, GL_FALSE, p_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(sizeof(float) * 8)); + glVertexAttribDivisor(p_attrib_base_index + 2, 1); } - multimesh_dirty_list = nullptr; + if (p_has_color_or_custom_data) { + uint32_t color_custom_offset = p_uses_format_2d ? 8 : 12; + glEnableVertexAttribArray(p_attrib_base_index + 3); + glVertexAttribIPointer(p_attrib_base_index + 3, 4, GL_UNSIGNED_INT, p_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(color_custom_offset * sizeof(float))); + glVertexAttribDivisor(p_attrib_base_index + 3, 1); + } else { + // Set all default instance color and custom data values to 1.0 or 0.0 using a compressed format. + uint16_t zero = Math::make_half_float(0.0f); + uint16_t one = Math::make_half_float(1.0f); + GLuint default_color = (uint32_t(one) << 16) | one; + GLuint default_custom = (uint32_t(zero) << 16) | zero; + glVertexAttribI4ui(p_attrib_base_index + 3, default_color, default_color, default_custom, default_custom); + } } /* SKELETON API */ diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 31858cd372c6..cc2fdec728d3 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -37,6 +37,7 @@ #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "drivers/gles3/shaders/skeleton.glsl.gen.h" +#include "servers/rendering/rendering_server_globals.h" #include "servers/rendering/storage/mesh_storage.h" #include "servers/rendering/storage/utilities.h" @@ -71,6 +72,9 @@ struct Mesh { // Cache vertex arrays so they can be created struct Version { uint32_t input_mask = 0; + bool uses_motion_vectors = false; + uint32_t current_vertex_buffer = 0; + uint32_t prev_vertex_buffer = 0; GLuint vertex_array = 0; Attrib attribs[RS::ARRAY_MAX]; @@ -153,9 +157,9 @@ struct MeshInstance { Mesh *mesh = nullptr; RID skeleton; struct Surface { - GLuint vertex_buffers[2] = { 0, 0 }; + GLuint blend_shape_vertex_buffers[2] = { 0, 0 }; GLuint vertex_arrays[2] = { 0, 0 }; - GLuint vertex_buffer = 0; + GLuint vertex_buffers[2] = { 0, 0 }; int vertex_stride_cache = 0; int vertex_size_cache = 0; int vertex_normal_offset_cache = 0; @@ -164,6 +168,11 @@ struct MeshInstance { Mesh::Surface::Version *versions = nullptr; //allocated on demand uint32_t version_count = 0; + + bool uses_motion_vectors = false; + int current_vertex_buffer = 0; + int prev_vertex_buffer = 0; + uint64_t last_change = 0; }; LocalVector surfaces; LocalVector blend_weights; @@ -200,7 +209,10 @@ struct MultiMesh { bool *data_cache_dirty_regions = nullptr; uint32_t data_cache_used_dirty_regions = 0; - GLuint buffer = 0; + GLuint buffer[2] = { 0, 0 }; + int current_buffer = 0; + int prev_buffer = 0; + uint64_t last_change = 0; bool dirty = false; MultiMesh *dirty_list = nullptr; @@ -240,7 +252,7 @@ class MeshStorage : public RendererMeshStorage { mutable RID_Owner mesh_owner; - void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr); + void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_uses_motion_vectors, MeshInstance::Surface *mis = nullptr, int p_current_vertex_buffer = 0, int p_prev_vertex_buffer = 0); /* Mesh Instance API */ @@ -413,7 +425,7 @@ class MeshStorage : public RendererMeshStorage { } // Use this to cache Vertex Array Objects so they are only generated once - _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, GLuint &r_vertex_array_gl) { + _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, bool p_uses_motion_vectors, GLuint &r_vertex_array_gl) { Mesh::Surface *s = reinterpret_cast(p_surface); s->version_lock.lock(); @@ -421,7 +433,7 @@ class MeshStorage : public RendererMeshStorage { // There will never be more than 3 or 4 versions, so iterating is the fastest way. for (uint32_t i = 0; i < s->version_count; i++) { - if (s->versions[i].input_mask != p_input_mask) { + if (s->versions[i].input_mask != p_input_mask || s->versions[i].uses_motion_vectors != p_uses_motion_vectors) { continue; } // We have this version, hooray. @@ -434,7 +446,7 @@ class MeshStorage : public RendererMeshStorage { s->version_count++; s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count); - _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask); + _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask, p_uses_motion_vectors); r_vertex_array_gl = s->versions[version].vertex_array; @@ -456,7 +468,7 @@ class MeshStorage : public RendererMeshStorage { // TODO: considering hashing versions with multimesh buffer RID. // Doing so would allow us to avoid specifying multimesh buffer pointers every frame and may improve performance. - _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint64_t p_input_mask, GLuint &r_vertex_array_gl) { + _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint64_t p_input_mask, bool p_uses_motion_vectors, GLuint &r_vertex_array_gl) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); ERR_FAIL_NULL(mi); Mesh *mesh = mi->mesh; @@ -465,14 +477,24 @@ class MeshStorage : public RendererMeshStorage { MeshInstance::Surface *mis = &mi->surfaces[p_surface_index]; Mesh::Surface *s = mesh->surfaces[p_surface_index]; + uint32_t current_buffer = mis->current_vertex_buffer; + + // Using the previous buffer is only allowed if the surface was updated this frame and motion vectors are required. + uint32_t previous_buffer = p_uses_motion_vectors && (RSG::rasterizer->get_frame_number() == mis->last_change) ? mis->prev_vertex_buffer : current_buffer; + s->version_lock.lock(); //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way for (uint32_t i = 0; i < mis->version_count; i++) { - if (mis->versions[i].input_mask != p_input_mask) { + if (mis->versions[i].input_mask != p_input_mask || mis->versions[i].uses_motion_vectors != p_uses_motion_vectors) { + continue; + } + + if (mis->versions[i].current_vertex_buffer != current_buffer || mis->versions[i].prev_vertex_buffer != previous_buffer) { continue; } + //we have this version, hooray r_vertex_array_gl = mis->versions[i].vertex_array; s->version_lock.unlock(); @@ -483,7 +505,7 @@ class MeshStorage : public RendererMeshStorage { mis->version_count++; mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count); - _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis); + _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, p_uses_motion_vectors, mis, current_buffer, previous_buffer); r_vertex_array_gl = mis->versions[version].vertex_array; @@ -525,6 +547,9 @@ class MeshStorage : public RendererMeshStorage { virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; void _update_dirty_multimeshes(); + void _update_dirty_multimesh(MultiMesh *p_multimesh, bool p_uses_motion_vectors); + + void multimesh_vertex_attrib_setup(GLuint p_instance_buffer, uint32_t p_stride, bool p_uses_format_2d, bool p_has_color_or_custom_data, int p_attrib_base_index); _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); @@ -551,7 +576,17 @@ class MeshStorage : public RendererMeshStorage { _FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - return multimesh->buffer; + return multimesh->buffer[multimesh->current_buffer]; + } + + _FORCE_INLINE_ GLuint multimesh_get_prev_gl_buffer(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->buffer[multimesh->prev_buffer]; + } + + _FORCE_INLINE_ uint64_t multimesh_get_last_change(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->last_change; } _FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const { diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index b60068128091..9de67e33b23b 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -1151,6 +1151,7 @@ void ParticlesStorage::update_particles() { } SWAP(particles->front_instance_buffer, particles->back_instance_buffer); + particles->last_change = RSG::rasterizer->get_frame_number(); // At the end of update, the back_buffer contains the most up-to-date-information to read from. diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 086f5f79361f..4bf17b193221 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -196,6 +196,8 @@ class ParticlesStorage : public RendererParticlesStorage { GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing. GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering. + uint64_t last_change = 0; + uint32_t instance_buffer_size_cache = 0; uint32_t instance_buffer_stride_cache = 0; uint32_t num_attrib_arrays_cache = 0; @@ -394,6 +396,20 @@ class ParticlesStorage : public RendererParticlesStorage { return particles->back_instance_buffer; } + _FORCE_INLINE_ GLuint particles_get_prev_gl_buffer(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_NULL_V(particles, 0); + + return particles->front_instance_buffer; + } + + _FORCE_INLINE_ uint64_t particles_get_last_change(RID p_particles) { + Particles *particles = particles_owner.get_or_null(p_particles); + ERR_FAIL_NULL_V(particles, 0); + + return particles->last_change; + } + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL_V(particles, 0); diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index c91547d2b166..f0b358bcd606 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -35,13 +35,6 @@ #include "texture_storage.h" #include "utilities.h" -#ifdef ANDROID_ENABLED -#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR -#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample -#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT -#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR -#endif // ANDROID_ENABLED - // Will only be defined if GLES 3.2 headers are included #ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 27ba89aa5fa0..1d8f9a653755 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -37,10 +37,6 @@ #include "config.h" #include "utilities.h" -#ifdef ANDROID_ENABLED -#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR -#endif - using namespace GLES3; TextureStorage *TextureStorage::singleton = nullptr; @@ -2346,6 +2342,16 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { return; } + for (KeyValue &E : rt->motion_vector_fbo_cache) { + glDeleteFramebuffers(1, &E.value); + } + rt->motion_vector_fbo_cache.clear(); + rt->motion_vector_fbo = 0; + + if (rt->motion_vector_texture.is_valid()) { + num_render_targets_with_motion_vectors--; + } + // Dispose of the cached fbo's and the allocated textures for (KeyValue &E : rt->overridden.fbo_cache) { glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr()); @@ -2575,6 +2581,119 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) { return rt->texture; } +void TextureStorage::render_target_set_motion_vectors_target(RID p_render_target, RID p_motion_vectors_texture, RID p_depth_texture, Size2i p_size, int p_view_count) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + + if (rt->motion_vector_texture.is_valid() && p_motion_vectors_texture.is_null()) { + num_render_targets_with_motion_vectors--; + } else if (rt->motion_vector_texture.is_null() && p_motion_vectors_texture.is_valid()) { + num_render_targets_with_motion_vectors++; + } + + rt->motion_vector_texture = p_motion_vectors_texture; + rt->motion_vector_depth_texture = p_depth_texture; + rt->motion_vector_target_size = p_size; + + if (p_motion_vectors_texture.is_null()) { + for (KeyValue &E : rt->motion_vector_fbo_cache) { + glDeleteFramebuffers(1, &E.value); + } + + rt->motion_vector_fbo_cache.clear(); + rt->motion_vector_fbo = 0; + + return; + } + + uint32_t hash_key = hash_murmur3_one_64(p_motion_vectors_texture.get_id()); + hash_key = hash_murmur3_one_64(p_depth_texture.get_id(), hash_key); + hash_key = hash_fmix32(hash_key); + + RBMap::Element *fbo; + if ((fbo = rt->motion_vector_fbo_cache.find(hash_key)) != nullptr) { + rt->motion_vector_fbo = fbo->get(); + return; + } + + GLuint new_motion_vector_fbo; + glGenFramebuffers(1, &new_motion_vector_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, new_motion_vector_fbo); + + GLuint texture_target = p_view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + GLuint motion_vector_texture_id = texture_get_texid(p_motion_vectors_texture); + glBindTexture(texture_target, motion_vector_texture_id); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +#ifndef IOS_ENABLED + if (p_view_count > 1) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, motion_vector_texture_id, 0, 0, p_view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, motion_vector_texture_id, 0); + } + + GLuint motion_vector_depth_texture_id = texture_get_texid(p_depth_texture); + glBindTexture(texture_target, motion_vector_texture_id); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +#ifndef IOS_ENABLED + if (p_view_count > 1) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, motion_vector_depth_texture_id, 0, 0, p_view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, motion_vector_depth_texture_id, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &new_motion_vector_fbo); + WARN_PRINT("Could not create motion vector render target, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + rt->motion_vector_fbo_cache.insert(hash_key, new_motion_vector_fbo); + rt->motion_vector_fbo = new_motion_vector_fbo; +} + +RID TextureStorage::render_target_get_motion_vectors_texture(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, RID()); + + return rt->motion_vector_texture; +} + +RID TextureStorage::render_target_get_motion_vectors_depth_texture(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, RID()); + + return rt->motion_vector_depth_texture; +} + +Size2i TextureStorage::render_target_get_motion_vectors_target_size(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, Size2i(0, 0)); + + return rt->motion_vector_target_size; +} + +int TextureStorage::get_num_render_targets_with_motion_vectors() const { + return num_render_targets_with_motion_vectors; +} + void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 2bf8546c0fc5..8b93e0a99ebc 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -348,6 +348,12 @@ struct RenderTarget { GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + GLuint motion_vector_fbo = 0; + RID motion_vector_texture; + RID motion_vector_depth_texture; + Size2i motion_vector_target_size = Size2i(0, 0); + RBMap motion_vector_fbo_cache; + bool hdr = false; // For Compatibility this effects both 2D and 3D rendering! GLuint color_internal_format = GL_RGBA8; GLuint color_format = GL_RGBA; @@ -465,6 +471,8 @@ class TextureStorage : public RendererTextureStorage { RID shader_version; } sdf_shader; + int num_render_targets_with_motion_vectors = 0; + public: static TextureStorage *get_singleton(); @@ -688,6 +696,12 @@ class TextureStorage : public RendererTextureStorage { virtual RID render_target_get_texture(RID p_render_target) override; + virtual void render_target_set_motion_vectors_target(RID p_render_target, RID p_motion_vectors_texture, RID p_depth_texture, Size2i p_size, int p_view_count) override; + virtual RID render_target_get_motion_vectors_texture(RID p_render_target) const override; + virtual RID render_target_get_motion_vectors_depth_texture(RID p_render_target) const override; + virtual Size2i render_target_get_motion_vectors_target_size(RID p_render_target) const override; + int get_num_render_targets_with_motion_vectors() const; + void bind_framebuffer(GLuint framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); } diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 95b537d1b453..7ab2e2718d67 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -62,6 +62,7 @@ class OpenXRExtensionWrapper { virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when we create our OpenXR session. virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when creating OpenXR swap chains. virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) { return p_next_pointer; } + virtual void *set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) { return p_next_pointer; }; virtual PackedStringArray get_suggested_tracker_names() { return PackedStringArray(); } diff --git a/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.cpp new file mode 100644 index 000000000000..b11bf18b1bc4 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.cpp @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* openxr_fb_space_warp_extension_wrapper.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#include "openxr_fb_space_warp_extension_wrapper.h" + +#include "../openxr_interface.h" +#include "platform_gl.h" + +OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::singleton = nullptr; + +OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::get_singleton() { + return singleton; +} + +OpenXRFbSpaceWarpExtensionWrapper::OpenXRFbSpaceWarpExtensionWrapper() { + singleton = this; +} + +OpenXRFbSpaceWarpExtensionWrapper::~OpenXRFbSpaceWarpExtensionWrapper() { + singleton = nullptr; +} + +HashMap OpenXRFbSpaceWarpExtensionWrapper::get_requested_extensions() { + HashMap request_extensions; + + request_extensions[XR_FB_SPACE_WARP_EXTENSION_NAME] = &available; + + return request_extensions; +} + +void *OpenXRFbSpaceWarpExtensionWrapper::set_system_properties_and_get_next_pointer(void *p_next_pointer) { + return &system_space_warp_properties; +} + +void *OpenXRFbSpaceWarpExtensionWrapper::set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) { + if (enabled) { + return &space_warp_info[p_view_index]; + } else { + return nullptr; + } +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_session_created(const XrSession p_instance) { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + if (!openxr_api) { + return; + } + + openxr_api->register_projection_views_extension(this); +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_session_destroyed() { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + if (!openxr_api) { + return; + } + + openxr_api->unregister_projection_views_extension(this); +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_state_ready() { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + + if (!openxr_api) { + return; + } + + int width = system_space_warp_properties.recommendedMotionVectorImageRectWidth; + int height = system_space_warp_properties.recommendedMotionVectorImageRectHeight; + int view_count = openxr_api->get_xr_interface()->get_view_count(); + + motion_vector_swapchain_info.create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, GL_RGBA16F, width, height, 1, view_count); + motion_vector_depth_swapchain_info.create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, GL_DEPTH24_STENCIL8, width, height, 1, view_count); +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_main_swapchains_created() { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + int view_count = openxr_api->get_xr_interface()->get_view_count(); + + XrCompositionLayerProjectionView *projection_views = openxr_api->get_projection_views(); + XrPosef identity_pose = { { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }; + + space_warp_info = memnew_arr(XrCompositionLayerSpaceWarpInfoFB, 2); + for (int i = 0; i < view_count; i++) { + space_warp_info[i].type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB; + + space_warp_info[i].next = nullptr; + + space_warp_info[i].layerFlags = 0; + + space_warp_info[i].motionVectorSubImage.swapchain = motion_vector_swapchain_info.get_swapchain(); + space_warp_info[i].motionVectorSubImage.imageRect.offset.x = 0; + space_warp_info[i].motionVectorSubImage.imageRect.offset.y = 0; + space_warp_info[i].motionVectorSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth; + space_warp_info[i].motionVectorSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight; + space_warp_info[i].motionVectorSubImage.imageArrayIndex = i; + + space_warp_info[i].appSpaceDeltaPose = identity_pose; + + space_warp_info[i].depthSubImage.swapchain = motion_vector_depth_swapchain_info.get_swapchain(); + space_warp_info[i].depthSubImage.imageRect.offset.x = 0; + space_warp_info[i].depthSubImage.imageRect.offset.y = 0; + space_warp_info[i].depthSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth; + space_warp_info[i].depthSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight; + space_warp_info[i].depthSubImage.imageArrayIndex = i; + + space_warp_info[i].minDepth = 0.0; + space_warp_info[i].maxDepth = 1.0; + + space_warp_info[i].farZ = openxr_api->get_render_state_z_near(); + space_warp_info[i].nearZ = openxr_api->get_render_state_z_far(); + + projection_views[i].next = &space_warp_info[i]; + } +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_pre_render() { + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + + if (!openxr_api) { + return; + } + + bool should_render = true; // Can ignore should_render. + motion_vector_swapchain_info.acquire(should_render); + motion_vector_depth_swapchain_info.acquire(should_render); + + openxr_api->set_motion_vector_texture(motion_vector_swapchain_info.get_image()); + openxr_api->set_motion_vector_depth_texture(motion_vector_depth_swapchain_info.get_image()); + + int target_width = system_space_warp_properties.recommendedMotionVectorImageRectWidth; + int target_height = system_space_warp_properties.recommendedMotionVectorImageRectHeight; + Size2i render_target_size = { target_width, target_height }; + + openxr_api->set_motion_vector_target_size(render_target_size); + + int view_count = openxr_api->get_xr_interface()->get_view_count(); + for (int i = 0; i < view_count; i++) { + space_warp_info[i].farZ = openxr_api->get_render_state_z_near(); + space_warp_info[i].nearZ = openxr_api->get_render_state_z_far(); + } +} + +void OpenXRFbSpaceWarpExtensionWrapper::on_post_draw_viewport(RID p_render_target) { + motion_vector_swapchain_info.release(); + motion_vector_depth_swapchain_info.release(); +} + +bool OpenXRFbSpaceWarpExtensionWrapper::is_available() { + return available; +} + +void OpenXRFbSpaceWarpExtensionWrapper::enable_space_warp(bool p_enable) { + if (enabled == p_enable) { + return; + } + + enabled = p_enable; +} + +void OpenXRFbSpaceWarpExtensionWrapper::_bind_methods() { + ClassDB::bind_method(D_METHOD("enable_space_warp"), &OpenXRFbSpaceWarpExtensionWrapper::enable_space_warp); +} diff --git a/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.h new file mode 100644 index 000000000000..b5d0361080b0 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_space_warp_extension_wrapper.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* openxr_fb_space_warp_extension_wrapper.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#ifndef OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H +#define OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H + +#include "openxr_extension_wrapper.h" + +#include "../openxr_api.h" + +class OpenXRFbSpaceWarpExtensionWrapper : public Object, public OpenXRExtensionWrapper { + GDCLASS(OpenXRFbSpaceWarpExtensionWrapper, Object); + +public: + static OpenXRFbSpaceWarpExtensionWrapper *get_singleton(); + + OpenXRFbSpaceWarpExtensionWrapper(); + virtual ~OpenXRFbSpaceWarpExtensionWrapper() override; + + virtual HashMap get_requested_extensions() override; + + void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override; + void *set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) override; + + void on_session_created(const XrSession p_instance) override; + void on_session_destroyed() override; + void on_state_ready() override; + void on_main_swapchains_created() override; + void on_pre_render() override; + void on_post_draw_viewport(RID p_render_target) override; + + bool is_available(); + + void enable_space_warp(bool p_enable); + +protected: + static void _bind_methods(); + +private: + static OpenXRFbSpaceWarpExtensionWrapper *singleton; + + bool available = false; + bool enabled = true; + + XrSystemSpaceWarpPropertiesFB system_space_warp_properties = { + XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB, // type + nullptr, // next + 0, // recommendedMotionVectorImageRectWidth + 0, // recommendedMotionVectorImageRectHeight + }; + + OpenXRAPI::OpenXRSwapChainInfo motion_vector_swapchain_info; + OpenXRAPI::OpenXRSwapChainInfo motion_vector_depth_swapchain_info; + + XrCompositionLayerSpaceWarpInfoFB *space_warp_info; +}; + +#endif // OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index c67be5a2b313..e4b3c4e5bf64 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -1916,6 +1916,9 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z } } + render_state.z_near = p_z_near; + render_state.z_far = p_z_far; + // now update our projection return graphics_extension->create_projection_fov(render_state.views[p_view].fov, p_z_near, p_z_far, p_camera_matrix); } @@ -2319,6 +2322,10 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) { return true; } +XrCompositionLayerProjectionView *OpenXRAPI::get_projection_views() { + return render_state.projection_views; +} + XrSwapchain OpenXRAPI::get_color_swapchain() { ERR_NOT_ON_RENDER_THREAD_V(XR_NULL_HANDLE); @@ -2342,6 +2349,30 @@ RID OpenXRAPI::get_depth_texture() { } } +void OpenXRAPI::set_motion_vector_texture(RID p_render_target) { + motion_vector_texture = p_render_target; +} + +RID OpenXRAPI::get_motion_vector_texture() { + return motion_vector_texture; +} + +void OpenXRAPI::set_motion_vector_depth_texture(RID p_render_target) { + motion_vector_depth_texture = p_render_target; +} + +RID OpenXRAPI::get_motion_vector_depth_texture() { + return motion_vector_depth_texture; +} + +void OpenXRAPI::set_motion_vector_target_size(Size2i p_target_size) { + motion_vector_target_size = p_target_size; +} + +Size2i OpenXRAPI::get_motion_vector_target_size() { + return motion_vector_target_size; +} + void OpenXRAPI::post_draw_viewport(RID p_render_target) { // Must be called from rendering thread! ERR_NOT_ON_RENDER_THREAD; @@ -2447,6 +2478,21 @@ void OpenXRAPI::end_frame() { render_state.view_count, // viewCount render_state.projection_views, // views }; + + // @todo Is this an OK time to do this for multithreading? + if (projection_views_extensions.size() > 0) { + for (uint32_t v = 0; v < render_state.view_count; v++) { + void *next_pointer = nullptr; + for (OpenXRExtensionWrapper *wrapper : projection_views_extensions) { + void *np = wrapper->set_projection_views_and_get_next_pointer(v, next_pointer); + if (np != nullptr) { + next_pointer = np; + } + } + render_state.projection_views[v].next = next_pointer; + } + } + ordered_layers_list.push_back({ (const XrCompositionLayerBaseHeader *)&projection_layer, 0 }); // Sort our layers. @@ -3497,6 +3543,14 @@ void OpenXRAPI::unregister_composition_layer_provider(OpenXRCompositionLayerProv composition_layer_providers.erase(provider); } +void OpenXRAPI::register_projection_views_extension(OpenXRExtensionWrapper *p_extension) { + projection_views_extensions.append(p_extension); +} + +void OpenXRAPI::unregister_projection_views_extension(OpenXRExtensionWrapper *p_extension) { + projection_views_extensions.erase(p_extension); +} + const XrEnvironmentBlendMode *OpenXRAPI::get_supported_environment_blend_modes(uint32_t &count) { count = num_supported_environment_blend_modes; return supported_environment_blend_modes; diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 0d1e4eb414ac..a68cd088daa0 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -102,6 +102,9 @@ class OpenXRAPI { // composition layer providers Vector composition_layer_providers; + // projection views extensions + Vector projection_views_extensions; + // view configuration uint32_t num_view_configuration_types = 0; XrViewConfigurationType *supported_view_configuration_types = nullptr; @@ -153,7 +156,6 @@ class OpenXRAPI { enum OpenXRSwapChainTypes { OPENXR_SWAPCHAIN_COLOR, OPENXR_SWAPCHAIN_DEPTH, - // OPENXR_SWAPCHAIN_VELOCITY, OPENXR_SWAPCHAIN_MAX }; @@ -165,6 +167,10 @@ class OpenXRAPI { XrSpace view_space = XR_NULL_HANDLE; XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE; + RID motion_vector_texture; + RID motion_vector_depth_texture; + Size2i motion_vector_target_size; + // When LOCAL_FLOOR isn't supported, we use an approach based on the example code in the // OpenXR spec in order to emulate it. // See: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_local_floor @@ -345,6 +351,9 @@ class OpenXRAPI { bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled. bool view_pose_valid = false; + double z_near = 0.0; + double z_far = 0.0; + Size2i main_swapchain_size; OpenXRSwapChainInfo main_swapchains[OPENXR_SWAPCHAIN_MAX]; } render_state; @@ -474,9 +483,16 @@ class OpenXRAPI { void pre_render(); bool pre_draw_viewport(RID p_render_target); + XrCompositionLayerProjectionView *get_projection_views(); XrSwapchain get_color_swapchain(); RID get_color_texture(); RID get_depth_texture(); + void set_motion_vector_texture(RID p_render_target); + RID get_motion_vector_texture(); + void set_motion_vector_depth_texture(RID p_render_target); + RID get_motion_vector_depth_texture(); + void set_motion_vector_target_size(Size2i p_target_size); + Size2i get_motion_vector_target_size(); void post_draw_viewport(RID p_render_target); void end_frame(); @@ -505,6 +521,9 @@ class OpenXRAPI { int64_t get_color_swapchain_format() const { return color_swapchain_format; } int64_t get_depth_swapchain_format() const { return depth_swapchain_format; } + double get_render_state_z_near() const { return render_state.z_near; } + double get_render_state_z_far() const { return render_state.z_far; } + // action map String get_default_action_map_resource_name(); @@ -542,6 +561,9 @@ class OpenXRAPI { void register_composition_layer_provider(OpenXRCompositionLayerProvider *provider); void unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider); + void register_projection_views_extension(OpenXRExtensionWrapper *p_extension); + void unregister_projection_views_extension(OpenXRExtensionWrapper *p_extension); + const XrEnvironmentBlendMode *get_supported_environment_blend_modes(uint32_t &count); bool is_environment_blend_mode_supported(XrEnvironmentBlendMode p_blend_mode) const; bool set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 73ac529537af..9d9f39305774 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -1056,6 +1056,34 @@ RID OpenXRInterface::get_depth_texture() { } } +RID OpenXRInterface::get_velocity_texture() { + return RID(); +} + +RID OpenXRInterface::get_motion_vectors_texture() { + if (openxr_api) { + return openxr_api->get_motion_vector_texture(); + } else { + return RID(); + } +} + +RID OpenXRInterface::get_motion_vectors_depth_texture() { + if (openxr_api) { + return openxr_api->get_motion_vector_depth_texture(); + } else { + return RID(); + } +} + +Size2i OpenXRInterface::get_motion_vectors_target_size() { + if (openxr_api) { + return openxr_api->get_motion_vector_target_size(); + } else { + return Size2i(); + } +}; + void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrackingExtension::HandTrackedHands p_hand) { OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); if (hand_tracking_ext && hand_tracking_ext->get_active()) { diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index f0ee0dc3c4ed..54c6cb8f8dab 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -184,6 +184,10 @@ class OpenXRInterface : public XRInterface { virtual RID get_color_texture() override; virtual RID get_depth_texture() override; + virtual RID get_velocity_texture() override; + virtual RID get_motion_vectors_texture() override; + virtual RID get_motion_vectors_depth_texture() override; + virtual Size2i get_motion_vectors_target_size() override; virtual void process() override; virtual void pre_render() override; diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index f3fda2517ce6..ba57a7984c9e 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -51,6 +51,7 @@ #include "extensions/openxr_debug_utils_extension.h" #include "extensions/openxr_eye_gaze_interaction.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" +#include "extensions/openxr_fb_space_warp_extension_wrapper.h" #include "extensions/openxr_hand_interaction_extension.h" #include "extensions/openxr_hand_tracking_extension.h" #include "extensions/openxr_htc_controller_extension.h" @@ -133,6 +134,9 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRMxInkExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension)); + // ClassDB::register_class(); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbSpaceWarpExtensionWrapper)); + // register gated extensions if (int(GLOBAL_GET("xr/openxr/extensions/debug_utils")) > 0) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRDebugUtilsExtension)); @@ -190,6 +194,8 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(OpenXRVisibilityMask); + // Engine::get_singleton()->add_singleton(Engine::Singleton("OpenXRFbSpaceWarpExtensionWrapper", OpenXRFbSpaceWarpExtensionWrapper::get_singleton())); + XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { openxr_interface.instantiate(); diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 6da7446e692c..f528c3ab06cf 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -208,6 +208,11 @@ class TextureStorage : public RendererTextureStorage { virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); } virtual RID render_target_get_texture(RID p_render_target) override { return RID(); } + + virtual void render_target_set_motion_vectors_target(RID p_render_target, RID p_motion_vectors_texture, RID p_depth_texture, Size2i p_size, int p_view_count) override {} + virtual RID render_target_get_motion_vectors_texture(RID p_render_target) const override { return RID(); } + virtual RID render_target_get_motion_vectors_depth_texture(RID p_render_target) const override { return RID(); } + virtual Size2i render_target_get_motion_vectors_target_size(RID p_render_target) const override { return Size2i(0, 0); } }; } // namespace RendererDummy diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 8aecc78f0711..3b72595b59bf 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -786,6 +786,11 @@ class TextureStorage : public RendererTextureStorage { virtual RID render_target_get_texture(RID p_render_target) override; + virtual void render_target_set_motion_vectors_target(RID p_render_target, RID p_motion_vectors_texture, RID p_depth_texture, Size2i p_size, int p_view_count) override {} + virtual RID render_target_get_motion_vectors_texture(RID p_render_target) const override { return RID(); } + virtual RID render_target_get_motion_vectors_depth_texture(RID p_render_target) const override { return RID(); } + virtual Size2i render_target_get_motion_vectors_target_size(RID p_render_target) const override { return Size2i(0, 0); } + RID render_target_get_rd_framebuffer(RID p_render_target); RID render_target_get_rd_texture(RID p_render_target); RID render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer); diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 4d6435f48a53..1fee221c1817 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -791,6 +791,12 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { xr_interface->get_depth_texture(), xr_interface->get_velocity_texture()); + RSG::texture_storage->render_target_set_motion_vectors_target(vp->render_target, + xr_interface->get_motion_vectors_texture(), + xr_interface->get_motion_vectors_depth_texture(), + xr_interface->get_motion_vectors_target_size(), + xr_interface->get_view_count()); + // render... RSG::scene->set_debug_draw_mode(vp->debug_draw); diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index fd17b052ee5d..58a982f64c0c 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -186,6 +186,12 @@ class RendererTextureStorage { // get textures virtual RID render_target_get_texture(RID p_render_target) = 0; + + // Motion vectors + virtual void render_target_set_motion_vectors_target(RID p_render_target, RID p_motion_vectors_texture, RID p_depth_texture, Size2i p_size, int p_view_count) = 0; + virtual RID render_target_get_motion_vectors_texture(RID p_render_target) const = 0; + virtual RID render_target_get_motion_vectors_depth_texture(RID p_render_target) const = 0; + virtual Size2i render_target_get_motion_vectors_target_size(RID p_render_target) const = 0; }; #endif // TEXTURE_STORAGE_H diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 26f315a45426..edbf9919a33a 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -187,6 +187,18 @@ RID XRInterface::get_velocity_texture() { return RID(); } +RID XRInterface::get_motion_vectors_texture() { + return RID(); +} + +RID XRInterface::get_motion_vectors_depth_texture() { + return RID(); +} + +Size2i XRInterface::get_motion_vectors_target_size() { + return Size2i(); +} + PackedStringArray XRInterface::get_suggested_tracker_names() const { PackedStringArray arr; diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index 55495731c558..f412d10b4f9b 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -137,6 +137,9 @@ class XRInterface : public RefCounted { virtual RID get_color_texture(); /* obtain color output texture (if applicable) */ virtual RID get_depth_texture(); /* obtain depth output texture (if applicable, used for reprojection) */ virtual RID get_velocity_texture(); /* obtain velocity output texture (if applicable, used for spacewarp) */ + virtual RID get_motion_vectors_texture(); + virtual RID get_motion_vectors_depth_texture(); + virtual Size2i get_motion_vectors_target_size(); virtual void pre_render() {} virtual bool pre_draw_viewport(RID p_render_target) { return true; }; /* inform XR interface we are about to start our viewport draw process */ virtual Vector post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* inform XR interface we finished our viewport draw process */