CNA-BUG-113: An enabled zero-area ScissorRectangle draws unclipped on SDL_GPU, while every other rasterizing renderer clips everything

CNA snapshot 3b43bd86  ·  Known Issues › Current bugs  ·  source links pinned to 3b43bd86

✓

Evidence basis: source-verified at the pinned commit; recorded by CNA's own run (not repeated here); tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

SDL_GPU's ApplyScissorForRef treats a zero width or height like a disabled scissor and uses the full target; XNA's contract (rasterise only inside the rectangle) means nothing is drawn, as the other renderers in CNA's shared scissor fixture do.

Identifier
CNA-BUG-113
Category
Bug
Subsystem
Graphics & renderers
Status
Open
Verified against
CNA 3b43bd86 (3b43bd8687ed46523d873f9bec4b32bd8481dcb5)
Severity
Medium (a triage suggestion, not a project priority)
Evidence basis
Recorded by CNA: CNA's own recorded run, not repeated here
Tests touching this area
Yes: see Current tests
Affected contract
GraphicsDevice.ScissorRectangle with zero width or height and RasterizerState.ScissorTestEnable = true

Expected behaviour

With the scissor test enabled, only pixels inside the rectangle are written; an empty rectangle writes nothing (Direct3D 9, which XNA runs on, behaves this way, and so do WEBGPU, EasyGL, SOFTWARE and DIRECTX9 in CNA).

Actual behaviour at TARGET

SdlGpuRenderer.cpp's ApplyScissorForRef takes its full-target branch unless scissorEnabled && scissorW > 0 && scissorH > 0, so an enabled rectangle of zero width or height draws over the whole target. WebGPURenderer.cpp's ApplyDrawScissor clips an enabled empty rectangle to nothing, and the other rasterizing renderers do the same. So a UI panel whose clip rectangle collapses to zero height draws its whole content over the screen on SDL_GPU (the default renderer on Windows, macOS and iOS) and disappears elsewhere. CNA already records this: the shared fixture deferred_scissor_capture_test.cpp declares emptyScissorDrawsNothing false for SDL_GPU (‘measured here’), and its check E1 (3D draws into a render target) asserts the unclipped output there, so the wrong result is pinned as expected output.

Source locations

Evidence

Checked by reading the named sources at 3b43bd86; nothing was built or executed for this entry.

Independent re-verification: Checked by reading the named sources at 3b43bd86; nothing was built or executed for this entry. CNA's own records agree: deferred_scissor_capture_test.cpp, registered as SdlGpu_Deferred_Scissor, sets zero-width, zero-height and 0x0 scissors in check E1 (3D draws into a render target) and declares through emptyScissorDrawsNothing=false that SDL_GPU does not clip them, while the other families in that fixture declare that a degenerate rectangle rasterizes nothing. The SpriteBatch path with an empty rectangle is not exercised.

Re-verified at c6dec91f: the fixture's contract table now covers WEBGPU, EASYGL, SDL_GPU, SOFTWARE, HEADLESS and DIRECTX9; only SDL_GPU declares emptyScissorDrawsNothing false (EasyGL's own early return was fixed by SOFTWARE-310), and ApplyScissorForRef is unchanged. Not executed.

Focused reproduction

// Illustrative; not compiled or run for this entry.
device.setScissorRectangleProperty(Rectangle(10, 10, 0, 50));   // collapsed panel
batch.Begin(SpriteSortMode::Deferred, nullptr, nullptr, nullptr, scissorOn);
batch.Draw(panelContent, Rectangle(0, 0, 800, 600), Color::White);
batch.End();
// Expected: nothing drawn. SDL_GPU: full-screen panel content.

Current tests

SdlGpu_Deferred_Scissor (deferred_scissor_capture_test.cpp, check E1) draws 3D geometry into a render target with zero-width, zero-height and 0×0 scissors and pins the unclipped output for SDL_GPU (emptyScissorDrawsNothing=false); the other families in that fixture declare that a degenerate rectangle rasterizes nothing. The SpriteBatch path with an empty rectangle is not exercised.

Regression test

Flip the emptyScissorDrawsNothing declaration for SDL_GPU once the renderer clips a degenerate rectangle: the fixture then fails until it does.

Blast radius

Clipped UI (scroll views, collapsing panels, animated reveals) that can reach zero size on SDL_GPU.

Workaround

Skip drawing when the clip rectangle is empty.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index