Opengl depth buffer to cuda - opengl

I'm a new programmer to Opengl,
my aim is to retrieve the depth buffer into a FBO to be able to transfer to cuda without using glReadpixels.
Here is what I've already done:
void make_Fbo()
{
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
fbo);
check_gl_error("make_fbo");
}
void make_render_buffer()
{
glGenRenderbuffers(1, &rb);
glBindRenderbuffer(GL_RENDERBUFFER, rb);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT,
win.width,
win.height);
check_gl_error("make render_buffer");
}
This code create my FBO with correct depth values.
A new problem appear now, according to the article "fast triangle rasterization using irregular z-buffer on cuda"
It's not possible to acces to depth buffer attached to the FBO from Cuda.
Here is is the quote from the article:
Textures or render buffers can be attached onto the depth
attachment point of FBOs to accommodate the depth values. However, as far as
we have tested, they cannot be accessed by CUDA kernels. [...]
we managed to use the color attachment points on the FBO. Apparently
in this case we have to write a simple shader program to dump the depth values onto
the color channels of the frame buffer. According to the GLSL specification [KBR06],
the special variable gl_FragCoord
Are the statements still true?
What do you advise me to dump the depth buffer to the color channels?
to a texture ?

Well yes and no. The problem is that you can't access resources in CUDA while they are bound to the FBO.
As I understand it, with cudaGraphicsGLRegisterImage() you enable cuda access to any type of image data. So if you use a depth buffer that is a rendertarget and is NOT bound to the FBO, you can use it.
Here's the cuda API information:
https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__OPENGL.html#group__CUDART__OPENGL_1g80d12187ae7590807c7676697d9fe03d
And in this article they explain that you should round-robin or double-buffer the depth-buffer, or copy the data before using it in CUDA (but then you more or less void the whole idea of interop).
http://codekea.com/xLj7d1ya5gD6/modifying-opengl-fbo-texture-attachment-in-cuda.html

Related

Rendering to TBO

I need to render to Buffer texture. Why TBO? TBO can be mapped easily as CUDA resource for graphic interop. It can also store byte sized data, which is what I need. I was trying to find related info in GL specs. Here it is stated that:
Buffer Textures work like 1D texture, only they have a single image,
identified by mipmap level​ 0.
But when I try to attach TBO to FBO I am always getting "Missing attachment" error when checking completeness, which leads me to a conclusion that GL_TEXTURE_BUFFER is not supported as FBO attachment.
Two questions:
Is it true?
Is the only alternative here to write to SSBO
More detailed info:
I am getting
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
When I am trying to attach the TBO to the framebuffer.There is no point to attach the whole code here as it is both heavily abstracted API and belive me,I am pretty experienced in using OpenGL and framebuffers.Attaching a regular GL_TEXTURE_2D works great.
Here is the chunk of the TBO creation and the attachment stage:
GLuint tbo;
GLuint tboTex;
glGenBuffers(1, &tbo);
glBindBuffer(GL_TEXTURE_BUFFER, tbo);
glBufferData(GL_TEXTURE_BUFFER, viewportWidth * viewportHeight * 4, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
glGenTextures(1, &tboTex);
glBindTexture(GL_TEXTURE_BUFFER, tboTex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, tbo);
glBindTexture(GL_TEXTURE_BUFFER, 0);
Then attach to FBO:
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tboTex, 0);
///also tried:
/// glFramebufferTexture1D
/// glFramebufferTexture2D
Buffer textures cannot be attached to FBOs:
GL_INVALID_OPERATION is generated by[sic] if texture is a buffer texture.
A good reminder for why you should always check your OpenGL errors.
Is the only alternative here to write to SSBO
If your goal is to use a rendering operation to write stuff to a buffer object, you could also use Image Load/Store operations with buffer textures. But if your hardware could handle that, then it should handle SSBOs too.
You could also try to use geometry shaders and transform feedback operations to write whatever you're trying to write.
However:
It can also store byte sized data
Images can store "byte sized data" as well. The image format GL_R8UI represents a single-channel, 8-bit unsigned integer.
That doesn't resolve any CUDA-interop issues, but rendering to bytes is very possible.

Draw OpenGL renderbuffer to screen

