How to set the Texture for the CanvasItem created by RenderingServer?

Godot Version



I have created a CavansItem using RenderingServer, and then used the RenderingServer. CanvasItemAddTextureRect() method to add a Texture to this CavansItem. How can I change the Texture for this CanvasItem using RenderingServer?
I can’t use the CanvasItemAddTextureRect() method, which adds a Texture to this CanvasItem, which can cause serious performance losses. What should I do?

You can’t. Literally, a CanvasItem does not hold a texture. The “add” methods just add the textured rect to the next draw call, not to the actual object. The next frame, the drawing stack will start empty again.
A CanvasItem made using RenderingServer.canvas_item_create() is NOT a Node. It is a Resource. In fact, it cannot be converted to a Node, only used for operations within the RenderingServer.
I don’t know why you think any of these functions cause performance losses. They are used to draw everything else in godot.

[quote=“wuming123, post:1, topic:46187”]

Because calling CanvasItem.draw_texture_rect is drawing a Texture to the screen again.

If I want to redraw it, I have to create a new RID, freeing the original one.

No, you can use a RID you had before, even on a different frame.

I don’t understand what it means. If I want to render a new Texture, I have to recreate a RID, otherwise the Texture bound to the original RID will continue to render

From the docs:

canvas_item_add_texture_rect ( RID item, Rect2 rect, RID texture, bool tile=false, Color modulate=Color(1, 1, 1, 1), bool transpose=false )

The parameters are:

  • item: the canvas item we’re currently drawing; this will give us an offset and a transform if those are set.
  • rect: the rectangle on the screen we are drawing to, relative to the CanvasItem.
  • texture: the texture we are drawing. it can be a different one on each call, the only requirement is it was loaded as a resource.
  • tile: does the texture repeat if we’re drawing from beyond its borders.
  • modulate: multiply by this color for tinting (white means no change).
  • transpose: wether to flip the texture around both axes.

Any texture you have registered will have a RID, and thus, can be used in this call. You can call this multiple times with the same or different texture. The RID of a texture can be stored for use later. This is how you load a Texture2D once and use it multiple times, by referencing the RID. Usually this is done automatically.

Using the same RID again will use the same texture, no added cost. If you register two different textures they are guaranteed to have different RIDs. You can keep track of these numbers to use them in this call depending on which you want to draw.

My God, I created a CanvasItem through RenderingServer and bound a Texture to this CanvasItem using the RenderingServer.canvas_item_add_texture_rect() method. If I want to re-render a new Texture to this CanvasItem, I must free this RID and create a new RID.

No. That function does not bind the texture. You can still use it on other things and use a different texture. If you want to see the rect being drawn to the screen every frame, this function has to be called every frame. It adds a rect (with texture) to the DRAWING STACK, not to the object. The reference to the CanvasItem is only used for the transform and visibility.

		private Texture2D _texture_1;
		private Texture2D _texture_2;

		public override void _Ready() {
			_texture_1 = GD.Load<Texture2D>("res://texture/tile/tile-1.png");
			_texture_2 = GD.Load<Texture2D>("res://texture/tile/tile-2.png");
			Rid rid = RenderingServer.CanvasItemCreate();
			RenderingServer.CanvasItemSetParent(rid, GetCanvasItem());
			RenderingServer.CanvasItemSetTransform(rid, Transform2D.Identity);
				new Rect2(new Vector2(32, 32), new Vector2(32, 32)),
				new Rect2(new Vector2(4, 4), new Vector2(32, 32)),

You can refer to this code. I cannot re-render without Freeing the RID, because calling CanvasItemAddTextureRect retains the last called Texture and renders it.

It would have been good to know at the very start that you are not just using RenderingServer, but another CanvasItem-derived Node to draw these textures. And you are not overriding the draw call, but the ready call. These are very relevant details you ommited. Because you are attaching this CanvasItem to a Node, the drawing stack you set here stays stored. Not the textures, not the result, the drawing COMMANDS you give is what is stored.
If this was a draw call override as I assumed, you’d be making a new CanvasItem or at least the clear function on it. Using a ready override for drawing calls is not a very common way to use RenderingServer.

Now, would you mind talking about what you want to acheive, rather than the rabbit hole you go tinto in the first place?

Rendering a million sprites with the low-level RenderingDevice : godot (
I want it to be able to render millions of sprites per frame, but it’s very difficult

It’s not. Just override the _draw() call and use RenderingServer properly. And a texture atlas rather than separate textures also helps. Since you are inheriting from a Node already, all you need to worry about is the texture commands.
Make your _Ready call only have the texture loading.
In your _Draw call, do not create any objects. Just use CanvasItemAddTextureRect (or the Region version for an atlas texture) and make the first rid, the one you’re drawing to, GetCanvasItem().
You must also call RenderingServer.CanvasItemClear(GetCanvasItem()) at the start of _Draw().

I understand what you’re saying, but millions of 2d Mesh have 4m vertices, and Godot would have a hard time doing that without customizing the rendering itself.

A texture rect draw call is not a mesh.