Skip to content

mgras: zoomed glDrawPixels and 1-2 texel RGB textures draw right - #221

Closed
atomchild411 wants to merge 2 commits into
techomancer:mainfrom
atomchild411:mgras-pixzoom
Closed

atomchild411 wants to merge 2 commits into
techomancer:mainfrom
atomchild411:mgras-pixzoom

Conversation

@atomchild411

Copy link
Copy Markdown
Contributor

Two IMPACT bugs found with an OpenGL 1.2 demo running through SGI's own libGL on an emulated Max IMPACT (IP28, IRIX 6.5.22). Its 320x240 "retro" frames are drawn back with glPixelZoom(3.2, 3.2), and they came out as a smeared ~240-pixel strip down the left side. Its Amiga-style ball chain, textured with a 2x2 GL_RGB red/white checker, came out yellow, red and black. Both bugs are in the board model and shared by every IMPACT board.

1. glDrawPixels under a pixel zoom (43fb5a6)

Traced (mgras trace hq,rss): the driver sends a zoomed DrawPixels by DMA in column chunks (156 RGBA or 144 RGB pixels at whole zooms, 96 at 3.2). Before each chunk, pixel state 0xDA8 gives the chunk's rectangle on the screen, already zoomed (relative to the raster position), and the DMA brings its rows unzoomed: the board stretches them over the rectangle. 0x192 holds 1 / the y zoom only (0x191 stays 0); the x zoom has no state of its own.

The model applied 0x192's zoom to both axes. It also placed a chunk only when 0xDA8's transfer size matched the rows that came, which never happens under a y zoom, so every chunk landed at the origin on top of the last. In addition, at x zoom 3.2 the transfer size's width is the pixel count times the zoom rounded up (0x180 for 96 pixels), and the "narrow host pixel" guess read that as 1-byte pixels.

Now a chunk whose 0xDA8 rectangle is its rows times the y zoom high (zoomed_chunk) is placed at that rectangle, with its rows stretched over it, nearest pixel. The host's pixel size there comes from the row words, the transfer size and the rectangle (zoomed_host_bpp). FIFO images keep 0x192's zoom both ways, so snoop's magnifier and gl_draw_pixels_zoomed are unchanged.

2. Texture rows by DMA padded to words (5b50b38)

The host DMA pads each line to whole words. A texture row of 1 or 2 RGB texels (3 or 6 bytes) reached the loader as 4 or 8 bytes, and the loader took bytes-per-texel as the row's bytes over its width: 4, i.e. RGBA. So the 2x2 checker drew its white texels as f5 f5 00, the blue being a pad byte. Wider RGB rows fill their words (4 texels = 12 bytes) and were right. The HQ now passes the DMA's LINE_BYTES (6 here) to the GL emulation, and the loader takes the texel size from it when the padded row is longer (dma_row_bpt). Rows sent by FIFO are as before.

Tested

Two small GLX programs on IRIX through SGI's libGL only (no host GL), on Max IMPACT. Each draws known patterns, reads them back, and checks every source pixel or texel:

before after
glDrawPixels 320x240, RGBA and RGB at zoom 1, 2, 3.2, plus 3.2 x 1 and 1 x 3.2 2 / 8 (zoom 1 only) 8 / 8
N x N red/white checker textures, RGB 2, 4, 8, 16, 64 and RGBA 2, 4, 64 7 / 8 (2x2 RGB) 8 / 8

The demo now draws its full-screen retro eras and red-and-white ball chain correctly (22 fps). mgras tests: 155 pass, including two new ones built from the traced values (gl_zoomed_chunk_host_pixel_size, gl_dma_texture_row_texel_size). The repro programs are small; happy to add them under test/gltest if you'd like.

🤖 Generated with Claude Code

atomchild411 and others added 2 commits October 12, 2026 23:25
IRIX's driver sends a zoomed glDrawPixels by DMA in chunks of columns
(156 RGBA or 144 RGB pixels at whole zooms, 96 at 3.2): before each, pixel
state 0xDA8 gives the chunk's rectangle on the screen, already zoomed
(relative to the raster position), and the DMA brings its rows unzoomed.
The board stretches them over the rectangle. 0x192 holds 1 / the y zoom
only; the x zoom has no state of its own.

The model took 0x192's zoom for both axes and placed a chunk only when
0xDA8's transfer size matched the rows that came, which a y zoom never
does: every chunk was drawn at the origin over the last, stretched by the
y zoom across. A 320 x 240 image at zoom 3.2 came out ~240 pixels wide and
smeared (a demo's 320 x 240 eras), and at zoom 1 x 3.2 499 wide. And at
x zoom 3.2 the transfer size's width is the pixel count times the zoom
rounded up (0x180 for 96 pixels), which the narrow-host-pixel guess took
for 1-byte pixels.

Now a chunk whose 0xDA8 rectangle is its rows times the y zoom high
(zoomed_chunk) is placed at that rectangle and its rows stretched over
it, nearest pixel; the host's pixel size there comes from the row words,
the transfer size and the rectangle (zoomed_host_bpp). FIFO images keep
0x192's zoom both ways (snoop's magnifier).

A repro drawing a 320 x 240 gradient through SGI's libGL (Max IMPACT,
IRIX 6.5.22) passed 2 of 8 cases (only zoom 1); now 8 of 8: RGBA and RGB
at zoom 1, 2 and 3.2, and 3.2 x 1 and 1 x 3.2. mgras tests 153 + 1 new.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The host DMA pads each line to whole words. A texture row of 1 or 2 RGB
texels (3 or 6 bytes) comes to the texture loader as 4 or 8, and the
loader took bytes a texel as the row's bytes over its width: 4, RGBA. A
2 x 2 GL_RGB red and white checker (a demo's Amiga ball, unpack alignment
1) drew its white texels yellow, f5 f5 00, the blue a pad byte. Wider RGB
rows fill their words (4 texels, 12 bytes) and were right.

The HQ now tells the GL emulation the DMA's LINE_BYTES, the row as it was
in memory (traced: 6 for 2 RGB texels), and the loader takes the texel
size from it when the padded row is longer (dma_row_bpt). Rows by FIFO
are as before.

A repro loading N x N red and white checkers through SGI's libGL (Max
IMPACT, IRIX 6.5.22) failed 2 x 2 RGB only (texel (1,0) f5f500 for
f5f5f5); now all pass: RGB 2, 4, 8, 16, 64 and RGBA 2, 4, 64. mgras tests
154 + 1 new.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@atomchild411

Copy link
Copy Markdown
Contributor Author

cancel spotfix for overlap against techomancer's mega fix 🙌

techomancer added a commit that referenced this pull request Oct 13, 2026
Cases for what PR #221 fixed in the old draw path: 200-pixel-wide RGBA
and RGB images (DMA column chunks of 156 / 144) at zooms 1, 2, 3.2 and
fractional x zooms, and 1x1 / 2x2 / 4x4 GL_RGB textures with unpack
alignment 1 (word-padded DMA rows). All pass on the GE pipeline path:
365 pass, 0 fail, 12 XFAIL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant