]> git.tdb.fi Git - libs/gl.git/commitdiff
Rework multisample resolve to use resolve attachments
authorMikko Rasa <tdb@tdb.fi>
Tue, 19 Apr 2022 07:27:53 +0000 (10:27 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 19 Apr 2022 08:02:16 +0000 (11:02 +0300)
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.

14 files changed:
source/backends/opengl/commands_backend.cpp
source/backends/opengl/commands_backend.h
source/backends/opengl/framebuffer_backend.cpp
source/backends/opengl/framebuffer_backend.h
source/backends/vulkan/commands_backend.cpp
source/backends/vulkan/commands_backend.h
source/backends/vulkan/framebuffer_backend.h
source/core/framebuffer.cpp
source/core/framebuffer.h
source/render/renderer.cpp
source/render/renderer.h
source/render/rendertarget.cpp
source/render/rendertarget.h
source/render/sequence.cpp

index 76469ab395be70ef4da6c8273eacf9604af91159..0a4416c69814bea21aa00fda272c09642743e55f 100644 (file)
@@ -90,33 +90,32 @@ void OpenGLCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count
        glDispatchCompute(count_x, count_y, count_z);
 }
 
        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);
 
 
        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)
        {
 
        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
        {
        }
        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);
                glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, buffers, GL_NEAREST);
-
-               glBindFramebuffer(GL_FRAMEBUFFER, source->id);
+               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->id);
        }
 }
 
        }
 }
 
index e6a3161ad105f464e2d825baab1360d1ad0a7eb6..94ddc56026f6cb8c6e9fb9960ec8267566d4efbc 100644 (file)
@@ -25,7 +25,7 @@ protected:
        void draw(const Batch &);
        void draw_instanced(const Batch &, unsigned);
        void dispatch(unsigned, unsigned, unsigned);
        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);
 
        void begin_query(const QueryPool &, unsigned);
        void end_query(const QueryPool &, unsigned);
index 18566ac3c2553fd82ea1dd71ab735dc0f3cd3168..8ba947dfe1eb0528e91521fa3d853ec04a74e082 100644 (file)
@@ -43,6 +43,8 @@ OpenGLFramebuffer::~OpenGLFramebuffer()
 {
        if(id)
                glDeleteFramebuffers(1, &id);
 {
        if(id)
                glDeleteFramebuffers(1, &id);
+       if(resolve_id)
+               glDeleteFramebuffers(1, &resolve_id);
 }
 
 void OpenGLFramebuffer::set_system_format(const FrameFormat &fmt)
 }
 
 void OpenGLFramebuffer::set_system_format(const FrameFormat &fmt)
