4 Render Targets
dxtr edited this page 2026-10-20 03:57:13 +02:00

Render Targets

An engine also draws into textures of its own, render targets, before its window, so that what its window shows can sample what was drawn into them that same frame, as a viewport-container, which shows another scene tree, does:

(let ((map (pyssel:make-render-target *engine* 160 90 :draw 'draw-map
                                                      :clear-color '(0 0 0 1))))
  …
  (pyssel:destroy-render-target map))

make-render-target makes a target of the given size in units, at its :scale of pixels to a unit, by default the engine's. In each frame it is drawn into, it is cleared to its :clear-color, by default the engine's, and its :draw function is called as the engine's draw is, with a render pass over it, while engine-frame-texture and engine-frame-scale are the target's.

Which frames those are, its :update-mode says, as Godot's SubViewport.render_target_update_mode does: :when-visible, the default, each frame in which its :shown function, called on the render thread before the frame, returns true, and each frame without one; :always, each frame; :once, the next frame, after which the mode is :disabled; and :disabled, never, so the target keeps what was drawn into it last. A new texture, as the target is made or a new size makes it, is drawn into once whatever the mode. (setf render-target-update-mode) changes the mode from the next frame, from any thread. A viewport container gives its target its own visibility as :shown, so a hidden one is not drawn into.

Drawn only when what they show changed

A target may also have a :prepare function, called with the engine on the render thread in each frame its mode allows, before its :draw and outside any render pass, with engine-frame-texture and engine-frame-scale the target's. It does the frame's work that comes before drawing and returns true if what :draw would draw changed since it last drew; the target is drawn into only then, and keeps its texture otherwise, as a canvas keeps what it recorded. A new texture, a new clear colour, size or scale, and :once draw it whatever :prepare returns. Without :prepare, a target is drawn into in every frame its mode allows.

(pyssel:make-render-target engine 160 90
                           :prepare (lambda (engine)
                                      (declare (ignore engine))
                                      (pyssel/scene-2d:prepare-viewport root))
                           :draw (lambda (engine pass)
                                   (declare (ignore engine))
                                   (pyssel/scene-2d:draw-viewport root pass)))

pyssel/scene-2d:prepare-viewport is the :prepare of a target a viewport is drawn into, as a viewport container's and a stretched window's content are: it finds a change where a canvas item was drawn anew, added, removed, moved or clipped, a canvas transform changed, a window embedded in it opened or closed, a painter's data changed, or a render target it shows was drawn anew. Below it, pyssel/2d:prepare-canvases prepares one renderer's canvases. So an editor's scene, shown in a container, is drawn again only as it changes, however often the editor's window is. A :prepare that switches what :draw draws, as a container does when it shows another tree, also returns true then: the other tree's renderers find nothing changed since they last drew, unaware that another drew into the target between.

render-target-version counts a target's drawings. A target that a canvas draws is drawn before the targets that show it, whatever the order they were made in: prepare-canvases calls ensure-render-target-drawn for each, which draws a target that is due and not yet drawn in the frame there and then, so what shows it sees this frame's picture. Otherwise targets are drawn in the order they were made.

Reading and changing targets

render-target-texture is the texture, of render-target-format, the format of the engine's window, which the engine's pipelines draw into; its colours are premultiplied by alpha, as the 2D renderer blends them; pyssel/2d:draw-texture draws a target on a canvas. render-target-size, render-target-scale and render-target-pixel-size describe a target, and engine-render-targets lists them. resize-render-target gives one another size, and a new texture once its pixels change; (setf render-target-clear-color) another colour; destroy-render-target destroys it, and so does stopping the engine. A target is made and resized between frames, not in a draw function; from another thread, at the sync point. read-render-target reads back what was drawn into a target last, its pixels as red, green, blue and alpha whatever order its format keeps them in, and its width and height in pixels, waiting for the GPU: for previews, tests and screenshots, not every frame, as the editor's File System dock draws a scene's preview.