The canonical per-frame render path allocates on every frame, for every renderer. docs/render-pass-flow.md presents this as the pattern to follow, so every consumer inherits it.
Two sources, per render pass per frame:
new RenderPassBuilder(commandBuffer) allocates the builder plus the two List<> instances in RenderPassBuilderState (src/Pixely/Gpu/RenderPassBuilder.cs:37-44), and Build() allocates the IRenderPass returned by CommandBuffer.CreateRenderPass (src/Pixely/Gpu/CommandBuffer.cs:86).
GpuMemorySystem.UpdateStorageBuffer and UpdateVertexBuffer create and track a fresh native transfer buffer on every call through CreateAndTrackTransferBuffer (src/Pixely/Gpu/CopyPass.cs:279), released at copy pass disposal. A renderer that uploads instance data every frame therefore churns a native allocation per frame.
This matters on the Raspberry Pi 5 target, where a consumer treats per-frame allocation in a render path as a defect rather than a later optimization. A scene with three renderers pays it three times over for the builder and once per dynamic buffer.
Both look addressable without changing the consumer-facing shape: the builder and its lists could be pooled on the CommandBuffer that already owns the frame, and transfer buffers could be pooled by size for the lifetime of the copy pass or the device rather than created per update.
Found while adding a light accumulation pass to Khral, which is a third caller of the same pattern; the existing sprite and ground renderers have the same behaviour, so this is not specific to any one consumer.
The canonical per-frame render path allocates on every frame, for every renderer.
docs/render-pass-flow.mdpresents this as the pattern to follow, so every consumer inherits it.Two sources, per render pass per frame:
new RenderPassBuilder(commandBuffer)allocates the builder plus the twoList<>instances inRenderPassBuilderState(src/Pixely/Gpu/RenderPassBuilder.cs:37-44), andBuild()allocates theIRenderPassreturned byCommandBuffer.CreateRenderPass(src/Pixely/Gpu/CommandBuffer.cs:86).GpuMemorySystem.UpdateStorageBufferandUpdateVertexBuffercreate and track a fresh native transfer buffer on every call throughCreateAndTrackTransferBuffer(src/Pixely/Gpu/CopyPass.cs:279), released at copy pass disposal. A renderer that uploads instance data every frame therefore churns a native allocation per frame.This matters on the Raspberry Pi 5 target, where a consumer treats per-frame allocation in a render path as a defect rather than a later optimization. A scene with three renderers pays it three times over for the builder and once per dynamic buffer.
Both look addressable without changing the consumer-facing shape: the builder and its lists could be pooled on the
CommandBufferthat already owns the frame, and transfer buffers could be pooled by size for the lifetime of the copy pass or the device rather than created per update.Found while adding a light accumulation pass to Khral, which is a third caller of the same pattern; the existing sprite and ground renderers have the same behaviour, so this is not specific to any one consumer.