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_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 843b6eac05de..cbdab568767b 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -55,6 +55,7 @@ #define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F #define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 #define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251 +#define _EXT_DEBUG_TYPE_MARKER_ARB 0x8268 #define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 #define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 #define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145 @@ -140,7 +141,7 @@ void RasterizerGLES3::clear_depth(float p_depth) { #ifdef CAN_DEBUG static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) { // These are ultimately annoying, so removing for now. - if (type == _EXT_DEBUG_TYPE_OTHER_ARB || type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) { + if (type == _EXT_DEBUG_TYPE_OTHER_ARB || type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB || type == _EXT_DEBUG_TYPE_MARKER_ARB) { return; } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index a73f14c79614..b815bcb9af86 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_vectors_fbo = rt->overridden.velocity_fbo; + if (motion_vectors_fbo != 0 && GLES3::Config::get_singleton()->max_vertex_attribs >= 22) { + RENDER_TIMESTAMP("Motion Vectors Pass"); + glBindFramebuffer(GL_FRAMEBUFFER, motion_vectors_fbo); + + Size2i motion_vectors_target_size = rt->velocity_target_size; + glViewport(0, 0, motion_vectors_target_size.x, motion_vectors_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); @@ -3462,8 +3512,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]++; } @@ -3488,32 +3538,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) { @@ -3637,7 +3695,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; @@ -538,29 +601,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,20 +631,15 @@ void main() { highp vec4 position; #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; -#else - mat4 projection_matrix = scene_data.projection_matrix; - mat4 inv_projection_matrix = scene_data.inv_projection_matrix; - vec3 eye_offset = vec3(0.0, 0.0, 0.0); -#endif //USE_MULTIVIEW +#ifndef USE_MULTIVIEW + mat4 projection_matrix = scene_data_input.projection_matrix; + mat4 inv_projection_matrix = scene_data_input.inv_projection_matrix; +#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 @@ -605,8 +663,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; @@ -634,14 +692,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 @@ -655,6 +713,7 @@ void main() { binormal_interp = binormal; #endif +#ifndef RENDER_MOTION_VECTORS // Calculate shadows. #ifdef USE_ADDITIVE_LIGHTING #if defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) @@ -694,23 +753,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 @@ -722,7 +783,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); @@ -768,7 +829,97 @@ 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 + multiview_data_block.prev_data.projection_matrix_view[ViewIndex], + multiview_data_block.prev_data.inv_projection_matrix_view[ViewIndex], + multiview_data_block.prev_data.eye_offset[ViewIndex].xyz, +#endif + uv_scale, + prev_clip_position); +#else + vec4 clip_position; +#endif // defined(RENDER_MOTION_VECTORS) + + 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 + multiview_data_block.data.projection_matrix_view[ViewIndex], + multiview_data_block.data.inv_projection_matrix_view[ViewIndex], + multiview_data_block.data.eye_offset[ViewIndex].xyz, +#endif + uv_scale, + clip_position); + + gl_Position = clip_position; +} + /* clang-format off */ #[fragment] @@ -817,6 +968,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 @@ -914,7 +1071,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; @@ -962,16 +1119,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 */ @@ -1203,6 +1370,7 @@ uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; uniform highp uint model_flags; uniform highp uint instance_offset; +#endif // !RENDER_MOTION_VECTORS #if defined(RENDER_MATERIAL) layout(location = 0) out vec4 albedo_output_buffer; @@ -1211,11 +1379,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_vectors; +#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; @@ -1278,8 +1452,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; @@ -1527,32 +1701,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 @@ -1560,18 +1734,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); } @@ -1725,20 +1899,22 @@ 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 //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); @@ -1812,7 +1988,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; @@ -1905,7 +2081,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 @@ -1934,7 +2110,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; @@ -1947,11 +2123,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 @@ -1987,21 +2163,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 } @@ -2014,7 +2190,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; @@ -2030,7 +2206,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 @@ -2108,7 +2284,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; @@ -2211,7 +2387,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 :) @@ -2495,7 +2671,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 @@ -2503,4 +2679,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_vectors = 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 04cbf7f2cd14..265bff76654e 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,14 +1232,14 @@ 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["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR"; - 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 73d95d75baf6..cf7969408983 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 = RSG::viewport->get_num_viewports_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 = RSG::viewport->get_num_viewports_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 = RSG::viewport->get_num_viewports_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 8615b89a30d3..b3fc5e4e03c4 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 5f49a84fe8a2..17dd478ee189 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; @@ -2052,7 +2048,7 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const { GLuint TextureStorage::system_fbo = 0; -void TextureStorage::_update_render_target(RenderTarget *rt) { +void TextureStorage::_update_render_target_color(RenderTarget *rt) { // do not allocate a render target with no size if (rt->size.x <= 0 || rt->size.y <= 0) { return; @@ -2220,6 +2216,60 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); } +void TextureStorage::_update_render_target_velocity(RenderTarget *rt) { + GLuint new_velocity_fbo; + glGenFramebuffers(1, &new_velocity_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, new_velocity_fbo); + + uint32_t view_count = 2; + GLuint texture_target = view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + GLuint velocity_texture_id = texture_get_texid(rt->overridden.velocity); + glBindTexture(texture_target, velocity_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 (view_count > 1) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, velocity_texture_id, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, velocity_texture_id, 0); + } + + GLuint velocity_depth_texture_id = texture_get_texid(rt->overridden.velocity_depth); + glBindTexture(texture_target, velocity_depth_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 (view_count > 1) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, velocity_depth_texture_id, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, velocity_depth_texture_id, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &new_velocity_fbo); + WARN_PRINT("Could not create motion vector render target, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } else { + rt->overridden.velocity_fbo = new_velocity_fbo; + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { ERR_FAIL_COND_MSG(rt->backbuffer_fbo != 0, "Cannot allocate RenderTarget backbuffer: already initialized."); ERR_FAIL_COND(rt->direct_to_screen); @@ -2340,6 +2390,12 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { return; } + for (KeyValue &E : rt->overridden.velocity_fbo_cache) { + glDeleteFramebuffers(1, &E.value); + } + rt->overridden.velocity_fbo_cache.clear(); + rt->overridden.velocity_fbo = 0; + // 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()); @@ -2392,7 +2448,6 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { } rt->depth = 0; - rt->overridden.velocity = RID(); rt->overridden.is_overridden = false; if (rt->backbuffer_fbo != 0) { @@ -2421,7 +2476,7 @@ RID TextureStorage::render_target_create() { t.is_render_target = true; render_target.texture = texture_owner.make_rid(t); - _update_render_target(&render_target); + _update_render_target_color(&render_target); return render_target_owner.make_rid(render_target); } @@ -2470,7 +2525,7 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in rt->size = Size2i(p_width, p_height); rt->view_count = p_view_count; - _update_render_target(rt); + _update_render_target_color(rt); } // TODO: convert to Size2i internally @@ -2481,21 +2536,22 @@ Size2i TextureStorage::render_target_get_size(RID p_render_target) const { return rt->size; } -void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) { +void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); ERR_FAIL_COND(rt->direct_to_screen); - rt->overridden.velocity = p_velocity_texture; + bool create_new_color_fbo = true; + bool create_new_velocity_fbo = true; - if (rt->overridden.color == p_color_texture && rt->overridden.depth == p_depth_texture) { + if (rt->overridden.color == p_color_texture && rt->overridden.depth == p_depth_texture && rt->overridden.velocity == p_velocity_texture && rt->overridden.velocity_depth == p_velocity_depth_texture) { return; } if (p_color_texture.is_null() && p_depth_texture.is_null()) { _clear_render_target(rt); - _update_render_target(rt); - return; + _update_render_target_color(rt); + create_new_color_fbo = false; } if (!rt->overridden.is_overridden) { @@ -2504,6 +2560,8 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->overridden.color = p_color_texture; rt->overridden.depth = p_depth_texture; + rt->overridden.velocity = p_velocity_texture; + rt->overridden.velocity_depth = p_velocity_depth_texture; rt->overridden.is_overridden = true; uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); @@ -2517,24 +2575,51 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color rt->depth = cache->get().depth; rt->size = cache->get().size; rt->texture = p_color_texture; - return; + create_new_color_fbo = false; + } + + uint32_t velocity_hash_key = hash_murmur3_one_64(p_velocity_texture.get_id()); + velocity_hash_key = hash_murmur3_one_64(p_velocity_depth_texture.get_id(), velocity_hash_key); + velocity_hash_key = hash_fmix32(velocity_hash_key); + + RBMap::Element *fbo; + if ((fbo = rt->overridden.velocity_fbo_cache.find(velocity_hash_key)) != nullptr) { + rt->overridden.velocity_fbo = fbo->get(); + create_new_velocity_fbo = false; } - _update_render_target(rt); + if (p_velocity_texture.is_null()) { + for (KeyValue &E : rt->overridden.velocity_fbo_cache) { + glDeleteFramebuffers(1, &E.value); + } - RenderTarget::RTOverridden::FBOCacheEntry new_entry; - new_entry.fbo = rt->fbo; - new_entry.color = rt->color; - new_entry.depth = rt->depth; - new_entry.size = rt->size; - // Keep track of any textures we had to allocate because they weren't overridden. - if (p_color_texture.is_null()) { - new_entry.allocated_textures.push_back(rt->color); + rt->overridden.velocity_fbo_cache.clear(); + rt->overridden.velocity_fbo = 0; + create_new_velocity_fbo = false; } - if (p_depth_texture.is_null()) { - new_entry.allocated_textures.push_back(rt->depth); + + if (create_new_color_fbo) { + _update_render_target_color(rt); + + RenderTarget::RTOverridden::FBOCacheEntry new_entry; + new_entry.fbo = rt->fbo; + new_entry.color = rt->color; + new_entry.depth = rt->depth; + new_entry.size = rt->size; + // Keep track of any textures we had to allocate because they weren't overridden. + if (p_color_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->color); + } + if (p_depth_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->depth); + } + rt->overridden.fbo_cache.insert(hash_key, new_entry); + } + + if (create_new_velocity_fbo) { + _update_render_target_velocity(rt); + rt->overridden.velocity_fbo_cache.insert(velocity_hash_key, rt->overridden.velocity_fbo); } - rt->overridden.fbo_cache.insert(hash_key, new_entry); } RID TextureStorage::render_target_get_override_color(RID p_render_target) const { @@ -2558,6 +2643,13 @@ RID TextureStorage::render_target_get_override_velocity(RID p_render_target) con return rt->overridden.velocity; } +RID TextureStorage::render_target_get_override_velocity_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, RID()); + + return rt->overridden.velocity_depth; +} + RID TextureStorage::render_target_get_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL_V(rt, RID()); @@ -2569,6 +2661,20 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) { return rt->texture; } +void TextureStorage::render_target_set_velocity_target_size(RID p_render_target, Size2i p_target_size) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + + rt->velocity_target_size = p_target_size; +} + +Size2i TextureStorage::render_target_get_velocity_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->velocity_target_size; +} + 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); @@ -2577,7 +2683,7 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t if (rt->overridden.color.is_null()) { _clear_render_target(rt); - _update_render_target(rt); + _update_render_target_color(rt); } } @@ -2603,8 +2709,9 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo rt->overridden.color = RID(); rt->overridden.depth = RID(); rt->overridden.velocity = RID(); + rt->overridden.velocity_depth = RID(); } - _update_render_target(rt); + _update_render_target_color(rt); } bool TextureStorage::render_target_get_direct_to_screen(RID p_render_target) const { @@ -2640,7 +2747,7 @@ void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSA _clear_render_target(rt); rt->msaa = p_msaa; - _update_render_target(rt); + _update_render_target_color(rt); } RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) const { @@ -2660,7 +2767,7 @@ void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_h _clear_render_target(rt); rt->hdr = p_use_hdr_2d; - _update_render_target(rt); + _update_render_target_color(rt); } bool TextureStorage::render_target_is_using_hdr(RID p_render_target) const { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index d85d10e23529..d708d3fd7435 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -349,6 +349,8 @@ struct RenderTarget { GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + Size2i velocity_target_size = Size2i(0, 0); + bool hdr = false; // For Compatibility this effects both 2D and 3D rendering! GLuint color_internal_format = GL_RGBA8; GLuint color_format = GL_RGBA; @@ -377,6 +379,7 @@ struct RenderTarget { RID color; RID depth; RID velocity; + RID velocity_depth; struct FBOCacheEntry { GLuint fbo; @@ -386,6 +389,9 @@ struct RenderTarget { Vector allocated_textures; }; RBMap fbo_cache; + + GLuint velocity_fbo = 0; + RBMap velocity_fbo_cache; } overridden; RID texture; @@ -450,7 +456,8 @@ class TextureStorage : public RendererTextureStorage { mutable RID_Owner render_target_owner; void _clear_render_target(RenderTarget *rt); - void _update_render_target(RenderTarget *rt); + void _update_render_target_color(RenderTarget *rt); + void _update_render_target_velocity(RenderTarget *rt); void _create_render_target_backbuffer(RenderTarget *rt); void _render_target_allocate_sdf(RenderTarget *rt); void _render_target_clear_sdf(RenderTarget *rt); @@ -685,13 +692,17 @@ class TextureStorage : public RendererTextureStorage { virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } - virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) override; + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override; virtual RID render_target_get_override_color(RID p_render_target) const override; virtual RID render_target_get_override_depth(RID p_render_target) const override; virtual RID render_target_get_override_velocity(RID p_render_target) const override; + virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override; virtual RID render_target_get_texture(RID p_render_target) override; + virtual void render_target_set_velocity_target_size(RID p_render_target, Size2i p_target_size) override; + virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const override; + void bind_framebuffer(GLuint framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); } diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml index 432b331eecd2..ae7f0df08820 100644 --- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml +++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml @@ -43,6 +43,12 @@ Returns an error string for the given [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url]. + + + + Returns the name of the graphics API being used. + + @@ -82,12 +88,32 @@ Returns the predicted display timing for the current frame. + + + + Returns the far boundary value of the camera frustum. + [b]Note:[/b] This is only accessible in the render thread. + + + + + + Returns the near boundary value of the camera frustum. + [b]Note:[/b] This is only accessible in the render thread. + + Returns the OpenXR session, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSession.html]XrSession[/url] cast to an integer. + + + + Returns an array of supported swapchain formats. + + @@ -133,6 +159,54 @@ Returns [code]true[/code] if OpenXR is enabled. + + + + + Acquires the image of the provided swapchain. + + + + + + + + + + + + + Returns a pointer to a new swapchain created using the provided parameters. + + + + + + + Destroys the provided swapchain and frees it from memory. + + + + + + + Returns the RID of the provided swapchain's image. + + + + + + + Returns the [code]XrSwapchain[/code] handle of the provided swapchain. + + + + + + + Releases the image of the provided swapchain. + + @@ -140,6 +214,13 @@ Registers the given extension as a composition layer provider. + + + + + Registers the given extension as a provider of additional data structures to projections views. + + @@ -156,6 +237,27 @@ Set the object name of an OpenXR object, used for debug output. [param object_type] must be a valid OpenXR [code]XrObjectType[/code] enum and [param object_handle] must be a valid OpenXR object handle. + + + + + Sets the render target of the velocity depth texture. + + + + + + + Sets the target size of the velocity and velocity depth textures. + + + + + + + Sets the render target of the velocity texture. + + @@ -170,6 +272,13 @@ Unregisters the given extension as a composition layer provider. + + + + + Unregisters the given extension as a provider of additional data structures to projections views. + + diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 813c9d582ebb..beb794400909 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -90,6 +90,13 @@ Called right after the main swapchains are (re)created. + + + + + Called right after the given viewport begins rendering. + + @@ -202,6 +209,14 @@ Adds additional data structures when the OpenXR instance is created. + + + + + + Adds additional data structures to the projection view of the given [param view_index]. + + diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp index dc30b95b27e9..c961dfad968d 100644 --- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp +++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp @@ -210,7 +210,7 @@ void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i if (subviewport.viewport != p_viewport) { if (subviewport.viewport.is_valid()) { RID rt = rs->viewport_get_render_target(subviewport.viewport); - RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID()); + RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID()); } subviewport.viewport = p_viewport; @@ -323,7 +323,7 @@ void OpenXRViewportCompositionLayerProvider::on_pre_render() { if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) { // Render to our XR swapchain image. RID rt = rs->viewport_get_render_target(subviewport.viewport); - RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID()); + RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID()); } } } diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 95b537d1b453..bd6daef64eff 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(); } @@ -118,6 +119,7 @@ class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper { public: virtual void get_usable_swapchain_formats(Vector &p_usable_swap_chains) = 0; // `get_usable_swapchain_formats` should return a list of usable color formats. virtual void get_usable_depth_formats(Vector &p_usable_swap_chains) = 0; // `get_usable_depth_formats` should return a list of usable depth formats. + virtual String get_graphics_api_name() const = 0; // `get_graphics_api_name` should return the name of the graphics api this class uses. virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0; // `get_swapchain_format_name` should return the constant name of a given format. virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0; // `get_swapchain_image_data` extracts image IDs for the swapchain images and stores there in an implementation dependent data structure. virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0; // `cleanup_swapchain_graphics_data` cleans up the data held in our implementation dependent data structure and should free up its memory. diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 07ca476421d8..81fa099c90ef 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -39,6 +39,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer"); GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer"); GDVIRTUAL_BIND(_set_hand_joint_locations_and_get_next_pointer, "hand_index", "next_pointer"); + GDVIRTUAL_BIND(_set_projection_views_and_get_next_pointer, "view_index", "next_pointer"); GDVIRTUAL_BIND(_get_composition_layer_count); GDVIRTUAL_BIND(_get_composition_layer, "index"); GDVIRTUAL_BIND(_get_composition_layer_order, "index"); @@ -52,6 +53,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_on_pre_render); GDVIRTUAL_BIND(_on_main_swapchains_created); GDVIRTUAL_BIND(_on_session_destroyed); + GDVIRTUAL_BIND(_on_post_draw_viewport, "render_target"); GDVIRTUAL_BIND(_on_state_idle); GDVIRTUAL_BIND(_on_state_ready); GDVIRTUAL_BIND(_on_state_synchronized); @@ -138,6 +140,16 @@ void *OpenXRExtensionWrapperExtension::set_hand_joint_locations_and_get_next_poi return nullptr; } +void *OpenXRExtensionWrapperExtension::set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) { + uint64_t pointer; + + if (GDVIRTUAL_CALL(_set_projection_views_and_get_next_pointer, p_view_index, GDExtensionPtr(p_next_pointer), pointer)) { + return reinterpret_cast(pointer); + } + + return nullptr; +} + PackedStringArray OpenXRExtensionWrapperExtension::get_suggested_tracker_names() { PackedStringArray ret; @@ -208,6 +220,10 @@ void OpenXRExtensionWrapperExtension::on_session_destroyed() { GDVIRTUAL_CALL(_on_session_destroyed); } +void OpenXRExtensionWrapperExtension::on_post_draw_viewport(RID p_render_target) { + GDVIRTUAL_CALL(_on_post_draw_viewport, p_render_target); +} + void OpenXRExtensionWrapperExtension::on_state_idle() { GDVIRTUAL_CALL(_on_state_idle); } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index 5cdf288c934a..6877f7bc1b67 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -60,6 +60,7 @@ class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWra virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; virtual void *set_hand_joint_locations_and_get_next_pointer(int p_hand_index, void *p_next_pointer) override; + virtual void *set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) override; virtual int get_composition_layer_count() override; virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override; @@ -71,6 +72,7 @@ class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWra GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr); GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr); GDVIRTUAL2R(uint64_t, _set_hand_joint_locations_and_get_next_pointer, int, GDExtensionPtr); + GDVIRTUAL2R(uint64_t, _set_projection_views_and_get_next_pointer, int, GDExtensionPtr); GDVIRTUAL0R(int, _get_composition_layer_count); GDVIRTUAL1R(uint64_t, _get_composition_layer, int); GDVIRTUAL1R(int, _get_composition_layer_order, int); @@ -88,6 +90,7 @@ class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWra virtual void on_pre_render() override; virtual void on_main_swapchains_created() override; virtual void on_session_destroyed() override; + virtual void on_post_draw_viewport(RID p_render_target) override; GDVIRTUAL0(_on_register_metadata); GDVIRTUAL0(_on_before_instance_created); @@ -98,6 +101,7 @@ class OpenXRExtensionWrapperExtension : public Object, public OpenXRExtensionWra GDVIRTUAL0(_on_pre_render); GDVIRTUAL0(_on_main_swapchains_created); GDVIRTUAL0(_on_session_destroyed); + GDVIRTUAL1(_on_post_draw_viewport, RID); virtual void on_state_idle() override; virtual void on_state_ready() override; diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp index caded14ca768..74cba0636d15 100644 --- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp +++ b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp @@ -299,6 +299,10 @@ void OpenXROpenGLExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g *p_swapchain_graphics_data = nullptr; } +String OpenXROpenGLExtension::get_graphics_api_name() const { + return "OpenGL"; +} + #define ENUM_TO_STRING_CASE(e) \ case e: { \ return String(#e); \ diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.h b/modules/openxr/extensions/platform/openxr_opengl_extension.h index 8da3ca48f4f6..0f6c47b20ce4 100644 --- a/modules/openxr/extensions/platform/openxr_opengl_extension.h +++ b/modules/openxr/extensions/platform/openxr_opengl_extension.h @@ -51,6 +51,7 @@ class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper { virtual void get_usable_swapchain_formats(Vector &p_usable_swap_chains) override; virtual void get_usable_depth_formats(Vector &p_usable_swap_chains) override; + virtual String get_graphics_api_name() const override; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override; virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override; virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override; diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp index da613f8435ff..1a3fbc1db10d 100644 --- a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp +++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp @@ -238,6 +238,10 @@ void OpenXRVulkanExtension::get_usable_depth_formats(Vector &p_usable_s p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT); } +String OpenXRVulkanExtension::get_graphics_api_name() const { + return "Vulkan"; +} + bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) { XrSwapchainImageVulkanKHR *images = nullptr; diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.h b/modules/openxr/extensions/platform/openxr_vulkan_extension.h index a3f86a9968b9..bd8ec635dbb2 100644 --- a/modules/openxr/extensions/platform/openxr_vulkan_extension.h +++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.h @@ -58,6 +58,7 @@ class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks virtual void get_usable_swapchain_formats(Vector &p_usable_swap_chains) override; virtual void get_usable_depth_formats(Vector &p_usable_swap_chains) override; + virtual String get_graphics_api_name() const override; virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override; virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override; virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override; diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index c67be5a2b313..89977d66d8a3 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); } @@ -2342,6 +2345,30 @@ RID OpenXRAPI::get_depth_texture() { } } +void OpenXRAPI::set_velocity_texture(RID p_render_target) { + velocity_texture = p_render_target; +} + +RID OpenXRAPI::get_velocity_texture() { + return velocity_texture; +} + +void OpenXRAPI::set_velocity_depth_texture(RID p_render_target) { + velocity_depth_texture = p_render_target; +} + +RID OpenXRAPI::get_velocity_depth_texture() { + return velocity_depth_texture; +} + +void OpenXRAPI::set_velocity_target_size(Size2i p_target_size) { + velocity_target_size = p_target_size; +} + +Size2i OpenXRAPI::get_velocity_target_size() { + return velocity_target_size; +} + void OpenXRAPI::post_draw_viewport(RID p_render_target) { // Must be called from rendering thread! ERR_NOT_ON_RENDER_THREAD; @@ -2447,6 +2474,20 @@ void OpenXRAPI::end_frame() { render_state.view_count, // viewCount render_state.projection_views, // views }; + + 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. @@ -2577,6 +2618,14 @@ Size2 OpenXRAPI::get_play_space_bounds() const { return ret; } +PackedInt64Array OpenXRAPI::get_supported_swapchain_formats() { + PackedInt64Array supported_swapchain_list; + for (uint32_t i = 0; i < num_swapchain_formats; i++) { + supported_swapchain_list.push_back(supported_swapchain_formats[i]); + } + return supported_swapchain_list; +} + OpenXRAPI::OpenXRAPI() { // OpenXRAPI is only constructed if OpenXR is enabled. singleton = this; @@ -2678,6 +2727,14 @@ OpenXRAPI::~OpenXRAPI() { singleton = nullptr; } +String OpenXRAPI::get_graphics_api_name() const { + if (graphics_extension == nullptr) { + return ""; + } + + return graphics_extension->get_graphics_api_name(); +} + Transform3D OpenXRAPI::transform_from_pose(const XrPosef &p_pose) { Quaternion q(p_pose.orientation.x, p_pose.orientation.y, p_pose.orientation.z, p_pose.orientation.w); Basis basis(q); @@ -3497,6 +3554,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..41b2b8c55678 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 velocity_texture; + RID velocity_depth_texture; + Size2i velocity_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; @@ -403,6 +412,8 @@ class OpenXRAPI { String get_runtime_name() const { return runtime_name; }; String get_runtime_version() const { return runtime_version; }; + String get_graphics_api_name() const; + // helper method to convert an XrPosef to a Transform3D Transform3D transform_from_pose(const XrPosef &p_pose); @@ -477,6 +488,12 @@ class OpenXRAPI { XrSwapchain get_color_swapchain(); RID get_color_texture(); RID get_depth_texture(); + void set_velocity_texture(RID p_render_target); + RID get_velocity_texture(); + void set_velocity_depth_texture(RID p_render_target); + RID get_velocity_depth_texture(); + void set_velocity_target_size(Size2i p_target_size); + Size2i get_velocity_target_size(); void post_draw_viewport(RID p_render_target); void end_frame(); @@ -502,9 +519,13 @@ class OpenXRAPI { Size2 get_play_space_bounds() const; // swapchains + PackedInt64Array get_supported_swapchain_formats(); 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 +563,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_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp index f3bc178d3a25..6be0c710dc5f 100644 --- a/modules/openxr/openxr_api_extension.cpp +++ b/modules/openxr/openxr_api_extension.cpp @@ -61,6 +61,26 @@ void OpenXRAPIExtension::_bind_methods() { ClassDB::bind_method(D_METHOD("register_composition_layer_provider", "extension"), &OpenXRAPIExtension::register_composition_layer_provider); ClassDB::bind_method(D_METHOD("unregister_composition_layer_provider", "extension"), &OpenXRAPIExtension::unregister_composition_layer_provider); + ClassDB::bind_method(D_METHOD("register_projection_views_extension", "extension"), &OpenXRAPIExtension::register_projection_views_extension); + ClassDB::bind_method(D_METHOD("unregister_projection_views_extension", "extension"), &OpenXRAPIExtension::unregister_projection_views_extension); + + ClassDB::bind_method(D_METHOD("get_render_state_z_near"), &OpenXRAPIExtension::get_render_state_z_near); + ClassDB::bind_method(D_METHOD("get_render_state_z_far"), &OpenXRAPIExtension::get_render_state_z_far); + + ClassDB::bind_method(D_METHOD("set_velocity_texture", "render_target"), &OpenXRAPIExtension::set_velocity_texture); + ClassDB::bind_method(D_METHOD("set_velocity_depth_texture", "render_target"), &OpenXRAPIExtension::set_velocity_depth_texture); + ClassDB::bind_method(D_METHOD("set_velocity_target_size", "target_size"), &OpenXRAPIExtension::set_velocity_target_size); + + ClassDB::bind_method(D_METHOD("get_graphics_api_name"), &OpenXRAPIExtension::get_graphics_api_name); + ClassDB::bind_method(D_METHOD("get_supported_swapchain_formats"), &OpenXRAPIExtension::get_supported_swapchain_formats); + + ClassDB::bind_method(D_METHOD("openxr_swapchain_create", "create_flags", "usage_flags", "swapchain_format", "width", "height", "sample_count", "array_size"), &OpenXRAPIExtension::openxr_swapchain_create); + ClassDB::bind_method(D_METHOD("openxr_swapchain_free", "swapchain"), &OpenXRAPIExtension::openxr_swapchain_free); + ClassDB::bind_method(D_METHOD("openxr_swapchain_get_swapchain", "swapchain"), &OpenXRAPIExtension::openxr_swapchain_get_swapchain); + ClassDB::bind_method(D_METHOD("openxr_swapchain_acquire", "swapchain"), &OpenXRAPIExtension::openxr_swapchain_acquire); + ClassDB::bind_method(D_METHOD("openxr_swapchain_get_image", "swapchain"), &OpenXRAPIExtension::openxr_swapchain_get_image); + ClassDB::bind_method(D_METHOD("openxr_swapchain_release", "swapchain"), &OpenXRAPIExtension::openxr_swapchain_release); + ClassDB::bind_method(D_METHOD("set_emulate_environment_blend_mode_alpha_blend", "enabled"), &OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend); ClassDB::bind_method(D_METHOD("is_environment_blend_mode_alpha_supported"), &OpenXRAPIExtension::is_environment_blend_mode_alpha_blend_supported); @@ -193,6 +213,99 @@ void OpenXRAPIExtension::unregister_composition_layer_provider(OpenXRExtensionWr OpenXRAPI::get_singleton()->unregister_composition_layer_provider(p_extension); } +void OpenXRAPIExtension::register_projection_views_extension(OpenXRExtensionWrapperExtension *p_extension) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->register_projection_views_extension(p_extension); +} + +void OpenXRAPIExtension::unregister_projection_views_extension(OpenXRExtensionWrapperExtension *p_extension) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->unregister_projection_views_extension(p_extension); +} + +double OpenXRAPIExtension::get_render_state_z_near() { + ERR_NOT_ON_RENDER_THREAD_V(0.0); + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0.0); + return OpenXRAPI::get_singleton()->get_render_state_z_near(); +} + +double OpenXRAPIExtension::get_render_state_z_far() { + ERR_NOT_ON_RENDER_THREAD_V(0.0); + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0.0); + return OpenXRAPI::get_singleton()->get_render_state_z_far(); +} + +void OpenXRAPIExtension::set_velocity_texture(RID p_render_target) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->set_velocity_texture(p_render_target); +} + +void OpenXRAPIExtension::set_velocity_depth_texture(RID p_render_target) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->set_velocity_depth_texture(p_render_target); +} + +void OpenXRAPIExtension::set_velocity_target_size(Size2i p_target_size) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + OpenXRAPI::get_singleton()->set_velocity_target_size(p_target_size); +} + +String OpenXRAPIExtension::get_graphics_api_name() { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), ""); + return OpenXRAPI::get_singleton()->get_graphics_api_name(); +} + +PackedInt64Array OpenXRAPIExtension::get_supported_swapchain_formats() { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), PackedInt64Array()); + return OpenXRAPI::get_singleton()->get_supported_swapchain_formats(); +} + +uint64_t OpenXRAPIExtension::openxr_swapchain_create(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size) { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0); + + OpenXRAPI::OpenXRSwapChainInfo *new_swapchain_info = memnew(OpenXRAPI::OpenXRSwapChainInfo); + new_swapchain_info->create(p_create_flags, p_usage_flags, p_swapchain_format, p_width, p_height, p_sample_count, p_array_size); + return reinterpret_cast(new_swapchain_info); +} + +void OpenXRAPIExtension::openxr_swapchain_free(uint64_t p_swapchain_info) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + + OpenXRAPI::OpenXRSwapChainInfo *swapchain_info = reinterpret_cast(p_swapchain_info); + swapchain_info->free(); + memfree(swapchain_info); +} + +uint64_t OpenXRAPIExtension::openxr_swapchain_get_swapchain(uint64_t p_swapchain_info) { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0); + + OpenXRAPI::OpenXRSwapChainInfo *swapchain_info = reinterpret_cast(p_swapchain_info); + XrSwapchain swapchain = swapchain_info->get_swapchain(); + return reinterpret_cast(swapchain); +} + +void OpenXRAPIExtension::openxr_swapchain_acquire(uint64_t p_swapchain_info) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + + OpenXRAPI::OpenXRSwapChainInfo *swapchain_info = reinterpret_cast(p_swapchain_info); + bool should_render = true; // Can ignore should_render. + swapchain_info->acquire(should_render); +} + +RID OpenXRAPIExtension::openxr_swapchain_get_image(uint64_t p_swapchain_info) { + ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), RID()); + + OpenXRAPI::OpenXRSwapChainInfo *swapchain_info = reinterpret_cast(p_swapchain_info); + return swapchain_info->get_image(); +} + +void OpenXRAPIExtension::openxr_swapchain_release(uint64_t p_swapchain_info) { + ERR_FAIL_NULL(OpenXRAPI::get_singleton()); + + OpenXRAPI::OpenXRSwapChainInfo *swapchain_info = reinterpret_cast(p_swapchain_info); + swapchain_info->release(); +} + void OpenXRAPIExtension::set_emulate_environment_blend_mode_alpha_blend(bool p_enabled) { ERR_FAIL_NULL(OpenXRAPI::get_singleton()); OpenXRAPI::get_singleton()->set_emulate_environment_blend_mode_alpha_blend(p_enabled); diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h index 1b88b418f61f..37ca0d7237c1 100644 --- a/modules/openxr/openxr_api_extension.h +++ b/modules/openxr/openxr_api_extension.h @@ -82,6 +82,26 @@ class OpenXRAPIExtension : public RefCounted { void register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension); void unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension); + void register_projection_views_extension(OpenXRExtensionWrapperExtension *p_extension); + void unregister_projection_views_extension(OpenXRExtensionWrapperExtension *p_extension); + + double get_render_state_z_near(); + double get_render_state_z_far(); + + void set_velocity_texture(RID p_render_target); + void set_velocity_depth_texture(RID p_render_target); + void set_velocity_target_size(Size2i p_target_size); + + String get_graphics_api_name(); + PackedInt64Array get_supported_swapchain_formats(); + + uint64_t openxr_swapchain_create(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size); + void openxr_swapchain_free(uint64_t p_swapchain_info); + uint64_t openxr_swapchain_get_swapchain(uint64_t p_swapchain_info); + void openxr_swapchain_acquire(uint64_t p_swapchain_info); + RID openxr_swapchain_get_image(uint64_t p_swapchain_info); + void openxr_swapchain_release(uint64_t p_swapchain_info); + enum OpenXRAlphaBlendModeSupport { OPENXR_ALPHA_BLEND_MODE_SUPPORT_NONE = 0, OPENXR_ALPHA_BLEND_MODE_SUPPORT_REAL = 1, diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 500a58acc37b..1c7917c1b6ea 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -1053,6 +1053,30 @@ RID OpenXRInterface::get_depth_texture() { } } +RID OpenXRInterface::get_velocity_texture() { + if (openxr_api) { + return openxr_api->get_velocity_texture(); + } else { + return RID(); + } +} + +RID OpenXRInterface::get_velocity_depth_texture() { + if (openxr_api) { + return openxr_api->get_velocity_depth_texture(); + } else { + return RID(); + } +} + +Size2i OpenXRInterface::get_velocity_target_size() { + if (openxr_api) { + return openxr_api->get_velocity_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..4137be070522 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -184,6 +184,9 @@ 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_velocity_depth_texture() override; + virtual Size2i get_velocity_target_size() override; virtual void process() override; virtual void pre_render() override; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8755d5f51e4a..2cce1107ebdc 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -4448,7 +4448,7 @@ void Viewport::set_use_xr(bool p_use_xr) { // Reset render target override textures. RID rt = RS::get_singleton()->viewport_get_render_target(viewport); - RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID()); + RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID()); } } } diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 6735f6bcda11..19b20fb0a503 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -200,12 +200,16 @@ class TextureStorage : public RendererTextureStorage { virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } - virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) override {} + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override {} virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); } virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); } virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); } + virtual RID render_target_get_override_velocity_depth(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_velocity_target_size(RID p_render_target, Size2i p_target_size) override {} + virtual Size2i render_target_get_velocity_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.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 42fce65b2d22..30c7d73afd90 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -3434,7 +3434,7 @@ RID TextureStorage::render_target_get_texture(RID p_render_target) { return rt->texture; } -void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) { +void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 866fdd50ac92..b4aeb2fd17b7 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -780,15 +780,19 @@ class TextureStorage : public RendererTextureStorage { virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override; virtual RID render_target_get_vrs_texture(RID p_render_target) const override; - virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) override; + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override; virtual RID render_target_get_override_color(RID p_render_target) const override; virtual RID render_target_get_override_depth(RID p_render_target) const override; RID render_target_get_override_depth_slice(RID p_render_target, const uint32_t p_layer) const; virtual RID render_target_get_override_velocity(RID p_render_target) const override; RID render_target_get_override_velocity_slice(RID p_render_target, const uint32_t p_layer) const; + virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override { return RID(); } virtual RID render_target_get_texture(RID p_render_target) override; + virtual void render_target_set_velocity_target_size(RID p_render_target, Size2i p_target_size) override {} + virtual Size2i render_target_get_velocity_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..cdb055f1f0c0 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -789,7 +789,16 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { RSG::texture_storage->render_target_set_override(vp->render_target, xr_interface->get_color_texture(), xr_interface->get_depth_texture(), - xr_interface->get_velocity_texture()); + xr_interface->get_velocity_texture(), + xr_interface->get_velocity_depth_texture()); + + RSG::texture_storage->render_target_set_velocity_target_size(vp->render_target, xr_interface->get_velocity_target_size()); + + if (xr_interface->get_velocity_texture().is_valid()) { + viewport_set_force_motion_vectors(vp->self, true); + } else { + viewport_set_force_motion_vectors(vp->self, false); + } // render... RSG::scene->set_debug_draw_mode(vp->debug_draw); @@ -990,7 +999,7 @@ void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int } bool RendererViewport::_viewport_requires_motion_vectors(Viewport *p_viewport) { - return p_viewport->use_taa || p_viewport->scaling_3d_mode == RenderingServer::VIEWPORT_SCALING_3D_MODE_FSR2 || p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS; + return p_viewport->use_taa || p_viewport->scaling_3d_mode == RenderingServer::VIEWPORT_SCALING_3D_MODE_FSR2 || p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS || p_viewport->force_motion_vectors; } void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { @@ -1338,6 +1347,25 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb _configure_3d_render_buffers(viewport); } +void RendererViewport::viewport_set_force_motion_vectors(RID p_viewport, bool p_force_motion_vectors) { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_NULL(viewport); + + if (viewport->force_motion_vectors == p_force_motion_vectors) { + return; + } + + bool motion_vectors_before = _viewport_requires_motion_vectors(viewport); + viewport->force_motion_vectors = p_force_motion_vectors; + + bool motion_vectors_after = _viewport_requires_motion_vectors(viewport); + if (motion_vectors_before != motion_vectors_after) { + num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1; + } + + _configure_3d_render_buffers(viewport); +} + void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index bf97905f8694..21cc06d6e7ab 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -77,6 +77,7 @@ class RendererViewport { RS::ViewportScreenSpaceAA screen_space_aa = RenderingServer::VIEWPORT_SCREEN_SPACE_AA_DISABLED; bool use_taa = false; bool use_debanding = false; + bool force_motion_vectors = false; RendererSceneRender::CameraData prev_camera_data; uint64_t prev_camera_data_frame = 0; @@ -277,6 +278,7 @@ class RendererViewport { void viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode); void viewport_set_use_taa(RID p_viewport, bool p_use_taa); void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding); + void viewport_set_force_motion_vectors(RID p_viewport, bool p_force_motion_vectors); void viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling); void viewport_set_occlusion_rays_per_thread(int p_rays_per_thread); void viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 766ec8fa9634..c298fc53c736 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -723,6 +723,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) FUNC2(viewport_set_use_taa, RID, bool) FUNC2(viewport_set_use_debanding, RID, bool) + FUNC2(viewport_set_force_motion_vectors, RID, bool) FUNC2(viewport_set_use_occlusion_culling, RID, bool) FUNC1(viewport_set_occlusion_rays_per_thread, int) FUNC1(viewport_set_occlusion_culling_build_quality, ViewportOcclusionCullingBuildQuality) diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index e5283848915c..404e61649403 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -178,13 +178,18 @@ class RendererTextureStorage { virtual RID render_target_get_vrs_texture(RID p_render_target) const = 0; // override color, depth and velocity buffers (depth and velocity only for 3D) - virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) = 0; + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) = 0; virtual RID render_target_get_override_color(RID p_render_target) const = 0; virtual RID render_target_get_override_depth(RID p_render_target) const = 0; virtual RID render_target_get_override_velocity(RID p_render_target) const = 0; + virtual RID render_target_get_override_velocity_depth(RID p_render_target) const = 0; // get textures virtual RID render_target_get_texture(RID p_render_target) = 0; + + // Motion vectors + virtual void render_target_set_velocity_target_size(RID p_render_target, Size2i p_target_size) = 0; + virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const = 0; }; #endif // TEXTURE_STORAGE_H diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 6c1e1274d48f..be90ad74b8d2 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1014,6 +1014,8 @@ class RenderingServer : public Object { virtual void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) = 0; + virtual void viewport_set_force_motion_vectors(RID p_viewport, bool p_force_motion_vectors) = 0; + virtual void viewport_set_mesh_lod_threshold(RID p_viewport, float p_pixels) = 0; virtual void viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) = 0; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 26f315a45426..e26d0682f258 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -187,6 +187,14 @@ RID XRInterface::get_velocity_texture() { return RID(); } +RID XRInterface::get_velocity_depth_texture() { + return RID(); +} + +Size2i XRInterface::get_velocity_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..09a6c0e447ff 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -137,6 +137,8 @@ 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_velocity_depth_texture(); + virtual Size2i get_velocity_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 */