]> git.tdb.fi Git - libs/gl.git/commitdiff
Add a RenderTarget class to manage and annotate FBOs
authorMikko Rasa <tdb@tdb.fi>
Sat, 3 Dec 2016 16:03:12 +0000 (18:03 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 3 Dec 2016 16:03:12 +0000 (18:03 +0200)
I want to have deferred lighting support at some point, and it requires
multiple outputs from the geometry rasterization stage.  There needs to
be some way to annotate which buffer holds which information.

source/pipeline.cpp
source/pipeline.h
source/rendertarget.cpp [new file with mode: 0644]
source/rendertarget.h [new file with mode: 0644]

index e468825748a640ed53cab215c6fc0ebee8c14fb3..a5d7436aaace7cd48074d6c6c02a9ecefda94948 100644 (file)
@@ -183,7 +183,7 @@ void Pipeline::render(Renderer &renderer, const Tag &tag) const
 
        if(target[0])
        {
-               Framebuffer &fbo = (samples ? target_ms->fbo : target[0]->fbo);
+               Framebuffer &fbo = (samples ? target_ms : target[0])->get_framebuffer();
                fbo.bind();
                fbo.clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT);
        }
@@ -218,16 +218,18 @@ void Pipeline::render(Renderer &renderer, const Tag &tag) const
                Blend::unbind();
 
                if(samples)
-                       target[0]->fbo.blit_from(target_ms->fbo, COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT, false);
+                       target[0]->blit_from(*target_ms);
 
                for(unsigned i=0; i<postproc.size(); ++i)
                {
                        unsigned j = i%2;
                        if(i+1<postproc.size())
-                               target[1-j]->fbo.bind();
+                               target[1-j]->get_framebuffer().bind();
                        else
                                out_fbo->bind();
-                       postproc[i]->render(renderer, target[j]->color, target[j]->depth);
+                       const Texture2D &color = target[j]->get_target_texture(RENDER_COLOR);
+                       const Texture2D &depth = target[j]->get_target_texture(RENDER_DEPTH);
+                       postproc[i]->render(renderer, color, depth);
                }
        }
 
@@ -250,7 +252,8 @@ void Pipeline::create_targets(unsigned recreate)
                target_ms = 0;
        }
 
-       PixelFormat fmt = (hdr ? RGB16F : RGB);
+       PixelFormat color_pf = (hdr ? RGB16F : RGB);
+       RenderTargetFormat fmt = (RENDER_COLOR,color_pf, RENDER_DEPTH);
        if(!postproc.empty() || samples)
        {
                if(!target[0])
@@ -260,7 +263,7 @@ void Pipeline::create_targets(unsigned recreate)
        }
 
        if(!target_ms && samples)
-               target_ms = new MultisampleTarget(width, height, samples, fmt);
+               target_ms = new RenderTarget(width, height, samples, fmt);
 }
 
 
@@ -298,35 +301,5 @@ Pipeline::Slot::Slot(const Renderable *r):
        renderable(r)
 { }
 
-
-Pipeline::RenderTarget::RenderTarget(unsigned w, unsigned h, PixelFormat f)
-{
-       color.set_min_filter(NEAREST);
-       color.set_mag_filter(NEAREST);
-       color.set_wrap(CLAMP_TO_EDGE);
-       color.storage(f, w, h);
-       fbo.attach(COLOR_ATTACHMENT0, color, 0);
-
-       depth.set_min_filter(NEAREST);
-       depth.set_mag_filter(NEAREST);
-       depth.set_wrap(CLAMP_TO_EDGE);
-       depth.storage(DEPTH_COMPONENT, w, h);
-       fbo.attach(DEPTH_ATTACHMENT, depth, 0);
-
-       fbo.require_complete();
-}
-
-
-Pipeline::MultisampleTarget::MultisampleTarget(unsigned w, unsigned h, unsigned s, PixelFormat f)
-{
-       color.storage_multisample(s, f, w, h);
-       fbo.attach(COLOR_ATTACHMENT0, color);
-
-       depth.storage_multisample(s, DEPTH_COMPONENT, w, h);
-       fbo.attach(DEPTH_ATTACHMENT, depth);
-
-       fbo.require_complete();
-}
-
 } // namespace GL
 } // namespace Msp
index d07e643176666f861e55dcb75a65f0de28b73021..e3f95994defff957a831987716901620aab94ad1 100644 (file)
@@ -6,6 +6,7 @@
 #include "framebuffer.h"
 #include "renderable.h"
 #include "renderbuffer.h"
