Drawing a PNG onto a RenderTarget

Hi! I have the following setup. A big number of horizontal surface mesh actors and their default texture assets. These surfaces need to be paintable in runtime, so to their materials, I assign UCanvasRenderTarget2Ds on which I draw their default textures using Canvas::K2_DrawMaterial. After painting, I save updated textures to PNG files. If a PNG file exists on startup, it will be loaded and painted onto the render target instead of the original texture.

I am currently painting the PNG image on the render target by loading the image using the IImageWrapper, copying its data to a transient texture, and then applying it to the render target the same way I do for the default texture. This works fine, but I noticed it causes a huge RAM spike during loading, compared to when only default texture assets are loaded. RAM returns to the same state as when only default textures are used right after the loading. This is a problem because the application can run just fine if only default textures are used, but then run out of memory and crash on loading if PNG overwrites exist for a few of the textures.

Here is the memory increase when loading 100 default texture assets (I would like to have many more):


And here is the memory increase when loading 100 PNGs (it takes longer than loading assets because individual files are loaded and parsed):

These 100 PNGs are 35MB in total and their loading causes a memory jump of 6GB?!

I “optimized” this approach by creating only one transient texture and using it to load all images, with no effect. I also tried using the same raw data from the first loaded image for all of the meshes, but running IImageWrapper only once made very little effect. Finally, I realized that the UTexture2D::UpdateResource method, called after raw data is copied to the transient texture, uses a lot of memory. Just commenting out the UpdateResource call completely removed the memory spike (and of course made all loaded images black). This is strange because UpdateResource is also called over the original texture asset to make it not streamable, but does not cause the same problem.

Here is the memory increase when loading 100 PNGs, but with the UpdateResource commented out:

I am not sure what I can do about this, except for loading 10 images, waiting for 10 seconds for the memory to be released, repeat…
I looked around, but could not find a way to draw raw image data onto a render target without using a texture. Looking into FImageUtils class, there are methods for accessing render target raw data and saving it as an image, but not for loading them.
I am creating the transient texture like this:

UTexture2D* Texture2D = UTexture2D::CreateTransient(Width, Height, PF_B8G8R8A8);
Texture2D->MipLoadOptions = ETextureMipLoadOptions::OnlyFirstMip;
Texture2D->LODGroup = TextureGroup::TEXTUREGROUP_UI;
Texture2D->NeverStream = true;
Texture2D->UpdateResource();

Is there a better way to set up this texture?

Can anyone help me fix my solution or suggest another way of approaching this problem?

Thanks!

Hi,

Is this something that you may be able to reproduce in a template project and share with me? Then I can investigate further. It seems odd you are seeing that kind of bottleneck to be honest but I need to see what is going on ideally.

Thanks,
Jon

Hi Jon,

Thank you for the reply, I’ll try to create a minimal reproduction and get back to you.

Thanks,
Nikola

1 Like