Skip to content

Emulate VK_EXT_host_query_reset so DXVK can create a D3D11 device - #12

Open
abesmon wants to merge 1 commit into
GameNative:wrapper-25from
abesmon:pr/dx1011
Open

Emulate VK_EXT_host_query_reset so DXVK can create a D3D11 device#12
abesmon wants to merge 1 commit into
GameNative:wrapper-25from
abesmon:pr/dx1011

Conversation

@abesmon

@abesmonabesmon commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Emulate VK_EXT_host_query_reset so DXVK can create a D3D11 device

The problem

DXVK's D3D11 path forces hostQueryReset on and cannot be talked out of it.
D3D11Device::GetDeviceFeatures:

if (featureLevel >= D3D_FEATURE_LEVEL_9_1) {
...
enabled.extHostQueryReset.hostQueryReset = VK_TRUE;
}

and DxvkAdapter::checkFeatureSupport gates device creation on
(m_deviceFeatures.… || !required.…). So a driver that does not advertise the
feature gets no D3D11 device at any feature level, not just the high ones.

On Tegra X1 under the NVIDIA proprietary Android driver that is why D3D9 ran
while every D3D10 and D3D11 title died at device creation. From a Unity title:

d3d11: D3D_FEATURE_LEVEL_11_1 not-recognized (only 11.0 runtime installed?)
d3d11: failed to create device and context (80070057).

80070057 is E_INVALIDARG. DXGI works and the adapter is found; only device
creation is refused, at 11_1, 11_0, 10_1 and 10_0 alike.

This is the only capability in that function that behaves this way.
Everything else DXVK asks for there — memoryPriority and timelineSemaphore
included — is assigned as enabled.x = supported.x, so it is required only when
already present and can never fail the check. Read at the version the device
actually runs, DXVK-Sarek v1.11.0.

Prior art

leegao/bionic-vulkan-wrapper reached the same conclusion independently and is
the precedent for the approach. It advertises exactly this extension, naming
DXVK as the reason:

// Needed by dxvk
exts->EXT_transform_feedback = true;
exts->EXT_host_query_reset = true;
exts->EXT_custom_border_color = true;

and masks the feature back off in the chain forwarded to the driver, which is
what process_pnext_chain does here. It advertises nothing for memory priority.

Where this patch differs is what sits behind the advertisement. That wrapper
implements no vkResetQueryPool. On its targets that costs nothing — Adreno 6xx
and Mali G52 onwards support the feature natively, so the advertisement is a
no-op and the driver's own entry point is used. On a driver that genuinely lacks
it there is nothing behind the claim, and DXVK calls the entry point on a hot
path: DxvkGpuQueryManager::writeTimestamp and beginSingleQuery reset a query
before every timestamp and every query begin.

So it is emulated rather than merely claimed.

Gating

Driver-gated, the same way as the native-BCn preference and the AHB ownership
release already in this tree:

