Of it, the backends read the EFB scale, now passed as the backbuffer
scale; GLContext::Create tested for libretro, its only context; and
the controller interface kept a render window that is always null. The
video backends' Initialize, InitBackendInfo and PopulateBackendInfo,
Core::Init, EmuThread and StartEmuThread, BootCore and the controller
interface lose the parameter; PrepareWindow, ChangeWindow, the window
change refresh and the Vulkan instance's surface extensions go, as
nothing presents but the frontend.
Where the device sets the logic op per draw, its mode is no longer part
of a pipeline, so one ubershader pipeline with integer output draws
every mode. The precompile also builds the logic op pipelines with
dual-source output asked for, as BlendingState::Generate leaves it.
The stats build counts the pipelines the driver compiles or links, and
those a draw waits for.
SHA1, HMAC, hex dumps, PNG loading, file search, DHCP options, HTTP
posts and DirectIOFile reads and writes take pointer plus size; callers
pass their buffers straight through. Contains.h is gone; lookups are
plain loops. File search and FAT FST sorting are in-place heap sorts;
min/max/clamp/copy/fill become ternaries, memcpy and memset. Watch
string import and export had no callers and are removed.
VKHeap.c takes large blocks from the driver and hands out offsets in
them: the lowest free span that fits, spans kept sorted so a free is a
binary search and a merge. The handle is a small struct kept by value,
so nothing is allocated per allocation. Host-visible blocks are mapped
once and stay mapped; stream buffers and staging buffers write straight
into the memory the GPU reads. Buffers and linear images keep to blocks
of their own, apart from optimal images. What the driver requires or
prefers dedicated, and anything of half a block or more, gets memory of
its own sized to fit. Host blocks are 16 MiB, device blocks 64 MiB, and
neither more than an eighth of its driver heap; one empty block of each
kind is kept and the rest go back to the driver.
The Vulkan Memory Allocator is gone, and with it the memory budget
extension only it used.
Where the device has VK_EXT_graphics_pipeline_library with fast
linking, an ubershader pipeline, the one built on the spot when a draw
first needs it, is linked from libraries of its four parts instead of
built whole: the vertex input, the vertex and geometry shaders, the
pixel shader and the colour output, each found or built with only what
that part takes. With cull mode, depth state and blending set per draw
the parts are shared widely; a pipeline whose parts exist is linked in
well under a millisecond, where building it whole took tens, and only
a shader's first use builds its library.
The libraries live in the shared pipeline table until the backend
shuts down. A link that fails builds the pipeline whole.
Where the device has VK_EXT_extended_dynamic_state3's colour blend
enable, equation and write mask, a GX or ubershader pipeline's blending
is set per draw with its cull mode and depth state, and left out of the
pipeline and of the key it is shared by: pipelines that differ only in
blend enable, factors, equations or which channels are written are one
VkPipeline. Logic op and dual-source blending stay in the pipeline, the
latter as it decides the shader's outputs. The state tracker sets the
blending for every colour attachment when it, or the attachments,
differ from what the command buffer has set already.
Where the device has VK_EXT_extended_dynamic_state, the cull mode and
the depth test, write and compare function of a GX or ubershader
pipeline are set per draw, and pipelines that differ only there are one
VkPipeline: a pipeline asked for whose shaders, vertex layout, blending,
topology and framebuffer match one already built takes that one at once
instead of being compiled. Fewer pipelines are compiled, in the
background and on the spot alike.
The shared pipelines sit in a fixed open-addressed table every thread
building pipelines reads and adds to without a lock, counted by the
pipelines using them; a shader is told apart by a number given it when
it is made, not by its address. The state tracker sets only what
differs from what the command buffer has set already, and sets it again
after a pipeline with the state built in.
The colour blend attachments of a pipeline are kept on the stack.
The application info the core hands the frontend for its instance asks
for the highest version the Vulkan loader has, up to 1.2, instead of
1.0. The backend calls the 1.1 queries (vkGetPhysicalDeviceProperties2
and Features2) where the device has them; on a 1.0 instance those were
invalid, and the loader answered them by emulation that drops the
structures chained to them, so no extension feature could be queried.
The memory allocator is told the lower of the instance's and the
device's versions.
The frontend owns the window and presents, so the D3D11, D3D12 and
Vulkan backends draw their picture into a FrameOutput, a render target
the frontend reads where it is, rather than a swap chain. D3D11 hands
over one texture at pixel shader slot 0. D3D12 negotiates version 2 of
its interface and keeps one texture per sync index, handed over with
the fence its picture completes on, falling back to set_texture on
version 1. Vulkan keeps one image per sync index and hands it over with
set_image, so the fake swap chain and the WSI calls hooked under it are
gone, and the device may come without a surface. The outputs are RGBA8
and report that format to the presenter.
The DXGI and Vulkan swap chains, the backends' standalone device
creation and exclusive fullscreen are removed.
The compute header comes back for the decoders, and compute shaders and
GPU texture decoding are reported as supported once more. The core
option still decides whether textures are decoded on the GPU.
rslc now has storage buffers and atomics (updated from RetroArch's
libretro-common), so the Vulkan backend offers again what needs them:
the bounding box where the device has fragmentStoresAndAtomics, the
dynamic vertex loader, and the vertex-shader line and point
expansion. GPU texture decoding stays off until rslc has a compute
stage.
Vendors rslc from RetroArch's libretro-common (gfx/shaders, built with
RSLC_EXTENDED) and has the Vulkan backend hand every vertex, geometry
and pixel stage to it for SPIR-V: no glslang on that path, no global
lock, any number of compiles in parallel. D3D and Metal still go
through glslang and SPIRV-Cross until rslc writes their code.
A stage rslc refuses is logged and written, with the error, to the
dump directory as bad_<stage>_Vulkan_<n>.txt.
rslc has no compute stage, storage buffers, atomics or subgroup
operations yet, so for now the backend does not offer what needs them:
GPU texture decoding (decoding stays on the CPU), the bounding box,
the dynamic vertex loader and the vertex-shader line and point
expansion (the geometry shader expands them).
Both fed user-written GLSL to the backends at runtime, which the move to
bytecode emitted straight from the shader generators leaves no compiler
for. The frontend's own shader pipeline covers post-processing.
The presentation pass keeps a plain copy and the built-in correction
shader for output resampling, colour space and gamma correction and HDR
scaling, now compiled in rather than loaded from Sys/Shaders. The
graphics-mod system keeps its draw, EFB, projection and texture actions;
custom pipelines, materials, shader, mesh and sampler assets, the custom
constant buffers in every backend and the two mods built on them are
gone, along with the shader generators' custom-shader hooks.
In WiimoteReal.cpp, JitRegCache.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.
In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.
To further increase the accuracy of the post process phase, I've added (scRGB) HDR support, which is necessary
to fully display the PAL and NTSC-J color spaces, and also to improve the quality of post process texture samplings and
do them in linear space instead of gamma space (which is very important when playing at low resolutions).
For SDR, the quality is also slightly increased, at least if any post process runs, as the buffer is now
R10G10B10A2 (on Vulkan, DX11 and DX12) if supported; previously it was R8G8B8A8 but the alpha bits were wasted.
Gamma correction is arguably the most important thing as Dolphin on Windows outputted in "sRGB" (implicitly)
as that's what Windows expects by default, though sRGB gamma is very different from the gamma commonly used
by video standards dating to the pre HDR era (roughly gamma 2.35).
Additionally, the addition of HDR support (which is pretty straight forward and minimal), added support for
our own custom AutoHDR shaders, which would allow us to achieve decent looking HDR in Dolphin games without
having to use SpecialK or Windows 11 AutoHDR. Both of which don't necessarily play nice with older games
with strongly different and simpler lighting. HDR should also be supported in Linux.
Development of my own AutoHDR shader is almost complete and will come next.
This has been carefully tested and there should be no regression in any of the different features that Dolphin
offers, like multisampling, stereo rendering, other post processes, etc etc.
Fixes: https://bugs.dolphin-emu.org/issues/8941
Co-authored-by: EndlesslyFlowering <[email protected]>
Co-authored-by: Dogway <[email protected]>
The former is deprecated and pretty much all modern drivers
support VK_EXT_debug_utils.
Android drivers dont support it. On those drivers,
we use the implementation provided by the validation layers.
Adds a pass to process driver deficiencies between UID caching and use, allowing a full view of the whole pipeline, since some bugs/workarounds involve interactions between blend modes and the pixel shader