I created a Renderbuffer, that's then modified in OpenCL.
//OpenGL
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 600, 600);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
//OpenCL
renderEngine = new OpenCLProgram("render.cl");
renderEngine->addArgumentGLRBuffer(colorRenderbuffer);
How would I then proceed drawing my OpenCL creation, the buffer to the screen? I could bind it to a texture and draw a quad the size of my window, but I am not that sure, if it is the most efficient way. Also, if there was a better way of drawing to the screen from OpenCL, that would help!
The call you're looking for is glBlitFramebuffer(). To use this, you bind your FBO as the read framebuffer, and the default framebuffer as the draw framebuffer:
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, dstWidth, dstHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
Adjust the parameters for your specific use based on the linked man page.
This is preferable over writing your own shader and rendering a screen sized quad. Not only is it simpler, and requires fewer state changes, it can also be more efficient. Knowing that a blit operation needs to be performed gives the implementation a chance to use a more efficient path. For example, where present, it could use a dedicated blit engine that can run asynchronously to the general rendering functionality of the GPU.
Whether you should use a renderbuffer or texture is not as clear cut. Chances are that it won't make much of a difference. Still, I would recommend to use a renderbuffer as long as that's all you need. Because it has more limited functionality, the driver has the option to create a memory allocation that is more optimized for the purpose. Rendering to a renderbuffer can potentially be more efficient than rendering to a texture on some hardware, particularly if your rendering is pixel output limited.
Don't make it a renderbuffer.
OpenGL renderbuffers exist for the sole purpose of being render targets. The only OpenGL operations that read from them are per-sample operations during rendering to the framebuffer, framebuffer blits, and pixel transfer operations.
Use a texture instead. There is no reason you couldn't create a 600x600 GL_RGBA8 2D texture.

GL_TEXTURE_3D color and stencil FBO attachments

