Merge pull request #97151 from devloglogan/motion-vectors

Implement motion vectors in compatibility renderer
This commit is contained in:
Thaddeus Crews 2025-10-03 12:01:10 -05:00
commit d9ba9ba3da
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
14 changed files with 871 additions and 341 deletions

View file

@ -112,6 +112,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);
GLint max_vertex_output;
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);

View file

@ -60,6 +60,7 @@ public:
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;
uint32_t max_shader_varyings = 0;
@ -113,7 +114,13 @@ public:
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; }

View file

@ -1244,13 +1244,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";
@ -1272,15 +1272,15 @@ 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"] = String::num(Math::PI);
actions.renames["TAU"] = String::num(Math::TAU);
actions.renames["E"] = String::num(Math::E);
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["IN_SHADOW_PASS"] = "IN_SHADOW_PASS";
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";
@ -1323,10 +1323,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)";

View file

@ -872,7 +872,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.
@ -1024,7 +1024,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);
}
@ -1044,12 +1044,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;
}
void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) {
@ -1189,16 +1205,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
@ -1226,9 +1243,11 @@ void MeshStorage::_mesh_instance_remove_surface(MeshInstance *mi, int p_surface)
surface.vertex_buffers[1] = 0;
}
if (surface.vertex_buffer != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffer);
surface.vertex_buffer = 0;
for (int i = 0; i < 2; i++) {
if (surface.vertex_buffers[i] != 0) {
GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffers[i]);
surface.vertex_buffers[i] = 0;
}
}
mi->surfaces.remove_at(p_surface);
@ -1268,7 +1287,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);
@ -1310,7 +1329,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);
@ -1337,6 +1356,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.
@ -1348,7 +1397,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;
}
@ -1383,9 +1432,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();
@ -1407,15 +1456,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;
@ -1451,7 +1502,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);
@ -1497,7 +1548,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);
}
@ -1541,9 +1592,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) {
@ -1571,9 +1624,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);
}
@ -1604,7 +1657,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<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float));
Vector<uint8_t> 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);
@ -1628,7 +1681,7 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const {
float *w = multimesh->data_cache.ptrw();
if (multimesh->buffer_set) {
Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float));
Vector<uint8_t> 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();
@ -1971,6 +2024,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &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.
@ -2016,7 +2073,7 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &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);
@ -2029,7 +2086,7 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &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);
}
@ -2068,14 +2125,14 @@ Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());
Vector<float> ret;
if (multimesh->buffer == 0 || multimesh->instances == 0) {
if (multimesh->buffer[multimesh->current_buffer] == 0 || multimesh->instances == 0) {
return Vector<float>();
} 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<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float));
Vector<uint8_t> 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();
@ -2175,53 +2232,27 @@ 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;
uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances;
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);
GLint region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float);
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);
}
for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) {
multimesh->data_cache_dirty_regions[i] = false;
}
multimesh->data_cache_used_dirty_regions = 0;
// 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;
}
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);
}
}
multimesh->current_buffer = new_buffer_index;
multimesh->last_change = RSG::rasterizer->get_frame_number();
}
_update_dirty_multimesh(multimesh, uses_motion_vectors);
multimesh_dirty_list = multimesh->dirty_list;
multimesh->dirty_list = nullptr;
@ -2231,6 +2262,86 @@ void MeshStorage::_update_dirty_multimeshes() {
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();
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);
}
for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) {
p_multimesh->data_cache_dirty_regions[i] = false;
}
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);
}
}
}
}
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);
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);
}
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 */
RID MeshStorage::skeleton_allocate() {

View file

@ -36,6 +36,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"
@ -70,6 +71,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];
@ -152,9 +156,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;
@ -163,6 +167,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<Surface> surfaces;
LocalVector<float> blend_weights;
@ -199,7 +208,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;
@ -239,7 +251,7 @@ private:
mutable RID_Owner<Mesh, true> 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);
void _mesh_surface_clear(Mesh *mesh, int p_surface);
/* Mesh Instance API */
@ -418,7 +430,7 @@ public:
}
// 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<Mesh::Surface *>(p_surface);
s->version_lock.lock();
@ -426,7 +438,7 @@ public:
// 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.
@ -439,7 +451,7 @@ public:
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;
@ -461,7 +473,7 @@ public:
// 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;
@ -470,14 +482,24 @@ public:
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();
@ -488,7 +510,7 @@ public:
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;
@ -532,6 +554,9 @@ public:
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);
@ -563,7 +588,17 @@ public:
_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
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 {

View file

@ -1169,6 +1169,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.

View file

@ -195,6 +195,8 @@ private:
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;
@ -397,6 +399,20 @@ public:
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, false);

