mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 20:58:31 +09:00
[Perf] (MG_Backend): stop paying for descriptor slots and mip barriers nobody asked for
Five independent bits of per-draw and per-operation waste in the DirectVulkan backend, all removing work whose answer was already known. The per-draw descriptor walk iterated all 256 slots of bindingKinds to find the one to eight bindings a real GL program declares, because that vector is sized to the binding cap rather than to the program. Reflection now records the bindings it actually assigned, and the draw path iterates that. It is built at the end of ReflectLayout, not where bindingKinds is sized - at that point the vector is only zero-initialised and the kinds are assigned further down, so a list built there would be empty. It has to stay ascending: Vulkan consumes pDynamicOffsets in binding order and the writer pushes them in iteration order, so an unordered list would silently mis-pair dynamic offsets with their uniform blocks. Descriptor pools were sized maxSets * the 256-binding cap, declaring 81,920 descriptors per pool and 245,760 across the frames in flight, for sets that hold what shader reflection found. Sized from eight now; an outlier program is absorbed by the VK_ERROR_OUT_OF_POOL_MEMORY path that already exists, which works because pool sizes are aggregate budgets rather than per-set limits. TrackLiveResource swept the whole live-buffer vector on every insert once it passed 256 entries, and when the buffers are all live the sweep removes nothing and the vector grows by one - so creating N live buffers cost about N^2/2 expired() checks. It sweeps on a doubling watermark now, with the same reclamation semantics. GenerateMipmap transitioned each destination level individually inside its loop, but every generated level starts in the same layout and the loop only moves a level out of TRANSFER_DST after writing it, so the whole range can be prepared in one barrier - 3(N-1)+1 barrier commands become 2(N-1)+2. Each level is still transitioned to TRANSFER_SRC before it is read, so the dependency between consecutive levels is unchanged. WaitForFrameSerial drained the entire graphics queue, as its own comment admitted. Every submission records the frame serial it was made under, so it now waits on the first fence at or past the requested serial. The narrow path deliberately does not call NotifyDeviceIdle(): that claims every submission has retired, which is only true after a real drain, so it stays on the fallback. Verified with an 8213-case A/B (textures, buffers, queries, mipmaps, uniforms and the whole direct_state_access suite): the Espryt failure list is identical, the Magma failure list differs by one case, and both crash sets are unchanged on Magma. That one case, buffer_storage.map_persistent_draw, does not reproduce in isolation - running the buffer_storage group alone gives byte-identical results on both builds (the same three failures, not including it), and it reports NotSupported when run on its own. It is the same ordering-dependent behaviour this suite shows elsewhere, and the three Espryt crash-set differences are the known copy_image cluster moving chunk position. Flagging rather than hiding it. direct_state_access stays at Espryt 370/371 and Magma 371/371; unit tests 421/421.
This commit is contained in:
@@ -67,6 +67,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
Vector<DescriptorBindingKind> bindingKinds;
|
||||
// The bindings this program actually declares, ascending. bindingKinds is sized to the
|
||||
// 256-binding cap while a real GL program uses 1-8, so the per-draw descriptor walk was
|
||||
// scanning 256 slots to find a handful. MUST stay ascending: Vulkan consumes
|
||||
// pDynamicOffsets in binding order and the writer pushes them in iteration order, so an
|
||||
// unordered list would silently mis-pair dynamic offsets with their uniform blocks.
|
||||
Vector<Uint32> activeBindings;
|
||||
Vector<Uint32> dynamicBindings;
|
||||
Vector<Int> uniformBlockIndexByBinding;
|
||||
// Descriptor count per binding (1 except for UBO instance arrays, which occupy one
|
||||
@@ -114,6 +120,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
descriptorSetLayout = other.descriptorSetLayout;
|
||||
pipelineLayout = other.pipelineLayout;
|
||||
bindingKinds = std::move(other.bindingKinds);
|
||||
activeBindings = std::move(other.activeBindings);
|
||||
dynamicBindings = std::move(other.dynamicBindings);
|
||||
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
|
||||
bindingDescriptorCounts = std::move(other.bindingDescriptorCounts);
|
||||
@@ -162,6 +169,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
descriptorSetLayout = other.descriptorSetLayout;
|
||||
pipelineLayout = other.pipelineLayout;
|
||||
bindingKinds = std::move(other.bindingKinds);
|
||||
activeBindings = std::move(other.activeBindings);
|
||||
dynamicBindings = std::move(other.dynamicBindings);
|
||||
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
|
||||
bindingDescriptorCounts = std::move(other.bindingDescriptorCounts);
|
||||
|
||||
Reference in New Issue
Block a user