I am doing a layered rendering to an offscreen FBO using OpenGL 4.3.I used GL_TEXTURE_3D with several layers as COLOR attachment.Then I use geometry shader to index into different layers when writing the output.It works ok.Now I need also stencil attachment for stencil test I am performing during the rendering.First I tried just to attach a render buffer as in the case with 2D attachments.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, _stencilBuffer)
In this case,when checking FBO for completeness I am getting frame buffer error:
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB
Then I assumed that if the color attachment is 3D so the stencil attachment also must be 3D.And because there is no 3D render buffer I tried to attach a 3D texture for depth stencil slot of the FBO.
glTexImage3D(GL_TEXTURE_3D, 0, GL_DEPTH24_STENCIL8, width, height, depth,
0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
Where width - texture width,height-texture height,depth-number of layers inside texture 3D.
//Attach to FBO:
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texId, 0));
Doing it this way I am getting:
GL_FRAMEBUFFER_INCOMPLETE
INVALID_OPERATION
I have searched any possible example to find how such a setup should be done,but found nothing.I also tried to use GL_TEXTURE_2D_ARRAY instead,but same problem. (for some reason this actually fixed the problem which persisted in my earlier tests)
UPDATE
My fault as got confused with some of my findings during the debug.Basically half of what I wrote above can be discarded.But because other people may get into the same issues I will explain what happened.
At first,when I attached a 3d texture to COLOR attachment of FBO I created a render buffer for GL_DEPTH_STENCIL attachment.And yes,on completeness check I got:
GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB
Next,I tried instead:
glTexImage3D(GL_TEXTURE_3D, 0, GL_DEPTH24_STENCIL8, width, height, depth,
0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
which thrown:
INVALID_OPERATION
Now,instead of GL_TEXTURE_3D target I tried GL_TEXTURE_2D_ARRAY which finally caused the FBO to be complete.So,while I would still like to understand why GL_TEXTURE_3D causes INVALID_OPERATION(feel free to post an answer),this change has solved the problem.
Based on the spec, GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS means (quoted from OpenGL 4.5 spec):
If any framebuffer attachment is layered, all populated attachments must be layered. Additionally, all populated color attachments must be from textures of the same target (three-dimensional, one- or two-dimensional array, cube map, or cube map array textures).
Based on the first part of this, your initial attempt of using a single layer stencil attachment with a layered 3D texture color attachment was clearly illegal.
The second part sounds somewhat unclear to me. Since it only talks about "color attachments", it suggests that using a GL_TEXTURE_3D color attachment and a GL_TEXTURE_2D_ARRAY stencil attachment would be legal. But I'm not convinced that this is actually the intention. Unfortunately I couldn't find additional confirmation of this in the rest of the spec.
Using GL_TEXTURE_3D for a stencil or depth/stencil texture is a non-starter. There's no such thing as a 3D stencil texture. From the 4.5 spec, pages 191-192 in section "8.5 Texture Image Specification"
Textures with a base internal format of DEPTH_COMPONENT, DEPTH_STENCIL, or STENCIL_INDEX are supported by texture image specification commands only if target is TEXTURE_1D, TEXTURE_2D, TEXTURE_2D_MULTISAMPLE, TEXTURE_1D_ARRAY, TEXTURE_2D_ARRAY, TEXTURE_2D_MULTISAMPLE_ARRAY, TEXTURE_RECTANGLE, TEXTURE_CUBE_MAP, TEXTURE_CUBE_MAP_ARRAY, PROXY_TEXTURE_1D, PROXY_TEXTURE_2D, PROXY_TEXTURE_2D_MULTISAMPLE, PROXY_TEXTURE_1D_ARRAY, PROXY_TEXTURE_2D_ARRAY, PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY, PROXY_TEXTURE_RECTANGLE, PROXY_TEXTURE_CUBE_MAP, or PROXY_TEXTURE_CUBE_MAP_ARRAY.
That's a long list, but TEXTURE_3D is not in it.
Based on this, I believe that what you found to be working is the only option. You need to use textures with target GL_TEXTURE_2D_ARRAY for both the color and stencil attachment.

Multisampling with glBlitFramebuffer

This is my first attempt to do multisampling (for anti-aliasing) with opengl. Basically, I'm drawing a background to the screen (which should not get anti-aliased) and subsequently I'm drawing the vertices that should be anti-aliased.
What I've done so far:
//create the framebuffer:
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//Generate color buffer:
glGenRenderbuffers(1, &cb);
glBindRenderbuffer(GL_RENDERBUFFER, cb);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cb);
//Generate depth buffer:
glGenRenderbuffers(1, &db);
glBindRenderbuffer(GL_RENDERBUFFER, db);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db);
...
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//draw background ... ...
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//draw things that should get anti-aliased ... ...
//finally:
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, x_size, y_size, 0, 0, x_size, y_size, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
The problem is: when I call glBlitFramebuffer(...) the whole background gets black and I only see the anti-aliased vertices.
Any suggestions?
Normally, blending is the most obvious option if you want to render a new image/texture on top of existing rendering while taking transparency in the image into account. Looking at the rendering into the multisampled framebuffer as an image with transparency, that's exactly the situation you have.
In this case, there are a couple of challenges that make the use of blending more difficult than usual. First of all, glBlitFramebuffer() does not apply blending. From the spec:
Blit operations bypass the fragment pipeline. The only fragment operations which affect a blit are the pixel ownership test and the scissor test.
Without multisampling in play, this is fairly easy to overcome. Instead of using glBlitFramebuffer(), you perform the blit by drawing a screen sized textured quad. Since all fragment operations are in play now, you could use blending.
Howerver, the "drawing a textured quad" part gets much trickier since your content is multisampled. A few options come to mind.
Render background to FBO
You could render the background to the multisampled FBO instead of the primary framebuffer. Then you can use glBlitFramebuffer() exactly as you do now.
You may think: "But I don't want my background to be anti-aliased!" That's not really a problem. You simply disable multisampling while drawing the background:
glDisable(GL_MULTISAMPLE);
I think that should give you what you want. And if it does, it's by far the easiest option.
Multisample Textures
OpenGL 3.2 and later support multisample textures. For this, you would use a texture instead of a renderbuffer as the color buffer of your FBO. The texture is allocated with:
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8,
xsize, ysize, GL_FALSE);
There are other aspects that I can't all cover here. If you want to explore this option, you can read up on all the details in the spec or other sources. For example, sampling of the texture in the shader code works differently, with a different sampler type, and sampling functions that only allow you to read one sample at a time.
Two-Stage Blitting
You could use a hybrid of glBlitFramebuffer() for resolving the multisample content, and the "manual" blit for blending the content into the default framebuffer:
Create a second FBO where the color attachment is a regular, not multisampled texture.
Use glBlitFramebuffer() to copy from multisampled renderbuffer in first FBO to texture in second FBO.
Set up and enable blending.
Draw a screen sized quad using the texture that was the attachment to the second FBO.
While this seems somewhat awkward, and requires an extra copy which is undesirable for performance, it is fairly straightforward.
Render the background last
For this, you do exactly what you're doing now, copying the multisampled FBO content to the default framebuffer with glBlitFramebuffer(). But you do this first, and render the background afterwards.
You may think that this wouldn't work because it puts the background in front of the other content, which makes it... not much of a background.
But here is where blending comes into play again. While blending content on top of other content is the most common way of using blending, you can also use it to render things behind existing content. To do this, you need a few things:
A framebuffer with alpha planes. How you request that depends on the window system/toolkit you use for your OpenGL setup. It's typically in the same area where you request your depth buffer, stencil buffer (if needed), etc. It is often specified as a number of alpha planes, which you typically set to 8.
The right blend function. For front to back blending, you typically use:
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
This adds the new rendering where nothing was previously rendered (i.e. the alpha in the destination is 0), and will keep the previous rendering unchanged where there was already rendering (i.e. the destination alpha is 1).
The blending setup can get a little trickier if your rendering involves partial transparency.
This may look somewhat complicated, but it's really quite intuitive once you wrap your head around how the blend functions work. And I think it's overall an elegant and efficient solution for your overall problem.