View file

@ -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

View file

@ -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;
@ -2265,7 +2261,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;
@ -2437,6 +2433,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 = rt->view_count;
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(vformat("Could not create motion vector render target, status: %s.", 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);
@ -2578,6 +2628,12 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
return;
}
for (KeyValue<uint32_t, GLuint> &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<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) {
glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr());
@ -2630,7 +2686,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) {
@ -2659,7 +2714,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);
}
@ -2708,7 +2763,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
@ -2727,9 +2782,10 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
// Remember what our current color output is.
RID was_color_texture = render_target_get_texture(p_render_target);
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;
}
@ -2740,8 +2796,8 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
}
_clear_render_target(rt);
_update_render_target(rt);
return;
_update_render_target_color(rt);
create_new_color_fbo = false;
}
if (!rt->overridden.is_overridden) {
@ -2751,6 +2807,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.depth_has_stencil = p_depth_texture.is_null();
rt->overridden.velocity = p_velocity_texture;
rt->overridden.velocity_depth = p_velocity_depth_texture;
rt->overridden.is_overridden = true;
// Update to our new color output.
@ -2771,25 +2829,51 @@ void TextureStorage::render_target_set_override(RID p_render_target, RID p_color
rt->depth_has_stencil = cache->get().depth_has_stencil;
rt->size = cache->get().size;
rt->texture = p_color_texture;
return;
create_new_color_fbo = false;
}
_update_render_target(rt);
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);
RenderTarget::RTOverridden::FBOCacheEntry new_entry;
new_entry.fbo = rt->fbo;
new_entry.color = rt->color;
new_entry.depth = rt->depth;
new_entry.depth_has_stencil = rt->depth_has_stencil;
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);
RBMap<uint32_t, GLuint>::Element *fbo = rt->overridden.velocity_fbo_cache.find(velocity_hash_key);
if (fbo != nullptr) {
rt->overridden.velocity_fbo = fbo->get();
create_new_velocity_fbo = false;
}
if (p_depth_texture.is_null()) {
new_entry.allocated_textures.push_back(rt->depth);
if (p_velocity_texture.is_null()) {
for (KeyValue<uint32_t, GLuint> &E : rt->overridden.velocity_fbo_cache) {
glDeleteFramebuffers(1, &E.value);
}
rt->overridden.velocity_fbo_cache.clear();
rt->overridden.velocity_fbo = 0;
create_new_velocity_fbo = false;
}
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 {
@ -2813,6 +2897,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;
}
void TextureStorage::render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt);
@ -2838,6 +2929,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, const 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);
@ -2846,7 +2951,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);
}
}
@ -2872,8 +2977,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 {
@ -2909,7 +3015,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 {
@ -2929,7 +3035,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 {

View file

@ -359,6 +359,8 @@ struct RenderTarget {
GLuint backbuffer_depth = 0;
bool depth_has_stencil = true;
Size2i velocity_target_size;
bool hdr = false; // For Compatibility this effects both 2D and 3D rendering!
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
@ -390,6 +392,7 @@ struct RenderTarget {
RID color;
RID depth;
RID velocity;
RID velocity_depth;
struct FBOCacheEntry {
GLuint fbo;
@ -400,6 +403,9 @@ struct RenderTarget {
bool depth_has_stencil;
};
RBMap<uint32_t, FBOCacheEntry> fbo_cache;
GLuint velocity_fbo = 0;
RBMap<uint32_t, GLuint> velocity_fbo_cache;
} overridden;
RID texture;
@ -464,7 +470,8 @@ private:
mutable RID_Owner<RenderTarget> 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);
@ -707,15 +714,15 @@ public:
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 { return RID(); }
virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override;
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(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, const Size2i &p_target_size) override {}
virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const override { return Size2i(); }
virtual void render_target_set_velocity_target_size(RID p_render_target, const 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);