Post

Replies

Boosts

Views

Activity

Reply to Separate CGBitmapContexts drawn concurrently produce wrong pixels when another thread fills translucent shapes (FB25211753)
Thanks for the detailed follow-up. I tested both workarounds on a Mac16,8 (Apple M4 Pro) running macOS 27.0.1 (26A434). Every context is checked against its own serial reference, including the aggressors. Each line is 50 rounds of 64 concurrent renders, and the two control lines in the same run show the problem is still being caught. Renders wrong (victims, aggressors): No workaround (control): 1593/1600, 141/1600 Lock on the victim side only (control): 1600/1600, 221/1600 One lock shared by all contexts, held only around translucent fills and strokes: 0/1600, 0/1600 Antialiasing off on the victim only: 0/1600, 0/1600 Antialiasing off on the aggressor only: 0/1600, 0/1600 Victim in 16-bit float, converted to 8-bit by drawing makeImage() into an RGBA8 context: 0/1600, 0/1600 Victim in 16-bit float, converted with vImageBuffer_InitWithCGImage: 0/1600, 0/1600 Aggressor in 16-bit float, converted with vImage: 0/1600, 0/1600 With 2, 3 and 4 victims drawing different content and no aggressors, 612, 733 and 786 of 3,200 renders were wrong without a workaround. With the shared lock or the float path on every context, none were. These results match yours: a lock on one side doesn't help, changing eit contexts, and the aggressors' own output is also corrupted. The floatpath isn't pixel-identical to drawing directly in 8-bit. About 2.5% of bytes differ, by at most 1, which is fine for my use. I didn't test text under the lock, and I only tested on this Mac. As for my app: it currently renders all its artwork on a single thread, rmal use. The only place it was exposed is the parallel test runner, andI've serialised the rendering tests. If I move rendering onto several threads later, I'll use the float path or turn off antialiasing on all but one context, so the drawing itself doesn't have to be serialised.
Topic: Graphics & Games SubTopic: General Tags:
1w
Reply to Separate CGBitmapContexts drawn concurrently produce wrong pixels when another thread fills translucent shapes (FB25211753)
Thanks for such a thorough follow-up, and for the welcome! It helps a lot to know this reproduces on M5 Max and on iOS 26 and 27, and that the wrong pixels on the edges contain the other thread's colours. That fits what I saw: the corruption got worse from 26.5.1 to 27.0.1, and per-thread colour spaces made no difference. I'll try both workarounds in my real app (not just the repro) and post the results here. A few questions in the meantime: Scope of the workaround. Does every context that draws at the same time need to switch to 16-bit float or turn off antialiasing? Or is it enough to change only the contexts that draw translucent shapes? In my app the "aggressor" work and the "victim" work come from different subsystems, so it matters which side has to change. Narrower serialisation. You found that fills which keep a single translucent colour are fine. Would it be safe to serialise only the antialiased translucent fills where the colour or alpha changes, behind a shared lock, and leave everything else concurrent? Or could the problem reach other drawing operations too? Converting back to 8-bit. If I render into a 16-bit float context and then convert to 8-bit RGBA for output, is that conversion step safe to run concurrently too? Fix timing. I understand you can't share release plans, but should I treat this as a long-term workaround or a short-term one? Thanks again for reproducing it so quickly and adding your results to FB25211753.
Topic: Graphics & Games SubTopic: General Tags:
2w
Reply to Separate CGBitmapContexts drawn concurrently produce wrong pixels when another thread fills translucent shapes (FB25211753)
Thanks for the detailed follow-up. I tested both workarounds on a Mac16,8 (Apple M4 Pro) running macOS 27.0.1 (26A434). Every context is checked against its own serial reference, including the aggressors. Each line is 50 rounds of 64 concurrent renders, and the two control lines in the same run show the problem is still being caught. Renders wrong (victims, aggressors): No workaround (control): 1593/1600, 141/1600 Lock on the victim side only (control): 1600/1600, 221/1600 One lock shared by all contexts, held only around translucent fills and strokes: 0/1600, 0/1600 Antialiasing off on the victim only: 0/1600, 0/1600 Antialiasing off on the aggressor only: 0/1600, 0/1600 Victim in 16-bit float, converted to 8-bit by drawing makeImage() into an RGBA8 context: 0/1600, 0/1600 Victim in 16-bit float, converted with vImageBuffer_InitWithCGImage: 0/1600, 0/1600 Aggressor in 16-bit float, converted with vImage: 0/1600, 0/1600 With 2, 3 and 4 victims drawing different content and no aggressors, 612, 733 and 786 of 3,200 renders were wrong without a workaround. With the shared lock or the float path on every context, none were. These results match yours: a lock on one side doesn't help, changing eit contexts, and the aggressors' own output is also corrupted. The floatpath isn't pixel-identical to drawing directly in 8-bit. About 2.5% of bytes differ, by at most 1, which is fine for my use. I didn't test text under the lock, and I only tested on this Mac. As for my app: it currently renders all its artwork on a single thread, rmal use. The only place it was exposed is the parallel test runner, andI've serialised the rendering tests. If I move rendering onto several threads later, I'll use the float path or turn off antialiasing on all but one context, so the drawing itself doesn't have to be serialised.
Topic: Graphics & Games SubTopic: General Tags:
Replies
Boosts
Views
Activity
1w
Reply to Separate CGBitmapContexts drawn concurrently produce wrong pixels when another thread fills translucent shapes (FB25211753)
Thanks for such a thorough follow-up, and for the welcome! It helps a lot to know this reproduces on M5 Max and on iOS 26 and 27, and that the wrong pixels on the edges contain the other thread's colours. That fits what I saw: the corruption got worse from 26.5.1 to 27.0.1, and per-thread colour spaces made no difference. I'll try both workarounds in my real app (not just the repro) and post the results here. A few questions in the meantime: Scope of the workaround. Does every context that draws at the same time need to switch to 16-bit float or turn off antialiasing? Or is it enough to change only the contexts that draw translucent shapes? In my app the "aggressor" work and the "victim" work come from different subsystems, so it matters which side has to change. Narrower serialisation. You found that fills which keep a single translucent colour are fine. Would it be safe to serialise only the antialiased translucent fills where the colour or alpha changes, behind a shared lock, and leave everything else concurrent? Or could the problem reach other drawing operations too? Converting back to 8-bit. If I render into a 16-bit float context and then convert to 8-bit RGBA for output, is that conversion step safe to run concurrently too? Fix timing. I understand you can't share release plans, but should I treat this as a long-term workaround or a short-term one? Thanks again for reproducing it so quickly and adding your results to FB25211753.
Topic: Graphics & Games SubTopic: General Tags:
Replies
Boosts
Views
Activity
2w