Hacker News new | ask | show | jobs
by Taniwha 43 days ago
I used to be a graphics card/chip architect for macs in the early/mid 90s - our chips were the fastest, but some programs were resistant because they did stupid stuff: pagemaker invalidated the font cache every time it went thru its main loop, quark with ATM did an n*2 thing every time it wrote text etc etc. We had special hardware to accelerate text drawing and it did nothing because the software pissed it away. We considered creating a plugin that fixed all these things, it would have been hard to maintain, in the end we travelled around to the people who made these apps and talked them through their problems

To be fair excel would erase places white that it wanted to write up to 9 times before it drew any black pixels, we made that very fast! we didn't tell them :-)

At the time 24-bit framebuffers were so slow that before we built graphics acceleration hardware people would switch back to 8-bit to get stuff done, making 24-bit/true colour your daily driver was a big step forward.

5 comments

Does that make you the first in a long tradition of GPU developers going to blockbuster app devs to say "hey, you should be doing this instead?"

PS – I am looking through the NuBus cards that I have... did you work for SuperMac or RasterOps?

I was probably not the first to have to do that, we knew what apps our customers used, making them better was the whole point of the operation

I did the architectural design for the SuperMac cards. I figured out what needed to be accelerated, dropping code into people's machines to see where the cycles were going. Others did the physical design for the first 2 cards, I did the design of the chip in the Thunder and later cards (designed the data paths and state machines and a full simulation, someone else actually laid the gates)

If your card has a SQD01 on it it's my work. It peaks at 1.5Gb/s on solid fills

This is a horrible and yet not unexpected insight into the internals of Excel
To be fair this was Excell 25 years ago, may no longer be true.

One of the other bugs (the Quark/ATM one) was also because of the programmers were worried about writing over stuff that hadn't been completely erased, the Quark guys wrote a string with 2 spaces at the end through a box that masked the end of the string, the ATM font renderer saw it couldn't fit the text so it split it in half and tried again so it drew N/2 N/4 N/8 ... strings. It spent all it's time in the 68k's multiply instructions figuring out how wide the strings (and substrings) were, our fancy 24-bit character rendering hardware was an afterthought

Nowadays Excel is a webapp that reflows the entire DOM 400 times per keystroke!
There's a good chance it was Excel's workaround for some other GPU's buggy behavior.
there were no GPUs at that time. What we were building in the late 80s/early 90s were the first generation of Mac graphics accelerators, really just glorified blit engines, a class of things that eventually grew into what we now call GPUs as people started to push CPUs into them
I see. I took "early/mid 90s" literally and thought you mean like 1991 through 1995, inclusive.
In all of the software you’ve written, are you aware of how many on-screen pixels you’ve overdrawn?
> To be fair excel would erase places white that it wanted to write up to 9 times before it drew any black pixels

I feel like I'm having a stroke trying to read this, what does it mean??

Well all they needed to do was erase the screen with white and draw on it, but their app's internal logic meant that they erased it more than once.

I was capturing QuickDraw library calls - the low level graphics primitives, to figure out where the graphics time in apps was going and found out sometimes excel did it 9 times

Of course users didn't see it more than once, but our hardware made all that wasted time run faster

It's more likely that one dev wrote the draw-cell code.

Another dev who's fixing a bug, realizes if they call a certain function either directly or indirectly, their particular bug gets fixed.

Oh, and as a side effect, the cell gets erased (again).

A few more fixes/new features added like this and the code is inadvertently erasing the same cell multiple times.

It takes a certain type of dev to step through in a debugger and Notice the app is doing way too much work and then to untangle the mess of code without causing regressions.

Maybe their CRTs had horrible burn-in and they had to erase everything 9 times before it was gone...
Several layers of white is what makes the black really pop. (Just kidding).
It means they were time travellers! Secretly, they came from an alternate future where everyone used e-ink displays, and wanted Excel to be ready!
I think it could call (their equivalent of) clearRect up to 9 times on an already cleared region before drawing there?
It’s necessary for erasing cat pixels.
before writing to some area, it would erase it (clearing with white) up to 9 times
I remember when 24 bit color was exotic and aspirational and you had to settle for 16.
I swear if Sun Microsystems was still around, their machines would still ship with 8-bit pseudocolor and you'd have to pay an extra $3k for 24-bit.
I got the extra vram in my LC to allow for 24-bit color but it was dog slow. The 16 bit data path didn't help. If I wanted it, I'd get things done in 8 bit or mono until it was ready, then switch to 24 bit for the final look.
Yeah, even in Linux we were doing these things with X Windows bit depths.

