Advertisement

Blitting FBO Color Attachments

Started by March 10, 2020 12:15 AM
8 comments, last by JensF 4 years, 10 months ago

Hi!

SOLVED: For anyone stumbling onto this issue. When you are using glDrawBuffers() and using a call to glDrawBuffer() you are overwriting your glDrawBuffers() state. It needs to be reset to its orignal glDrawBuffers() state.

I am wondering if someone can help me out.

I have 2 FBOs + MRT and they have identical attachments(4 color attachments each). using glBlitFrameBuffer works as expected for the depth buffer and for one color_attachment. However, when i blit multiple color attachments, things go bad. I have done a lot of research and tried a lot of different methods, none works. I am not using renderBufferStorage because my textures have different internalFormats(RGBA and RGB16F). This sounds like the same problem, except i am not using multisampling, only MRT.

OpenGL version 4.3

Why: I want to create a state of the terrain pre- lighting calculations so that i only need to render the terrain if there are changes(ie. camera movement). This is for deferred shading→ another deferred renderer which combines these FBO's and then runs through the lighting pass.

At first i expected that i should use glDrawBuffers which i am using in the initial setup, but there is no glReadBuffers and therefore i am assuming i have no way to link them. I might be wrong here, not an expert yet ?

//CODE

//FBO - Original code writting in Golang, should be easily tranferable to C++(will translate if requested)

gl.GenFramebuffers(1, &gb.ID)
gl.BindFramebuffer(gl.FRAMEBUFFER, gb.ID)

//setting up color attachments

gl.GenTextures(1, &gb.Position)
gl.BindTexture(gl.TEXTURE_2D, gb.Position)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, windowWidth, windowHeight, 0, gl.RGB, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, gb.Position, 0)

//repeated 3 times for the additional color attachments

var attachments = [4]uint32{gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3}
gl.DrawBuffers(4, &attachments[0])

gl.GenRenderbuffers(1, &gb.DepthBuffer)
gl.BindRenderbuffer(gl.RENDERBUFFER, gb.DepthBuffer)
gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT, windowWidth, windowHeight)
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, gb.DepthBuffer)

gl.BindFramebuffer(gl.FRAMEBUFFER, 0)

//Blitting - translated to C++ for easier readability for most.

glBindFramebuffer(GL_READ_FRAMEBUFFER, INbufferID);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OUTbufferID);

//works
glBlitFramebuffer(0, 0, windowWidth, windowHeight, 0, 0, windowWidth, windowHeight, GL_DEPTH_BUFFERBIT, GL_NEAREST);

//works
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, windowWidth, windowHeight, 0, 0, windowWidth, windowHeight, GL_COLOR_BUFFER _BIT, GL_LINEAR);


//now it fails - no errors, just way off from expected result and same goes for the next two.
glReadBuffer(GL_COLOR_ATTACHMENT1);
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, windowWidth, windowHeight, 0, 0, windowWidth, windowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);

Any thoughts? A geek losing his mind here =)

Can't say much because you don't show how you build the framebuffer. Also, do you have a debug context ? That can be very helpful !

I build the framebuffer like this (example with one colour and one depth attachment):

glCreateFramebuffers( 1, m_framebuffer );
// Add colour attachment
glCreateRenderbuffers( 1, m_colorAttachment );
if( GL_TRUE != glIsRenderbuffer( m_colorAttachment ) )
	std::cerr << "Error creating color renderbuffer.\n";
glNamedRenderbufferStorage( m_colorAttachment, colorFormat, m_sizeX, m_sizeY );
glNamedFramebufferRenderbuffer( m_framebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorAttachment );
// add depth attachment
glCreateRenderbuffers( 1, m_depthAttachment );
if( GL_TRUE != glIsRenderbuffer( m_depthAttachment ) )
	std::cerr << "Error creating depth renderbuffer.\n";
glNamedRenderbufferStorage( m_depthAttachment, depthFormat, m_sizeX, m_sizeY );
glNamedFramebufferRenderbuffer( m_framebuffer, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthAttachment );
// check for completeness (important, see documentation !)
if( GL_FRAMEBUFFER_COMPLETE != glCheckNamedFramebufferStatus( m_framebuffer, GL_FRAMEBUFFER ) { /* climb up the wall */ }

// In the renderloop:
// First our framebuffer object
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, m_framebuffer );
m_framebuffer->clear( omath::vec4{ 0.0f, 0.0f, 0.0f, 1.0f } );
// … rendering along
// Then blit out framebuffer to the default buffer:
glBindFramebuffer( GL_READ_FRAMEBUFFER, m_framebuffer );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
// clear to blue to distinguish between draw framebuffer; color::blue
glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBlitFramebuffer( 0, 0, m_window->getWidth(), m_window->getHeight(),
0, 0, m_window->getWidth(), m_window->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST );

I hope it's complete because it is copied from parts of my framework where i i have a framebuffer class, ogl 4.5)

