My adventure writing a display adapter driver

If your graphics card doesn’t support full 32-bit bitmap cursor, maybe it should use the old BeOS style non-antialized hardware cursor as existing drivers do. I think in that case, only the cursor is accelerated, but not the drag bitmap?

We could implement something similar for the full color cursor hook as well. Ideally, there would be a way for the driver to inform the app_server of what it can handle (color formats, maximum bitmap size, etc) and then app_server can decide on its own if the drag bitmap can be accelerated or not, send the right data to the driver, and do the rest of the drawing by itself. But that will require changes in Haiku. Which is why I asked about your plans for upstreaming.

The exact rule in place for Haiku currently is this:

The Haiku project cannot accept contributions (e.g. code, documentation, translations, etc.) which are under ambiguous or incompatible licenses. (This includes most content produced with the use of LLMs. )

So, it depends if your workflow involves copying code from the LLM output (in which case, yes, maybe some of that code comes from Linux or other existing drivers for these cards, and may be under copyright), or if you just asked questions to the LLM and wrote the code yourself, which would set you clear of that rule. No copyright problem in that case.

Some developers here (including myself) have other objections to the use of LLMs, but the rules are what’s written, not what people think they should be. And I don’t see how we could possibly set a rule of “if you ever read the output of a LLM chatbot, you are not ever allowed to touch Haiku sourcecode again” (which no one suggested, but just as an example to show why it’s important to be very precise with the wording of these rules).

I also don’t want to use your work as a guinea pig for testing the current rules. So, if you don’t want to upstream it and enter a somewhat tense conflict zone, that’s fine too :slight_smile:

3 Likes

thanks for asking, but maybe I’m missing something here
my accelerant has implemented these hooks which concerns Hardware Cursor:

case B_MOVE_CURSOR:                 return (void*)sm750_move_cursor;			//0x200
case B_SET_CURSOR_SHAPE:            return (void*)sm750_set_cursor_shape;
case B_SHOW_CURSOR:                 return (void*)sm750_show_cursor;
case B_SET_CURSOR_BITMAP:           return (void*)sm750_set_cursor_bitmap;

when you say:

do you intend the B_SET_CURSOR_SHAPE?
If so I’ll inform you that B_SET_CURSOR_SHAPE is never called for default haiku cursor (I placed a debug printf inside) as @X512 said

(I confirm that WonderBrush uses it, just noticed it in syslog)

so by default existing drivers do not use an hardware accelerated cursor drawing if they don’t implement B_SET_CURSOR_BITMAP.
My implementation of B_SET_CURSOR_BITMAP flattens the bitmap to trasparent/black/white/gray color depending on ARGB values

About this I think that:

  1. For now I don’t want to upstream it due the current rules.
  2. One day, after I rewrite by hand the code of dubious origins that the LLM suggested me, I’ll be happy to ask to upstream it, for now it is
  3. a study case for me, as this is a project to learn new things.
  4. But for sure I need further help by humans experts to solve some quirks (I’ve got one of those in my mind, concerning irqs and msi).
2 Likes

Overall, good work, i think, i hope you can polish the driver so it can be commited

OK, so things are more broken than I remember.

Gerald Zajac had the hardware cursors (the old 2-bit ones) working on some drivers, and maybe Rudolf Cornelissen also did. But at that time B_SET_CURSOR_BITMAP didn’t exist at all if I remember correctly. So maybe introducing that led to removal of code to support the “old way”?

I guess I will do some historical research in app_server sources to find out.

The good news is I have a train trip of a few hours planned today, a good time to dig into this surely :slight_smile:

7 Likes

IIRC, the introduction of B_SET_CURSOR_BITMAP leads to drop using the old 2bits harware cursor. Either the new way is supported by hardware accelerant, or the app_server does fallback on software rendering of the cursor on the framebuffer.

FWIW, I had noticed something similar, while trying to figure out an issue on the vmware_addons.

To quote myself from what I saw at the time:

So, basically any SetCursor() call that is not B_HAND_CURSOR or B_I_BEAM_CURSOR ends up calling the SET_CURSOR_SHAPE() hook. Edit: ShowImage’s using B_CURSOR_ID_GRABBING do not trigger it either. So I guess that none of the cursors on the BCursorID enum will trigger it.

I’m not sure a display driver is the easiest thing to write, so good job and thanks for sharing!

