Skip to content

Latest commit

 

History

History
1545 lines (1339 loc) · 88.9 KB

File metadata and controls

1545 lines (1339 loc) · 88.9 KB

d3d9 conformance against Wine's test suite

The de-facto conformance suite for any D3D9 reimplementation is Wine's dlls/d3d9/tests/ (there is no public, portable Microsoft D3D9 conformance kit — the WHQL/HLK tests are driver-certification machinery). Those tests build into one d3d9_test.exe per architecture, with four subtests selected by source-file stem: device, visual, stateblock, d3d9ex.

Because our d3d9.dll is installed as a Wine builtin (make install), running d3d9_test.exe exercises our implementation directly.

Running

make conformance                # diff both arches vs baseline.txt
make conformance-i686           # one arch, one runner process (what CI runs)
make conformance-x86_64
make conformance-intel          # both arches under the intel.* config keys
make conformance-intel-i686     # one arch under the intel.* keys
make conformance-scale          # both arches at render.scale = 0.75
make conformance-scale-i686     # one arch at render.scale = 0.75 (what CI runs, on one image)
make conformance-baseline       # (re)record this machine's six legs of baseline.txt in sequence

A leg is one architecture under one variant on one GPU family. The intel variant (--variant intel) runs the same binary with every intel.* key of mtld3d.conf turned on, so the suite sees the answers an Intel/AMD Mac gives: packed 16-bit formats expanded, 32-bit float filtering denied, Managed buffers, the 256-byte linear texture alignment. Its results record under [<arch>+intel/<subtest>] entries of the same baseline.txt. The GPU family is read from the machine, not chosen: an Apple Silicon Mac records the plain entries, an Intel/AMD Mac records [<leg>@mac2/<subtest>] entries, because the GPU underneath still decides what the suite sees past the forced answers (a tile-based Apple GPU elides depth stores and merges hidden overdraw before the visibility counter, encodes special floats its own way, and its validation layer applies different texture rules). The scale variant (--variant scale) runs the same binary at render.scale = 0.75, so every frame is rasterized on a smaller grid and read back through the resolve that brings it up to the reported size; its results record under [<arch>+scale/<subtest>] and CI runs it on one image and one arch, like the scaled end-to-end leg. The sites share the classifications below whatever the leg, since a site's nature does not depend on the leg that hit it. On the Apple family the two keys that only change a code path and no answer, intel.managedMemory and intel.linearAlign256, must move no count at all, and a site that fails only under the variant is expected to trace to one of the two caps keys. The @mac2 entries are recorded by CI, whose Intel image is the one Mac2 machine the project runs on: dispatch the workflow with record_intel_baseline and copy the @mac2 sections out of the baseline-mac2-<arch> artifacts (make conformance-baseline on an Apple Silicon machine leaves them untouched, the merge being leg-scoped).

Set MTLD3D_CONFORMANCE_RAW_DIR=<dir> to also persist each subtest's full raw output to <dir>/<leg>-<subtest>.log. The normal run reduces output to per-site counts and drops the assertion text; the raw logs keep every <file>.c:<line>: Test failed: Got <actual>, expected <expected> message (plus the Metal-validation lines), which is what the per-cluster audit below was built from — the actual-vs-expected values distinguish a real defect from an acceptable caps difference. Off unless the variable is set.

The runner reads the test process's stderr as it arrives, and the driver's GPU-hang line (kIOAccelCommandBufferCallbackErrorHang, or the ...SubmissionsIgnored line the driver prints for every command buffer it drops afterwards) stops the subtest at once and ends the leg: exit code 3, no verdict, no baseline write. Every count after that line is a read off a GPU that runs nothing, and waiting out the subtest's budget would only make the same non-verdict cost minutes. On the Intel CI image the paravirtual GPU stays hung for the rest of the machine's life, so the later subtests would hang too and the job names the re-run of the failed jobs, which lands on a fresh runner. A hang on a real GPU is worth a look on its own (a shader that hangs the GPU is a bug), but the leg has to run again for its counts either way.

