* Most of the interfaces are not supported - protected sessions, raytracing,
etc.
* We also split apart the 1/2/3/4 wrapped implementations to avoid uber files.
* The problem with storing resource pointers in descriptors is that it can be
invalidated without detection - a kind of A-B-A problem - if the resource is
deleted and then another resource is allocated with the same pointer.
* Descriptor creation in D3D12 is extremely complex and there are many ways a
resource could become incompatible with the descriptor metadata struct.
Detecting all possible ways a new resource could be incompatible is not
feasible.
* As a solution, we store the ResourceId which we know is immutable, serialise
via the pointer, and keep the live ResourceId on replay. If the resource was
deleted, the serialisation will fail because we look up the pointer at the
point of serialise, and a deleted resource will end up being NULL.
* To try and abstract this away and avoid potential confusion with the
ResourceIds, we make the descriptor contents private and provide accessors.
* For Clear*View and OMSetRenderTargetViews, the CPU descriptor handles
used are read immediately, the heap can be modified or even deleted
after the call.
* So we save the contents of the descriptor, ignore the heap, and then
fill a temporary heap with the descriptor whenever it's needed.
* The problem is we need to keep current state for both graphics and
compute because while setting a root signature invalidates bindings,
setting compute doesn't invalidate graphics and vice-versa.
* However it can be invalid to try and re-apply in one go if we're only
drawing and not dispatching and some of the compute elements are
invalid.
* Rather than try to figure out which one *should* be valid, instead we
just skip invalid binds that don't have matching heaps.