Intel published a performance query extension support defined back in
2013. This is available on Windows & Linux (in the Mesa driver). This
should provide the same types of counters as the MDAPI backend for
DX11.
Frameretrace [1] (a fork/branch of Apitrace) uses the same extension.
v2: Fix build without OpenGL
Simplify logic to enable counters
Warn about non enabled counters on Linux/Mesa
Generate counter Uuid
v3: Turn asserts into errors
Don't load perf entry points manually
v4: More clang-format
v5: Fix some Windows conversion warnings
v6: Fix errors on Windows where the driver reports an error on
glGetPerfQueryInfoINTEL as a mean to say that the queryId cannot
be used through the extension
v7: clang-format
v8: Initialize variable passed by pointers to GL entry points
v9: Only try to use the INTEL_performance_query on Mesa, experience
shows the Intel Windows driver doesn't report anything useful.
[1]: https://github.com/janesma/apitrace/wiki/screen-shots
* Nomachine is one program that requests these, but has a fallback path for if
they're not available. Denying the request makes things more compatible.
* When multiple buffers are aliased over the same heap memory, they'll have the
same GPU base address. Our mapping needs to ensure it removes the right entry
when a resource is released.
* On GL/D3D11 we delay the start of the 'first frame' until a swapchain has been
created for D3D11, or an attribs-created context has been created for GL. This
makes it more useful and doesn't fire when creating devices/contexts for
enumeration or trampolining.
* On Vulkan/D3D12 the first device that's created is useful, so we can start
capturing immediately once it's available.
* When a descriptor table is unbounded in the root signature we don't want to
pass potentially multiple 100k descriptors through just because they're
technically available.
* Instead use the declared array size of the last register in the space (since a
descriptor table in the RS might correspond to multiple HLSL variables) to
limit which registers are actually available, which is possibly orders of
magnitude less.
* Even if a struct isn't trivial (e.g. it contains a constructor) it can still
be trivially copyable (just POD with memcpy), or trivially destructable (with
no destructor). Make sure we don't fall back to slower copies/destroys for
this case as it's relatively common.
* When we have a cached buffer that already has the map contents, reserialising
the byte buffer is pointless as we don't use it for anything.
* The serialise involves allocating a buffer, memcpy'ing it, then freeing it
which can add up for large map writes.