A raw log is stdout followed by stderr, and ends with how the process ended: [conformance] subtest exited: code N or signal N (a number, never a name, so a fault the process survived cannot read as a crash to the scanner), or the TIMED OUT line when the runner killed it. A process the runner kills for its budget is sampled first (sample <pid> 2, every thread's stack), and so is the wineserver serving its prefix, the process it may be waiting on: the samples are kept beside the raw log as <leg>-<subtest>.sample.txt and <leg>-<subtest>.wineserver-sample.txt, both named on that line; without a raw directory they go to the runner's stderr. They are the one account of where a hang was: the raw log of a process parked in a syscall ends in that line and nothing else, and a thread parked in wine_server_call shows nothing of the server that owes it a reply. The server is matched on two things, so no other prefix's is ever read: the binary it was exec'd from, which the Makefile names with --wineserver out of the same Wine SDK as the loader, and the directory it works from, which wineserver names after the device and inode of the prefix it serves. When no such server is running, the file says so instead of failing the leg. A run without the framework's tests executed summary is a crash whatever else it holds, and this line is what tells an unhandled Win32 exception (Wine ends the process with the exception code, of which unix keeps the low byte: code 5 is an access violation) from a signal (11 SIGSEGV, 10 SIGBUS, 6 SIGABRT, 9 SIGKILL). A run that reached its summary reads code 0 once a device existed (the layer ends the process from its detach, see CONTRIBUTING.md), else the framework's failure count capped at 255. Each process is also told log.dir=Z:<dir>/<leg>-<subtest>, so its log file (d3d9_test-<pid>.log, and any GPU trace) lands in a directory beside its raw output, one per process. The runner's --log <filter> (LOG= for the Makefile targets) is the RUST_LOG those processes run under; the default off writes nothing, and the crash handlers' lines then go to stderr, i.e. into the raw log.

A repeat run (make conformance-isolate REPEAT=<n>) keeps every run, <leg>-<subtest>-<n>.log, with its own log directory. Dispatching the workflow with conformance_repeat=<n> runs it on every conformance job, under LOG=debug, and uploads the lot as conformance-raw-<image>-<arch>: the way to make a subtest that dies one run in a few die in one sitting, on the machine where it does.

There is no conformance-specific input to set. The test binaries ship inside the Wine SDK bundle ($WINE_SDK/lib/wine/tests/{i386,x86_64}-windows/d3d9_test.exe, published by the wine-build bundle step), which is the same install make install puts our builtin d3d9.dll into, so a CI job needs nothing but that tarball and no Wine build tree at all. The binaries are not vendored here: they are large and drift with the Wine version, so baseline.txt records the Wine version it was taken against instead.

The runner is the Rust tool mtld3d-conformance (unix/conformance/). It takes the loader and one test binary as explicit paths (--wine, --exe, plus --arch as the label to record under) and resolves nothing itself: every Wine location lives in the Makefile. One invocation therefore covers one leg, which is what lets the 32-bit and 64-bit gates, and the native and Intel variants, be separate CI jobs, and --update-baseline rewrites only its own leg's entries. It runs each subtest as its own process, so a crash in one cannot poison another's counts, with Metal API validation left on in nslog mode (it logs rather than aborting, so it cannot mask the failure counts) and with our logs and Wine's debug channels silenced.

The layer's validation errors are reported, deduplicated, as metal-validation: lines; its warnings are ignored. A warning is a performance hint, not misuse (a resource bound to an encoder no draw went on to read, a state setter overwritten before the next draw), and a leg emits thousands of them, so leaving them on buried the error lines in output that looked identical. A metal-validation: line therefore means the layer committed API misuse, so any of them fails the leg. The expected number is zero, kept as a constant in the runner next to the reporting code rather than in baseline.txt, which records machine-owned per-site counts and nothing else. There is no tolerance to keep in step: a leg that logs a message has started misusing Metal, and the fix is the misuse, not the number.

The layer writes a report as a headline naming the check that fired (Sampler Descriptor Validation) followed by unadorned detail lines, and the detail is the half that names what was rejected. The runner keeps them together: a recognised line opens a message and the lines under it are its detail, up to the next NSLog line, Wine channel line or blank line, so the whole report reaches the log indented under its metal-validation: line instead of only in the raw output. One report counts once.

This is not part of make test: many checks fail by design (see below), so it is a tracked-score tool, not a pass/fail gate on zero. The runner exits non-zero on a regression vs the baseline — a per-site failure count that went up, a new failing site, or a subtest that started crashing — and equally on a stale baseline — a count that dropped, a site that disappeared, or a crash that cleared. An improvement fails the gate on purpose: tolerating it would let baseline.txt overstate reality, and the surplus becomes a budget a later regression can hide in. The fix for a stale baseline is make conformance-baseline plus the matching triage edit here, not a code hunt. The flaky and ceiling classes are the two tolerances (see below).

A recognized upstream skip is an absent measurement, not a passing assertion. The runner retains the skip line separately from failures and reports the bypassed assertion as SKIPPED, without requiring a re-record. This applies only to the reviewed source location and message below, in a completed subtest with no failure at the affected assertion. Other missing failures still gate as stale, and crashes and counts above the pin still gate. Baseline recording retains an explicitly skipped assertion's prior count and reports that retention; it never invents a count when no prior pin exists.

Metal validation is the third verdict, and it is independent of the counts: a leg that logged any metal-validation: line exits non-zero even when every site holds its pin, because API misuse is invisible to a pass/fail count. A --update-baseline or --repeat run never gates, so neither one applies it.

Kept divergences

Divergences from D3D9 kept on purpose, because closing them costs frame time, memory, or a game that relies on the looser behaviour. docs/STATUS.md lists them in one line each; this section is where the reasoning lives. Where Wine's suite observes a divergence, the cluster below carries the site-level detail and is named here; the rest have no conformance site and this is their only record. A knob, where one makes sense, is named with its default.

  • IDirect3DTexture9::LockRect serves a level of a DEFAULT-pool 2D texture created without D3DUSAGE_DYNAMIC, which D3D9 rejects. The surface entry point, cube and volume locks still reject it. A game that streams into such a texture would otherwise lose every upload. The cost is system memory: the level's staging, released once its upload retires, is re-created, and a partial lock after that release leaves the pixels outside its rect out of step with the GPU copy (warned once per texture). No knob.
  • GetData(D3DGETDATA_FLUSH) can answer a pending occlusion query at once instead of waiting for the GPU. Off by default. This saves API-thread time only for a title verified to use the poll as a submission throttle, without reading the count or gating reuse of CPU-writable dynamic storage. Metal does not track a CPU write through D3DLOCK_NOOVERWRITE into pages a queued draw still reads, so an immediate answer cannot safely fence that reuse. The wow profile currently turns it on because skipping its loading-screen waits was measured to save seconds per load; that benefit does not establish the absence of this hazard. Knob: query.flushImmediate, default false.
  • EVENT query polls queue their open frame even without D3DGETDATA_FLUSH. D3D9 permits a poll without the flag to remain pending indefinitely. We make progress for callers that fence storage reuse before Present without setting the flag. The first poll splits the frame and queues its work on the existing bounded encoder channel; later polls only read retirement. There is no synchronous encoder or submit completion wait, but admission can backpressure like Present. Completion still requires GPU retirement, unless the next entry's knob is on. No knob.
  • An EVENT query poll can answer completed at once instead of waiting for the GPU to retire the frame the query was issued in. Off by default. A title that polls the query only to keep the CPU from running ahead of the GPU gains nothing from the real answer, because the encoder and submit threads already bound how far ahead it can get; waiting for retirement makes its CPU and GPU work run back to back instead. The immediate answer cannot fence reuse of CPU-writable dynamic storage behind a D3DLOCK_NOOVERWRITE lock, so it is only for titles verified not to rely on that. The wow profile turns it on for gxFixLag, which took 3.3.5a from 120 FPS to 77 with the same GPU time per frame; through 0.9.0 every EVENT poll answered at once on both clients. Knob: query.eventImmediate, default false.
  • Depth and stencil are discarded at every Present on a surface nothing samples. D3D9 keeps a depth-stencil surface's contents across Present and leaves them undefined only when the game asks for it: D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL on the implicit surface, or Discard = TRUE passed to CreateDepthStencilSurface. We consult neither. The last pass of a frame to use a depth surface stores nothing (store-action Rule B) and the first pass of the next frame loads nothing (Rule A), unless the surface is sampleable or has been bound as a texture. Preserving depth costs a store and a load of every depth surface on every frame of every game that clears it anyway, which is all tested ones. Content that tests against depth or stencil from an earlier frame without clearing reads undefined values. Sites: z_range_test and texdepth_test below. No knob: the gates are compile-time constants in windows/core/src/passes.rs.
  • A partial Lock of a dynamic vertex or index buffer without D3DLOCK_DISCARD returns a pointer into memory a queued draw may still read. D3D9 keeps the game's writes from landing under a draw the GPU has not reached; here D3DLOCK_NOOVERWRITE semantics apply by default. Matching D3D9 means stalling or renaming the backing on every such lock, and a dynamic buffer is what a UI or particle batcher locks dozens of times per frame. The rename path has been measured peaking near 1.4 GB of retained backings, which is why memory.vbibRetentionCapMB exists. Site: test_map_synchronisation below. No knob.
  • A partial LockRect of a texture level without D3DLOCK_NOOVERWRITE or D3DLOCK_READONLY returns a pointer into staging an upload may still read. The same trade for a font atlas or lightmap page written a few rectangles at a time. A whole-level lock is renamed and its contents preserved, because Half-Life 2's lightmap pages rely on that. No knob.
  • A DEFAULT-pool D3DUSAGE_WRITEONLY static vertex or index buffer keeps no CPU copy once every byte has reached the GPU. D3D9 preserves contents across a plain Lock whatever the usage says, so a title that reads back through the pointer sees zeros, and one that writes past its announced window loses those bytes (warned once). Inside a large-address-aware 32-bit title those copies measured near a gigabyte of the 4 GiB the title needs itself. An indexed triangle fan on a released index buffer copies it back off the GPU once, at one mid-frame GPU wait. Knob: buffer.ignoreLockBounds keeps the copy, default false.
  • The window procedure mtld3d subclasses is the device window's, and it follows a Reset that names another one. D3D9 subclasses the focus window and leaves that subclass where it is across a retarget. Our hook is what realizes the D3D9 cursor over the client area and what keeps a windowed back buffer matching the client rect, so it belongs on the window the device presents into rather than on the one that holds focus. Sites: test_wndproc and test_wndproc_windowed for where the hook sits, test_device_window_reset for its following a retarget, all below. No knob.
  • D3DRS_MULTISAMPLEANTIALIAS = FALSE is ignored. Metal ties the sample count to the pass's attachments with no per-draw override. D3DPRASTERCAPS_MULTISAMPLE_TOGGLE is not advertised, which is how D3D9 says the toggle is unavailable, and the first write is logged. No knob.
  • A windowed device's SetGammaRamp changes nothing on screen, and only the implicit swap chain carries a ramp at all. The ramp is stored and GetGammaRamp reports it back either way, and it starts applying as soon as a Reset takes the device fullscreen. D3D9 ramps the whole desktop for a windowed device, through a path that outlives the process that asked for it; that is the display's configuration rather than this window's frame, and the present pass is the only place we are willing to carry a ramp. A second device, or the compositor, would otherwise inherit a brightness a game set for itself. The hardware cursor keeps its own brightness for the same reason: macOS composites it and no API ramps it. Wine's suite has no GammaRamp coverage, so no site observes any of this. No knob: the behaviour a knob would restore is one we cannot implement, not a trade.
  • A draw whose shader library or render pipeline is still building can be left out of its frame. D3D9 draws every call in the frame that makes it. A first-use Metal compile takes tens of milliseconds, and building it inline stalls that frame by as much; building it on a worker thread and leaving the draw out until it lands trades one or two frames of a missing draw for no stall. A draw is left out only when every colour target its pass attaches is the discard-effect back buffer or was cleared whole in this frame and the one before, and the depth and stencil planes it tests or writes were cleared in both frames too: those are rebuilt every frame, so the draw appears once its build lands. One clear does not qualify, because a target cleared and drawn once, at load, is cleared exactly when its shaders are cold. A draw into any other target, and every draw while an occlusion query counts, is kept in its frame, whose submission waits for its build, and so is a draw into a target that was copied or sampled into kept content in the last 600 frames. Only the first frame of such a read can miss a skipped draw (one more per StretchRect or mid-frame flush on the way to the kept target), and a read-back to system memory, such as a screenshot, sees the frame as drawn, skipped draws included. The runner pins the knob off, so no site observes it. Knob: shader.asyncCompile, default true.

Range-fog coverage

D3DPRASTERCAPS_FOGRANGE is advertised. Fixed-function computed vertex fog uses the transformed vertex's distance from the eye when D3DRS_RANGEFOGENABLE is set, including sequential and indexed vertex blending. Table fog, pretransformed supplied factors and programmable vertex fog do not use that switch. Ordinary vertex fog keeps its Z-based formula.

FOGTABLE and WFOG are also advertised, so Wine's table- and range-fog visual cases run. Table fog evaluates LINEAR, EXP and EXP2 per pixel, using Z for an orthographic projection and reciprocal rasterizer W otherwise. It overrides vertex and range fog and preserves output alpha. SM3 shaders implement their own fog; their draw keys suppress the automatic fog stage. The end-to-end tests cover that boundary, shader rebinds and stateblocks without depending on Wine's capability gates.

Fill-mode coverage

D3DFILL_WIREFRAME uses Metal's native triangle-line fill, preserving triangle culling and the existing triangle-list conversion for fans. D3DFILL_POINT remains unimplemented: Metal exposes solid and line polygon fill only, and the layer warns once before drawing solid. D3D9 has no fill-mode capability bit.

The upstream stateblock.c covers the solid default, stored wireframe value, and pixel-stateblock membership (render_state_indices, render_state_default_data_init, render_state_test_data_init and render_state_test_init). It has no visual fill-mode assertion. The end-to-end suite covers rendered edges and interiors, state transitions and internal clear triangles; these capabilities do not imply a baseline count reduction.

Alpha-to-coverage controls

The explicit ATOC and A2M controls work independently of the reported adapter vendor. ATOC through ADAPTIVETESS_Y requires ALPHATESTENABLE; A2M1/A2M0 through POINTSIZE set and clear an independent latch. Either request enables coverage on multisampled RT0. Effective coverage replaces the ordinary alpha test; a single-sampled target restores it. Disabling one request leaves the other intact. A2M tokens are not advertised as resource formats.

Numeric POINTSIZE writes and RESZ preserve the latch. A2M and RESZ controls preserve the last numeric point size, while GetRenderState retains the raw DWORD. ALL and VERTEX state blocks restore both hidden values; PIXEL blocks restore neither. Recorded blocks retain which components their writes touched across Capture: refreshing a raw DWORD cannot add or remove latch or numeric size membership. Reset restores the default numeric size and clears A2M.

This is a compatibility policy for sequences on which the references differ. WineD3D keeps an independent latch, but retains the ordinary alpha test and reads control DWORDs as numeric point size. DXVK preserves numeric size for controls and suppresses alpha testing under coverage, but derives its request from raw POINTSIZE on selected invalidations. mtld3d combines independent latch lifetime with preserved numeric size and alpha-test suppression, avoiding behavior that depends on which later state update refreshed a cached request. Complete native AMD behavior for these mixed sequences has not been measured. The local sequence tests define this policy; Wine's AMD visual branch covers only enable, disable and VERTEX-state-block restoration.

Dynamic depth texture coverage

Wine's resource-access test creates a DEFAULT-pool, D3DUSAGE_DYNAMIC depth texture and uses the plain texture probe to derive the expected HRESULT. Dynamic D16, D24X8 and D24S8 2D textures now support packed locks and explicit mip chains. Dynamic D32 and legacy, lockable or vendor formats remain unavailable. Depth attachments with DYNAMIC, CPU pools, plain depth surfaces, depth cubes and volumes remain unavailable. AUTOGENMIPMAP queries return NOAUTOGEN and creation falls back to one level, without enabling automatic generation.

RESZ into a dynamic destination copies depth and the common stencil plane, using sample zero for multisampled sources. A depth-only source preserves the destination stencil. This follows DXVK's common-aspect ResolveZ contract and Wine's plane copy behavior; native AMD documentation only establishes depth sample zero. Scaled attachments are resampled on the GPU into the logical-sized destination. Readback and packing wait until a preservation or READONLY lock needs the GPU-authoritative bytes; a whole DISCARD skips that readback and versions any destination still named by pending work. The readback counts as the level's initial upload, so a READONLY lock of a level only the GPU wrote publishes nothing and the texture keeps the transferred depth rather than its packed code.

A multisampled RESZ into any other destination, and a depth-to-depth StretchRect out of a multisampled surface, take the same transfer: sample zero of depth and of the common stencil plane, written by compute and blit work. Neither uses a render-pass depth resolve, whose destination the Intel CI image's paravirtual device leaves reading what an earlier pass stored there (resz_test 17724 and 17862 read the INTZ's clear through one). Every transfer lands a clear still waiting for a pass first, so a Clear issued just before it is what it reads.

