From caddce14dd7b3139c8f1616038a91a1f9eccaa50 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Thu, 26 Oct 2023 12:10:38 +1100 Subject: [PATCH] Add 3D MSAA and scaling support to GLES3 --- doc/classes/ProjectSettings.xml | 2 + drivers/gles3/rasterizer_scene_gles3.cpp | 178 ++++++- drivers/gles3/rasterizer_scene_gles3.h | 3 +- drivers/gles3/storage/config.cpp | 35 ++ drivers/gles3/storage/config.h | 12 + drivers/gles3/storage/material_storage.cpp | 4 - .../storage/render_scene_buffers_gles3.cpp | 494 +++++++++++++++++- .../storage/render_scene_buffers_gles3.h | 68 ++- drivers/gles3/storage/texture_storage.cpp | 44 +- drivers/gles3/storage/texture_storage.h | 8 +- drivers/gles3/storage/utilities.cpp | 19 +- drivers/gles3/storage/utilities.h | 22 + .../openxr/doc_classes/OpenXRInterface.xml | 2 + 13 files changed, 834 insertions(+), 57 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 3fc9e94427ec..0e82bc1497b0 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2778,9 +2778,11 @@ If true and foveation is supported, will automatically adjust foveation level based on framerate up to the level set on [member xr/openxr/foveation_level]. + [b]Note:[/b] Only works on compatibility renderer. Applied foveation level if supported: 0 = off, 1 = low, 2 = medium, 3 = high. + [b]Note:[/b] Only works on compatibility renderer. Specify the default reference space. diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 205f9a552ef6..22092233a961 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2238,9 +2238,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ bool fb_cleared = false; - Size2i screen_size; - screen_size.x = rb->width; - screen_size.y = rb->height; + Size2i screen_size = rb->internal_size; bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; @@ -2360,8 +2358,10 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); - glViewport(0, 0, rb->width, rb->height); + GLuint fbo = rb->get_render_fbo(); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); @@ -2463,25 +2463,48 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } if (scene_state.used_screen_texture || scene_state.used_depth_texture) { - texture_storage->copy_scene_to_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo); - if (scene_state.used_screen_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer); - } - if (scene_state.used_depth_texture) { - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, - 0, 0, rt->size.x, rt->size.y, - GL_DEPTH_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); - glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth); - } - glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + Size2i size; + GLuint backbuffer_fbo = 0; + GLuint backbuffer = 0; + GLuint backbuffer_depth = 0; + + if (rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_OFF) { + texture_storage->check_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); // note, badly names, this just allocates! + + size = rt->size; + backbuffer_fbo = rt->backbuffer_fbo; + backbuffer = rt->backbuffer; + backbuffer_depth = rt->backbuffer_depth; + } else { + rb->check_backbuffer(scene_state.used_screen_texture, scene_state.used_depth_texture); + size = rb->get_internal_size(); + backbuffer_fbo = rb->get_backbuffer_fbo(); + backbuffer = rb->get_backbuffer(); + backbuffer_depth = rb->get_backbuffer_depth(); + } + + if (backbuffer_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backbuffer_fbo); + if (scene_state.used_screen_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, backbuffer); + } + if (scene_state.used_depth_texture) { + glBlitFramebuffer(0, 0, size.x, size.y, + 0, 0, size.x, size.y, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, backbuffer_depth); + } + } + + // Bound framebuffer may have changed, so change it back + glBindFramebuffer(GL_FRAMEBUFFER, fbo); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -2498,14 +2521,110 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } if (rb.is_valid()) { - _render_buffers_debug_draw(rb, p_shadow_atlas); + _render_buffers_debug_draw(rb, p_shadow_atlas, fbo); } glDisable(GL_BLEND); + + _render_post_processing(&render_data); + texture_storage->render_target_disable_clear_request(rb->render_target); glActiveTexture(GL_TEXTURE0); } +void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_render_data) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + Ref rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + + RID render_target = rb->get_render_target(); + Size2i internal_size = rb->get_internal_size(); + Size2i target_size = rb->get_target_size(); + uint32_t view_count = rb->get_view_count(); + + // bool msaa2d_needs_resolve = texture_storage->render_target_get_msaa(render_target) != RS::VIEWPORT_MSAA_DISABLED && !GLES3::Config::get_singleton()->rt_msaa_supported; + bool msaa3d_needs_resolve = rb->get_msaa_needs_resolve(); + GLuint fbo_msaa_3d = rb->get_msaa3d_fbo(); + GLuint fbo_int = rb->get_internal_fbo(); + GLuint fbo_rt = texture_storage->render_target_get_fbo(render_target); // TODO if MSAA 2D is enabled and we're not using rt_msaa, get 2D render target here. + + if (view_count == 1) { + // Resolve if needed. + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + // We can use blit to copy things over + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_msaa_3d); + + if (fbo_int != 0) { + // We can't combine resolve and scaling, so resolve into our internal buffer + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_int); + } else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + } + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + if (fbo_int != 0) { + // TODO If we have glow or other post processing, we upscale only depth here, post processing will also do scaling. + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + } else if ((fbo_msaa_3d != 0 && msaa3d_needs_resolve) || (fbo_int != 0)) { + // TODO investigate if it's smarter to cache these FBOs + GLuint fbos[2]; // read and write + glGenFramebuffers(2, fbos); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + + if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { + GLuint read_color = rb->get_msaa3d_color(); + GLuint read_depth = rb->get_msaa3d_depth(); + GLuint write_color = 0; + GLuint write_depth = 0; + + if (fbo_int != 0) { + write_color = rb->get_internal_color(); + write_depth = rb->get_internal_depth(); + } else { + write_color = texture_storage->render_target_get_color(render_target); + write_depth = texture_storage->render_target_get_depth(render_target); + } + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + if (fbo_int != 0) { + GLuint read_color = rb->get_internal_color(); + GLuint read_depth = rb->get_internal_depth(); + GLuint write_color = texture_storage->render_target_get_color(render_target); + GLuint write_depth = texture_storage->render_target_get_depth(render_target); + + for (uint32_t v = 0; v < view_count; v++) { + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); + + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); + glDeleteFramebuffers(2, fbos); + } +} + template void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); @@ -3123,7 +3242,7 @@ Ref RasterizerSceneGLES3::render_buffers_create() { return rb; } -void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref p_render_buffers, RID p_shadow_atlas) { +void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref p_render_buffers, RID p_shadow_atlas, GLuint p_fbo) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton(); @@ -3200,8 +3319,11 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Reffbo); - glViewport(0, 0, rt->size.width, rt->size.height); + + // Set back to FBO + glBindFramebuffer(GL_FRAMEBUFFER, p_fbo); + Size2i size = p_render_buffers->get_internal_size(); + glViewport(0, 0, size.width, size.height); glBindTexture(GL_TEXTURE_2D, shadow_atlas_texture); copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5))); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 7d3c8896dad2..beb597ff9988 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -518,6 +518,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); + void _render_post_processing(const RenderDataGLES3 *p_render_data); template _FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false); @@ -530,7 +531,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - void _render_buffers_debug_draw(Ref p_render_buffers, RID p_shadow_atlas); + void _render_buffers_debug_draw(Ref p_render_buffers, RID p_shadow_atlas, GLuint p_fbo); /* Camera Attributes */ diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 4bf6165fe91c..1200bf9626bf 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -92,14 +92,49 @@ Config::Config() { anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); } + glGetIntegerv(GL_MAX_SAMPLES, &msaa_max_samples); +#ifdef WEB_ENABLED + msaa_supported = (msaa_max_samples > 0); +#else + msaa_supported = extensions.has("GL_EXT_framebuffer_multisample"); +#endif +#ifndef IOS_ENABLED + msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample"); multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#endif + #ifdef ANDROID_ENABLED + // These are GLES only + rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture"); + rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture"); + if (multiview_supported) { eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); if (eglFramebufferTextureMultiviewOVR == nullptr) { multiview_supported = false; } } + + if (msaa_multiview_supported) { + eglTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)eglGetProcAddress("glTexStorage3DMultisample"); + if (eglTexStorage3DMultisample == nullptr) { + msaa_multiview_supported = false; + } + } + + if (rt_msaa_supported) { + eglFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress("glFramebufferTexture2DMultisampleEXT"); + if (eglFramebufferTexture2DMultisampleEXT == nullptr) { + rt_msaa_supported = false; + } + } + + if (rt_msaa_multiview_supported) { + eglFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"); + if (eglFramebufferTextureMultisampleMultiviewOVR == nullptr) { + rt_msaa_multiview_supported = false; + } + } #endif force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index b2dd98f02584..2ad13331adcd 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -42,6 +42,9 @@ #ifdef ANDROID_ENABLED typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean); +typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei); +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei); #endif namespace GLES3 { @@ -82,9 +85,18 @@ class Config { bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + GLint msaa_max_samples = 0; + bool msaa_supported = false; + bool msaa_multiview_supported = false; + bool rt_msaa_supported = false; + bool rt_msaa_multiview_supported = false; bool multiview_supported = false; + #ifdef ANDROID_ENABLED PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; + PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr; + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr; + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr; #endif static Config *get_singleton() { return singleton; }; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index b7a42141fd31..7aac62e57d7b 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -3000,10 +3000,6 @@ void SceneShaderData::set_code(const String &p_code) { WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend."); } - if (uses_depth_texture) { - WARN_PRINT_ONCE_ED("Reading from the depth texture is not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } - if (uses_normal_texture) { WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends."); } diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 829574cae04a..33bc9ab2602d 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -31,30 +31,508 @@ #ifdef GLES3_ENABLED #include "render_scene_buffers_gles3.h" +#include "config.h" #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 +#endif RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } +GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) { + FBDEF new_fbo; + +#ifdef ANDROID_ENABLED + // There shouldn't be more then 3 entries in this... + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) { + return cached_fbo.fbo; + } + } + + new_fbo.color = p_color; + new_fbo.depth = p_depth; + + glGenFramebuffers(1, &new_fbo.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo); + + if (p_view_count > 1) { + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count); + } else { + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + + glDeleteFramebuffers(1, &new_fbo.fbo); + + new_fbo.fbo = 0; + } else { + // cache it! + msaa3d.cached_fbos.push_back(new_fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + return new_fbo.fbo; +} + void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) { - //internal_size.x = p_config->get_internal_size().x; // ignore for now - //internal_size.y = p_config->get_internal_size().y; - width = p_config->get_target_size().x; - height = p_config->get_target_size().y; - //scaling_3d_mode = p_config->get_scaling_3d_mode() + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + free_render_buffer_data(); + + internal_size = p_config->get_internal_size(); + target_size = p_config->get_target_size(); + scaling_3d_mode = p_config->get_scaling_3d_mode(); //fsr_sharpness = p_config->get_fsr_sharpness(); //texture_mipmap_bias = p_config->get_texture_mipmap_bias(); render_target = p_config->get_render_target(); - //msaa = p_config->get_msaa_3d(); + msaa3d.mode = p_config->get_msaa_3d(); //screen_space_aa = p_config->get_screen_space_aa(); //use_debanding = p_config->get_use_debanding(); - view_count = p_config->get_view_count(); + view_count = config->multiview_supported ? p_config->get_view_count() : 1; - free_render_buffer_data(); + ERR_FAIL_COND(view_count == 0); + bool use_multiview = view_count > 1; + + // Check our scaling mode + if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size == target_size) { + // If size matches, we won't use scaling. + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; + } else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR) { + // We only support bilinear scaling atm. + WARN_PRINT_ONCE("GLES only supports bilinear scaling."); + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; + } + + bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF; // TODO also need this if doing post processing like glow + if (use_internal_buffer) { + // Setup our internal buffer. + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + // Create our color buffer. + glGenTextures(1, &internal3d.color); + glBindTexture(texture_target, internal3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * 4, "3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &internal3d.depth); + glBindTexture(texture_target, internal3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D depth texture"); + + // Create our internal 3D FBO. + // Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects. + glGenFramebuffers(1, &internal3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, internal3d.fbo); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_intermediate_buffers(); + WARN_PRINT("Could not create 3D buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + // Check if we support MSAA. + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) { + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) { + WARN_PRINT_ONCE("Multiview MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } + + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED) { + // Setup MSAA. + const GLsizei samples[] = { 1, 2, 4, 8 }; + msaa3d.samples = samples[msaa3d.mode]; + + // Constrain by limits of OpenGL driver. + if (msaa3d.samples > config->msaa_max_samples) { + msaa3d.samples = config->msaa_max_samples; + } + + if (!use_multiview && !config->rt_msaa_supported) { + // Render to texture extensions not supported? fall back to MSAA framebuffer through GL_EXT_framebuffer_multisample. + // Note, if 2D MSAA matches 3D MSAA and we're not scaling, it would be ideal if we reuse our 2D MSAA buffer here. + // We can't however because we don't trigger a change in configuration if 2D MSAA changes. + // We'll accept the overhead in this situation. + + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenRenderbuffers(1, &msaa3d.color); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color render buffer"); + + // Create our depth buffer. + glGenRenderbuffers(1, &msaa3d.depth); + glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y); + GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED) + } else if (use_multiview && !config->rt_msaa_multiview_supported) { + // Render to texture extensions not supported? fall back to MSAA textures through GL_EXT_multiview_texture_multisample. + msaa3d.needs_resolve = true; + msaa3d.check_fbo_cache = false; + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + + // Create our color buffer. + glGenTextures(1, &msaa3d.color); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color texture"); + + // Create our depth buffer. + glGenTextures(1, &msaa3d.depth); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth); + +#ifdef ANDROID_ENABLED + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#else + glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); +#endif + + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * msaa3d.samples, "MSAA 3D depth texture"); + + // Create our MSAA 3D FBO. + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) // Only supported on OpenGLES! + } else if (!use_internal_buffer) { + // We are going to render directly into our render target textures, + // these can change from frame to frame as we cycle through swapchains, + // hence we'll use our FBO cache here. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = true; +#endif +#ifdef ANDROID_ENABLED + } else if (use_internal_buffer) { + // We can combine MSAA and scaling/effects. + msaa3d.needs_resolve = false; + msaa3d.check_fbo_cache = false; + + // We render to our internal textures, MSAA is only done in tile memory only. + // On mobile this means MSAA never leaves tile memory = efficiency! + glGenFramebuffers(1, &msaa3d.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo); + + if (use_multiview) { + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, msaa3d.samples, 0, view_count); + glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, msaa3d.samples, 0, view_count); + } else { + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, internal3d.color, 0, msaa3d.samples); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, internal3d.depth, 0, msaa3d.samples); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_msaa3d_buffers(); + WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + } else { + // HUH? how did we get here? + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } + } else { + msaa3d.samples = 1; + msaa3d.check_fbo_cache = false; + } +} + +void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() { + for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { + GLuint fbo = cached_fbo.fbo; + glDeleteFramebuffers(1, &fbo); + } + msaa3d.cached_fbos.clear(); + + if (msaa3d.fbo) { + glDeleteFramebuffers(1, &msaa3d.fbo); + msaa3d.fbo = 0; + } + + if (msaa3d.color != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.color); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.color); + } + msaa3d.color = 0; + } + + if (msaa3d.depth != 0) { + if (view_count == 1) { + GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.depth); + } else { + GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.depth); + } + msaa3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::_clear_intermediate_buffers() { + if (internal3d.fbo) { + glDeleteFramebuffers(1, &internal3d.fbo); + internal3d.fbo = 0; + } + + if (internal3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.color); + internal3d.color = 0; + } + + if (internal3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(internal3d.depth); + internal3d.depth = 0; + } +} + +void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_depth) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + // Setup our back buffer + + if (backbuffer3d.fbo == 0) { + glGenFramebuffers(1, &backbuffer3d.fbo); + } + + glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo); + + bool is_transparent = texture_storage->render_target_get_transparent(render_target); + GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; + GLuint color_format = GL_RGBA; + GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + + bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + + if (backbuffer3d.color == 0 && p_need_color) { + glGenTextures(1, &backbuffer3d.color); + glBindTexture(texture_target, backbuffer3d.color); + + if (use_multiview) { + glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * 4, "3D Back buffer color texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer3d.color, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, backbuffer3d.color, 0); + } + } + + if (backbuffer3d.depth == 0 && p_need_depth) { + glGenTextures(1, &backbuffer3d.depth); + glBindTexture(texture_target, backbuffer3d.depth); + + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D back buffer depth texture"); + +#ifndef IOS_ENABLED + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count); + } else { +#else + { +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0); + } + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + _clear_back_buffers(); + WARN_PRINT("Could not create 3D back buffers, status: " + texture_storage->get_framebuffer_error(status)); + } + + glBindTexture(texture_target, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void RenderSceneBuffersGLES3::_clear_back_buffers() { + if (backbuffer3d.fbo) { + glDeleteFramebuffers(1, &backbuffer3d.fbo); + backbuffer3d.fbo = 0; + } + + if (backbuffer3d.color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.color); + backbuffer3d.color = 0; + } + + if (backbuffer3d.depth != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.depth); + backbuffer3d.depth = 0; + } } void RenderSceneBuffersGLES3::free_render_buffer_data() { + _clear_msaa3d_buffers(); + _clear_intermediate_buffers(); + _clear_back_buffers(); +} + +GLuint RenderSceneBuffersGLES3::get_render_fbo() { + if (msaa3d.check_fbo_cache) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + GLuint color = texture_storage->render_target_get_color(render_target); + GLuint depth = texture_storage->render_target_get_depth(render_target); + + return _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count); + } else if (msaa3d.fbo != 0) { + // We have an MSAA fbo, render to our MSAA buffer + return msaa3d.fbo; + } else if (internal3d.fbo != 0) { + // We have an internal buffer, render to our internal buffer! + return internal3d.fbo; + } else { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + return texture_storage->render_target_get_fbo(render_target); + } } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 543e1aeb152b..95097f54d880 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -41,21 +41,40 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); public: - // Original implementation, need to investigate which ones we'll keep like this and what we'll change... - - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; + Size2i internal_size; // Size of the buffer we render 3D content to. + Size2i target_size; // Size of our output buffer (render target). + RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; //float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + //bool use_taa = false; //bool use_debanding = false; uint32_t view_count = 1; RID render_target; - //built-in textures used for ping pong image processing and blurring + struct FBDEF { + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + }; + + struct RTMSAA3D { + RS::ViewportMSAA mode = RS::VIEWPORT_MSAA_DISABLED; + bool needs_resolve = false; + GLsizei samples = 1; + GLuint color = 0; + GLuint depth = 0; + GLuint fbo = 0; + + bool check_fbo_cache = false; + Vector cached_fbos; + } msaa3d; // MSAA buffers used to render 3D + + FBDEF internal3d; // buffers used to either render 3D (scaled/post) or to resolve MSAA into + + FBDEF backbuffer3d; // our back buffer + + // Built-in textures used for ping pong image processing and blurring. struct Blur { RID texture; @@ -72,6 +91,12 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { Blur blur[2]; //the second one starts from the first mipmap private: + void _clear_msaa3d_buffers(); + void _clear_intermediate_buffers(); + void _clear_back_buffers(); + + GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); + public: virtual ~RenderSceneBuffersGLES3(); virtual void configure(const RenderSceneBuffersConfiguration *p_config) override; @@ -81,6 +106,33 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { virtual void set_use_debanding(bool p_use_debanding) override{}; void free_render_buffer_data(); + + void check_backbuffer(bool p_need_color, bool p_need_depth); // check if we need to initialise our backbuffer + + GLuint get_render_fbo(); + GLuint get_msaa3d_fbo() const { return msaa3d.fbo; } + GLuint get_msaa3d_color() const { return msaa3d.color; } + GLuint get_msaa3d_depth() const { return msaa3d.depth; } + bool get_msaa_needs_resolve() const { return msaa3d.needs_resolve; } + GLuint get_internal_fbo() const { return internal3d.fbo; } + GLuint get_internal_color() const { return internal3d.color; } + GLuint get_internal_depth() const { return internal3d.depth; } + GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; } + GLuint get_backbuffer() const { return backbuffer3d.color; } + GLuint get_backbuffer_depth() const { return backbuffer3d.depth; } + + // Getters + + _FORCE_INLINE_ RID get_render_target() const { return render_target; } + _FORCE_INLINE_ uint32_t get_view_count() const { return view_count; } + _FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; } + _FORCE_INLINE_ Size2i get_target_size() const { return target_size; } + _FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; } + //_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; } + _FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa3d.mode; } + //_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; } + //_FORCE_INLINE_ bool get_use_taa() const { return use_taa; } + //_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; } }; #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index dec0a3f811f6..77dd15fa28d4 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1732,7 +1732,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, rt->color, 0); } // depth @@ -1765,7 +1765,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { #else { #endif - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, rt->depth, 0); } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -1874,7 +1874,7 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } -void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { +void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) { if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) { return; } @@ -1935,7 +1935,7 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo } } void TextureStorage::_clear_render_target(RenderTarget *rt) { - // there is nothing to clear when DIRECT_TO_SCREEN is used + // there is nothing else to clear when DIRECT_TO_SCREEN is used if (rt->direct_to_screen) { return; } @@ -2229,6 +2229,7 @@ void TextureStorage::render_target_clear_used(RID p_render_target) { void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); + ERR_FAIL_COND(rt->direct_to_screen); if (p_msaa == rt->msaa) { return; } @@ -2284,6 +2285,41 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); } +GLuint TextureStorage::render_target_get_fbo(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + return rt->fbo; +} + +GLuint TextureStorage::render_target_get_color(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.color.is_valid()) { + Texture *texture = get_texture(rt->overridden.color); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->color; + } +} + +GLuint TextureStorage::render_target_get_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 0); + + if (rt->overridden.depth.is_valid()) { + Texture *texture = get_texture(rt->overridden.depth); + ERR_FAIL_NULL_V(texture, 0); + + return texture->tex_id; + } else { + return rt->depth; + } +} + void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 27e358ec31c7..2ce719eb0871 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -484,7 +484,7 @@ class TextureStorage : public RendererTextureStorage { /* Texture API */ - Texture *get_texture(RID p_rid) { + Texture *get_texture(RID p_rid) const { Texture *texture = texture_owner.get_or_null(p_rid); if (texture && texture->is_proxy) { return texture_owner.get_or_null(texture->proxy_to); @@ -602,7 +602,7 @@ class TextureStorage : public RendererTextureStorage { RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }; - void copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); + void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture); virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; @@ -636,6 +636,10 @@ class TextureStorage : public RendererTextureStorage { void render_target_disable_clear_request(RID p_render_target) override; void render_target_do_clear_request(RID p_render_target) override; + GLuint render_target_get_fbo(RID p_render_target) const; + GLuint render_target_get_color(RID p_render_target) const; + GLuint render_target_get_depth(RID p_render_target) const; + virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override; GLuint render_target_get_sdf_texture(RID p_render_target); diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 72bcbe879c7a..c73ab5a56655 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -85,6 +85,21 @@ Utilities::~Utilities() { } } + if (render_buffer_mem_cache) { + uint32_t leaked_data_size = 0; + for (const KeyValue &E : render_buffer_allocs_cache) { +#ifdef DEV_ENABLED + ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes."); +#else + ERR_PRINT("Render buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes."); +#endif + leaked_data_size += E.value.size; + } + if (leaked_data_size < render_buffer_mem_cache) { + ERR_PRINT("Render buffer cache is not empty. There may be an additional render buffer leak of " + itos(render_buffer_mem_cache - leaked_data_size) + " bytes."); + } + } + if (buffer_mem_cache) { uint32_t leaked_data_size = 0; @@ -362,11 +377,11 @@ void Utilities::update_memory_info() { uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) { if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) { - return texture_mem_cache; + return texture_mem_cache + render_buffer_mem_cache; // Add render buffer memory to our texture mem. } else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) { return buffer_mem_cache; } else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) { - return texture_mem_cache + buffer_mem_cache; + return texture_mem_cache + buffer_mem_cache + render_buffer_mem_cache; } return 0; } diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index 76e4d510de1b..ea7bf4a4c29f 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -50,9 +50,11 @@ class Utilities : public RendererUtilities { uint32_t size = 0; }; HashMap buffer_allocs_cache; + HashMap render_buffer_allocs_cache; HashMap texture_allocs_cache; uint64_t buffer_mem_cache = 0; + uint64_t render_buffer_mem_cache = 0; uint64_t texture_mem_cache = 0; public: @@ -88,6 +90,26 @@ class Utilities : public RendererUtilities { buffer_allocs_cache.erase(p_id); } + _FORCE_INLINE_ void render_buffer_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { + render_buffer_mem_cache += p_size; +#ifdef DEV_ENABLED + ERR_FAIL_COND_MSG(render_buffer_allocs_cache.has(p_id), "trying to allocate render buffer with name " + p_name + " but ID already used by " + render_buffer_allocs_cache[p_id].name); +#endif + ResourceAllocation resource_allocation; + resource_allocation.size = p_size; +#ifdef DEV_ENABLED + resource_allocation.name = p_name + ": " + itos((uint64_t)p_id); +#endif + render_buffer_allocs_cache[p_id] = resource_allocation; + } + + _FORCE_INLINE_ void render_buffer_free_data(GLuint p_id) { + ERR_FAIL_COND(!render_buffer_allocs_cache.has(p_id)); + glDeleteRenderbuffers(1, &p_id); + render_buffer_mem_cache -= render_buffer_allocs_cache[p_id].size; + render_buffer_allocs_cache.erase(p_id); + } + // Records that data was allocated for state tracking purposes. _FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") { texture_mem_cache += p_size; diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 6d1c215ffcb2..666d20d7e74c 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -129,9 +129,11 @@ Enable dynamic foveation adjustment, the interface must be initialized before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level]. + [b]Note:[/b] Only works on compatibility renderer. Set foveation level from 0 (off) to 3 (high), the interface must be initialized before this is accessible. + [b]Note:[/b] Only works on compatibility renderer. The render size multiplier for the current HMD. Must be set before the interface has been initialized.