Add 3D MSAA and scaling support to GLES3

This commit is contained in:
Bastiaan Olij 2023-10-26 12:10:38 +11:00
parent d76c1d0e51
commit caddce14dd
13 changed files with 832 additions and 55 deletions

View file

@ -2778,9 +2778,11 @@
</member> </member>
<member name="xr/openxr/foveation_dynamic" type="bool" setter="" getter="" default="false"> <member name="xr/openxr/foveation_dynamic" type="bool" setter="" getter="" default="false">
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]. 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.
</member> </member>
<member name="xr/openxr/foveation_level" type="int" setter="" getter="" default="&quot;0&quot;"> <member name="xr/openxr/foveation_level" type="int" setter="" getter="" default="&quot;0&quot;">
Applied foveation level if supported: 0 = off, 1 = low, 2 = medium, 3 = high. Applied foveation level if supported: 0 = off, 1 = low, 2 = medium, 3 = high.
[b]Note:[/b] Only works on compatibility renderer.
</member> </member>
<member name="xr/openxr/reference_space" type="int" setter="" getter="" default="&quot;1&quot;"> <member name="xr/openxr/reference_space" type="int" setter="" getter="" default="&quot;1&quot;">
Specify the default reference space. Specify the default reference space.

View file

@ -2238,9 +2238,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
bool fb_cleared = false; bool fb_cleared = false;
Size2i screen_size; Size2i screen_size = rb->internal_size;
screen_size.x = rb->width;
screen_size.y = rb->height;
bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME;
@ -2360,8 +2358,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
} }
} }
glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); GLuint fbo = rb->get_render_fbo();
glViewport(0, 0, rb->width, rb->height);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, rb->internal_size.x, rb->internal_size.y);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
@ -2463,25 +2463,48 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
} }
if (scene_state.used_screen_texture || scene_state.used_depth_texture) { 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); Size2i size;
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); GLuint backbuffer_fbo = 0;
glReadBuffer(GL_COLOR_ATTACHMENT0); GLuint backbuffer = 0;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo); GLuint backbuffer_depth = 0;
if (scene_state.used_screen_texture) {
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, if (rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_OFF) {
0, 0, rt->size.x, rt->size.y, texture_storage->check_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture); // note, badly names, this just allocates!
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); size = rt->size;
glBindTexture(GL_TEXTURE_2D, rt->backbuffer); 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 (scene_state.used_depth_texture) {
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, if (backbuffer_fbo != 0) {
0, 0, rt->size.x, rt->size.y, glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
GL_DEPTH_BUFFER_BIT, GL_NEAREST); glReadBuffer(GL_COLOR_ATTACHMENT0);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backbuffer_fbo);
glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth); 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);
}
} }
glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
// Bound framebuffer may have changed, so change it back
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
} }
RENDER_TIMESTAMP("Render 3D Transparent Pass"); RENDER_TIMESTAMP("Render 3D Transparent Pass");
@ -2498,14 +2521,110 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
} }
if (rb.is_valid()) { if (rb.is_valid()) {
_render_buffers_debug_draw(rb, p_shadow_atlas); _render_buffers_debug_draw(rb, p_shadow_atlas, fbo);
} }
glDisable(GL_BLEND); glDisable(GL_BLEND);
_render_post_processing(&render_data);
texture_storage->render_target_disable_clear_request(rb->render_target); texture_storage->render_target_disable_clear_request(rb->render_target);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_render_data) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
Ref<RenderSceneBuffersGLES3> 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 <PassMode p_pass_mode> template <PassMode p_pass_mode>
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) { 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(); GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
@ -3123,7 +3242,7 @@ Ref<RenderSceneBuffers> RasterizerSceneGLES3::render_buffers_create() {
return rb; return rb;
} }
void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas) { void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton(); GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton();
@ -3200,8 +3319,11 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
} }
} }
} }
glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
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); glBindTexture(GL_TEXTURE_2D, shadow_atlas_texture);
copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5))); copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5)));

View file

@ -518,6 +518,7 @@ private:
void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); 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_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<RenderGeometryInstance *> &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_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &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 <PassMode p_pass_mode> template <PassMode p_pass_mode>
_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); _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 @@ protected:
float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_amount = 0.25;
float screen_space_roughness_limiter_limit = 0.18; float screen_space_roughness_limiter_limit = 0.18;
void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas); void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo);
/* Camera Attributes */ /* Camera Attributes */

View file

@ -92,14 +92,49 @@ Config::Config() {
anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); 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"); multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
#endif
#ifdef ANDROID_ENABLED #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) { if (multiview_supported) {
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
if (eglFramebufferTextureMultiviewOVR == nullptr) { if (eglFramebufferTextureMultiviewOVR == nullptr) {
multiview_supported = false; 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 #endif
force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");

View file

@ -42,6 +42,9 @@
#ifdef ANDROID_ENABLED #ifdef ANDROID_ENABLED
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); 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 #endif
namespace GLES3 { namespace GLES3 {
@ -82,9 +85,18 @@ public:
bool support_anisotropic_filter = false; bool support_anisotropic_filter = false;
float anisotropic_level = 0.0f; 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; bool multiview_supported = false;
#ifdef ANDROID_ENABLED #ifdef ANDROID_ENABLED
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
#endif #endif
static Config *get_singleton() { return singleton; }; static Config *get_singleton() { return singleton; };

View file

@ -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."); 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) { 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."); WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends.");
} }

View file