Both architectures and all local variants pass device.c:13838. Hosted Mac2 recordings also pass on both architectures; its obsolete pins are removed.

Managed 2D dirty publication coverage

Managed 2D textures retain their sampled image after a NO_DIRTY_UPDATE lock (19210/19217) until an explicit publication. AddDirtyRect publishes its region across the application mip levels (19232), using the existing partial-upload path. Initial contents and eviction still publish the CPU image. READONLY adds no publication after initialization.

The earlier eager-publication rationale no longer applies to this path. Separate policies remain: mip staging is independently allocated, dirty rectangles union to one bounding box per mip, and overlapping partial locks may modify bytes an earlier queued upload still reads. This change neither adds exact disjoint-region tracking nor changes that partial-lock policy.

Sites 19210, 19217 and 19232 pass on both architectures in the local normal, Intel and scale variants and in both hosted Mac2 recordings. Their obsolete baseline pins are removed.

Premultiplied texture-alpha blending

BLENDTEXTUREALPHAPM is advertised and emits saturate(arg1 + arg2 * (1 - texture_alpha)) for color and alpha operations. Wine's visual.c/texop_test exercises its previously skipped row. The direct E2E also checks implicit texture-alpha use with nontexture arguments, argument modifiers and saturation before a following texture stage.

An implicit-only missing texture uses zero alpha, following the native observation recorded in DXVK commit 0b49a39896f25896b83ed01c0609393dfc3bb85c. This is a reference choice, not a new native measurement: Wine's D3D9 GL dummy texture uses alpha one. Existing explicit-unbound-argument handling and ordinary BLENDTEXTUREALPHA behavior remain unchanged.

What the baseline records — and where classes live

Each datum has exactly one authoritative home, split by who writes it:

  • baseline.txt (machine-owned) records the current results: for each (arch, subtest), the crash bit plus every failing assertion as a <file>.c:<line> site with a hit count. No classifications — the file is freely rewritten by make conformance-baseline without ever touching prose.

    [i686/device] crash=1
      device.c:125 count=28
      ...
    
  • This document (human-owned) records why each site fails: the per-cluster section below declares every site's classification as a <line>=<class> token on a Sites: line, next to the rationale prose. The runner loads classes from here (for the flaky/ceiling tolerances and untriaged reporting); a unit test in the runner crate fails make test unless the two files cover exactly the same sites — so a new baseline site stays loud until someone writes its rationale, and a fixed site's prose must be removed rather than lingering as history.

Per-site granularity is what makes the score actionable: the 74 device failures, for instance, are really three source lines hit repeatedly in a loop, not 74 distinct defects. Recording the location (not just a total) means a fixed bug and a new regression can no longer net out to the same number and hide each other.

make conformance-baseline re-records baseline.txt and prints exactly which sites are new (add them to a cluster below, with a rationale) and which were dropped (delete their tokens and trim the prose). A run whose Wine version differs from the baseline's recorded version warns that file:line sites may have drifted (a Wine update renumbers source lines) and a re-baseline is expected — the Sites: tokens here renumber with it.

Classification tags

Each failing site carries one tag. The tag is a deterministic property of the divergence's NATURE — never of fixability, difficulty, or in-game value (a hard-to-fix or low-value defect is still real):

  • real — a genuine defect we intend to fix: our output/behavior is wrong and no deliberate design rationale covers the divergence. A mixed line (any intend-to-fix component alongside by-design assertions) is real, with the remainder explained in prose.
  • caps — the failure exists only because the test assumes a capability we deliberately don't advertise, AND our actual behavior is the conformant response for a device without that capability (correct pixels, or the spec-correct rejection). A cap-respecting test simply passes and never lands here; caps covers cap-blind assertions (Wine's tests assume caps that real desktop drivers always have) and escapes offered only under broken(), which the runner does not honor. If our response to the missing capability is itself non-conformant, the site is real.
  • expected — we deliberately do not implement this and intend to keep it that way, for a positive, documented reason: a scope decision (device loss, desktop mode switching, the D3D9-on-D3D12 bridge — see below), a kept perf tradeoff (the TBDR depth-store elision, buffer-rename over stalls), or an accepted platform limitation (Metal's 0xffff primitive restart, GPU-defined NaN encodings). "We don't want to fix it" or "the fix is invasive" is not a rationale — without a positive reason to keep the divergence, the site is real.
  • flaky — environmental/non-deterministic (display config, Retina scale, macdrv window-manager timing). Count changes in either direction never gate. Tag reactively — only once a flutter actually trips the gate — and pin the HIGHER observed count so a flutter back up is not a false regression.
  • ceiling — the pinned count is a cross-environment MAXIMUM, not an exact value: the same baseline serves environments where the site legitimately reads lower (a CI runner's virtual display accepts the mode changes this machine's macdrv rejects, so the desktop-mode sites read zero there; the fetch4 counts wobble with the attached display). Reading below the pin is tolerated and does not demand a re-record; reading above it gates like any regression. The tag adds only that tolerance — the divergence's nature stays in the cluster prose, and like flaky it is assigned reactively, from a measured cross-environment delta, never speculatively.
  • crash — a site attributed to a crash/abort path.
  • untriaged — an explicit placeholder for a site a human has not yet triaged. Normally untriaged means absent from this document (the sync test stays red until prose exists); writing =untriaged is the escape hatch for landing a re-baseline before the triage is done — the runner still flags it on every run.

The counts are the signal, not a target of zero. Wine's todo_wine/broken() annotations are tuned for a real-GPU driver, not for us, so a raw failure is not necessarily a real defect — the classification is what turns the number into something actionable. Note that when a subtest crashes, the counts cover only the failures reached before the crash truncated the run.

Per-cluster classification

This section is the authoritative home of every failing site's classification and rationale, grouped by enclosing Wine test function. The classes exist only here (baseline.txt holds counts); the runner loads them at gate time, and a unit test in the runner crate fails if any baseline site has no Sites: entry below, any entry names a site that no longer fails, or a site is declared twice. A second test recounts those tokens against the "Current classifications" sentence below and fails with the sentence to copy in when they disagree. When a re-baseline adds or removes sites, update the matching cluster block (and its rationale) in the same commit.

Line numbers refer to the Wine version recorded in the baseline header. A Sites: line lists every baseline site of the cluster as <line>=<class>; prose explains why. One source line can fire many assertions and can mix sub-causes — a real line may carry a by-design remainder (noted in prose), per the mixed-site rule: if any intend-to-fix component remains on a line, the line is real.

Audit provenance: every cluster below was re-derived on 2026-07-20 from the Wine test source, the raw actual-vs-expected failure messages (MTLD3D_CONFORMANCE_RAW_DIR), and the implementation — independently re-checked before retagging. Current classifications, counted from the Sites: tokens below on 2026-09-24: 0 real, 130 expected, 1 caps, 22 ceiling, 3 flaky, 0 untriaged, 156 unique sites in all. The audit recorded all 24 Apple-family subtest-legs crash=0. (2026-09-05: the two answers a device without the packed 16-bit formats derives from its render-target answer, CheckDeviceType for a 16-bit back buffer and the AUTOGENMIPMAP probe, were made to follow it, so device.c:3626 and device.c:7927 dropped off the Intel legs and their clusters left the audit; ValidateDevice learnt the sampler filter rules the same day and device.c:8181 went with them.) (2026-09-05: the scale legs added 32 visual.c sites in eleven clusters, every one expected for the one reason "The scaled leg" below gives; they carry no device.c site of their own since #408. The resz_test site they also carried, 17946, was real and is fixed: a lockable render target created at the reported back-buffer size declined the scale while the depth-stencil bound with it took it, issue #407.) (2026-09-05: the @mac2 legs, recorded on the Intel CI image, added 22 sites, every one expected and every one a property of that machine, its single-mode display or the GPU family, in the test_mode_change, test_get_display_mode, swizzle-format, test_fetch4, fp_special_test, test_multisample_get_front_buffer_data and multisampled_depth_buffer_test clusters; the eight @mac2 subtest-legs are crash=0 too.) (2026-09-04: the Intel legs, which run every subtest under the intel.* config keys, added device.c:3626, 7927 and 8181 as real (issues #362, #363; all three are fixed and gone) and visual.c:28024 as caps; no site moved under the two keys that change only a code path, intel.managedMemory and intel.linearAlign256. On the Apple family the ceiling and flaky pins of the native device legs are carried on the Intel legs at the same counts, since the environment they depend on is the same; the @mac2 legs, recorded on the Intel CI image, carry their own counts, see the family paragraph under "Running".) (2026-08-27: device.c:15088 moved from expected to ceiling, it fires only where the Wine build ships a loadable d3d12.dll; the SRGBTEXTURE decode landing the same day changed no site counts — the newly-running srgbtexture_test passes. 2026-08-28: honouring D3DCREATE_NOWINDOWCHANGES dropped test_window_style 5215, and test_wndproc 4551 was re-derived from the raw capture and corrected from real to expected. Multisampling then moved the counts in both directions. Four device.c/test_reset sites now pass, because a fullscreen Reset with a zeroed D3DPRESENT_PARAMETERS is rejected for its D3DFMT_UNKNOWN back-buffer format. Seven visual.c tests stopped skipping (every one of them gates on CheckDeviceMultiSampleType) and five of the seven pass outright; the clusters below cover what the other two and the tests they unblocked leave failing. The four real sites they added are since fixed, test_multisample_get_front_buffer_data 17179 and 17181 by the system-memory read-back destinations and resz_test 17724 and 17862 by the RESZ depth resolve, and multisampled_depth_buffer_test 17476 went with them once the depth-to-depth StretchRect resolved a multisampled source, so its cluster leaves this document too. 2026-09-06: a fullscreen Reset that names another device window hands the session over instead of giving the old window back, so test_device_window_reset 5968 passes and that cluster leaves the document as well; retargeting the Metal layer and the cursor subclass onto that window in the same Reset then added 5975 and 5978, the same window-procedure decision test_wndproc already records, and the cluster comes back for them. 2026-09-24: both multisampled depth resolves (RESZ and the depth-to-depth StretchRect) copy through a depth transfer instead of a render pass with a depth resolve attachment, so the paravirtual device's resolve fault no longer reaches multisampled_depth_buffer_test and its @mac2 cluster (17330, 17476) leaves the document; test_multisample_mismatch 20959 and 20962 pass on the @mac2 legs as well.) Only two tags change what the gate tolerates: flaky (count changes in either direction) and ceiling (reads below the pin). Every other tag is documentation, so a correction between real, expected and caps is never a gate change.

Desktop mode switching, and how fullscreen honors the requested size

A fullscreen device sets the display mode the app asked for through user32, as native does, then takes a borderless window over the monitor. The mode-set is meant to stay virtual: with Wine's EmulateModeset on (the harness pins it, and so does the launcher) win32u leaves the physical display alone, answers the desktop mode, GetSystemMetrics, GetMonitorInfo, the client rect and every mouse coordinate in the mode, and scales the window onto the physical monitor; without it the mac driver would hand the change to CGDisplaySetDisplayMode and switch the whole desktop. The device leaves the z-order alone: raising the window to the topmost level deadlocks winemac (see test_window_style 5220).

The mode list EnumAdapterModes serves is a bounded subset of EnumDisplaySettingsW's (the sizes of the display's own aspect, largest first), so an enumerated mode is one win32u accepts by construction, and a fullscreen request for any mode in the full list is set whether or not the bounded list carries it. The test binary, being the process's main module, enumerates the same bounded list through its own EnumDisplaySettingsW import (d3d9 redirects it at load; user32's list is untouched and ENUM_CURRENT_SETTINGS passes through), so a mode the test picks from either list is one user32 accepts. When the app requests one, the device sets it and the back buffer is that mode; present scales it to the drawable, which stays at the display's size (MetalFX when enlarging, the same resample render.scale rides). Both halves of the contract then agree with the size the app rendered for: the default viewport and scissor, the reported present parameters, the device's and swap chain's GetDisplayMode, and the Win32 metrics and mouse. (Until 2026-08 the back buffer honored the mode under a monitor-sized window, which kept the D3D9 half right and left mouse input in monitor space; before that it followed the window and apps that sized their viewport from their own request rendered into a corner.) A request that matches no mode user32 accepts still follows the window: native would reject it, so nothing can depend on it being honored, and the apps that make such requests (WoW's windowed-to-fullscreen toggle carries its window size) size their rendering and mouse handling from the window, so the window-sized back buffer is the assignment that keeps them consistent. We still do not reject such a request, which is the one expected site left in this area.

