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);
}
}
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);
{
if(id)
glDeleteFramebuffers(1, &id);
+ if(resolve_id)
+ glDeleteFramebuffers(1, &resolve_id);
}
void OpenGLFramebuffer::set_system_format(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);
{
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;
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(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
- 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<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)
- 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);
}
{
/* 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
{
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
{
#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
protected:
unsigned id = 0;
+ unsigned resolve_id = 0;
mutable unsigned status;
OpenGLFramebuffer(bool);
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 &);
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");
}
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);
~VulkanFramebuffer();
bool is_format_supported(const FrameFormat &);
+ void format_changed(const FrameFormat &) { }
static void require_layered() { }
void update(unsigned) const;
format = fmt;
attachments.resize(format.size());
+ format_changed(format);
}
void Framebuffer::update() const
}
}
-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(a==attch)
{
- attachments[i].set(tex, level, layer);
+ attachments[i].set(tex, res, level, layer);
dirty |= 1<<i;
check_size();
return;
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)
{
- 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)
{
- 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();
- 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();
- 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)
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::Attachment::set(Texture &t, unsigned l, int z)
+void Framebuffer::Attachment::set(Texture &t, Texture *r, unsigned l, int z)
{
tex = &t;
+ resolve = r;
level = l;
layer = z;
}
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.
*/
struct Attachment
{
Texture *tex = 0;
+ Texture *resolve = 0;
unsigned level = 0;
int layer = 0;
- void set(Texture &, unsigned, int);
+ void set(Texture &, Texture *, unsigned, int);
void clear();
};
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
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. */
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. */
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");
- 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());
- commands.resolve_multisample(target);
+ commands.resolve_multisample();
}
void Renderer::begin_query(const QueryPool &pool, unsigned index)
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);
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);
}
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]);
}
{
#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())
{
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;
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
{
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);
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);
+ source = target[j];
}
}
}