@@ -69,6 +71,17 @@ bool OpenGLFramebuffer::is_format_supported(const FrameFormat &fmt)
        return true;
 }
 
        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);
 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<const Framebuffer *>(this);
 
 {
        const Framebuffer &self = *static_cast<const Framebuffer *>(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<const Framebuffer *>(this);
+       unsigned obj_id = (resolve ? resolve_id : id);
+       Texture *Framebuffer::Attachment::*member = (resolve ? &Framebuffer::Attachment::resolve : &Framebuffer::Attachment::tex);
+
        vector<GLenum> color_bufs;
        color_bufs.reserve(self.format.size());
        unsigned i = 0;
        vector<GLenum> color_bufs;
        color_bufs.reserve(self.format.size());
        unsigned i = 0;
@@ -94,28 +126,29 @@ void OpenGLFramebuffer::update(unsigned mask) const
                if(mask&(1<<i))
                {
                        const Framebuffer::Attachment &attch = self.attachments[i];
                if(mask&(1<<i))
                {
                        const Framebuffer::Attachment &attch = self.attachments[i];
-                       if(attch.tex)
+                       Texture *tex = attch.*member;
+                       if(tex)
                        {
                                if(ARB_direct_state_access)
                                {
                        {
                                if(ARB_direct_state_access)
                                {
-                                       if(attch.tex->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
                                        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)
                                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<TextureCubeFace>(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<TextureCubeFace>(attch.layer)), tex->id, attch.level);
                        }
                        else if(ARB_direct_state_access)
                        }
                        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);
                }
                        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. */
        {
                /* 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
        {
        }
        else
        {
@@ -148,10 +181,13 @@ void OpenGLFramebuffer::update(unsigned mask) const
                        glReadBuffer(first_buffer);
        }
 
                        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
 }
 
 void OpenGLFramebuffer::require_complete() const
@@ -168,7 +204,14 @@ void OpenGLFramebuffer::set_debug_name(const string &name)
 {
 #ifdef DEBUG
        if(KHR_debug)
 {
 #ifdef DEBUG
        if(KHR_debug)
+       {
                glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
                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
 #else
        (void)name;
 #endif
index 24f43441b3aacb333821cb7d7823edf81b318153..b2e936147d73ce3c707ef15cabf5cc2af2d054cf 100644 (file)
@@ -15,6 +15,7 @@ class OpenGLFramebuffer: public NonCopyable
 
 protected:
        unsigned id = 0;
 
 protected:
        unsigned id = 0;
+       unsigned resolve_id = 0;
        mutable unsigned status;
 
        OpenGLFramebuffer(bool);
        mutable unsigned status;
 
        OpenGLFramebuffer(bool);
@@ -23,10 +24,12 @@ protected:
 
        void set_system_format(const FrameFormat &);
        static bool is_format_supported(const FrameFormat &);
 
        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;
        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 &);
        void require_complete() const;
 
        void set_debug_name(const std::string &);
index bca1a678940d75b1625a59ac03a62c719efd86e6..2273d91920e555741f86991c1694e761bc83a877 100644 (file)
@@ -250,7 +250,7 @@ void VulkanCommands::dispatch(unsigned count_x, unsigned count_y, unsigned count
        vkCmd.Dispatch(count_x, count_y, count_z);
 }
 
        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");
 }
 {
        throw logic_error("VulkanCommands::resolve_multisample is unimplemented");
 }
index 7c98b15f0d061bbe7ea43bce73e8ae9559c1996a..feb7d0778aaef40d4100445370402df92deee696 100644 (file)
@@ -70,7 +70,7 @@ protected:
        void draw(const Batch &);
        void draw_instanced(const Batch &, unsigned);
        void dispatch(unsigned, unsigned, unsigned);
        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);
 
        void begin_query(const QueryPool &, unsigned);
        void end_query(const QueryPool &, unsigned);
index 3fa75421cb03b2f61a1ca61e54561999c5441000..30ca17011a0fdc65e585a62c4211533cc6ca05b5 100644 (file)
@@ -28,6 +28,7 @@ protected:
        ~VulkanFramebuffer();
 
        bool is_format_supported(const FrameFormat &);
        ~VulkanFramebuffer();
 
        bool is_format_supported(const FrameFormat &);
+       void format_changed(const FrameFormat &) { }
        static void require_layered() { }
 
        void update(unsigned) const;
        static void require_layered() { }
 
        void update(unsigned) const;
index 45eaff656b65322a5d137133061e9e2ce35924ac..4f30dbb158b346a71d726fcd9f431da171193b9a 100644 (file)
@@ -44,6 +44,7 @@ void Framebuffer::set_format(const FrameFormat &fmt)
 
        format = fmt;
        attachments.resize(format.size());
 
        format = fmt;
        attachments.resize(format.size());
+       format_changed(format);
 }
 
 void Framebuffer::update() const
 }
 
 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");
 {
        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)
                {
        {
                if(a==attch)
                {
-                       attachments[i].set(tex, level, layer);
+                       attachments[i].set(tex, res, level, layer);
                        dirty |= 1<<i;
                        check_size();
                        return;
                        dirty |= 1<<i;
                        check_size();
                        return;
@@ -126,34 +127,34 @@ void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned l
 
 void Framebuffer::attach(FrameAttachment attch, Texture2D &tex, unsigned level)
 {
 
 void Framebuffer::attach(FrameAttachment attch, Texture2D &tex, unsigned level)
 {
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, 0, 0);
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, level, 0, 0);
 }
 
 }
 
-void Framebuffer::attach(FrameAttachment attch, Texture2DMultisample &tex)
+void Framebuffer::attach(FrameAttachment attch, Texture2DMultisample &tex, Texture2D *res)
 {
 {
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, 0, tex.get_samples());
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, res, 0, 0, tex.get_samples());
 }
 
 void Framebuffer::attach(FrameAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
 {
 }
 
 void Framebuffer::attach(FrameAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
 {
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, layer, 0);
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, level, layer, 0);
 }
 
 void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
 {
 }
 
 void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
 {
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, face, 0);
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, level, face, 0);
 }
 
 void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level)
 {
        require_layered();
 }
 
 void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level)
 {
        require_layered();
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, level, -1, 0);
 }
 
 void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level)
 {
        require_layered();
 }
 
 void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level)
 {
        require_layered();
-       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
+       set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, level, -1, 0);
 }
 
 void Framebuffer::detach(FrameAttachment attch)
 }
 
 void Framebuffer::detach(FrameAttachment attch)
@@ -184,6 +185,28 @@ const Texture *Framebuffer::get_attachment(unsigned i) const
        return (i<attachments.size() ? attachments[i].tex : 0);
 }
 
        return (i<attachments.size() ? attachments[i].tex : 0);
 }
 
+const Texture *Framebuffer::get_resolve_attachment(FrameAttachment attch) const
+{
+       if(attachments.empty())
+               return 0;
+
+       int i = format.index(attch);
+       return (i>=0 ? attachments[i].resolve : 0);
+}
+
+const Texture *Framebuffer::get_resolve_attachment(unsigned i) const
+{
+       return (i<attachments.size() ? attachments[i].resolve : 0);
+}
+
+bool Framebuffer::has_resolve_attachments() const
+{
+       for(const Attachment &a: attachments)
+               if(a.resolve)
+                       return true;
+       return false;
+}
+
 void Framebuffer::require_complete() const
 {
        bool layered = (!attachments.empty() && attachments.front().layer<0);
 void Framebuffer::require_complete() const
 {
        bool layered = (!attachments.empty() && attachments.front().layer<0);
@@ -199,9 +222,10 @@ void Framebuffer::require_complete() const
 }
 
 
 }
 
 
-void Framebuffer::Attachment::set(Texture &t, unsigned l, int z)
+void Framebuffer::Attachment::set(Texture &t, Texture *r, unsigned l, int z)
 {
        tex = &t;
 {
        tex = &t;
+       resolve = r;
        level = l;
        layer = z;
 }
        level = l;
        layer = z;
 }
index 5476596317000dbecd53eca3146adce3dcc4407c..b3359a34aeb5998052a728b1aff98f733f2fd74f 100644 (file)
@@ -28,6 +28,12 @@ Uses one or more textures as buffers to draw into.  Framebuffers can contain
 multiple color buffers to match multiple outputs from a fragment shader, but
 only one depth and stencil buffer.
 
 multiple color buffers to match multiple outputs from a fragment shader, but
 only one depth and stencil buffer.
 
+If a framebuffer has a multisampled format, each attachment can optionally have
+a single-sampled resolve attachment associated with it.  When a multisample
+resolve operation is performed on the framebuffer (such as by a Sequence), the
+sample values from the primary attachments are converted to a single value per
+pixel in the corresponding resolve attachments.
+
 RenderTarget provides a higher-level interface which manages the textures as
 well as the framebuffer itself.
 */
 RenderTarget provides a higher-level interface which manages the textures as
 well as the framebuffer itself.
 */
@@ -39,10 +45,11 @@ protected:
        struct Attachment
        {
                Texture *tex = 0;
        struct Attachment
        {
                Texture *tex = 0;
+               Texture *resolve = 0;
                unsigned level = 0;
                int layer = 0;
 
                unsigned level = 0;
                int layer = 0;
 
-               void set(Texture &, unsigned, int);
+               void set(Texture &, Texture *, unsigned, int);
                void clear();
        };
 
                void clear();
        };
 
@@ -79,7 +86,7 @@ public:
 protected:
        void update() const;
        void check_size();
 protected:
        void update() const;
        void check_size();
-       void set_attachment(FrameAttachment, Texture &, unsigned, int, unsigned);
+       void set_attachment(FrameAttachment, Texture &, Texture *, unsigned, int, unsigned);
 
 public:
        /** Attaches a texture to the framebuffer.  Only the attachment point
 
 public:
        /** Attaches a texture to the framebuffer.  Only the attachment point
@@ -88,7 +95,11 @@ public:
        in the framebuffer for this attachment point. */
        void attach(FrameAttachment attch, Texture2D &, unsigned level = 0);
 
        in the framebuffer for this attachment point. */
        void attach(FrameAttachment attch, Texture2D &, unsigned level = 0);
 
-       void attach(FrameAttachment attch, Texture2DMultisample &);
+       /** Attaches a multisample texture to the framebuffer.  The texture must
+       have a sample count matching the frame format.  A resolve attachment may
+       be given as well and used to resolve the multisample image into a single
+       value per texel. */
+       void attach(FrameAttachment attch, Texture2DMultisample &, Texture2D *);
 
        /** Attaches a single layer from a 3-dimensional texture to the
        framebuffer. */
 
        /** Attaches a single layer from a 3-dimensional texture to the
        framebuffer. */
@@ -105,6 +116,9 @@ public:
 
        const Texture *get_attachment(FrameAttachment) const;
        const Texture *get_attachment(unsigned) const;
 
        const Texture *get_attachment(FrameAttachment) const;
        const Texture *get_attachment(unsigned) const;
+       const Texture *get_resolve_attachment(FrameAttachment) const;
+       const Texture *get_resolve_attachment(unsigned) const;
+       bool has_resolve_attachments() const;
 
        /** Ensures that the framebuffer is complete, throwing an exception if it
        isn't. */
 
        /** Ensures that the framebuffer is complete, throwing an exception if it
        isn't. */
index ce0b3447b2b02a43df1029db2b358cd4f8aa2cb8..50d3887369a30cbcdf6b8b1f99e83ea33ca08a94 100644 (file)
@@ -304,21 +304,16 @@ void Renderer::dispatch(unsigned count_x, unsigned count_y, unsigned count_z)
        commands.dispatch(count_x, count_y, count_z);
 }
 
        commands.dispatch(count_x, count_y, count_z);
 }
 
-void Renderer::resolve_multisample(Framebuffer &target)
+void Renderer::resolve_multisample()
 {
        const State &state = get_state();
 
        if(!state.framebuffer)
                throw invalid_operation("Renderer::resolve_multisample");
 
 {
        const State &state = get_state();
 
        if(!state.framebuffer)
                throw invalid_operation("Renderer::resolve_multisample");
 
-       unsigned width = state.framebuffer->get_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());
        apply_framebuffer();
        commands.use_pipeline(&get_pipeline_state());
-       commands.resolve_multisample(target);
+       commands.resolve_multisample();
 }
 
 void Renderer::begin_query(const QueryPool &pool, unsigned index)
 }
 
 void Renderer::begin_query(const QueryPool &pool, unsigned index)
index 3341d9aa061e038f3acb59c5515cc6f776c9d5d7..39ac462fbb96d4901c8f9ec884ba141dce04ecb6 100644 (file)
@@ -235,8 +235,8 @@ public:
        void dispatch(unsigned, unsigned = 1, unsigned = 1);
 
        /** Resolves multisample attachments from the active framebuffer into
        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);
 
        void begin_query(const QueryPool &, unsigned);
        void end_query(const QueryPool &, unsigned);
index 00176710c4a796047e02c462e899fde26d0c8ffd..7eba2b2beb379c1a779d9fd0cc058b2cf1cc1f65 100644 (file)
@@ -15,23 +15,26 @@ RenderTarget::RenderTarget(unsigned w, unsigned h, const FrameFormat &f):
        height(h),
        fbo(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);
 
        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);
                {
                        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_ms);
+                       textures.push_back(tex2d);
                }
                else
                {
                }
                else
                {
-                       Texture2D *tex2d = new Texture2D;
-                       tex2d->storage(pf, width, height, 1);
                        fbo.attach(a, *tex2d);
                        textures.push_back(tex2d);
                }
                        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)
        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<const Texture2D *>(textures[i]);
 }
 
        return *static_cast<const Texture2D *>(textures[i]);
 }
@@ -67,6 +70,7 @@ void RenderTarget::set_debug_name(const string &name)
 {
 #ifdef DEBUG
        fbo.set_debug_name(name+" [FBO]");
 {
 #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())
        {
        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");
                        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;
        }
 #else
        (void)name;
index b7d4af03840f4e81196fad3222255b74be345732..a59e382c105f81ad18916056deab8a94e27f333c 100644 (file)
@@ -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
 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
 {
 */
 class RenderTarget: public NonCopyable
 {
index e2986a249904a8c1fae136c6ef90e21a6d5c26bb..4f10026ad497386b39ebb3a89fbd43e5e974518d 100644 (file)
@@ -143,8 +143,12 @@ void Sequence::render(Renderer &renderer, Tag tag) const
 
        if(target[0])
        {
 
        if(target[0])
        {
+               RenderTarget *source = target[0];
                if(target_ms)
                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);
 
                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; i<postproc.size(); ++i)
                {
 
                for(unsigned i=0; i<postproc.size(); ++i)
                {
-                       unsigned j = i%2;
-                       renderer.set_framebuffer(i+1<postproc.size() ? &target[1-j]->get_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+1<postproc.size() ? &target[j]->get_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);
                        postproc[i]->render(renderer, color, depth);
+                       source = target[j];
                }
        }
 }
                }
        }
 }