+#include "rendertarget.h"
 #include "texture2d.h"
 
 namespace Msp {
@@ -78,24 +79,6 @@ private:
                Slot(const Renderable *);
        };
 
-       struct RenderTarget
-       {
-               Framebuffer fbo;
-               Texture2D color;
-               Texture2D depth;
-
-               RenderTarget(unsigned, unsigned, PixelFormat);
-       };
-
-       struct MultisampleTarget
-       {
-               Framebuffer fbo;
-               Renderbuffer color;
-               Renderbuffer depth;
-
-               MultisampleTarget(unsigned, unsigned, unsigned, PixelFormat);
-       };
-
        typedef std::list<Pass> PassList;
 
        PassList passes;
@@ -107,7 +90,7 @@ private:
        bool hdr;
        unsigned samples;
        RenderTarget *target[2];
-       MultisampleTarget *target_ms;
+       RenderTarget *target_ms;
        mutable bool in_frame;
 
 public:
diff --git a/source/rendertarget.cpp b/source/rendertarget.cpp
new file mode 100644 (file)
index 0000000..376af04
--- /dev/null
@@ -0,0 +1,183 @@
+#include <msp/core/maputils.h>
+#include "error.h"
+#include "renderbuffer.h"
+#include "rendertarget.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+RenderTargetFormat::RenderTargetFormat():
+       count(0)
+{ }
+
+RenderTargetFormat::RenderTargetFormat(RenderOutput o):
+       count(1)
+{
+       outputs[0] = o;
+}
+
+RenderTargetFormat RenderTargetFormat::operator,(RenderOutput o) const
+{
+       if(count>=MAX_OUTPUTS)
+               throw invalid_operation("RenderTargetFormat::operator,");
+
+       RenderTargetFormat result = *this;
+       result.outputs[result.count++] = o;
+
+       return result;
+}
+
+RenderTargetFormat RenderTargetFormat::operator,(PixelFormat f) const
+{
+       if(!count)
+               throw invalid_operation("RenderTargetFormat::operator,");
+
+       PixelFormat unsized = get_unsized_pixelformat(f);
+       unsigned size = get_component_size(f);
+       unsigned char out = outputs[count-1];
+       if(get_output_type(out)>=get_output_type(RENDER_DEPTH))
+       {
+               if(unsized!=DEPTH_COMPONENT)
+                       throw invalid_argument("RenderTargetformat::operator,");
+               if(size>1)
+                       --size;
+       }
+       else
+       {
+               if(unsized!=RGB && unsized!=RGBA)
+                       throw invalid_argument("RenderTargetformat::operator,");
+               if(size>3)
+                       --size;
+       }
+
+       out = (out&~15) | (size<<2) | (get_component_count(f)-1);
+       RenderTargetFormat result = *this;
+       result.outputs[result.count-1] = out;
+
+       return result;
+}
+
+int RenderTargetFormat::index(RenderOutput o)
+{
+       unsigned type = get_output_type(o);
+       unsigned i = 0;
+       for(const unsigned char *j=begin(); j!=end(); ++j, ++i)
+               if(get_output_type(*j)==type)
+                       return i;
+       return -1;
+}
+
+
+PixelFormat get_output_pixelformat(unsigned char o)
+{
+       unsigned ncomp = (o&3)+1;
+       unsigned size = ((o>>2)&3);
+       PixelFormat base;
+       if(get_output_type(o)>=get_output_type(RENDER_DEPTH))
+       {
+               base = DEPTH_COMPONENT;
+               if(size)
+                       ++size;
+       }
+       else
+       {
+               base = (ncomp==4 ? RGBA : RGB);
+               if(size==3)
+                       ++size;
+       }
+
+       if(size)
+               return get_sized_pixelformat(base, size);
+       else
+               return base;
+}
+
+
+RenderTarget::RenderTarget(unsigned w, unsigned h, const RenderTargetFormat &f)
+{
+       init(w, h, 0, f);
+}
+
+RenderTarget::RenderTarget(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
+{
+       init(w, h, s, f);
+}
+
+void RenderTarget::init(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
+{
+       width = w;
+       height = h;
+       samples = s;
+       format = f;
+
+       for(const unsigned char *i=format.begin(); i!=format.end(); ++i)
+       {
+               unsigned type = get_output_type(*i);
+               FramebufferAttachment att;
+               if(type>=get_output_type(RENDER_DEPTH))
+                       att = DEPTH_ATTACHMENT;
+               else
+                       att = static_cast<FramebufferAttachment>(COLOR_ATTACHMENT0+type);
+
+               PixelFormat pf = get_output_pixelformat(*i);
+
+               TargetBuffer tgt;
+               if(samples)
+               {
+                       tgt.buffer = new Renderbuffer;
+                       tgt.buffer->storage_multisample(samples, pf, width, height);
+                       fbo.attach(att, *tgt.buffer);
+               }
+               else
+               {
+                       tgt.texture = new Texture2D;
+                       tgt.texture->storage(pf, width, height);
+                       tgt.texture->set_filter(NEAREST);
+                       tgt.texture->set_wrap(CLAMP_TO_EDGE);
+                       fbo.attach(att, *tgt.texture);
+               }
+               buffers.push_back(tgt);
+       }
+
+       fbo.require_complete();
+}
+
+RenderTarget::~RenderTarget()
+{
+       for(vector<TargetBuffer>::iterator i=buffers.begin(); i!=buffers.end(); ++i)
+       {
+               if(samples)
+                       delete i->buffer;
+               else
+                       delete i->texture;
+       }
+}
+
+const Texture2D &RenderTarget::get_target_texture(unsigned i)
+{
+       if(i>=buffers.size())
+               throw out_of_range("RenderTarget::get_target_texture");
+       if(samples)
+               throw invalid_operation("RenderTarget::get_target_texture");
+
+       return *buffers[i].texture;
+}
+
+const Texture2D &RenderTarget::get_target_texture(RenderOutput o)
+{
+       int index = format.index(o);
+       if(index<0)
+               throw key_error(o);
+
+       return get_target_texture(index);
+}
+
+void RenderTarget::blit_from(const RenderTarget &other)
+{
+       fbo.blit_from(other.fbo, COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT, false);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/rendertarget.h b/source/rendertarget.h
new file mode 100644 (file)
index 0000000..e38f6f5
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef RENDERTARGET_H_
+#define RENDERTARGET_H_
+
+#include "framebuffer.h"
+#include "texture2d.h"
+
+namespace Msp {
+namespace GL {
+
+enum RenderOutput
+{
+       RENDER_COLOR = 0|3,
+       RENDER_DEPTH = 192|12
+};
+
+class RenderTargetFormat
+{
+private:
+       enum { MAX_OUTPUTS = 7 };
+
+       unsigned char count;
+       unsigned char outputs[MAX_OUTPUTS];
+
+public:
+       RenderTargetFormat();
+       RenderTargetFormat(RenderOutput);
+
+       RenderTargetFormat operator,(RenderOutput) const;
+       RenderTargetFormat operator,(PixelFormat) const;
+
+       bool empty() const { return !count; }
+       const unsigned char *begin() const { return outputs; }
+       const unsigned char *end() const { return outputs+count; }
+       int index(RenderOutput);
+};
+
+inline RenderTargetFormat operator,(RenderOutput o1, RenderOutput o2)
+{ return (RenderTargetFormat(o1), o2); }
+
+inline RenderTargetFormat operator,(RenderOutput o, PixelFormat f)
+{ return (RenderTargetFormat(o), f); }
+
+inline unsigned get_output_type(unsigned char o)
+{ return o>>4; }
+
+PixelFormat get_output_pixelformat(unsigned char);
+
+
+class RenderTarget
+{
+private:
+       union TargetBuffer
+       {
+               Texture2D *texture;
+               Renderbuffer *buffer;
+       };
+
+       unsigned width;
+       unsigned height;
+       unsigned samples;
+       RenderTargetFormat format;
+       std::vector<TargetBuffer> buffers;
+       Framebuffer fbo;
+
+public:
+       RenderTarget(unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
+       RenderTarget(unsigned, unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
+private:
+       RenderTarget(const RenderTarget &);
+       RenderTarget &operator=(const RenderTarget &);
+       void init(unsigned, unsigned, unsigned, const RenderTargetFormat &);
+public:
+       ~RenderTarget();
+
+       const RenderTargetFormat &get_format() const { return format; }
+       Framebuffer &get_framebuffer() { return fbo; }
+       const Texture2D &get_target_texture(unsigned);
+       const Texture2D &get_target_texture(RenderOutput);
+       void blit_from(const RenderTarget &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif