This issue is similar to
https://community.khronos.org/t/using-bindless-textures-with-framebuffer-objects-causes-artifacts/107468
as well as
https://computergraphics.stackexchange.com/questions/5337/glclears-result-cant-be-seen-when-using-bindless-texture
However I’d also like to present my case since I haven’t found any satisfying workarounds for this.
I am writing a 2D engine using bindless textures and OpenGL 4.3. In the engine, you can create “surfaces”, that is, a structure that holds data for a framebuffer object. The app uses an “application surface” to draw stuff on it by default, but users can create other surfaces, set them as draw targets, draw stuff in them, draw the surfaces themselves etc. Essentially it’s a wrapping for framebuffer objects. The code for them is nothing special:
SurfaceID SurfaceAdd(int width, int height) {
static SurfaceID SurfaceFreeID = 1;
Surface surf;
surf.width = width;
surf.height = height;
surf.tex = XETEXTURE::InitEmptyTexture(width, height);
surf.fbo = XETEXTURE::TexCreateFrameBufferColor(surf.tex);
surf.texHandle = glGetTextureHandleARB(surf.tex);
glMakeTextureHandleResidentARB(surf.texHandle); //making texture resident before creating the fbo completely glitches the fbo in AMD
SurfaceID myID = SurfaceFreeID++;
surf.ID = myID;
vecSurface.push_back(surf);
mapSurfaceIDIndex.insert({ myID, (SurfaceIndex)(vecSurface.size() - 1) });
return surf.ID;
}
GLuint InitEmptyTexture(GLuint _width, GLuint _height) {
size_t size = _width * _height * 4;
GLubyte* emptydata = (GLubyte*)malloc(size);
memset(emptydata, 0, size);
GLuint texPage;
glGenTextures(1, &texPage);
glBindTexture(GL_TEXTURE_2D, texPage);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, emptydata);
free(emptydata);
return texPage;
}
GLuint TexCreateFrameBufferColor(GLuint texid) {
int fboPrev = -1;
if (XEDRAW::currentSurface != -1) {
XEDRAW::Surface& surfC = XEDRAW::SurfaceGetStruct(XEDRAW::currentSurface);
fboPrev = surfC.fbo;
}
GLuint fbo_tPage;
glGenFramebuffers(1, &fbo_tPage);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_tPage);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texid, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { //this check stops fbo from glitching out in AMD. glreadpixels() works too
PLOGDE << "Framebuffer incomplete.";
std::terminate();
}
if (fboPrev != -1) {
glBindFramebuffer(GL_FRAMEBUFFER, fboPrev);
}
return fbo_tPage;
}
void Clear(uint32_t colorRGBA) {
int b = BreakBatch();
glClearColor((GLfloat)ColorGetRed(colorRGBA) / 255.0f, (GLfloat)ColorGetGreen(colorRGBA) / 255.0f, (GLfloat)ColorGetBlue(colorRGBA) / 255.0f, (GLfloat)ColorGetAlpha(colorRGBA) / 255.0f);
glClear(GL_COLOR_BUFFER_BIT);
numBatches += !b;
GLubyte pixels[4]; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); //this prevents AMD from glitching out
}
As you may have seen in the code above, AMD drivers have trouble understanding bindless textures.
On my NVIDIA GPU, I get a perfectly normal result:
However, on my AMD GPU, the following things are of note:
- Making the surface’s texture handle resident after attaching it to the fbo causes artifacts:
- Making the surface’s texture handle resident before attaching it to the fbo works only if I either:
a) Adding a framebuffer completeness check after attaching the texture to the fbo:
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
PLOGDE << "Framebuffer incomplete.";
std::terminate();
}
b) doing a glReadPixels() after attaching the texture to the fbo.
3. glClear() doesn’t work unless I also do a glReadPixels() right after it.
4. Putting glFinish() / glTextureBarrier() in various places doesn’t do anything.
5. Using the aforementioned workarounds for AMD (also see commented lines in code above) makes things seemingly work on AMD.
I would like some input on whether I have some rookie mistake somewhere in my code (I really hope I do), and also if there is any workaround that doesn’t involve glReadPixels() or any other cpu/gpu sync buzzkill. Let me know if you need more info and I’ll be more than happy to provide it.