PDA

View Full Version : Framebuffer outputs black on resize



McStones
08-30-2017, 04:51 AM
Hi, I`m trying to resize my framebuffer which uses Multisample Textures with 4 samples.
When I call my resize method, the framebuffer just outputs a black color.

Here is my resize method:



GL.DeleteFramebuffer(id);
foreach (GLXTexture texture in colorAttachments)
{
texture.Resize(width, height, 0);
}
if (depthAttachment != null)
{
depthAttachment.Resize(width, height, 0);
}
if (stencilAttachment != null)
{
stencilAttachment.Resize(width, height, 0);
}
id = GL.GenFramebuffer();
Create(colorAttachments, depthAttachment, stencilAttachment);


You can see a "Create" method at the bottom. I can include the source code but it is working fine, because if I dont use "Resize" on a Texture, the framebuffer renders ok. It then renders to a texture, which is not the same size and, because of this, distorted.

The interesting stuff happens in the Textures "Resize" method. If I use any openGL texture operations like "GL.BindTexture" and "GL.TexImage2DMultisample" in my case, the output is turns black. No error is generated.

Here is the code (INFO: IsMutable is true, which means that I`m using GL.TexImage2DMultisample):


public override void Resize(int width, int height, int depth)
{
if (!IsReserved)
{
throw new NotSupportedException("This texture has to be created before resizing!");
}
if (width == 0 || height == 0)
{
throw new NotSupportedException("You can`t resize this image to 0 width or height!");
}
this.width = width;
this.height = height;
if (!IsMutable) // If the texture is not mutable it has to be destroyed completely
{
Destroy();
ReserveTextureID();
}
GL.BindTexture(TextureTarget.Texture2DMultisample, id);
if (IsMutable)
{
// TexTarget is GL_TEXTURE_2D_MULTISAMPLE in this class
GL.TexImage2DMultisample((TextureTargetMultisample )TexTarget, samples, internalFormat, width, height, fixedSampleLocations);
}
else
{
GL.TexStorage2DMultisample((TextureTargetMultisamp le2d)TexTarget, samples, (SizedInternalFormat)internalFormat, width, height, fixedSampleLocations);
}
//ApplyOptions(IsMutable); Dont apply any options to a 2D Multisample Texture
GetError(false); // Print errors
GL.BindTexture(TextureTarget.Texture2DMultisample, 0);
}


Here is the desired output, which I get if I dont resize the screen. (Only these Gray blocks)
2468

After window resize:
2466

And here I have removed the GL.BindTexture and GL.TexImage2DMultisample lines and then resized the window:
2467


I tried this with Multisampled 2D Textures and only 2D Textures, both have the same effect.

Additional informations:
- The openGL context which created the textures and framebuffer is bound properly in the resize method.
- I`m using multiple draw buffers from GL_COLOR_ATTACHMENT0 to 3 which are bound using glDrawBuffers
- There is no Framebuffer error generated
- This PC has Intel(R) HD Graphics | Driver Version 9.17.10.2884

Dark Photon
08-30-2017, 06:36 AM
Are you checking for GL errors? Do you see any?

You don't actually show many of the GL calls you're making here. Showing a bit about how you're doing the downsample might help.

When I first saw your problem, I suspected you might be trying to downsample and resize your MSAA render target with one glBlitFramebuffer call (which you can't do; you have to use two). However, you didn't show this code so you might check that.

McStones
08-30-2017, 09:02 AM
Are you checking for GL errors? Do you see any?
No errors =/


You don't actually show many of the GL calls you're making here. Showing a bit about how you're doing the downsample might help.
I`m doing downsampling using a shader.


void main(){
vec4 color = vec4(0.0);
ivec2 coords = ivec2(texCoords.x * samplerResolution.x, samplerResolution.y - texCoords.y * samplerResolution.y);
for(int i = 0; i < 4; i++)
color += texelFetch(finalImage, coords, i);
fragColor = color / samples;
}