Yes, since hrev48185 (which I merged back in 2014), that’s how it works. So if you prefer app_server to send you a 2-bit bitmap (black and white + transparency or color inversion) you have to not implement B_SET_CURSOR_BITMAP and set the hook to NULL. Otherwise, the old hook is only called directly by apps (as in Wonderbrush’s case). We should change this so that if the hook fails, the monochrome version is tried as well. I’ll send a patch.

However, they both work from the same data anyways. If I read the code correctly, the software cursor in src/servers/app/drawing/HWInterface.cpp calls _AdoptDragBitmap, and the draw function uses fCursorAndDrawBitmap, drawing both the cursor and the drag bitmap. But the accelerated version (drawing/interface/local/AccelerantHWInterface.cpp, SetCursor) uses only the cursor data from the ServerCursor object. So this should be resolved: either it should handle the drag bitmap as part of the cursor, or it should let the base HWInterface.cpp code draw it in software (the latter makes more sense if the hardware doesn’t offer alpha blending and true color).

2 Likes

ok this makes sense! I should remove my workaround by flattening the cursor bitmap to 2bit colors, and set B_SET_CURSOR_BITMAP to NULL, BUT this normally fallbacks to software drawing the cursor, only specific apps uses accelerated 2bit cursor. Normal apps or app_server don’t use at all 2bit cursor acceleration to draw the default cursor. This means slow rendering of the cursor and artifacts in intense drawing apps like Iceweasel and co. This explains my workaround…
But now I realize that I can use the video alpha layer to draw the cursor in 32bit alpha mode… Gotta something new to experiment now :smiley:

anyway… even if I rewrite the B_SET_CURSOR_BITMAP, the missing part you described is still missing!
the accelerated part in src/servers/app/drawing/interface/local should have a:
void AccelerantHWInterface::SetDragBitmap(const ServerBitmap* bitmap, const BPoint& offsetFromCursor)
like in HWInterface.cpp?

That’s what OscarL says as well. I think the ServerCursor object created by app_server only has a full color bitmap and not the one needed for the “shape” mode. In that case, we should either dig out the old 1-bit bitmaps, or create new ones for this.

I think that’s the starting point, yes. Then the question is what to do exactly in that mehod.

I didn’t think much about the best way to do this, and it depends on what the driver can do. One way to make it work would be for the accelerated hardware interface to use fCursorAndDragBitmap (prepared by HWInterface) and send that to the driver’s B_SET_CURSOR_BITMAP hook instead of just the cursor, in both SetCursor and SetDragBitmap.

Another way is to introduce a new hook for the draw bitmap, and handle it separately from the cursor all the way down to the driver (or the accelerant), and let the driver decide how to draw the drag bitmap and the cursor. The nice thing about that solution is it allows driver to implement only the cursor hook (even only the monochrome “cursor shape” one) and in that case app_server should handle the drag bitmap. But it requires the driver to do the merging of the cursor and drag bitmap into a single bitmap if it wants to draw both, but needs a single bitmap to overlay on the display.

The easiest change to make it correctly right now is to turn off hardware cursor if too big drag bitmap do not fit to max hardware cursor size limits. Check error code of B_SET_CURSOR_BITMAP call and bring back software cursor on error.

Maybe something like AccelerantHWInterface::SetCursor