8 bit psuedo color, so the color palette switched with every focus-follows-mouse window boundary crossing. 16 bit direct color with banding but no more palette psychedlia.

This was equal parts to make it faster and to allow for higher framebuffer resolutions with limited VRAM.

16 bits? Luxury. I had 6 colors when I was a kid and was happy to have them.
My first computer was a TRS-80 Color Computer which had a tiny set of badly chosen colors!

Back then you did what you could with graphics and it wasn't a lot. After I got a PC I had indexed color for a long time and working with indexed color was pretty rough because anything physics-based like rendering or raytracing was going to be difficult. You could render a photo pretty well with 256 carefully chosen colors and dithering but if you wanted to, say, composite two photos and do general sorts of things you'd need to convert to "true color", do the math there, then re-quantize for display.

6?!? We had only 4 colors in low res mode and 2 in high res
Well my Hercules graphics card was only monochrome, but it was relatively high resolution.
I had a herc clone on the 286 machine I bought around 1987 and later added a Super VGA card. One cool thing about the IBM PC was that the monochrome and color graphic systems were sufficiently different in terms of memory map and ports so you could plug in two graphics cards and two monitors and that's what I had.
I think this was true for MDA (text only), but the Hercules had 64k of video memory at B0000, the latter half of which would overlap with the CGA card, and also VGA's text mode.
In my 68K Mac emulator running on modern (or even decade-old) hardware, performance in the traditional sense is less of a concern, but other issues arise. The big ones include CPU-burning loops that wait for a length of time or for an interrupt-decremented counter to reach zero, as well as invalid memory accesses (which I've made crash — no NULL deref for you).

> We considered creating a plugin that fixed all these things, it would have been hard to maintain, in the end we travelled around to the people who made these apps and talked them through their problems

Since talking to developers is no longer an option, I actually do write "Such-and-such Tune-up" extensions that patch applications dynamically to make them run better (or at all) in Advanced Mac Substitute, or even Mac OS itself.

Yeah those low core global system variables (including a readable/writeable 0) at fixed addresses were very much a thing, they were a bad design decision made for the original Macs with almost no memory, and made running more than one app (switcher/multifinder) a difficult transition back in the day. Someone wasn't planning ahead

I also worked on the original A/UX port for the Mac II, some hardware (like the IWM) required tiny buzzy loops, we ran into one bug where using the floppy caused ADB to freeze, but only on the release machines, not the prototypes all our engineers had, turned out there was hardware that made access to the VIA faster by pulling the clock in for 1 cycle, if you sat in a loop reading the timer in the via to measure a sector time for the IWM in too tight a loop it upped the output clock from the VIA to the ADB chip and over clocked it ....

What would have been the purpose of stupid code like that?

Was it a workaround for things that didn’t fully complete on one iteration, so the devs kept hammering away at it until it worked?

They were most likely just bugs. Quite possibly really stupid bugs.

Not every bug results in the program doing the wrong thing, they often just make the program do the right thing very slowly.

And nobody notices, since it still produces the right result.

Yes, they were bugs, I think programmers (and their marketing people) were more focused on new features than performance
Thankfully we’ve moved past that era.

Now the bugs that get ignored for new features cause bad results AND bad performance.

It's not necessarily stupid code in the game, but something the C library is doing that it probably shouldn't.

If the stream is buffered, then all operations, including fread, are supposed to go through the buffer.

All three of these should issue buffer-sized reads to the operating system:

1. A loop which calls getc(stream) 65536 times.

2. fread(buf, 1, 65536, stream)

3. fread(buf, 65536, 1, stream)

The more direct behavior of fread should only kick in if the stream is configured as unbuffered.

I would say that the way low-level reads are issued to the host operating system is a "visible effect" of the program, so I suspect this may actually be a matter of conformance. I.e. it's not okay to issue those reads however the stream library wants as long as the data is read.