OpenGL, how to use depthbuffer from framebuffer as usual depth buffer

I have frame buffer, with depth component and 4 color attachments with 4 textures
I draw some stuff into it and unbind the buffer after, using 4 textures for fragment shader (deferred lighting).
Later i want to draw some more stuff on the screen, using the depth buffer from my framebuffer, is it possible?
I tried binding the framebuffer again and specifying glDrawBuffer(GL_FRONT), but it does not work.
Like Nicol already said, you cannot use an FBOs depth buffer as the default framebuffer's depth buffer directly.
But you can copy the FBO's depth buffer over to the default framebuffer using the EXT_framebuffer_blit extension (which should be core since GL 3):
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
If this extension is not supported (which I doubt when you already have FBOs), you can use a depth texture for the FBO's depth attachment and render this to the default framebuffer using a textured quad and a simple pass through fragment shader that writes into gl_FragDepth. Though this might be slower than just blitting it over.
I just experienced that copying a depth buffer from a renderbuffer to the main (context-provided) depth buffer is highly unreliable when using glBlitFramebuffer. Just because you cannot guarantee the format does match. Using GL_DEPTH_COMPONENT24 as my internal depth-texture-format just didn't work on my AMD Radeon 6950 (latest driver) because Windows (or the driver) decided to use the equivalent to GL_DEPTH24_STENCIL8 as the depth-format for my front/backbuffer, although i did not request any stencil precision (stencil-bits set to 0 in the pixel format descriptor). When using GL_DEPTH24_STENCIL8 for my framebuffer's depth-texture the Blitting worked as expected, but I had other issues with this format. The first attempt worked fine on NVIDIA cards, so I'm pretty sure I did not mess things up.
What works best (in my experience) is copying via shader:
The Fragment-Program (aka Pixel-Shader) [GLSL]
#version 150
uniform sampler2D depthTexture;
in vec2 texCoords; //texture coordinates from vertex-shader
void main( void )
{
gl_FragDepth = texture(depthTexture, texCoords).r;
}
The C++ code for copying looks like this:
glDepthMask(GL_TRUE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_DEPTH_TEST); //has to be enabled for some reason
glBindFramebuffer(GL_FRAMEBUFFER, 0);
depthCopyShader->Enable();
DrawFullscreenQuad(depthTextureIndex);
I know the thread is old, but it was one of my first results when googeling my issue, so I want to keep it as consistent as possible.
You cannot attach images (color or depth) to the default framebuffer. Similarly, you can't take images from the default framebuffer and attach them to an FBO.