But this should`nt be the problem, because it works before I resize.

Here is the complete resizing procedure captured by GLIntercept:



//Begin Resize (width = 710, height = 307)
glIsFramebuffer(1)=true
glDeleteFramebuffers(1,108FF014)

// For each Texture: Resize(710, 307, 0); // 0 is ignored
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,4)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4,34837,710,307,true)
glGetError()=GL_NO_ERROR
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,0)

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,5)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4,33323,710,307,true)
glGetError()=GL_NO_ERROR
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,0)

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,3)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4,32856,710,307,true)
glGetError()=GL_NO_ERROR
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,0)

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,6)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4,34843,710,307,true)
glGetError()=GL_NO_ERROR
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,0)

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,7)
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4,35056,710,307,true)
glGetError()=GL_NO_ERROR
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,0)

// Continue with the framebuffer creation
glGenFramebuffers(1,108FF014)
glBindFramebuffer(GL_FRAMEBUFFER,1)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATT ACHMENT0,GL_TEXTURE_2D_MULTISAMPLE,4,0)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATT ACHMENT1,GL_TEXTURE_2D_MULTISAMPLE,5,0)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATT ACHMENT2,GL_TEXTURE_2D_MULTISAMPLE,3,0)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATT ACHMENT3,GL_TEXTURE_2D_MULTISAMPLE,6,0)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_STE NCIL_ATTACHMENT,GL_TEXTURE_2D_MULTISAMPLE,7,0)
glDrawBuffers(4,02A150A8)
glCheckFramebufferStatus(GL_FRAMEBUFFER)=GL_FRAMEB UFFER_COMPLETE


Thanks in advance for any hint!

Dark Photon
08-30-2017, 07:46 PM
As a test, try setting the GL_TEXTURE_MIN_FILTER on all of your textures to GL_NEAREST.

McStones
08-31-2017, 01:50 AM
As a test, try setting the GL_TEXTURE_MIN_FILTER on all of your textures to GL_NEAREST.

This generates the error: 1280-'InvalidEnum'
I`m using GL_TEXTURE_2D_MULTISAMPLE as Target.

I found this on the khronos`OpenGL documentation(https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_multisample.txt):

(2) What commands may be used on multisample textures?

RESOLVED: Multisample textures can be bound for rendering and texturing,
but they cannot be loaded/read with SubImage commands (TexSubImage,
CopyTexSubImage, GetTexImage), they don't support compressed formats,
and they don't need TexParameters since they can only be fetched with
texelFetchMultisample.

The screen is still black after resizing =/ Nothing changed even with the generated error.

#EDIT

I also tried to detach the textures from the framebuffer using:


GL.FramebufferTexture2D(FramebufferTarget.Framebuf fer, FramebufferAttachment.ColorAttachment0 + i, texture.TexTarget, 0, 0);


But still the same error.
Also, what may be interesting: if I delete the framebuffer after resizing any textures, It causes an AccessViolationException. This means that I`m writing to protected memory.
Is this normal behavior? I have to delete the framebuffer before resizing textures to avoid the exception.

Thanks in advance!

Dark Photon
08-31-2017, 06:17 AM
This generates the error: 1280-'InvalidEnum'
I`m using GL_TEXTURE_2D_MULTISAMPLE as Target.

Sorry about that. That error makes sense. I guess I was remembering having issues when using 2D textures with render-to-texture, not 2D MSAA textures, and needing to reset the MIN_FILTER so that it wouldn't try to access MIPmap levels that didn't exist. That's not an issue for MSAA textures.


Also, what may be interesting: if I delete the framebuffer after resizing any textures, It causes an AccessViolationException. This means that I`m writing to protected memory.
Is this normal behavior? I have to delete the framebuffer before resizing textures to avoid the exception.

No, it's not normal. That indicates either 1) a bug in your code, and/or 2) a bug in your GL drivers.

At this point, I'd suggest you post a short, stand-alone GLUT text program that illustrates your problem. With that, folks here can build/run it, skim it, and give you feedback both on what you're doing as well as if/how it works on various GPUs and drivers.

Here's a shell to copy/paste your code into:


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>

void checkGLError( const char hdr[] )
{
int err = glGetError();
if( err )
{
fprintf(stderr, "ERROR %s: %s\n", hdr, gluErrorString(err));
exit(1);
}
}

void reshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glutPostRedisplay();
}

void keyboard( unsigned char key, int x, int y )
{
// Key Bindings
switch( key )
{
case 27 : exit(0); break;
}
}

void display()
{
glClearColor( 0,0,1,1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glutSwapBuffers();
checkGLError( "display() end" );
}

main( int argc, char *argv[] )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB );
glutCreateWindow( "window title" );
checkGLError( "Create window" );

glutReshapeFunc ( reshape );
glutDisplayFunc ( display );
glutKeyboardFunc( keyboard );

glutPostRedisplay();
glutMainLoop();
}

McStones
09-14-2017, 02:57 AM
Sorry about that. That error makes sense. I guess I was remembering having issues when using 2D textures with render-to-texture, not 2D MSAA textures, and needing to reset the MIN_FILTER so that it wouldn't try to access MIPmap levels that didn't exist. That's not an issue for MSAA textures.



No, it's not normal. That indicates either 1) a bug in your code, and/or 2) a bug in your GL drivers.


Hello Dark Photon, thanks for these hints and sorry, I was very busy, this is why I can answer only today.

I recently solved this issue. The problem was, that I tried to delete the framebuffer and resize the textures while one of these textures was still bound.

In my post processing unit, I`m using the latest framebuffer color result and bind it for MSAA resolving. But I dont unbind it... I've had some luck, because the resizing code gets executed right after the post processing and swap buffers, so I know there are no other texture binds. I could have run into the same issue some day and throw my hands up in horror, when it gets more complex.


Does it make sense to unbind all framebuffer targets before deleting the actual framebuffer?
Well, this is what makes it work for me.

Dark Photon
09-14-2017, 07:11 AM
Hello Dark Photon, thanks for these hints and sorry, I was very busy, this is why I can answer only today.

I recently solved this issue. The problem was, that I tried to delete the framebuffer and resize the textures while one of these textures was still bound.

No problem. I understand. Thanks for following up.

If we assume that the driver operates per-spec, I don't immediately see how your solution fixes the problem though. Could be you've hit a driver bug.

If you still have the version of your app that produces the artifact, it would be interesting to add a glFinish() right before you do all the FBO deletes and texture resizes to see if that magically fixes the artifact. If not, then try binding 0 for the active FBO and 0 to all the texture units and see if that fixes it. Could be the driver isn't properly synchronizing when you start making changes to a texture or FBO that it's currently using in the pipe.


Does it make sense to unbind all framebuffer targets before deleting the actual framebuffer?

I think it makes sense to do sufficient resource tracking to ensure you don't lose track of resources. However, it's not necessary to unbind textures from texture units before you delete the containing framebuffer.

Now if you have any internal state tracking that keeps track of your last bound texture(s) and you delete a texture that is bound, you need to make sure you update your state. However, you still don't need to manually unbind textures from texture units.

I also think it probably makes sense to: after you've rendered to or with a texture, don't resize the texture by just binding it and changing the format/size. Instead, just delete the texture, and recreate it from scratch. That should make it harder to hit strange driver bugs with changing the format of resources that are already in-flight. I could see where implementing that change might have fixed your problem.

McStones
09-18-2017, 03:11 AM
If you still have the version of your app that produces the artifact, it would be interesting to add a glFinish() right before you do all the FBO deletes and texture resizes to see if that magically fixes the artifact. If not, then try binding 0 for the active FBO and 0 to all the texture units and see if that fixes it. Could be the driver isn't properly synchronizing when you start making changes to a texture or FBO that it's currently using in the pipe.

Yes, I can reproduce the problem simply by removing the line, where I unbind a framebuffer texture before resizing. I tried the to use glFinish() but this did not work... Also deleting all textures and recreating them does not work. It only works if I unbind the textures before deleting the framebuffer in the resize process...
But at least it works with unbinding the textures. So I have a way of working around this.

Thanks!

Dark Photon
09-18-2017, 05:50 AM
Thanks for the follow-up! That's interesting. It sounds like your driver may have some problems tracking and synchronizing changes to GL objects.