// something like these controls
if (cursor == NULL || LockExclusiveAccess() == false)
		return;

	bool cursorSet = false;

	if (fDragBitmap == NULL && fAccSetCursorBitmap != NULL) {
		// Bitmap cursor
		// TODO are x and y switched for this, too?
		uint16 xHotSpot = (uint16)cursor->GetHotSpot().x;
		uint16 yHotSpot = (uint16)cursor->GetHotSpot().y;

		uint16 width = (uint16)cursor->Bounds().Width();
		uint16 height = (uint16)cursor->Bounds().Height();

		// Time to talk to the accelerant!
		cursorSet = fAccSetCursorBitmap(width, height, xHotSpot,
			yHotSpot, cursor->ColorSpace(), (uint16)cursor->BytesPerRow(),
			cursor->Bits()) == B_OK;
	} else if (cursor->CursorData() != NULL && fAccSetCursorShape != NULL) {
		// BeOS BCursor, 16x16 monochrome
		uint8 size = cursor->CursorData()[0];
		// CursorData()[1] is color depth (always monochrome)
		// x and y are switched
		uint8 xHotSpot = cursor->CursorData()[3];
		uint8 yHotSpot = cursor->CursorData()[2];

		// Create pointers to the cursor and/xor bit arrays
		// for the BeOS BCursor there are two 32 byte, 16x16 bit arrays
		// in the first:  1 is black,  0 is white
		// in the second: 1 is opaque, 0 is transparent
		// 1st	2nd
		//  0	 0	 transparent
		//  0	 1	 white
		//  1	 0	 transparent
		//  1	 1	 black
		// for the HW cursor the first is ANDed and the second is XORed
		// AND	XOR
		//  0	 0	 white
		//  0	 1	 black
		//  1	 0	 transparent
		//  1	 1	 reverse
		// so, the first 32 bytes are the XOR mask
		const uint8* xorMask = cursor->CursorData() + 4;
		// the second 32 bytes *NOTed* are the AND mask
		// TODO maybe this should be NOTed when copied to the ServerCursor
		uint8 andMask[32];
		const uint8* transMask = cursor->CursorData() + 36;
		for (int32 i = 0; i < 32; i++)
			andMask[i] = ~transMask[i];

		// Time to talk to the accelerant!
		cursorSet = fAccSetCursorShape(size, size, xHotSpot,
			yHotSpot, andMask, xorMask) == B_OK;
	}

	if (cursorSet && !fHardwareCursorEnabled) {
		// we switched from SW to HW, so we need to erase the SW cursor
		if (fCursorVisible && fFloatingOverlaysLock.Lock()) {
			IntRect r = _CursorFrame();
			fCursorVisible = false;
				// so the Invalidate doesn't draw it again
			_RestoreCursorArea();
			Invalidate(r);
			fCursorVisible = true;
			fFloatingOverlaysLock.Unlock();
		}
		// and we need to update our position
		if (fAccMoveCursor != NULL)
			fAccMoveCursor((uint16)fCursorLocation.x,
				(uint16)fCursorLocation.y);
	}

	if (fAccShowCursor != NULL)
		fAccShowCursor(cursorSet);

	UnlockExclusiveAccess();

	fHardwareCursorEnabled = cursorSet;
// and then calling
	HWInterface::SetDragBitmap(bitmap, offsetFromCursor);

The cursor size the graphics card supports might not be that large; IIRC the old Intel hardware I initially wrote the intel_extreme driver for, only supported 64 x 64 pixel.

Anyway, it was a deliberate choice to prefer software cursors, since they look better. And with double buffering, it shouldn’t make a huge difference - I’m not sure what’s wrong with Iceweasel that it causes so much flickering.

2 Likes

See Making sure you're not a bot!.

The problem is that to show software cursor, you need to blit it over current screen contents with alpha blending, but that screen contents may be in a middle of drawing right now, so mid-drawing contents will be displayed under cursor and flickering appears.

You may try to use front buffer in VRAM that have contents that already finished drawing, but note that VRAM → CPU RAM memcpu is VERY VERY SLOW for nearly all hardware. Even for 32x32 block copy cause significant slowdown. This is also why AGG can’t be used to draw directly on frontbuffer in VRAM: drawing with antialiasing require reading source image that is terribly slow for VRAM. This is also why AGG can’t work together with hardware 2D acceleration.

It worked in BeOS because BeOS had no antialiasing, so no need of VRAM reading by CPU. Antialiased font rendering in BeOS used low color as background, not actual contents under glyphs, exactly for the same reason to avoid slow VRAM read.

Good to see this development thread returned back to discussion. :cowboy_hat_face:

2 Likes

Thanks for sharing your adventure. Hopefully AI once would be not an evil shadow, but as it would be : a tool.

Actually - till there’s no Terminators with shotgun, which ones coming toward me on a corridor while roses are falling - I let the big evil AIs ruin other’s mood. :smiley:

2 Likes

Patch to generate the monochrome cursor from the color one on app_server side:

https://review.haiku-os.org/c/haiku/+/11033

Patch to handle B_SET_CURSOR_BITMAP failure and try B_SET_CURSOR_SHAPE in that case:

https://review.haiku-os.org/c/haiku/+/11030

This should solve " @BiPolar 's issue" that the default cursors are not sent to B_SET_CURSOR_SHAPE.

It doesn’t solve the problem with the drag bitmap.

I also likely got the conversion a little bit wrong, as I don’t have access to my machine with a Radeon graphics card at the moment. I’ll try to turn it on and test there when I’m back one.

6 Likes

Automatic switching to monochrome cursor may be undesired for some users. It should be configurable: do user prefer hardware cursor or nice looking cursor? Personally I would prefer second one.

Ideally this could be done in home/config/settings/kernel in a driver specific settings file. Like:

hardcursor true # if true use on-chip hardware cursor

1 Like

I guess we could add an option in the mouse preflet (or in appearance?)

1 Like