MOOR security

Dieser Eintrag ist auf Englisch verfasst.

Root arguments that outlive their signature, and depth clears that round

Entry 013495 of 778 tests pass

Thirteen more D3D12 conformance tests pass on Metal, mostly by copying behaviour native drivers have but the specification barely mentions.

What changed

DXGI (the Windows layer that lists GPUs and displays) now reports real displays and display modes, and it reports how much GPU memory the process uses. After that I worked through small conformance failures, one at a time.

  • Draws now bind every vertex-buffer slot the input layout reads. Empty slots read zeros instead of stale addresses.
  • A pipeline that declares no depth format still applies its depth and stencil tests to a bound depth buffer.
  • Changing the root signature keeps the root arguments already set. A root signature is D3D12's description of which constants and resource tables a shader gets. ClearState is implemented and zeroes them.
  • Shader resource arrays are checked against the ranges of the root signature.
  • Descriptor tables that point outside the bound heap read the heap they point into.
Scene depthclear: D16 depth cleared in bands with rects; a depth ramp drawn with LESS fills each band
Gallery scene depthclear: D16 depth cleared in bands with rects; a depth ramp drawn with LESS fills each band. All scenes in the gallery

The interesting part

Two fixes copy what native drivers do, even though the D3D12 documentation says little or nothing about it.

Root arguments. vkd3d-proton's test_uninit_root_parameters sets constants, switches to a second, compatible root signature, and dispatches. It expects the old constants to still be there. The test's comment says native drivers do not clear anything on a root-signature change. I had been zeroing the arguments on every change. Now only Reset and ClearState zero them.

Depth clears. Clearing a 16-bit depth buffer to 0.5 gave 32767 on Metal. D3D12 converts floats to 16-bit normalized values by rounding, which gives 32768, and test_depth_stencil_sampling compares against exactly that. Metal appears to truncate. The layer now nudges the clear value so that truncation lands on the rounded result. A new API test checks five values, including 0.37 and 0.9999, both for a plain clear and for a clear with a rect, which the layer draws. The review asked for the second case.

In one case the two test suites disagree. After IASetVertexBuffers(0, n, NULL), vkd3d expects the slots to be unbound. vkd3d-proton expects the call to be ignored. vkd3d's test skips NVIDIA as "broken", which suggests this is a vendor difference and not a rule. I kept vkd3d's behaviour and recorded the conflict.

Why a D16 depth clear to 0.5 needs a small nudge on Metal.clear depth to 0.5 on D16 0.5 × 65535 = 32767.5 → 32768 (D3D12 rounds); clear depth to 0.5 on D16 0.5 × 65535 = 32767.5 → 32767 ✘ (Metal truncates); clear depth to 0.5 on D16 0.5 × 65535 = 32767.5 → Metal truncates it to 32768 ✔ (mtl12 passes (32768 + 0.25) / 65535)clear depth to 0.5 on D160.5 × 65535 = 32767.53276832767 ✘Metal truncates it to 32768 ✔D3D12 roundsMetal truncatesmtl12 passes (32768 + 0.25) / 65535
Why a D16 depth clear to 0.5 needs a small nudge on Metal. (Mermaid source)

Numbers

  • Full run: 495 pass, 93 fail, 190 skip of 778, 0 newly failing.
  • Previous full run: 482 / 102 / 190 of 774. Four of the 13 newly passing tests are new API tests.
  • Failed checks: 3,861 -> 3,843.
Tests by suite at the end of entry 013: 495 of 778 pass (data as CSV)
  • pass
  • fail
  • skip

suite · share of each row's total

  1. vkd3d-proton (+5)334 pass, 64 fail, 179 skip of 577
  2. vkd3d (+4)150 pass, 29 fail, 11 skip of 190
  3. api (+4)8 pass, 0 fail, 0 skip of 8
  4. samples3 pass, 0 fail, 0 skip of 3