A polygon's texture descriptor set is written once and kept, a frame slot's
for as long as its frames bind it, known by the serials of the views it
holds (a view's serial is never another's, where a handle can be made
again at the same value), and handed to the draw that binds it with no
second lookup: plain arrays, an open-addressed table and a stack of free
sets. A render to a texture keeps its framebuffer while its attachments
are the same ones, the command pools keep the memory their commands were
recorded in and submit from a list kept for it, and the descriptor writes
and the main buffer's pieces of a frame are arrays on the stack.
The temporal upscaler writes where each vertex was straight into the
memory the GPU reads it from (the Vulkan buffer mapped, Direct3D 12's
upload ring), and matches strips through grow_buf.h memory and tables kept
from frame to frame - the kept strips found by signature through a
counting sort's index - instead of vectors and maps made again each frame.
A renderer hands its general, modifier volume, prepass and other fixed
pipelines to the pipeline thread when it starts, and no longer waits for
them; a draw takes one made, makes it itself if the thread has not
started on it, or waits for the thread if it has. The quad, SSAA resolve,
motion vector, texture decoder, TA decoder and compute renderer
pipelines go the same way, and one the thread could not make is made
where it is first used, failing there as it did at start. The compute
renderer's kernels are handed over first, as every frame uses them.
The compute renderer's frames go to the frontend's temporal upscaler as
the raster renderers' do: drawn at the size it asks for under its
jitter (the frame's transform in pixels moved with it), the screen's
walk storing each pixel's depth in an image of the frame slot's
(pvr_compute.h, PVRC_DEPTH_ON, as the raster renderers' depth buffer
has it), and the motion vector pass drawn over that depth in the
frame's command buffer, from the frame's vertices and indices in its
words. That depth is no attachment: the pass samples it and tests
against it in its fragment shader (shaders/mv_depth.frag), with a
layout of its own.
The depth blocks are Vulkan's: the Direct3D 12 kernels' text is
without them, and their bytecode is as it was.
tools/vulkan/vk_hdr.sh and vk_sanitize.sh run the compute renderer
with the upscaler; vk_hdr takes a depth stored as a float.
The per-pixel (OIT) renderer renders at the size the frontend's
temporal upscaler asks for, under its jitter, keeps each frame's depth
and draws its motion vectors over it, as the per-triangle renderer
does. Its depth is one image a frame slot while the upscaler has the
frames, stored by the last render pass instead of dropped, with the
render passes' framebuffers made for it; SSAA is off then, as the
upscaler is the anti-aliasing and reads a single-sample depth.
The motion vector pass is one piece of code for both renderers
(rend/vulkan/motion_pass.cpp): the pass, its pipelines by cull mode,
the vectors' images and the strips' previous positions.
While the upscaler is active the Internal Resolution and SSAA core
options have no effect, and are hidden.
tools/vulkan/vk_hdr.sh and vk_sanitize.sh run the per-pixel renderer
with the upscaler, the inputs read back and checked under the
validation layer and the sanitizers.