Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ All are Mesa patches in `patches/mesa`.
| 0082 | macOS has no `pthread_barrier`, so Mesa builds one from a mutex and a condition variable. That version told every thread it was the "serial" one, where the real one tells exactly one. `util_queue_finish` destroys its barrier in the serial thread, so on macOS the last thread to arrive destroyed the mutex and condition variable while the others were still waking up inside them, and one of them could block for good: a test process sat idle at exit in Zink's screen teardown for an hour (seen once, with the machine under load; an OpenGL program would hang when it quits). The barrier now returns true to the last thread only, and only after the others have left it. | A stress test of the barrier alone: the old version hangs within a minute, the new one passes 200,000 reuse rounds and 20,000 barriers destroyed by their serial thread |
| 0083 | Zink submits a finished batch from a separate thread, and a read of a buffer waits for the batch that last wrote it. Two races between the two, both seen in `shader_atomic_counter_ops`, which reads a buffer straight after `glFlush`. The reader checked the batch's "not submitted yet" flag and then waited on a condition variable without looking again, so when the submit thread finished in between, the wake-up was already gone and the read waited for good (25 of 300 runs of those four tests; a program doing this would freeze). And the check for "does this buffer have a pending use" read the batch's id and then the flag while the submit thread wrote both: the old id with the new flag reads as "no use", and the read returned without waiting (6 of 300 runs: the first buffer still held its input, the second, read microseconds later, was complete, and reading the first again gave the right value). The flag is now cleared under the batch's mutex and the reader waits on it as a predicate; the flag is read before the id, with an ordered load. The wrong-value race is established from the compiled code, the captured values and the comparison below, not reproduced by injection. | 0 of 200 runs fail, beside 31 of 300 for the build before it, run at the same time. A delay injected between the reader's check and its wait hung 2 of 2 runs before and 0 of 3 after. `gl46-r12` has no retried test |
| 0085 | The driver reported no depth bounds test below Apple GPU family 10, where Metal has none; DOOM (2016) needs it. It is emulated now: the render pass is ended before a draw with the test on so that the depth is in memory, and a variant of the fragment shader reads each sample's depth and drops the samples outside the bounds (a draw that writes depth reads a copy). The variant is built on first use, as the statistics one was, which 0085 generalises: in every shader it would turn off hidden surface removal for every Zink pipeline. Details and what is left out in findings #57. | 3,821 depth bounds tests pass where 1,844 did, 0 fail; `tests/mac/vk-depth-bounds-msaa.sh` for multisampled images, which the CTS does not test |
| 0086 | A buffer descriptor whose range runs past the end of its buffer, which is invalid but which DOOM (2016) writes, aborted on an assertion in Mesa's shared code; without the assertion its reads would have gone past the buffer into other memory. The range of a buffer descriptor and of a transform feedback buffer is clamped to the buffer now, so such reads are out of bounds like any other. Findings #58. | 52,804 robustness, uniform buffer, transform feedback and descriptor tests pass (one in three of those lists), 0 fail; `tests/mac/vk-ubo-range-past-end.sh` for the invalid range, which the CTS does not test |

0041, 0052, 0053, 0055, 0062, 0071, 0075, 0077 and 0083 are in Zink, 0058 and 0074 in the geometry shader emulation shared with other Mesa drivers, 0069 in NIR, part of 0081 in NIR's texture lowering and 0082 in Mesa's shared utilities. The rest are in the Vulkan driver, so they are not specific
to OpenGL: they can equally be hit by a Direct3D 12 game through vkd3d-proton or by a Vulkan game.
Expand Down
25 changes: 25 additions & 0 deletions docs/findings.md
Original file line number Diff line number Diff line change
Expand Up @@ -1868,3 +1868,28 @@ loaded from a pipeline cache. It found
that the MSL backend ignored the sample index of a multisampled image load (every sample read
sample 0; the driver offers no multisampled storage images, so nothing reached it before), and
the test reads through a texture fetch instead.

## A buffer descriptor whose range ran past its buffer aborted DOOM (fixed, #58, issue #128)

With depth bounds reported (#57), DOOM (2016) gets past device creation and then stops on an
assertion in Mesa, `range + offset <= buffer->size` (`vk_buffer.h`). It writes uniform buffer
descriptors with a range of 64 KB on buffers of 49,152 and 65,520 bytes (issue #128, found by
tracing its descriptor writes). That is invalid (VUID-VkDescriptorBufferInfo-range-00342), so
Vulkan leaves the result undefined; the game runs on Windows drivers, and on Linux, where Mesa is
built with its assertions off and the drivers use the range as given. Hadron's Mesa is built with
them on (`--buildtype=debugoptimized`, #47), so the game aborts.

Turning the assertions off would only trade the abort for reads of other memory: with the range
as given, KosmicKrisp's bounds checks let the shader read past the buffer into whatever is
allocated after it. Mesa patch 0086 clamps the range of a buffer descriptor (and of a transform
feedback buffer) to the part of the buffer from its offset, so that a read past the buffer's end
is out of bounds and is handled as any other one: zero with robust buffer access, nothing outside
the buffer without it. A valid range is unchanged.

`tests/mac/vk-ubo-range-past-end.sh` writes descriptors as DOOM does, plain and dynamic, with a
buffer filled with a marker right after the small one, and reads inside and past the end with
robustBufferAccess2 on. The installed driver aborts on the assertion; with the clamp every read
inside sees the buffer's data and every read past the end sees zero; with the range kept and no
assertion, the reads past the end see the next buffer's marker. A third of the Vulkan CTS lists for
robustness, uniform buffers, transform feedback and descriptor binding passes with the clamp:
52,804 pass, none fail.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
From f1769a547a249534953378b83d0ee094895b9e83 Mon Sep 17 00:00:00 2001
From: Broyojo <dha@xoba.com>
Date: Sat, 24 Oct 2026 19:30:16 -0400
Subject: [PATCH] kk: Clamp buffer descriptor range to buffer bounds

Generated-by: LLM
---
src/kosmickrisp/vulkan/kk_buffer.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/kosmickrisp/vulkan/kk_buffer.h b/src/kosmickrisp/vulkan/kk_buffer.h
index 61db828e239..c3fcb129311 100644
--- a/src/kosmickrisp/vulkan/kk_buffer.h
+++ b/src/kosmickrisp/vulkan/kk_buffer.h
@@ -51,9 +51,11 @@ kk_buffer_addr_range(const struct kk_buffer *buffer, uint64_t offset,
if (buffer == NULL)
return (struct kk_addr_range){.range = 0};

+ uint64_t avail = buffer->vk.size - MIN2(offset, buffer->vk.size);
+
return (struct kk_addr_range){
.addr = vk_buffer_address(&buffer->vk, offset),
- .range = vk_buffer_range(&buffer->vk, offset, range),
+ .range = range == VK_WHOLE_SIZE ? avail : MIN2(range, avail),
};
}

--
2.54.0 (Apple Git-157)

Loading
Loading