Memory leak when recycling a DMA Buf with EGL Image

Hi all,

I am experiencing a memory leak when trying to recycle a DMA Buf used for Pitch-linear to Block-linear transformation and vice-versa. I had another implementation without recycling and it works perfectly with no leakages.

To illustrate better, here is the code without recycling:

typedef struct _EglImageHandler {
  EGLImageKHR egl_image;
  PFNGLEGLIMAGETARGETTEXTURE2DOESPROC egl_target_proc;
  int dmabl;
} EglImageHandler;

bool preparation(uint8_t * buffer, EglImageHandler * handler) {
  int dmapl, dmatarget;
  ExtractFdFromNvBuffer((void *)buffer, (int *)&dmapl);

  /* Some checks to avoid allocating dmabl if unneeded */
  // Suppose output_create_params is loaded Ok
  NvBufferCreateEx(&handler->dmabl, &output_create_params); 

  /* 
     Some detection logic to see if the buffer is block linear
     the block linear buffer will be loaded in dmatarget (if it was transformed, dmatarget = handler->dmabl,
     dmatarget = dmapl otherwise
  */

  handler->egl_image = NvEGLImageFromFd(NULL, dmatarget);
  handler->egl_target_proc =
      (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress(
          "glEGLImageTargetTexture2DOES");
  handler->egl_target_proc(GL_TEXTURE_2D,
                               (GLeglImageOES)handler->egl_image);

  /* The functions are checked in case of failure and return false in this case. I kept this simple */
  return true;
}

bool conclusion(EglImageHandler * handler) {
  NvDestroyEGLImage(NULL, handler->egl_image);

  /* Logic to ransform back if the buffer was transformed to block-linear before */

  NvBufferDestroy(handler->dmabl);
  handler->dmabl = 0;  
}

This was working. Now, my recycling version:

typedef struct _EglImageHandler {
  EGLImageKHR egl_image;
  PFNGLEGLIMAGETARGETTEXTURE2DOESPROC egl_target_proc;
  int dmabl;
} EglImageHandler;

bool preparation(uint8_t * buffer, EglImageHandler * handler) {
  int dmapl, dmatarget;
  ExtractFdFromNvBuffer((void *)buffer, (int *)&dmapl);

  /* Some checks to avoid allocating dmabl if unneeded */
  if (handler->dmabl == 0) {
    // Suppose output_create_params is loaded Ok
    NvBufferCreateEx(&handler->dmabl, &output_create_params); 
  }

  /* 
     Some detection logic to see if the buffer is block linear
     the block linear buffer will be loaded in dmatarget (if it was transformed, dmatarget = handler->dmabl,
     dmatarget = dmapl otherwise
  */

  handler->egl_image = NvEGLImageFromFd(NULL, dmatarget);
  handler->egl_target_proc =
      (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress(
          "glEGLImageTargetTexture2DOES");
  handler->egl_target_proc(GL_TEXTURE_2D,
                               (GLeglImageOES)handler->egl_image);

  /* The functions are checked in case of failure and return false in this case. I kept this simple */
  return true;
}

bool conclusion(EglImageHandler * handler) {
  NvDestroyEGLImage(NULL, handler->egl_image);

  /* Logic to ransform back if the buffer was transformed to block-linear before */

}

// the handler->dmabl is deallocated at the end of the program

In this second code, I can see that there is an increasing in the memory consumption (when it is running) without a reason. I made sure of being allocating the buffer just once and deallocating it at the end of the execution.

Apparently, not executing NvBufferDestroy(handler->dmabl) is making a difference when having a leakage or not, even when I am not allocating dmabl constantly. Perhaps, the thing is in how EGLImageKHR works and what NvDestroyEGLImage and NvEGLImageFromFd does actually. It seems I am forced to deallocate the dma buffer that I used, but I want to avoid allocating/deallocating it many times, since these functions go into GStreamer.

Any explanation for this behaviour?

Thanks in advance for your kind help

Hi,
In the code, there are two NvBuffer, handler->dmabl and dmapl. Don’t see detail of using handler->dmabl. If possible, please share more information. One thought is handler->dmabl being cleaned to 0 somewhere in the code.

Hi DaneLLL

Thanks for yur answer. In my case, I am using NvBuffer since the incoming one is in Pitch Linear and EGL requires a Block Linear buffer as input. So, dmapl is the incoming buffer from the streaming and dmabl is the buffer that I use to transform the surface to Block Linear (like an intermediate step).

About the cleanup, I am cleaning up the buffer when destroying a class. From to code written above, I encapsulate those functions (preparation and conclusion) in a class, together with the process in EGL. The EglImageHandler is one private member of this class. Thus, when I destroy the class, I clean up everything, free the buffer and set it to zero. I have even added a std::cout in:

if (handler->dmabl == 0) {
    // Suppose output_create_params is loaded Ok
    NvBufferCreateEx(&handler->dmabl, &output_create_params); 
  }

To see if the buffer is being allocated several times but it’s not.

Another thing I would like to mention is that, I tested the same code on a TX2 with Jetpack 4.4 and I don’t have that leakage. The environment with leakage is a Xavier with Jetpack 4.2.1

Thanks for your help.

Hi,
We have Jetpack4.2.3. Is it possible you can try the version?

Hi,

Sorry for the late reply. I have tested the code on:

Xavier AGX with JP 4.2.1: Memory leak
TX2 with JP 4.4: No memory leak
Xavier NX with JP 4.4: No memory leak
Xavier AGX with JP 4.4: No memory leak

I will recreate the case on a snippet to allow you recreating the issue.

Regards,
Leon.

Hi,
Since it is not seen on later releases, would be great if you can upgrade the system instead of staying on JP4.2.1. Or check if the issue is present on JP4.2.3.

Hi,

As soon as I get a Xavier AGX back I will check and report it over here,

Thanks for the suggestion.