5

Consider the following test code:

for (int i = 0; i < 100; ++i) {
    GLuint fboid = 0;
    GLuint colortex = 0;
    GLuint depthtex = 0;

    // create framebuffer & textures
    glGenFramebuffers(1, &fboid);
    glGenTextures(1, &colortex);
    glGenTextures(1, &depthtex);

    glBindTexture(GL_TEXTURE_2D, colortex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4000, 4000, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);

    glBindTexture(GL_TEXTURE_2D, depthtex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 4000, 4000, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, fboid);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colortex, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthtex, 0);

    assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER));

    // clear it
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);

    // delete everything
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glDeleteFramebuffers(1, &fboid);
    glDeleteTextures(1, &colortex);
    glDeleteTextures(1, &depthtex);
}

// put breakpoint here

You will see in the activity monitor that "Memory used" at the bottom goes sky high (14 GB). As if the GPU was still referencing the already released textures.

I tried the following:

  • call glFlush() at various places
  • call glFinish() at various places
  • change the order of texture/fbo deletions
  • detach attachments from fbo before deletion
  • call [context flushBuffer];

None of these had any effect. However (!) if I remove the glClear() call, then the problem vanishes.

What might cause this? It is also reproable on Windows, and with another implementation (which unfortunately I can't share and is a lot more complex anyway).

Have any of you seen a memory leak problem like this?

UPDATE: it is pretty obvious now, that the depth/stencil buffer is leaking. If I create a depth-only attachment, then the issue vanishes again!

UPDATE: easier to repro with Intel cards. On my late 2011 mbpro the code runs ok with the discrete card (Radeon 6750M), but produces the described leak with the integrated card (HD 3000).

UPDATE: it has been fixed on High Sierra (10.13.x)

Asylum
  • 569
  • 4
  • 21
  • You should make a [mcve], i.e. a short snippet that we can compile and that uses raw GL calls instead of your custom wrappers. – HolyBlackCat Jan 12 '18 at 12:59
  • ok, modified the post, attached the simple repro code (with xcode project file). – Asylum Jan 12 '18 at 14:47
  • Just out of curiosity, have you tried deleting the textures before you delete the framebuffer? – Robinson Jan 13 '18 at 00:59
  • I'm unable to duplicate the problem on my MacOS 10.10.5 iMac. Best guess: this only happens because you are calling OpenGL functions in initWithCoder before the view prepareOpenGL. If you move [self memoryTest] into prepareOpenGL or drawRect, does it still leak? – Hugh Fisher Jan 13 '18 at 04:34
  • not every mac can reproduce it. So far we reproduced it on the following configs (probably all of them 10.12.x [Sierra] or higher): `iMac Retina 4K, 21.5 inch, 2017 Radeon Pro 555 2 GB;; iMac (21.5-inch, Late 2012) NVIDIA GeForce GT 640M 512 MB;; iMac (4K Retina, late 2015) Radeon Pro 555 2 GB;; iMac 2013 intel iris pro;;` About initWithCoder: I don't think so because we also encountered this problem in a simple unit test. – Asylum Jan 13 '18 at 10:30
  • 2
    @Robinson: yes, I tried almost everything that can be done. Pretty much seems tho that only this D24S8 format causes memory leak (for example a simple D24 doesn't). I reported the problem to Apple. – Asylum Jan 13 '18 at 10:46

1 Answers1

0

While I didn't find any suitable solution, I came up with a workaround (which unfortunately brings up a different leaking problem with Radeon Pro 580 (?) cards).

The workaround is the following:

  • first of all, I enable GL context sharing
  • then whenever I want to delete a D24S8 texture, I instead put it into a cache
  • if the implementation requests the creation of a D24S8 buffer I look at the cache first (don't forget, that MSAA sample count must match!)
  • if there is an item in the cache which is suitable (>= than the requested size), then I take it out, and return it
  • if there isn't, then I create one with the requested size

With this solution I managed to minimize leaking on the mentioned configs...aaaaaaaand mess it up on that AMD card (which I suppose is another kind of driver bug, but right now I can't repro it with a small program).

Asylum
  • 569
  • 4
  • 21