The focus half follows native too: WM_ACTIVATEAPP FALSE puts the registry mode back and WM_ACTIVATEAPP TRUE sets the mode and re-covers the monitor again; the window is never minimised and the device is never lost. The harness pins emulated mode switching and Retina mode so window-management assertions use a stable physical-pixel coordinate space.

Both pins are registry values, and a wineserver session enumerates the display once, when its desktop starts, and serves that geometry to every process in it afterwards. A pin therefore only takes effect in a session that started after it was written, and the session that creates a prefix predates them by construction, so configure-test-prefix ends that session once the keys are in. Without it the first leg in a fresh prefix runs against monitor geometry in the point space: test_window_position 15023 and test_reset_fullscreen 4903 fail outright, and the desktop-mode ceiling sites read 0 because the mode change the test asks for is accepted, exactly as on the CI runner's virtual display.

Two refinements landed 2026-08 after the CI runner exposed them (its virtual display accepts the mode changes this machine's macdrv rejects, so the tests walk further):

  • One source of display truth. EnumAdapterModes / GetAdapterDisplayMode come from EnumDisplaySettingsW, the same view win32u validates ChangeDisplaySettingsW against and derives GetMonitorInfoW from, instead of NSScreen: the current mode for GetAdapterDisplayMode (read live, so it follows a mode-set), the display-aspect subset of the enumerated list for EnumAdapterModes (so a mode a game picks is one user32 accepts, fills the display, and a menu built for a driver's short list does not overflow). On this machine the two views agree under the pinned Retina mode; on the runner's virtual display they disagreed by exactly 2x (Win32 2048x1536, NSScreen 1024x768), which split GetDisplayMode from the monitor rect (test_get_display_mode 14472/14474) and fed the tests modes that user32 then refused. Seeding the list from user32 is also what made the tests' own ChangeDisplaySettingsW calls succeed here (test_wndproc 4161/4231, test_reset 2234-2238, test_mode_change), since they pick their mode from EnumAdapterModes.
  • The mode contract. A fullscreen device sets the requested mode (2026-08), so the Win32 half of the contract holds: the desktop mode follows a create or Reset, GetSystemMetrics and the window rect report it, and the registry mode comes back when the device loses focus (WM_ACTIVATEAPP FALSE), leaves fullscreen (windowed Reset, final release) or the process exits. Where we diverge from native on purpose: the mode is set again on WM_ACTIVATEAPP TRUE rather than at the app's next Reset, because the device is never reported lost and so nothing would prompt that Reset (test_wndproc 4302).

The scaled leg

The scale legs run every subtest at render.scale = 0.75. For the tests' 640x480 device that rasterizes the back buffer, and every render target or depth buffer the test creates at that size, on a 480x360 grid, and every pixel the test reads comes back through the readback resolve, a filtered copy of the render grid up to the reported size (the display path's MetalFX scaler is not used for a readback: it writes an opaque alpha and takes only a few formats). Every coordinate the suite hands in is converted between the two spaces on the way in, so a probe several pixels clear of a colour boundary reads exactly what was rasterized at any scale, which is why the whole end-to-end suite holds at 0.75. What cannot survive the pair is a probe within a pixel of a boundary, a one-pixel feature, or a target too small for any pixel of it to be interior: those read the blend the resolve leaves, and the tell is a channel at one eighth or seven eighths of the neighbour (0x20, 0xdf) or within a step of it (0x04, 0xfb). A site of that shape is expected: the space separation is the design, and a probe on a boundary has no exact answer under a resample. A site whose values do not fit that mechanism is real, exactly as on any other leg; the legs carry none today.

The real backlog

Empty. No failing site is classified real on any leg.

Every other failing site is a recorded decision (expected), a capability we do not advertise (caps), a pin that reads zero on other hardware (ceiling), or a known flap (flaky), each with its rationale in the per-cluster section below.

The device subtest used to die silently inside test_volume_get_container (a GetContainer that answered E_NOINTERFACE with a null container, which the test then released), and the baseline recorded before the runner learnt to treat a missing end-of-run summary as a crash carried only the sites before that point. Every cluster from test_occlusion_query on was re-triaged when the run first reached its end again.

Vertex streams 1..15 and SetStreamSourceFreq instancing are implemented, so the clusters that used to sit on "single-stream rendering" (stream_test, fixed_function_decl_test, the stream-1 half of test_sysmem_draw, and the state-block stream capture in resource_check_data) no longer appear in the baseline.

device.c clusters

device.c/test_wndproc

Sites: 4207=expected 4212=expected 4214=expected 4219=expected Sites: 4223=expected 4248=expected 4257=expected 4293=expected Sites: 4298=expected 4302=expected 4319=ceiling 4340=expected 4420=expected Sites: 4424=expected 4432=expected 4487=expected 4525=expected 4545=expected Sites: 4572=expected 4161=ceiling 4231=ceiling 4551=expected 4475=flaky Sites: 4480=flaky

4161/4231 are the test's own ChangeDisplaySettingsW(CDS_FULLSCREEN) call, before any D3D9 object is involved; they read zero now that the mode the test picks from EnumAdapterModes is one user32 accepts, and stay ceiling pins from when it was not. The rest of the fullscreen focus lifecycle we deliberately do not drive: no focus/foreground mutation (4212/4214), no focus- window subclass (4223/4572), no WM_* activation/mode message generation (4207/4248/4293/4319/4340/4432/4525/4545), no focus-window minimize (4420), device-never-lost TestCooperativeLevel (4257/4298/4424/4487). 4302 (both iterations) expects the desktop still at the registry mode after the app is re-activated, native leaving the mode-set to the app's next Reset; we set the device's mode again on WM_ACTIVATEAPP TRUE, because a device that is never lost gives the app no reason to Reset. Caveat on 4219: it fails because OUR cursor wndproc subclass replaced the device window's proc — a deliberate, load-bearing hook we keep (cursor realization), not a missing feature.

4319 checks the hidden-device-window focus-loss message sequence immediately after SetForegroundWindow(GetDesktopWindow()): WM_DISPLAYCHANGE on the device window, then WM_ACTIVATEAPP(FALSE) on the focus window. It runs once with ordinary creation flags and once with D3DCREATE_NOWINDOWCHANGES, so the assertion can fail at most twice. An unchanged i686 build read one failure and then two; a clean control also read two. Both count-two raw captures lack the focus window's WM_ACTIVATEAPP(FALSE) in both iterations. The count-one run has no raw capture, so which iteration differed and why remain unknown. ceiling retains the pin of two while tolerating lower message-observation counts; unlike flaky, it still rejects counts above the pin. This changes no focus or activation behavior and does not establish that the message contract is fixed.

4257/4298/4424/4487 are the kept device-loss divergence, not an unwritten stub: no exclusive mode is ever taken, so nothing is ever lost, and TestCooperativeLevel answers D3D_OK across a focus change. The transition is detectable (the device window's subclass already handles WM_ACTIVATEAPP for the registry-mode restore), so this is a decision rather than a gap: reporting a loss that did not happen sends every fullscreen game through releasing and rebuilding its whole D3DPOOL_DEFAULT working set on each activation change, which costs frame time and risks the game's own recreate path, for a device that lost nothing. The D3DERR_DEVICENOTRESET half is real and implemented: a failed Reset latches it until one succeeds. Listed under Deliberately not implemented in docs/STATUS.md.

4551 is expected, and follows from the same no-modeset decision as the message sites above. It reads a WINDOWPOS the test's wndproc only captures once the expected-message walk reaches the fifth entry of mode_change_messages_hidden, and the walk stops one entry earlier, on the WM_SIZE the device window never receives: a fullscreen mode-change Reset resizes the back buffer, not the window, which already covers the monitor and keeps covering it, so its client rect is unchanged and user32 sends no WM_SIZE. 4525/4545 record that stall directly (both raw failures read Expected message 0x5), which leaves the capture zeroed and the assertion comparing against a null HWND. Reaching it needs a real mode-set, so the line moves only with that decision.

4475/4480 are flaky macdrv window-message timing sites; mtld3d does not call SetWindowPos or MoveWindow on those paths.

device.c/test_reset

Sites: 2234=ceiling 2237=ceiling 2238=ceiling 2250=ceiling Sites: 2251=ceiling

The fullscreen half of this cluster passes since a fullscreen device sets the requested mode: the request-side assertions (the default viewport matching the request at 2133/2134 and 2172/2173, GetPresentParameters reporting it at 2187/2189) and the Win32 half (2126/2127, 2179/2180, 2250/2251 reading the mode back from GetSystemMetrics(SM_CXSCREEN)). 2234/2237/2238 are the test's own ChangeDisplaySettingsW call, before any D3D9 object is involved; it succeeds now that the mode it picks from EnumAdapterModes is one user32 accepts. All five stay ceiling pins from when they failed here.

The fullscreen Resets to a mode user32 rejects (32x32, 801x600) return INVALIDCALL, for a reason that has nothing to do with the resolution: each zeroes its whole D3DPRESENT_PARAMETERS, so BackBufferFormat is D3DFMT_UNKNOWN, which a fullscreen Reset has to reject. The resolution itself is not validated against the mode list; such a request follows the window instead.

The windowed API contract in this test passes: Reset rejects an outstanding app reference to a DEFAULT-pool resource or an implicit surface, and a failed Reset latches DEVICENOTRESET until one succeeds.

device.c/test_scissor_size

Sites: 3685=expected 3700=expected

The default scissor rect must equal the back buffer the app asked for, both after create (3685) and after a Reset (3700). Every window in this test is created WS_MAXIMIZE, and a maximized window is sized by the window manager rather than the app, so we take its client rect and ignore the requested size — the same rule as fullscreen, for the same reason. The scissor itself is correct: it matches the back buffer we actually created.

Note 3700 additionally expects the full screen size, while a maximized window's client rect is the work area (screen minus menu bar and Dock), so this line would differ even if the create path honoured the request.

device.c/test_wndproc_windowed

Sites: 4681=expected 4697=expected 4701=expected 4708=expected 4751=expected Sites: 4774=expected 4778=expected 4785=expected

4701/4778 expect the focus window subclassed in fullscreen (we don't). The other six expect the device window's wndproc UNCHANGED and fail because of our cursor subclass — the same deliberate hook as test_wndproc 4219, kept on purpose (cursor realization is driven from it).

device.c/test_reset_fullscreen

Sites: 4871=expected

WM_ACTIVATEAPP delivery on a windowed→fullscreen Reset; we do not force-show/activate the window.

device.c/test_fpu_setup

Sites: 5041=expected 5051=expected

i686 only. Native D3D9 rewrites the x87 control word to single precision (0x7f) at device creation and keeps it for callbacks; we deliberately never touch the FPU control word. On x86_64 the same checks are todo_wine (free).

device.c/test_window_style

Sites: 5220=expected

5220 is expected: the fullscreen extended style must carry WS_EX_TOPMOST. We deliberately leave the z-order alone, because raising a window to the topmost level makes Wine's mac driver re-derive the Cocoa window's level and parent while holding winemac's per-window lock and hop to the main thread to do it; a focus event arriving meanwhile re-enters NtUserSetWindowPos on another thread and the process deadlocks. Reproduced in the visual subtest. A borderless window covering the monitor already presents as fullscreen, so the z-order buys nothing. (5200, the window-rect adoption, now passes.)

5215 passes since D3DCREATE_NOWINDOWCHANGES is honoured: a device created with that flag leaves the device window's style, rect and visibility to the app, in fullscreen as in windowed mode, so a window the app kept hidden is still hidden after the fullscreen round trip. The three todo_wine lines that the flag also covers (5179/5197/5238) now succeed inside their todo blocks, which the runner does not count.

device.c/test_mode_change

Sites: 5509=ceiling 5533=ceiling 5537=ceiling 5584=ceiling Sites: 5602=ceiling 5622=ceiling 5636=ceiling 5639=ceiling 5646=ceiling Sites: 5671=ceiling 5674=ceiling Sites: 5598=expected 5618=expected 5641=expected 5676=expected 5900=expected

Desktop display-mode-change lifecycle (ChangeDisplaySettingsW success, EnumDisplaySettings reflecting changes/restores, fullscreen window resize). The whole cluster passes since a fullscreen device sets and restores the mode and the test's own CDS calls pick a mode user32 accepts; the ceiling pins date from when physical mode switching was disabled and they failed here while reading zero on a CI runner. 5552/5554 (the back buffer must keep the size a fullscreen create asked for across an external mode change) pass because the back buffer honors the request and never follows the window. 5598/5618/5641/5676/5900 ("Failed to restore display modes") fire on the @mac2 legs only: the Intel CI image's paravirtual display lists a single mode, so user32 refuses the mode the test sets and the restore that follows it, and a fullscreen device there takes the non-mode path (the back buffer follows the window). Desktop mode switching is out of scope, so expected; a real Intel/AMD Mac lists its modes and reads zero here.

device.c/test_get_display_mode

Sites: 14378=expected 14379=expected 14383=expected 14384=expected Sites: 14390=expected 14391=expected

@mac2 legs only. The test sets 640x480 through ChangeDisplaySettingsW and expects GetDisplayMode to answer it; the paravirtual display refuses the mode (see test_mode_change above) and the answer stays the desktop mode. The same scope decision as the desktop-mode cluster.

device.c/test_device_window_reset

Sites: 5975=expected 5978=expected

Both read the window procedures after a Reset that retargets a fullscreen device from the focus window onto a separate device window: the device window's must be the application's own (5975) and the focus window's must still be D3D9's (5978). Native subclasses the focus window and leaves it there across the retarget. Ours is the cursor subclass, and it follows the window the device presents into, because that is the window the D3D9 cursor is realized over and the one whose client area the back buffer follows. Both halves of that decision are already recorded on test_wndproc 4219/4223/4572 and test_wndproc_windowed 4701/4778: our hook sits on the device window and never on the focus window. These two sites are the same decision seen through the retarget, so they move only if the hook moves.

device.c/test_occlusion_query

Sites: 6780=expected

The >2^32-sample query (65 fullscreen 8192x8192 quads under one query, depth test off) undercounts because Apple's TBDR hidden-surface removal merges same-encoder opaque overdraw before fragment processing: the visibility counter reports the samples that survive HSR, not every sample that would have passed the depth test on an immediate-mode GPU. Proven by instrumentation, not inferred: the GPU-written slot value itself is short (our BEGIN..END span is a single slot in a single frame, summed correctly), the value is always an integer number of 8192-wide quad ROWS (0x1de98f00 = 8192 x 61260; an earlier environment read 0x077a63c0 = 8192 x 15315), and tile-row-granular partial renders decide how many overdraw layers escape culling, which is why the number moves between environments. The same test's single-quad section counts bit-exactly (0x75cf00 = one 3456x2234 quad), so the machinery is precise whenever HSR has nothing to merge. Counting all overdraw layers would need an encoder per draw under active queries, destroying pass batching; the kept optimization is single-encoder pass batching, so this is expected. Real-game occlusion (a bounding box tested against a populated depth buffer, read as zero/non-zero) is unaffected.

Wine first times 1,000 query BEGIN/END pairs. If that loop takes more than 70 ms, device.c:6706 prints Tests skipped: Test loop took too long (...), ending with skipping large query tests., and jumps to cleanup before 6780. An Intel CI run measured 100 ms and took that branch. The scanner recognizes that exact site and message shape, including the elapsed time above 70 ms, as evidence that 6780 did not run. The mapping must be reviewed with the baseline's source locations when Wine changes. The classification remains expected and its count stays pinned at one: ceiling would also tolerate an assertion that ran and passed, which this observation does not establish.

device.c/test_timestamp_query

No failing sites. The test first creates TIMESTAMPFREQ and skips when it returns D3DERR_NOTAVAILABLE, before probing TIMESTAMP or TIMESTAMPDISJOINT. That skip establishes no result for either later query. All three types are unsupported; the end-to-end query test checks each capability probe and creation against D3DERR_NOTAVAILABLE, with EVENT and OCCLUSION as supported controls.

device.c/test_lockrect_invalid

Sites: 8664=expected 8682=expected 8701=expected

We PASS the accept-invalid lock checks (the broken()-guarded Win7 reject alternative is not what we take). These offset assertions then compare our returned pointer against blind top*pitch + left*bpp arithmetic on the invalid rect. parse_rect clamps invalid rects (negatives to 0, inverted/zero-area to the full mip), so our offsets differ; matching XP exactly would require handing out pointers OUTSIDE the staging allocation, which the lock-safety model forbids (lock_region_ptr bounds assert). Deliberate safety tradeoff, kept. (Cube's garbage offsets are pointer diffs across unrelated per-lock allocations: meaningless, not out-of-bounds.)

device.c/test_pinned_buffers

Sites: 10074=expected 10079=expected

The test expects a DISCARD re-lock to return the same pinned pointer with prior contents intact, a driver-specific optimization probe with no cap branch. Our rename-on-DISCARD model returns fresh backing by design, and DISCARD contents are spec-undefined, so our behavior is legal. Intent to keep (the rename model is core).

device.c/test_lost_device

Sites: 12144=expected 12146=expected 12153=expected 12155=expected Sites: 12199=expected

Focus-loss/device-lost lifecycle: TestCooperativeLevel/Present/Reset must report DEVICELOST/DEVICENOTRESET across a fullscreen focus cycle. Our device is never lost by design (no exclusive fullscreen, no GPU loss on Metal). The only non-OK TestCooperativeLevel answer we give is the DEVICENOTRESET latch a failed Reset leaves behind, which is the windowed API contract test_reset exercises, not focus-driven loss.

device.c/test_check_device_format

Sites: 12689=expected 12694=expected

CheckDepthStencilMatch(..., D3DFMT_D32): native returns NOTAVAILABLE; we return D3D_OK because D32 genuinely maps to Depth32Float and works. We advertise MORE than native here, deliberately; not an omitted-cap (caps) case, and our answer is truthful for our backend. 12694 is the R5G6B5 render-target row of that check, so it fires on the native legs only: on the Intel legs R5G6B5 is no render target and the answer is the NOTAVAILABLE the test expects.

device.c/test_miptree_layout

Sites: 12784=expected 12823=expected

The test asserts each mip's lock pointer sits at a contiguous offset from level 0 (single-allocation mip chain). Our staging is one PageBox per mip, which is load-bearing for the rename-at-overlap versioning model (each mip's Arc swaps independently); a contiguous chain is structurally incompatible with that design, which we keep. Site 12823 is the same pointer layout assertion across six cube faces and their mip levels. Cube staging is also one PageBox per subresource so a face or mip can rename independently. Per-subresource pixel data is correct.

device.c/test_cursor_clipping

Sites: 14930=ceiling

After a fullscreen device is created at a mode of another aspect than the display's, the cursor clip must equal the virtual screen, i.e. the mode. Under Wine's emulated mode-set win32u clips the foreground fullscreen window to the physical monitor and reports that rect mapped back into the mode, which for 640x480 on a 3:2 panel reads "(-51,0)-(691,480)": the letterbox bars are inside the clip. That is win32u's mapping, not ours; the device sets the mode exactly as native does. ceiling because it reads zero on a display whose aspect the mode matches (the CI runner's 4:3 virtual display has no letterbox for 640x480).

device.c/init_d3d9on12_modules

Sites: 15088=ceiling

win_skip("Direct3DCreate9On12 is not supported…"): under Wine, win_skip counts as a test failure. We don't provide the D3D9-on-D3D12 bridge; N/A on Metal. Ceiling, not expected, because the site only fires where the Wine build ships a loadable d3d12.dll: the win_skip sits after the three LoadLibrary calls, and a failed load takes a plain skip() that counts nothing. The pinned CI release is built with Vulkan and reads 1; the current local dist is built without (no winevulkan.dll, no i386 d3d12.dll at all) and reads 0. Not test-source drift: dlls/d3d9/tests/ is identical between the two builds.

device.c/test_d3d9on12

Sites: 15160=expected

win_skip("Failed to load d3d9on12 modules…"): the companion to 15088, same D3D9-on-D3D12 rationale. expected, not ceiling, because it fires on both kinds of build: this skip is the module-load failure itself, which under a Vulkan-less build happens one dll earlier but still lands on this line's win_skip in test_d3d9on12.

visual.c clusters

visual.c/z_range_test

Sites: 3887=expected 3889=expected 3891=expected 3894=expected Sites: 3963=expected 3965=expected

All six depend on a depth clear (0.75) written BEFORE a Present surviving into later frames with ZWRITE off. Store-action Rule B flips the auto DS store to DontCare at Present — the deliberate TBDR depth-store elision (the preserve fix was implemented and reverted to keep the optimization). The broken() r500 alternatives are ignored by the runner; the primary assertions need cross-Present depth.

visual.c/texdepth_test

Sites: 5360=expected 5398=expected 5436=expected 5454=expected

The ps_1_4 depth-gradient math is correct (the same-frame cycle passes and is absent here). The failing cycles read the gradient across Presents — the same Rule B depth-store elision as z_range_test.

visual.c/pixelshader_blending_test

Sites: 12008=expected

Renders into a one- or two-channel texture (G16R16, R16F, G16R16F, R32F, G32R32F) with blending on, then samples it and expects absent channels to read as 1.0. Render targets now keep separate attachment and sampling views, including both linear and sRGB roles for X8 formats. Native Apple GPUs apply the sampling swizzle while stored channels retain their blended values.

The remaining failure is the Mac2 Paravirtual device's ignored texture-view swizzle, shared with ordinary sampled textures below. It reads absent colour lanes as zero despite reporting the requested view swizzle. The E2E controls compare those lanes with an ordinary same-format texture on that device and still verify stored lanes, mip and cube addressing, and view retirement. Physical-GPU tests additionally require the specified all-ones missing lanes.

visual.c/test_fetch4

Sites: 15668=ceiling 15727=expected

Fetch4 uses native gather for point-filtered 2D L8/L16/R16F/R32F/A8 and INTZ/DF16/DF24 samplers. GET4 and GET1 commands through D3DSAMP_MIPMAPLODBIAS set and clear a latch; numeric bias writes preserve it. Gather honors projected coordinates and reads the base mip, including texldb/texldd/texldl. DF16/DF24 raw reads now return (depth, 0, 0, 1), while INTZ broadcasts depth.

The basic gather and DF raw-depth checks pass on local and @mac2 legs.

15668 keeps its existing ceilings. The test writes only four bytes of each format texture, so projected L16/R16F samples can read unwritten texels. ATI1 is outside the supported Fetch4 format set and keeps ordinary sampling, with alpha one rather than a fourth gathered texel. A8R8G8B8 also keeps ordinary sampling; its unwritten texels can have alpha one rather than the expected zero. These cases account for the remaining format-site failures. Keep the higher pins because counts vary with allocation contents. 15727 (@mac2 legs only) is the 3D section's L8 volume texture, which reads its green and blue lanes through the channel swizzle the paravirtual device ignores (the swizzle cluster above): the sample carries red alone there.

visual.c/fp_special_test

Sites: 16433=expected

VS special-float ops on NaN/±inf: the test accepts four distinct vendor results (r500/r600/nv40/nv50) plus broken(warp) — special-value handling is GPU-defined, not spec-mandated. Our Metal GPU produces a fifth valid IEEE result matching no vendor's encoding. Matching a specific vendor is neither feasible nor desirable. No capability involved (old caps tag incoherent). The @mac2 legs count three, not two: the paravirtual device encodes one more instruction's result its own way.

visual.c/float_texture_test, g16r16_texture_test, test_mipmap_autogen, test_signed_formats, volume_v16u16_test

Sites: 5090=expected 5169=expected 6034=expected 18787=expected Sites: 18790=expected 18793=expected 18796=expected 20702=expected Sites: 20751=expected

@mac2 legs only, one mechanism. Every format whose D3D9 sample fills a lane the Metal format lacks (R32F's green and blue, G16R16's alpha, V8U8's and V16U16's blue and alpha, X8R8G8B8's alpha, L8's green and blue) is handed out as a texture view with a channel swizzle. The paravirtual device on the Intel CI image creates that view, reports the swizzle on it, and samples through the base texture's lanes anyway, measured in the workflow's probe job; every real GPU family applies the swizzle. So R32F reads its missing lanes as zero (5090), G16R16 its alpha as zero (5169), V8U8 and V16U16 their blue as a stored lane (20702 and 20751, which count both formats' rows, and the four V16U16 volume probes 18787, 18790, 18793 and 18796), and the autogen X8R8G8B8 chain its padding byte as alpha (6034). A device limitation with no D3D9-side answer, so expected; none of these fire on the Apple family, and a real Intel/AMD Mac is expected to read zero here.

visual.c/test_multisample_get_front_buffer_data

Sites: 17167=expected 17169=expected 17179=expected 17181=expected

@mac2 legs only. After the Reset to a two-sample back buffer, GetFrontBufferData into a 640x480 system-memory surface answers D3DERR_INVALIDCALL there (17167, 17179) and the reads that follow see nothing (17169, 17181). The read-back measures the destination against the back buffer's extent, so the back buffer after that Reset is not 640x480 on the paravirtual display, whose single mode makes the request a non-mode one (test_mode_change above); that mechanism is inferred from the rejection, not read from a trace. The Apple-family legs pass every site.

visual.c/test_multisample_mismatch

Sites: 20880=expected 20883=expected 20959=expected 20962=expected

The whole test draws with a multisampled render target beside a single-sampled depth buffer and the other way round. Metal rejects a render pass whose attachments disagree on sample count, so mtld3d drops the mismatched depth attachment; the draws land but the depth test does not gate them. The pipelines and clear quads built for such a pass declare no depth or stencil format either, since Metal rejects a pipeline that names a format the pass has no attachment for. D3D9 never defined the case: every assertion here carries a second accepted colour under broken(), and the comments in the test record that AMD and Nvidia disagree about whether the draw happens at all.

visual.c/test_flip

Sites: 22053=expected 22064=expected

The device is created with D3DSWAPEFFECT_DISCARD, under which post-Present backbuffer contents are UNDEFINED by spec; the test observes native's incidental flip-chain content rotation. Not emulating that is spec-compliant. Surface identity and lockable read-back now pass. A title relying on flip-chain read-back under FLIP/COPY swap effects would be a different (real) matter.

The original expected classification of all five sites was wrong: their raw result was 0xdeadbeef, not a colour from an undefined backbuffer. The lockable read-back helper accepts only A8R8G8B8, and the backbuffer's GetDesc incorrectly reported X8R8G8B8. That descriptor defect was real, including on the mixed sites whose remaining assertions observe undefined contents.

Reporting the requested A8R8G8B8 format lets the helper read pixels. Sites 22055, 22066 and 22072 pass on both architectures across the Apple GPU legs and the Intel/AMD @mac2 runners. Their baseline pins have been removed. Sites 22053 and 22064 return actual colours from the shared backing rather than a rotating chain, so only their by-design remainder is classified expected.

visual.c/test_max_index16

Sites: 24133=expected 24135=expected

Metal treats index 0xffff as the un-disableable uint16 primitive-restart sentinel, dropping the triangle that uses it; the test additionally writes vertex 0xffff OUTSIDE its lock (UB, may never reach the GPU). broken(warp) shows even the MS reference rasterizer fails this; the runner ignores broken(). Accepted platform limitation (no cap branch — old caps tag was wrong).

visual.c/test_map_synchronisation

Sites: 25148=flaky

The failing config is exactly the plain (no DISCARD/NOOVERWRITE) PARTIAL lock of a contended Direct buffer, which native stalls for. Our buffer- rename design deliberately removed that stall (plan_lock → WriteInPlace); re-adding it is the only fix and is a rejected perf regression. Whether the test observes the divergence is a per-run CPU-vs-GPU race (the probe's Lock write lands before or after the GPU consumes the in-flight draw), so the count flutters between 0 and 1 across runs of the same binary; it read 0 on the CI runner and tripped the stale-baseline gate (PR #12), hence the flaky tolerance. That race is in the observation only, not in the decision: plan_lock is a pure function of a coherent_seq its caller read once with Acquire before calling, and only the unix side raises that counter (fetch_max, on GPU retirement), so a stale read can turn a legal in-place write into a needless rename but never the reverse. The kept divergence itself is unchanged.

visual.c/test_mipmap_upload

Sites: 27550=expected

The app writes the whole mip chain through a single level-0 lock pointer; with per-mip PageBox staging the upper mips never receive the data. Same architecture-we-keep rationale as test_miptree_layout — but this is the weakest expected in the file: it produces wrong rendered pixels for a real-app pattern (Wine cites shipped titles). If the per-mip staging commitment is ever softened, retag real first.

visual.c/test_default_attribute_components

Sites: 27902=expected

FLOAT→unorm rounding at exactly .5: Metal rounds 76.5 up (77), refrast truncates (76). A ±1 GPU rounding-convention difference with no cap branch; mimicking refrast exactly is not feasible or desirable.

visual.c/test_format_conversion

Sites: 28024=caps

Intel legs only. The test expects CheckDeviceFormatConversion(YUY2, R5G6B5) to answer D3D_OK, as every desktop driver does. A conversion destination has to be renderable, since the StretchRect quad draws into it, and on a device without the packed 16-bit formats R5G6B5 is no render target, so the answer is NOTAVAILABLE. That is the conformant answer for a device without the capability; the same rule is pinned by the e2e check_format_conversion test, which asks the device first.

visual.c/get_rt_readback

Sites: 199=expected

point_match walks outward from a point's centre until the colour changes and compares that radius with the size the test set; under the scaled leg every point's edge reads the resolve's blend one pixel wider than the rasterized point, so the radius it finds is off by one at every size ("The scaled leg").

visual.c/texkill_test

Sites: 5545=expected 5554=expected 5570=expected 5572=expected 5606=expected Sites: 5615=expected 5633=expected

Every probe sits one pixel inside a texkill boundary (x = 66, 575 or 578 on row 49) and reads the blend of the killed and the surviving side (0x9f6000, 0xdf2000) rather than the pure colour ("The scaled leg").

visual.c/test_fragment_coords

Sites: 10769=expected 10771=expected 10773=expected 10775=expected

The four probes straddle the reported centre (319/320, 239/240), where the four vPos quadrant colours meet, and each reads the blend of its two neighbours (0x00dfdf for 0x00ffff). The register itself answers in the reported space since #403: before it every probe read the wrong quadrant outright. The fraction assertion passes, frc(vPos) stays zero under the scale ("The scaled leg").

visual.c/test_pointsize

Sites: 11502=expected 11504=expected 11506=expected 11508=expected Sites: 11556=expected 11567=expected 11571=expected 11574=expected Sites: 11579=expected 11582=expected 11585=expected 11588=expected

Point sizes are stated in reported pixels and kept there (the vertex epilogue converts them to render pixels, see points.rs in the end-to-end suite), but the test probes each point's edge to the pixel and a one-pixel point outright; every such probe reads the resolve's blend (0xe3, 0xfb, 0xdf channels) ("The scaled leg").

visual.c/test_viewport

Sites: 14064=expected 14081=expected

Pixel (1, 119) and (1, 360) lie on the first column and the first and last rows of a viewport bound, and read a one-eighth blend (0x202020) of the colour across it ("The scaled leg").

visual.c/depth_buffer_test

Sites: 14557=expected

The test creates targets of 320x240, 480x360 and 640x480 and binds each with the device's one depth buffer, expecting depth written through one to be read through another at the same pixel. The 640x480 target is the back-buffer size and rasterizes at the scale, the depth buffer with it; the 480x360 target is its own size and does not. Depth written through the scaled target lands at three quarters of its reported coordinates, then is read through the unscaled target at the unconverted ones, so the two probes read the colour of the neighbouring quad. By design: the scale is a property of the targets at the back-buffer size (mtld3d.conf, render.scale), and a depth buffer shared between a scaled and an unscaled target of different sizes has no single mapping that serves both.

visual.c/clip_planes

Sites: 16129=expected 16131=expected

The plane cuts the quad at y = 240.5 in the reported space and the probes sit on rows 240 and 241, the two rows either side of it; under the scale that edge falls inside one render row and both probes read its blend (0x9ca13c) ("The scaled leg").

visual.c/test_filling_convention

Sites: 27409=expected

The test draws into 2x2 to 8x8 render targets and copies each onto the back buffer with StretchRect before probing it pixel by pixel; a target that small resampled onto the scaled back buffer has no pixel that is not a boundary, so every probe reads a blend (0x24db for 0x00ff). 221 hits, one per pixel per case ("The scaled leg").

visual.c/test_ffp_w

Sites: 28113=expected 28137=expected

Both probes sit on the edge of the w-tested quad and read the one-eighth blend (0xdf0020 for 0xff0000) in both the declaration and the FVF form ("The scaled leg").

stateblock.c clusters

d3d9ex.c clusters

No sites. Direct3DCreate9Ex is exported and answers D3DERR_NOTAVAILABLE, so START_TEST resolves it and proceeds instead of taking the win_skip("Failed to get address of Direct3DCreate9Ex") that counted as a failure under Wine (formerly site 5184, classified expected). Every test then fails to create its Ex device and skips, which is not a failure, so the suite reports none. D3D9Ex itself is not implemented yet; only the entry point resolves. It is wanted once a title needs it, so a site landing here later is not expected; docs/STATUS.md says what an extended device changes.

Wide-format offscreen conversion

Same-size DEFAULT offscreen StretchRect converts A16B16G16R16 and A32B32G32R32F directly into A8R8G8B8. UNORM16 channels round to the nearest 8-bit normalized value; float channels clamp to [0, 1] and round only at the final destination, with NaN mapping to zero. Partial rectangles preserve the pixels outside them through the existing staging ownership path. Contended conversion staging is renamed so queued uploads and retries retain their source bytes. A destination upload following an ordered GPU copy uses the ordered blit stream instead of running ahead of that copy at the frame head. Ordinary GPU copies and draws keep their existing routes.

The format gate rejects other cross-Metal-format offscreen pairs without a CPU codec before scheduling uploads. This is an implementation limitation, not a native format restriction; render-target destinations retain their GPU conversion path. Wide-to-wide, other wide destinations and offscreen scaling remain unsupported. Adding these two codecs does not broaden the separately validated UpdateSurface/UpdateTexture format set. The existing device.c StretchRect matrix covers A8R8G8B8, X8R8G8B8 and R5G6B5, so these corrections are pinned by end-to-end pixel regressions rather than a baseline reduction.

visual.c/yuv_layout_test, YV12 and NV12

The test creates a 20x16 DEFAULT offscreen plain per format, behind two gates: CheckDeviceFormat(SURFACE, usage 0) (visual.c:12991) and CheckDeviceFormatConversion into the back-buffer format (visual.c:12997). Both now answer yes for YV12 and NV12, so the planar halves run instead of skipping.

The layout the test writes is relative to the lock pitch, never to the width: the chroma starts at Pitch * height, YV12 stores a V plane and then a U plane whose rows stride Pitch / 2, NV12 one interleaved plane whose rows stride Pitch. mtld3d reports the width rounded up to four bytes as the pitch, allocates Pitch * (height + ceil(height / 2)) bytes, uploads them verbatim into one R8 texture as wide as the pitch, and addresses every plane from the pitch in the fragment decode and in the CPU converter. wined3d stores the same single-channel image but places the YV12 half-planes at half the texture width, which agrees only when the pitch equals the width; DXVK reports the same pitch and then addresses by the extent. Neither difference is visible at the test's 20x16.

The probes expect reduced-range BT.601 within 1 (visual.c:13074, visual.c:13077), the matrix the packed formats already decode with. DXVK converts with BT.709 and answers the conversion query no for both formats, so it takes the 12997 skip and its colours are never checked here.

Limits: DEFAULT pool only, YV12 heights even (the origin of the U plane of an odd-height surface follows floor(height / 2) rows in the test's arithmetic and ceil(height / 2) in a writer that stores the rounded-up row count, and the test's even surface pins neither), no texture advertisement (wined3d's choice; DXVK advertises one), and unfiltered chroma. The test's ColorFill calls expect S_OK only and read nothing back; mtld3d returns it and leaves the surface unfilled, as for YUY2 and UYVY.

visual.c/tssargtemp_test

The fixed-function cascade supports the optional TSSARGTEMP capability. RESULTARG selects CURRENT or a fragment-local TEMP register initialized to zero. Both color and alpha read the pre-stage register values before the whole result is assigned. The final output remains CURRENT. The Wine test at 12124 combines independently retained red CURRENT and green TEMP values into yellow; the focused end-to-end tests additionally cover initialization, modifiers, destination changes, stateblocks, and Reset. No uniform or texture allocation backs the register.

visual.c/test_per_stage_constant and test_texture_blending

PERSTAGECONSTANT is advertised. D3DTA_CONSTANT selects the current stage's D3DTSS_CONSTANT color, with alpha-replicate and complement modifiers. Values use fragment constants rather than shader keys. Only a shader consuming a stage constant extends the existing texture-factor buffer, to at most nine float4 rows. Ordinary shaders keep their existing one-row construction and unused-buffer bind suppression.

The dedicated test expects 0xa1b2c3, complement 0x5e4d3c, alpha-replicated 0x808080 and alpha-blended 0x80007f at 19538/19556/19574/19596. Together with TSSARGTEMP, the capability also opens test_texture_blending after its two capability guards. Its individual cases still check TextureOpCaps. Raw D3DTSS_CONSTANT defaults remain zero, as device.c:7746 asserts. ALL and recorded stateblocks retain the value; this feature preserves the existing PIXEL preset exclusion rather than changing capture policy.

visual.c/volume_v16u16_test and test_signed_formats

V16U16 textures use native RG16Snorm storage, with missing blue and alpha set to one. The signed-format and volume tests run through the same native byte uploads as other mapped formats. 2D and cube AUTOGEN requests answer D3DOK_NOAUTOGEN and create one actual level with the public usage retained; no mip-generation work is submitted. Render-target and sRGB queries remain unavailable.

Q8W8V8U8 textures use native RGBA8Snorm storage with four signed channels. The negative Q channel follows signed normalization, including a floor of minus one for both minimum encodings. The legacy R200 exception in Wine's signed-format alpha test is not an emulated target. All four lanes are stored, so sampling needs no conversion and no view swizzle, and the Q8W8V8U8 rows of test_signed_formats do not depend on the mechanism that fails the V8U8 rows on the @mac2 legs. Render-target and sRGB capabilities stay absent; AUTOGEN texture/cube requests preserve usage but use one actual level with no generated chain. DYNAMIC creation in the MANAGED and the SCRATCH pool is rejected for every format and every texture type, the way D3D9 rejects it, rather than for the signed and packed ten-bit formats alone or for volumes alone. X8L8V8U8 and L6V5U5 remain separate capabilities.

Q16W16V16U16 textures use native RGBA16Snorm storage and the same scoped NOAUTOGEN and unavailable render/sRGB policies. All four lanes are stored and no view swizzle applies, so nothing here depends on the mechanism that fails the V8U8 and V16U16 rows on the @mac2 legs. Upstream Wine has no Q16 positive row in test_signed_formats; existing mode/conversion references do not establish signed pixel correctness. Dedicated end-to-end tests pin all four signed16 lanes, alpha, eight-byte transport and float32 sampling precision. This feature does not claim a Wine positive-pixel skip reduction.

ColorFill of a DEFAULT offscreen plain surface in V8U8, V16U16, Q8W8V8U8 or Q16W16V16U16 writes each D3DCOLOR channel as the nearest nonnegative signed code, R, G, B, A into U, V, W, Q, which is the value a clear of the matching signed-normalized attachment to the colour's [0, 1] floats stores. The format table of visual.c/color_fill_test has no signed row and no other upstream test fills a signed surface, so no site moves. Dedicated end-to-end tests pin the whole and sub-rect bytes, the texels around a sub-rect, the rejected CPU-pool and texture-level destinations, and the upload the GPU samples.

device.c/test_getdc, A2R10G10B10

A2R10G10B10 textures and CPU surfaces are creatable, stored as native BGR10A2Unorm. D3D9 packs blue in the low ten bits, then green, red and the two alpha bits, which is that Metal format's layout, so locks expose the native words and sampling needs no conversion and no view swizzle. Every lane is stored, so nothing here depends on the mechanism that fails the V8U8 and V16U16 rows on the @mac2 legs.

The A2R10G10B10 row of test_getdc used to skip because neither the SYSTEMMEM surface nor the MANAGED texture could be created. It now runs and passes: the row expects GetDC to fail and the out pointer to stay untouched, and the format has no GDI mapping. The row uploads no pixels. The other upstream references are the display-format table of test_display_formats, where the format stays no display, back-buffer or render-target format, and the identity row of test_format_conversion. No failing site moves on any leg.

The end-to-end suite checks ten-bit precision per lane, the four alpha codes and the raw lock and copy words. Render-target, sRGB and legacy bump-map queries are rejected. Texture and cube AUTOGEN answers NOAUTOGEN with one physical level and the requested usage retained. ColorFill of a DEFAULT offscreen plain surface encodes each channel to its nearest code; the rounding of a Windows driver has not been measured.

A2B10G10R10 sampled textures

A2B10G10R10 textures and CPU surfaces are creatable, stored as native RGB10A2Unorm. D3D9 packs red in the low ten bits, then green, blue and the two alpha bits, which is that Metal format's layout and the mirror of A2R10G10B10, so locks expose the native words and sampling needs no conversion and no view swizzle. Every lane is stored, so nothing here depends on the mechanism that fails the V8U8 and V16U16 rows on the @mac2 legs.

Upstream names the format twice. test_display_modes (device.c:2620) expects EnumAdapterModes to answer INVALIDCALL, and it still does: the format stays no display, back-buffer or render-target format. test_format_conversion (visual.c:27957) lists it in all_formats, where the identity pair already answered S_OK and every other pair accepts either answer. Neither row creates a resource, so there is no skip to open and no failing site moves on any leg.

The format shares the A2R10G10B10 policy: render-target, sRGB and legacy bump-map queries are rejected, texture and cube AUTOGEN answers NOAUTOGEN with one physical level, and ColorFill of a DEFAULT offscreen plain surface encodes each channel to its nearest code. The end-to-end suite runs the A2R10G10B10 scenarios in this lane order and adds two witnesses of the order itself: the same word written to both formats samples with red and blue exchanged, and copies between the two formats are rejected with the destination unchanged.

visual.c/volume_dxtn_test and device.c/test_volume_blocks

DXT1 to DXT5 volume textures are creatable in every pool, stored as native BC1, BC2 and BC3 textures: a 3D texture when the base depth is above one, the 2D texture every depth-one volume already uses otherwise. Locks expose the compressed blocks with a row pitch of one block row and a slice pitch of the level's block rows, and uploads copy them verbatim, so nothing is decompressed and no shader or draw-time work is added. The level's compressed-layout marker (a zero bytes-per-pixel) is kept through volume creation, so an upload of a level that has reached one slice counts block rows, not texel rows, and the alignment repack never asks for bytes past the level's staging. That path was reachable before this capability through a SCRATCH volume bound for sampling. Cube creation keeps the marker as well, and the repack checks the rows it is asked for against the staging length before it copies.

volume_dxtn_test (visual.c:18588) skipped its DXT1, DXT3 and DXT5 rows on the VOLUMETEXTURE query and now runs them: three skips fewer and the row's eight readbacks at visual.c:18631 per format on every visual leg, all expected to pass, including the scaled legs (the back buffer is read at reported coordinates) and the @mac2 legs (BC formats store every lane and carry no view swizzle). test_volume_blocks (device.c:10713) takes its supported branch for the five DXT rows, where DEFAULT, SYSTEMMEM and MANAGED creates of every block-aligned size now succeed as device.c:10760 expects; its pitch, offset and misaligned-box checks already ran against the SCRATCH volume. A successful create skips the null-pointer check of a failed one, so the device assertion total can fall while nothing fails. No failing site is expected to move on any leg, and the table has no DXT2 or DXT4 row in visual.c.

Volume queries accept DYNAMIC, FILTER, SRGBREAD, VERTEXTEXTURE and WRAPANDMIP for the five formats and reject SRGBWRITE, RENDERTARGET, DEPTHSTENCIL, POSTPIXELSHADER_BLENDING and AUTOGENMIPMAP. SRGBREAD covers DXT2 and DXT4 on volumes, as on 2D and cube textures, because BC2 and BC3 have sRGB views, and no upstream test asks it of either format. ATI1 and packed-YUV volumes stay unadvertised and SCRATCH-only. DXT2 and DXT4 keep their format identities and share the DXT3 and DXT5 block encodings: the stored blocks are sampled as they are, with no multiply or divide by alpha, and the application chooses its blend factors.