* Rather than not serialising structs that we aren't interested in, we serialise
them (as best as we can in some cases) so that the information is available to
the user. Also if in future we decide to replay them they are available.
* We also now replay dedicated allocation extension use.
* We handle non-suffixed promoted functions but assume that calling
forward to either the promoted function or the old KHR extension is
equivalent.
* By this nature we won't record which function the user called, be that
extension or promoted, it will just be reported as the core function.
* Remove KHR suffixes from function names, enums, etc for promoted
extensions when serialising/stringising.
* We report no support for Ycbcr conversions by snooping the physical
device feature query.
* Just to be safe, we support pass-through/ignore implementations of
most of the functions and pNext structs.
* Any struct with a pNext pointer gets a Deserialise function, to clean
up any dynamically allocated chains.
* The mechanism is as follows:
- On writing, walk the pNext chain (assuming there is one) and skip
any structs we don't care to serialise.
- When we reach a struct we do want to serialise, save a nullable (i.e
optional) VkStructureType* for it, then recurse and serialise the
nullable casted struct.
- If there is no pNext (or none we care about) we serialise a NULL
VkStructureType*
- On reading, we serialise the VkStructureType*. If it's NULL, rename
it to look like a void *pNext = NULL.
- If it's not NULL, use the structure type to serialise a nullable
struct of the right type, and recurse.
* The pNext chain is dynamically allocated, which is why it gets cleaned
up in the Deserialise() function.
* We need to pass flags down from the parent struct to know, since it
depends on the type of descriptor. This could happen in any case, but
is most likely for templated descriptor updates where the memory that
is referenced could be garbage.
* This is a backwards-incompatible change, so it's done here before v1.0
so that we can freely break compatibility and not need to define a lot
of ugly compatibility code.
* The primary motivation is to make the serialised data more uniform
and have fewer special cases where the serialised form is naturally
defined from the functions/structs in question.
* There are still some special cases or variances but they should be
more isolated and only where really necessary.
* Remove some cases in the D3D12 struct serialising where we were
directly serialising child struct members in the parent struct.
* We now try to match argument/struct member order as closely as
possible.
* Serialising an array with a count no longer reads the count back out
into that variable, counts must be serialised separately. This means
all members/arguments are explicitly present in the structured data
and also eliminates the awkward case where a count needed to be
serialised separately after an array if we want to have the count be
a valid number even if the array could be NULL. It also means we don't
need the FIXED_COUNT() macro since array lengths can be plain values
and don't have to be a reference type.
* This also includes serialising vulkan handles as Ids by type instead
of using a macro as before.
* In contrast to the old code, we serialise handles as the wrapped type
since e.g. when serialising a command buffer in a vkCmd* function we
want to get back the wrapped type. This means some structs need to be
unwrapped on replay when before they were "pre-unwrapped" after the
serialisation, but this isn't a big deal.