It works. Hope that helps a bit.

Edit: just tried it with multiple colour attachments and only attachment 0 was copied.

Advertisement

@Green_Baron Thanks for the reply!

It don't ?

Yep, i am using the “modern” debugger, which is why i am using OpenGL 4.3. Also tested with NVIDIA NSIGHT and noting out of the ordinary either.

Added my FBO code, does it help?

The textures have different internal formats(RGBA and RGB16F), which makes me unable to use “glRenderbufferStorage”, right?

My problem is not blitting the depth buffer or a single color attachment, my problem is in blitting multiple color attachments. Everything works perfectly until i blit the second COLOR_ATTACHMENT.

Any chance you could run the two below versions to see if you get the expected output?

1. //works

glReadBuffer(GL_COLOR_ATTACHMENT_0);

glDrawBuffer(GL_COLOR_ATTACHMENT_0);

glBlitFramebuffer( 0, 0, m_window->getWidth(), m_window->getHeight(), 0, 0, m_window->getWidth(), m_window->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST );

2. //doesn’t work

glReadBuffer(GL_COLOR_ATTACHMENT_0);

glDrawBuffer(GL_COLOR_ATTACHMENT_0);

glBlitFramebuffer( 0, 0, m_window->getWidth(), m_window->getHeight(), 0, 0, m_window->getWidth(), m_window->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST );

glReadBuffer(GL_COLOR_ATTACHMENT_1);

glDrawBuffer(GL_COLOR_ATTACHMENT_1);

glBlitFramebuffer( 0, 0, m_window->getWidth(), m_window->getHeight(), 0, 0, m_window->getWidth(), m_window->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST );

Haven't tried because different versions.

My interpretation is that glReadBuffer does not work on an attachment object, but on a framebuffer object. Maybe the first version works accidentally because the attachment object 0 and the framebuffer object have the same handle number (though they are different objects). Edit: no, that was nonsense from my side.

An opengl debug context should fire with something like:

Debug message (1282): Source: API; Type: Error; Severity: high; GL_INVALID_OPERATION error generated. Framebuffer name must be generated before being bound.

when an attachment is bound instead of a framebuffer. But then again, i use different APIs, maybe i am wrong here. Edit: yes, so it seems :-)

Yeah, i would check a c/c++ version (almost) ready to paste …

I'm surprised it worked in the first case you pointed out. The purpose of the call to the glReadBuffer and glDrawBuffer is to is to specify the input and out of the framebuffer operations respectively. Framebuffer operations will read from the the framebuffer attachment bound to the target index and write to frame buffer attachment bound to the write index. In this case glBlitFramebuffer will read from the glReadBuffer and write to glDrawBuffer. The code you've display have the blit operation on the same source and destination which is undefined/illegal per the glBlitFrameBuffer documentation. Usually when you copy the source and destination should be different, otherwise there would be no need for a copy.
One potential solution is to have another FBO that you attach the target/destination color attachment to and then bind that to the glDrawBuffer. Then do the blit as usual.
If you are already doing this ( the code snippet posted was indicative of such), then another thing to look out for is the format of the data being blitted.
In either case take a look at the man page for glBlitFramebuffer and maybe it will shed more light on whats going on.

No i am not. Using two different FBOs:

glBindFramebuffer(GL_READ_FRAMEBUFFER, INbufferID);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OUTbufferID);

Or do you mean it is illegal to copy from FBO1.COLOR_ATTACHMENT_0 → FBO2.COLOR_ATTACHMENT_0?

Anyway i solved it. glDrawBuffer() resets the state of glDrawBuffers().

Thank you all for taking the time to help!

Advertisement

Good to hear you figured it out. I meant it was illegal to have the same FBO color attachment as the source and target for glBlitFrameBuffer.

I use the OpenGL 4.5 apis with named objects because i find them easier for integration into c++es oop, so i can't say nothing to the older ones. I am curious, what do you mean “resets the state” ?

@Green_Baron Naming doesn't bother me, i am weird that way =)

The reason i am using OpenGL 4.3 is to get the modern Debugger and a few other niceties whilst supporting a huge amout of GPU's and for a more modern approach i will add support for Vulkan.

Say you have a FBO that has multiple color buffers, then you set it up with a glDrawBuffers() which is now the draw state.

Then if you use blit

glReadBuffer(some color buffer)

glDrawBuffer(some color buffer) ← boom! Now you are not using glDrawBuffers() anymore, now you are using glDrawBuffer()

glBlitFramebuffer(…args);

//repeat this say 3 more times
//Now you have to tell the framebuffer to use glDrawBuffers() again.

var attachments = [4]uint32{gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3}
gl.DrawBuffers(4, &attachments[0])

Blit is actually doing what it is supposed to but it messes with draw state. Does that make sense? Not sure how else to explain it ?

This topic is closed to new replies.

Advertisement