constbool nvidia_proprietary =
pdevice->driver_properties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
if (!pdevice->base_supported_features.hostQueryReset &&
debug_get_bool_option("WRAPPER_EMULATE_HOST_QUERY_RESET", nvidia_proprietary)) {

WRAPPER_EMULATE_HOST_QUERY_RESET is tri-state: unset follows the driver
default, true opts another driver in, false is a kill switch

@abesmon
abesmon marked this pull request as draft September 6, 2026 17:37
@abesmon

Copy link
Copy Markdown
ContributorAuthor

wait... i found something. Need to research some more stuff

DXVK's D3D11 path forces this feature on and cannot be talked out of it.
D3D11Device::GetDeviceFeatures sets
enabled.extHostQueryReset.hostQueryReset = VK_TRUE;
inside the >= D3D_FEATURE_LEVEL_9_1 block, and DxvkAdapter::checkFeatureSupport
then gates device creation on (supported || !required). So on a driver that
does not advertise it, every feature level is refused, not only the high ones --
which is why on Tegra X1 under the NVIDIA proprietary Android driver D3D9 ran
while every D3D10/D3D11 title died at device creation with E_INVALIDARG, Unity
reporting "d3d11: failed to create device and context (80070057)".
This is the only capability that behaves that way. Everything else DXVK asks
for in that function, memoryPriority and timelineSemaphore included, is assigned
as `enabled.x = supported.x`, so it is required only when it is already present
and can never fail the check. Read from the version the device actually runs,
DXVK-Sarek v1.11.0, and confirmed on hardware: with the emulation on and nothing
else, D3D10 and D3D11 pass in both bitnesses.
leegao's bionic-vulkan-wrapper reached the same conclusion independently and is
the precedent for the approach. It advertises exactly this extension, under a
comment naming DXVK as the reason:
// Needed by dxvk
exts->EXT_host_query_reset = true;
and masks the feature back off in the chain forwarded to the driver, which is
what process_pnext_chain does here. It advertises nothing for memory priority.
Where this patch differs is the part behind the advertisement. That wrapper
implements no vkResetQueryPool, which costs nothing on its targets -- Adreno 6xx
and Mali G52 onwards support the feature natively, so the advertisement is a
no-op there and the driver's own entry point is used. On a driver that genuinely
lacks it there is nothing behind the claim, and DXVK calls the entry point on a
hot path: DxvkGpuQueryManager::writeTimestamp and beginSingleQuery reset a query
before every timestamp and every query begin.
So it is emulated rather than merely claimed. vkResetQueryPool must have taken
effect by the time it returns -- it "sets the status of query indices
[firstQuery, firstQuery + queryCount - 1] to unavailable" -- so it records
vkCmdResetQueryPool into a command buffer, submits it, and waits on a fence.
Pool, command buffer and fence are created once at device creation.
https://docs.vulkan.org/refpages/latest/refpages/source/vkResetQueryPool.html
The emulation inherits the entry point's own valid usage, which does most of the
work: the application must already have completed submitted commands referring
to the range, and must not have that range in use from another thread. So the
reset never races the application's use of those queries. What it can race is
unrelated work on the queue it submits to, and that is the whole design problem.
Sharing the application's queue couples the reset to whatever is already queued
on it: a legal application submit can wait on a host event the application only
signals after its reset returns, and the reset then waits for work that is
waiting for the reset. A probe arms a vkCmdWaitEvents on the application queue
and releases the event from a watchdog thread after one second, so this shows up
as latency rather than a hang. On Tegra a shared-queue implementation waited
1001 ms in five runs out of five -- the full watchdog, every time.
So the emulation asks vkCreateDevice for one extra queue in a family the
application already requested, and submits there. The application never learns
about it: wrapper_create_device_queue builds wrapper_queue objects from the
application's own VkDeviceCreateInfo, not from the one forwarded to the driver.
Same probe, same device: 0.2 ms instead of 1001 ms.
If the family has no spare queue the emulation falls back to sharing the
application's first graphics/compute queue and restores the serialisation that
needs, because a VkQueue is externally synchronised and the application cannot
synchronise against a submit it does not know about. Refusing to create the
device there would turn a slow device into no device at all.
wrapper_query_reset_owns_queue is false whenever the queue is private, so the
private path pays nothing for the fallback.
vkDeviceWaitIdle is serialised against the internal queue for the same reason:
its host synchronisation requirement covers "all VkQueue objects created from
device", and one of them is invisible to the application.
https://docs.vulkan.org/refpages/latest/refpages/source/vkDeviceWaitIdle.html
Only the core entry point is defined. The generated table lists vkResetQueryPool
and vkResetQueryPoolEXT separately but both resolve to one dispatch slot, and
defining the alias as well trips the "disp[disp_index] == NULL" assert in
vk_device_dispatch_table_from_entrypoints and kills device creation. That
mistake was made and caught on the device.
Gated like the native BCn preference and the AHB ownership release: on the
driver, with WRAPPER_EMULATE_HOST_QUERY_RESET as a tri-state override, and never
applied over native support. The emulation is not free -- every host reset is a
command buffer and a fence wait, about 80-120 us on this hardware -- so the
kill switch exists for a client that does not need it.
@abesmonabesmon changed the title Provide the two DXVK D3D11 requirements the NVIDIA proprietary driver lacksEmulate VK_EXT_host_query_reset so DXVK can create a D3D11 deviceSep 6, 2026
@abesmon

Copy link
Copy Markdown
ContributorAuthor

i've retested everything, and found that part of fix was unnecessary, so i removed it. Now only VK_EXT_host_query_reset emulation is here. Also found, that leegao already made similar stuff

@abesmon
abesmon marked this pull request as ready for review September 6, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@abesmon