Topic was automatically imported from the old Question2Answer platform.
Asked By
Hurricane
I’m using Godot 4.0 rc 1 for a small project. (With C#)
I’m drawing a lot of UI components. Essentially a few custom lists.
The lists are huge, thousands of items each (for a total of tens of thousands).
Each item has a texture of fixed dimension (128x96 at the moment)
I’ve quickly opted to draw them myself which solves most issues but one problem remains.
I can’t reasonably keep a hundred thousand of textures in memory, obviously.
My first idea was to create a cache as a huge texture (ImageTexture) and overwrite parts of it as needed. Drawing, I would use parts of the texture.
That would have been very easy.
Alas, as far as I’ve seen, there is no way to tell to only overwrite a part of the texture (if I’m wrong, please tell me.)
My second idea was to create a cache with a maximum amount of textures (Dictionary, MRU, all that jazz). But of course, I need to keep track of what textures are being used.
If I restrict myself to one use case, it’s fine. But I’d rather have a generic case.
For that purpose I was thinking making a wrapper inheriting, say, from Texture2D and either hook calls to a contained texture, either hook calls to a placeholder. Textures being released from the cache would thus be easily cleaned by the cache.
But here I’ve hit another snag : I don’t think I can do that in C# as it’s only a wrapper over the real classes.
I feel like such a tool/feature should already exist and that I’ve just not found it (yet).
Is there such a wrapper?
Is there a better mechanism for this?
create huge texture (ImageTexture) and overwrite parts of it as needed
using Godot;
public class Example : Node
{
private ImageTexture _texture;
public override void _Ready()
{
// Load the one of the 31 textures of 2048x1920
_texture = Load("path/to/image");
// Instead of color here you can create 3 for loops
// two for the x and y dimentions of the image to create the rect2
// and one to loop the pixel data of another image or something
// since you have not specified how you intend to "draw those images"
var color = new Color(1, 0, 0);
UpdateTexture(Rect2(0, 0, 128, 96), color);
}
private void UpdateTexture(Rect2 rect, Color color)
{
// Lock the image for writing
var image = _texture.GetData();
image.Lock();
// Loop through the pixels in the given rect and set them to the specified color
for (int y = (int)rect.Position.y; y < rect.Position.y + rect.Size.y; y++)
{
for (int x = (int)rect.Position.x; x < rect.Position.x + rect.Size.x; x++)
{
image.SetPixel(x, y, color);
}
}
// Unlock the image to apply the changes
image.Unlock();
_texture.SetData(image);
}
}
It is highly unlikely you will need all those images loaded at all times so you can always nullify the visually unused ones and re load when needed
The for loop above can look like this
for (int x = 0; x < 16 - 1; x++) # _texture.GetWidth() / 128 - 1
{
for (int y = 0; y < 20 - 1; y++) # _texture.GetHeight() / 96 - 1
{
var rect = new Rect2(x * 128, y * 96, 128, 96)
}
}
Wakatta | 2023-03-04 14:15
Math never bore me
As I said in my question, using an atlas was my first idea.
However, with the tools at my disposition, it’s not very efficient as there is no way to only update a part of a texture.
As demonstrated in your example :
you load the image from the texture,
you update a part of the image
(I hope there is a way to directly write an image into an image instead of doing it pixel by pixel)
you then reload the whole image into the texture
In the end I’ve opted for a set of textures overwritten with an update. The scene will have to make more bindings per frame but on the memory bandwidth front there should be no overhead. (Thankfully in Godot 4 we have _texture.Update(image) to avoid reallocating memory.)
Hurricane | 2023-03-06 12:04
hope there is a way to directly write an image into an image instead of doing it pixel by pixel
There is actually it is called BlitRect in the image class