From 94cadd1618f93239b1cb0acbd4f958257c035c98 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Apr 2022 10:27:53 +0300 Subject: [PATCH] Rework multisample resolve to use resolve attachments Vulkan can only resolve a depth attachment as part of a subpass, and in order to do that the attachments must all be in the same framebuffer. --- source/backends/opengl/commands_backend.cpp | 27 +++--- source/backends/opengl/commands_backend.h | 2 +- .../backends/opengl/framebuffer_backend.cpp | 83 ++++++++++++++----- source/backends/opengl/framebuffer_backend.h | 3 + source/backends/vulkan/commands_backend.cpp | 2 +- source/backends/vulkan/commands_backend.h | 2 +- source/backends/vulkan/framebuffer_backend.h | 1 + source/core/framebuffer.cpp | 44 +++++++--- source/core/framebuffer.h | 20 ++++- source/render/renderer.cpp | 9 +- source/render/renderer.h | 4 +- source/render/rendertarget.cpp | 18 ++-- source/render/rendertarget.h | 3 +- source/render/sequence.cpp | 15 ++-- 14 files changed, 162 insertions(+), 71 deletions(-) diff --git a/source/backends/opengl/commands_backend.cpp b/source/backends/opengl/commands_backend.cpp index 76469ab3..0a4416c6 100644 --- a/source/backends/opengl/commands_backend.cpp +++ b/source/backends/opengl/commands_backend.cpp @@ -90,33 +90,32 @@ void OpenGLCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count glDispatchCompute(count_x, count_y, count_z); } -void OpenGLCommands::resolve_multisample(Framebuffer &target) +void OpenGLCommands::resolve_multisample() { - const Framebuffer *source = (pipeline_state ? pipeline_state->get_framebuffer() : 0); - if(!source) - throw invalid_operation("OpenGLCommands::draw"); + const Framebuffer *framebuffer = (pipeline_state ? pipeline_state->get_framebuffer() : 0); + if(!framebuffer) + throw invalid_operation("OpenGLCommands::resolve_multisample"); static Require _req(EXT_framebuffer_blit); - unsigned width = min(source->get_width(), target.get_width()); - unsigned height = min(source->get_height(), target.get_height()); - unsigned buffers = get_gl_buffer_bits(source->get_format())&get_gl_buffer_bits(target.get_format()); + unsigned width = framebuffer->get_width(); + unsigned height = framebuffer->get_height(); + unsigned buffers = get_gl_buffer_bits(framebuffer->get_format()); if(ARB_direct_state_access) { - target.refresh(); - glBlitNamedFramebuffer(source->id, target.id, 0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST); + framebuffer->refresh(); + glBlitNamedFramebuffer(framebuffer->id, framebuffer->resolve_id, 0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST); } else { - glBindFramebuffer(GL_READ_FRAMEBUFFER, source->id); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.id); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); - target.refresh(); + framebuffer->refresh(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->resolve_id); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST); - - glBindFramebuffer(GL_FRAMEBUFFER, source->id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->id); } } diff --git a/source/backends/opengl/commands_backend.h b/source/backends/opengl/commands_backend.h index e6a3161a..94ddc560 100644 --- a/source/backends/opengl/commands_backend.h +++ b/source/backends/opengl/commands_backend.h @@ -25,7 +25,7 @@ protected: void draw(const Batch &); void draw_instanced(const Batch &, unsigned); void dispatch(unsigned, unsigned, unsigned); - void resolve_multisample(Framebuffer &); + void resolve_multisample(); void begin_query(const QueryPool &, unsigned); void end_query(const QueryPool &, unsigned); diff --git a/source/backends/opengl/framebuffer_backend.cpp b/source/backends/opengl/framebuffer_backend.cpp index 18566ac3..8ba947df 100644 --- a/source/backends/opengl/framebuffer_backend.cpp +++ b/source/backends/opengl/framebuffer_backend.cpp @@ -43,6 +43,8 @@ OpenGLFramebuffer::~OpenGLFramebuffer() { if(id) glDeleteFramebuffers(1, &id); + if(resolve_id) + glDeleteFramebuffers(1, &resolve_id); } void OpenGLFramebuffer::set_system_format(const FrameFormat &fmt) @@ -69,6 +71,17 @@ bool OpenGLFramebuffer::is_format_supported(const FrameFormat &fmt) return true; } +void OpenGLFramebuffer::format_changed(const FrameFormat &format) +{ + if(format.get_samples()>1 && !resolve_id) + { + if(ARB_direct_state_access) + glCreateFramebuffers(1, &resolve_id); + else + glGenFramebuffers(1, &resolve_id); + } +} + void OpenGLFramebuffer::require_layered() { static Require _req(ARB_geometry_shader4); @@ -85,6 +98,25 @@ void OpenGLFramebuffer::update(unsigned mask) const { const Framebuffer &self = *static_cast(this); + update(mask, false); + if(self.has_resolve_attachments()) + { + if(!ARB_direct_state_access) + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_id); + + update(mask, true); + + if(!ARB_direct_state_access) + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id); + } +} + +void OpenGLFramebuffer::update(unsigned mask, bool resolve) const +{ + const Framebuffer &self = *static_cast(this); + unsigned obj_id = (resolve ? resolve_id : id); + Texture *Framebuffer::Attachment::*member = (resolve ? &Framebuffer::Attachment::resolve : &Framebuffer::Attachment::tex); + vector color_bufs; color_bufs.reserve(self.format.size()); unsigned i = 0; @@ -94,28 +126,29 @@ void OpenGLFramebuffer::update(unsigned mask) const if(mask&(1<target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0) - glNamedFramebufferTexture(id, gl_attach_point, attch.tex->id, attch.level); + if(tex->target==GL_TEXTURE_2D || tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0) + glNamedFramebufferTexture(obj_id, gl_attach_point, tex->id, attch.level); else - glNamedFramebufferTextureLayer(id, gl_attach_point, attch.tex->id, attch.level, attch.layer); + glNamedFramebufferTextureLayer(obj_id, gl_attach_point, tex->id, attch.level, attch.layer); } - else if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE) - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level); + else if(tex->target==GL_TEXTURE_2D || tex->target==GL_TEXTURE_2D_MULTISAMPLE) + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, tex->target, tex->id, attch.level); else if(attch.layer<0) - glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level); - else if(attch.tex->target==GL_TEXTURE_2D_ARRAY) - glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level, attch.layer); - else if(attch.tex->target==GL_TEXTURE_3D) - glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level, attch.layer); - else if(attch.tex->target==GL_TEXTURE_CUBE_MAP) - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast(attch.layer)), attch.tex->id, attch.level); + glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, tex->id, attch.level); + else if(tex->target==GL_TEXTURE_2D_ARRAY) + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, tex->id, attch.level, attch.layer); + else if(tex->target==GL_TEXTURE_3D) + glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, tex->target, tex->id, attch.level, attch.layer); + else if(tex->target==GL_TEXTURE_CUBE_MAP) + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast(attch.layer)), tex->id, attch.level); } else if(ARB_direct_state_access) - glNamedFramebufferTexture(id, gl_attach_point, 0, 0); + glNamedFramebufferTexture(obj_id, gl_attach_point, 0, 0); else glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0); } @@ -134,8 +167,8 @@ void OpenGLFramebuffer::update(unsigned mask) const { /* ARB_direct_state_access ties the availability of these functions to framebuffers themselves, so no further checks are needed. */ - glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]); - glNamedFramebufferReadBuffer(id, first_buffer); + glNamedFramebufferDrawBuffers(obj_id, color_bufs.size(), &color_bufs[0]); + glNamedFramebufferReadBuffer(obj_id, first_buffer); } else { @@ -148,10 +181,13 @@ void OpenGLFramebuffer::update(unsigned mask) const glReadBuffer(first_buffer); } - if(ARB_direct_state_access) - status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER); - else - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(!resolve) + { + if(ARB_direct_state_access) + status = glCheckNamedFramebufferStatus(obj_id, GL_FRAMEBUFFER); + else + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + } } void OpenGLFramebuffer::require_complete() const @@ -168,7 +204,14 @@ void OpenGLFramebuffer::set_debug_name(const string &name) { #ifdef DEBUG if(KHR_debug) + { glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str()); + if(resolve_id) + { + string resolve_name = name+" [resolve]"; + glObjectLabel(GL_FRAMEBUFFER, resolve_id, resolve_name.size(), resolve_name.c_str()); + } + } #else (void)name; #endif diff --git a/source/backends/opengl/framebuffer_backend.h b/source/backends/opengl/framebuffer_backend.h index 24f43441..b2e93614 100644 --- a/source/backends/opengl/framebuffer_backend.h +++ b/source/backends/opengl/framebuffer_backend.h @@ -15,6 +15,7 @@ class OpenGLFramebuffer: public NonCopyable protected: unsigned id = 0; + unsigned resolve_id = 0; mutable unsigned status; OpenGLFramebuffer(bool); @@ -23,10 +24,12 @@ protected: void set_system_format(const FrameFormat &); static bool is_format_supported(const FrameFormat &); + void format_changed(const FrameFormat &); static void require_layered(); void resize_system(unsigned, unsigned); void update(unsigned) const; + void update(unsigned, bool) const; void require_complete() const; void set_debug_name(const std::string &); diff --git a/source/backends/vulkan/commands_backend.cpp b/source/backends/vulkan/commands_backend.cpp index bca1a678..2273d919 100644 --- a/source/backends/vulkan/commands_backend.cpp +++ b/source/backends/vulkan/commands_backend.cpp @@ -250,7 +250,7 @@ void VulkanCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count vkCmd.Dispatch(count_x, count_y, count_z); } -void VulkanCommands::resolve_multisample(Framebuffer &) +void VulkanCommands::resolve_multisample() { throw logic_error("VulkanCommands::resolve_multisample is unimplemented"); } diff --git a/source/backends/vulkan/commands_backend.h b/source/backends/vulkan/commands_backend.h index 7c98b15f..feb7d077 100644 --- a/source/backends/vulkan/commands_backend.h +++ b/source/backends/vulkan/commands_backend.h @@ -70,7 +70,7 @@ protected: void draw(const Batch &); void draw_instanced(const Batch &, unsigned); void dispatch(unsigned, unsigned, unsigned); - void resolve_multisample(Framebuffer &); + void resolve_multisample(); void begin_query(const QueryPool &, unsigned); void end_query(const QueryPool &, unsigned); diff --git a/source/backends/vulkan/framebuffer_backend.h b/source/backends/vulkan/framebuffer_backend.h index 3fa75421..30ca1701 100644 --- a/source/backends/vulkan/framebuffer_backend.h +++ b/source/backends/vulkan/framebuffer_backend.h @@ -28,6 +28,7 @@ protected: ~VulkanFramebuffer(); bool is_format_supported(const FrameFormat &); + void format_changed(const FrameFormat &) { } static void require_layered() { } void update(unsigned) const; diff --git a/source/core/framebuffer.cpp b/source/core/framebuffer.cpp index 45eaff65..4f30dbb1 100644 --- a/source/core/framebuffer.cpp +++ b/source/core/framebuffer.cpp @@ -44,6 +44,7 @@ void Framebuffer::set_format(const FrameFormat &fmt) format = fmt; attachments.resize(format.size()); + format_changed(format); } void Framebuffer::update() const @@ -100,7 +101,7 @@ void Framebuffer::check_size() } } -void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned level, int layer, unsigned samples) +void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, Texture *res, unsigned level, int layer, unsigned samples) { if(format.empty() || attachments.empty()) throw invalid_operation("Framebuffer::attach"); @@ -113,7 +114,7 @@ void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned l { if(a==attch) { - attachments[i].set(tex, level, layer); + attachments[i].set(tex, res, level, layer); dirty |= 1<=0 ? attachments[i].resolve : 0); +} + +const Texture *Framebuffer::get_resolve_attachment(unsigned i) const +{ + return (iget_width(); - unsigned height = state.framebuffer->get_height(); - if(target.get_width()!=width || target.get_height()!=height) - throw incompatible_data("Renderer::resolve_multisample"); - apply_framebuffer(); commands.use_pipeline(&get_pipeline_state()); - commands.resolve_multisample(target); + commands.resolve_multisample(); } void Renderer::begin_query(const QueryPool &pool, unsigned index) diff --git a/source/render/renderer.h b/source/render/renderer.h index 3341d9aa..39ac462f 100644 --- a/source/render/renderer.h +++ b/source/render/renderer.h @@ -235,8 +235,8 @@ public: void dispatch(unsigned, unsigned = 1, unsigned = 1); /** Resolves multisample attachments from the active framebuffer into - target. */ - void resolve_multisample(Framebuffer &target); + their corresponding resolve attachments. */ + void resolve_multisample(); void begin_query(const QueryPool &, unsigned); void end_query(const QueryPool &, unsigned); diff --git a/source/render/rendertarget.cpp b/source/render/rendertarget.cpp index 00176710..7eba2b2b 100644 --- a/source/render/rendertarget.cpp +++ b/source/render/rendertarget.cpp @@ -15,23 +15,26 @@ RenderTarget::RenderTarget(unsigned w, unsigned h, const FrameFormat &f): height(h), fbo(f) { - textures.reserve(f.size()); + bool multisample = (f.get_samples()>1); + textures.reserve(f.size()*(1+multisample)); unsigned samples = f.get_samples(); for(FrameAttachment a: f) { PixelFormat pf = get_attachment_pixelformat(a); - if(samples>1) + Texture2D *tex2d = new Texture2D; + tex2d->storage(pf, width, height, 1); + + if(multisample) { Texture2DMultisample *tex2d_ms = new Texture2DMultisample; tex2d_ms->storage(pf, width, height, samples); - fbo.attach(a, *tex2d_ms); + fbo.attach(a, *tex2d_ms, tex2d); textures.push_back(tex2d_ms); + textures.push_back(tex2d); } else { - Texture2D *tex2d = new Texture2D; - tex2d->storage(pf, width, height, 1); fbo.attach(a, *tex2d); textures.push_back(tex2d); } @@ -49,7 +52,7 @@ const Texture2D &RenderTarget::get_target_texture(unsigned i) const if(i>=textures.size()) throw out_of_range("RenderTarget::get_target_texture"); if(fbo.get_format().get_samples()>1) - throw invalid_operation("RenderTarget::get_target_texture"); + i = i*2+1; return *static_cast(textures[i]); } @@ -67,6 +70,7 @@ void RenderTarget::set_debug_name(const string &name) { #ifdef DEBUG fbo.set_debug_name(name+" [FBO]"); + bool multisample = (fbo.get_format().get_samples()>1); unsigned i = 0; for(FrameAttachment a: fbo.get_format()) { @@ -81,6 +85,8 @@ void RenderTarget::set_debug_name(const string &name) tex_name = Msp::format("%s/color%d", name, attach_pt); textures[i++]->set_debug_name(tex_name+".tex"); + if(multisample) + textures[i++]->set_debug_name(tex_name+"_resolve.tex"); } #else (void)name; diff --git a/source/render/rendertarget.h b/source/render/rendertarget.h index b7d4af03..a59e382c 100644 --- a/source/render/rendertarget.h +++ b/source/render/rendertarget.h @@ -14,7 +14,8 @@ class Texture2D; Wraps a Framebuffer and its attachments for easier management. All attachments will be created as 2D or 2D multisample textures, depending on -the sample count of the format. +the sample count of the format. For multisampled formats, resolve attachments +are also created. */ class RenderTarget: public NonCopyable { diff --git a/source/render/sequence.cpp b/source/render/sequence.cpp index e2986a24..4f10026a 100644 --- a/source/render/sequence.cpp +++ b/source/render/sequence.cpp @@ -143,8 +143,12 @@ void Sequence::render(Renderer &renderer, Tag tag) const if(target[0]) { + RenderTarget *source = target[0]; if(target_ms) - renderer.resolve_multisample(target[0]->get_framebuffer()); + { + renderer.resolve_multisample(); + source = target_ms; + } renderer.set_depth_test(0); renderer.set_stencil_test(0); @@ -152,11 +156,12 @@ void Sequence::render(Renderer &renderer, Tag tag) const for(unsigned i=0; iget_framebuffer() : out_fbo); - const Texture2D &color = target[j]->get_target_texture(COLOR_ATTACHMENT); - const Texture2D &depth = target[j]->get_target_texture(DEPTH_ATTACHMENT); + unsigned j = 1-i%2; + renderer.set_framebuffer(i+1get_framebuffer() : out_fbo); + const Texture2D &color = source->get_target_texture(COLOR_ATTACHMENT); + const Texture2D &depth = source->get_target_texture(DEPTH_ATTACHMENT); postproc[i]->render(renderer, color, depth); + source = target[j]; } } } -- 2.45.2