@ -31,30 +31,508 @@
#ifdef GLES3_ENABLED #ifdef GLES3_ENABLED
#include "render_scene_buffers_gles3.h" #include "render_scene_buffers_gles3.h"
#include "config.h"
#include "texture_storage.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() { RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
free_render_buffer_data(); 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) { void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) {
//internal_size.x = p_config->get_internal_size().x; // ignore for now GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
//internal_size.y = p_config->get_internal_size().y; GLES3::Config *config = GLES3::Config::get_singleton();
width = p_config->get_target_size().x;
height = p_config->get_target_size().y; free_render_buffer_data();
//scaling_3d_mode = p_config->get_scaling_3d_mode()
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(); //fsr_sharpness = p_config->get_fsr_sharpness();
//texture_mipmap_bias = p_config->get_texture_mipmap_bias(); //texture_mipmap_bias = p_config->get_texture_mipmap_bias();
render_target = p_config->get_render_target(); 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(); //screen_space_aa = p_config->get_screen_space_aa();
//use_debanding = p_config->get_use_debanding(); //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() { 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 #endif // GLES3_ENABLED

View file

@ -41,21 +41,40 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers {
GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers);
public: public:
// Original implementation, need to investigate which ones we'll keep like this and what we'll change... Size2i internal_size; // Size of the buffer we render 3D content to.
Size2i target_size; // Size of our output buffer (render target).
int internal_width = 0; RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
int internal_height = 0;
int width = 0;
int height = 0;
//float fsr_sharpness = 0.2f; //float fsr_sharpness = 0.2f;
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
//RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
//bool use_taa = false;
//bool use_debanding = false; //bool use_debanding = false;
uint32_t view_count = 1; uint32_t view_count = 1;
RID render_target; 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<FBDEF> 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 { struct Blur {
RID texture; RID texture;
@ -72,6 +91,12 @@ public:
Blur blur[2]; //the second one starts from the first mipmap Blur blur[2]; //the second one starts from the first mipmap
private: 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: public:
virtual ~RenderSceneBuffersGLES3(); virtual ~RenderSceneBuffersGLES3();
virtual void configure(const RenderSceneBuffersConfiguration *p_config) override; virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
@ -81,6 +106,33 @@ public:
virtual void set_use_debanding(bool p_use_debanding) override{}; virtual void set_use_debanding(bool p_use_debanding) override{};
void free_render_buffer_data(); 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 #endif // GLES3_ENABLED

View file

@ -1732,7 +1732,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
#else #else
{ {
#endif #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 // depth
@ -1765,7 +1765,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
#else #else
{ {
#endif #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); 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); 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) { if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) {
return; return;
} }
@ -1935,7 +1935,7 @@ void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const boo
} }
} }
void TextureStorage::_clear_render_target(RenderTarget *rt) { 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) { if (rt->direct_to_screen) {
return; 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) { 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); RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt); ERR_FAIL_NULL(rt);
ERR_FAIL_COND(rt->direct_to_screen);
if (p_msaa == rt->msaa) { if (p_msaa == rt->msaa) {
return; return;
} }
@ -2284,6 +2285,41 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); 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) { 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); RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_NULL(rt); ERR_FAIL_NULL(rt);

View file

@ -484,7 +484,7 @@ public:
/* Texture API */ /* Texture API */
Texture *get_texture(RID p_rid) { Texture *get_texture(RID p_rid) const {
Texture *texture = texture_owner.get_or_null(p_rid); Texture *texture = texture_owner.get_or_null(p_rid);
if (texture && texture->is_proxy) { if (texture && texture->is_proxy) {
return texture_owner.get_or_null(texture->proxy_to); return texture_owner.get_or_null(texture->proxy_to);
@ -602,7 +602,7 @@ public:
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; 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); }; 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 RID render_target_create() override;
virtual void render_target_free(RID p_rid) override; virtual void render_target_free(RID p_rid) override;
@ -636,6 +636,10 @@ public:
void render_target_disable_clear_request(RID p_render_target) override; void render_target_disable_clear_request(RID p_render_target) override;
void render_target_do_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 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; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
GLuint render_target_get_sdf_texture(RID p_render_target); GLuint render_target_get_sdf_texture(RID p_render_target);

View file

@ -85,6 +85,21 @@ Utilities::~Utilities() {
} }
} }
if (render_buffer_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &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) { if (buffer_mem_cache) {
uint32_t leaked_data_size = 0; 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) { uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) {
if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) { 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) { } else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) {
return buffer_mem_cache; return buffer_mem_cache;
} else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) { } 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; return 0;
} }

View file

@ -50,9 +50,11 @@ private:
uint32_t size = 0; uint32_t size = 0;
}; };
HashMap<GLuint, ResourceAllocation> buffer_allocs_cache; HashMap<GLuint, ResourceAllocation> buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> render_buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> texture_allocs_cache; HashMap<GLuint, ResourceAllocation> texture_allocs_cache;
uint64_t buffer_mem_cache = 0; uint64_t buffer_mem_cache = 0;
uint64_t render_buffer_mem_cache = 0;
uint64_t texture_mem_cache = 0; uint64_t texture_mem_cache = 0;
public: public:
@ -88,6 +90,26 @@ public:
buffer_allocs_cache.erase(p_id); 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. // 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 = "") { _FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") {
texture_mem_cache += p_size; texture_mem_cache += p_size;

View file

@ -129,9 +129,11 @@
</member> </member>
<member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false"> <member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false">
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]. 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.
</member> </member>
<member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0"> <member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0">
Set foveation level from 0 (off) to 3 (high), the interface must be initialized before this is accessible. 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.
</member> </member>
<member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0"> <member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0">
The render size multiplier for the current HMD. Must be set before the interface has been initialized. The render size multiplier for the current HMD. Must be set before the interface has been initialized.