Slow GOP framebuffer

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
CasualKyle
Posts: 12
Joined: Fri Oct 24, 2025 2:38 pm

Slow GOP framebuffer

Post by CasualKyle »

Hello, I'm currently investigating the feasibility of writing a very simple graphics driver that respects vsync.

I wrote the following test just to see if I can cause screen tearing to happen on my monitor. It simply draws a red square which moves across the screen. Each iteration of the loop moves the square 1 pixel to the right, then resets to column 0 when it reaches the end.

Code: Select all

vsync_test :: proc "c" (st: ^efi.SYSTEM_TABLE) {
	graphics_output_protocol_guid := efi.GRAPHICS_OUTPUT_PROTOCOL_GUID
	graphics_output_protocol: ^efi.GRAPHICS_OUTPUT_PROTOCOL
	st.BootServices.LocateProtocol(&graphics_output_protocol_guid, nil, cast(^rawptr) &graphics_output_protocol)

	base_address := cast(^u32) cast(uintptr) graphics_output_protocol.Mode.FrameBufferBase
	hres := cast(int) graphics_output_protocol.Mode.Info.HorizontalResolution
	
	print(st.ConOut, "\r\nPress any key to stop\r\n")
	st.ConIn.Reset(st.ConIn, false)

	key: efi.INPUT_KEY
	col := 0

	for {
		if st.ConIn.ReadKeyStroke(st.ConIn, &key) != .NOT_READY {
			break
		}

		// Clear left column
		if col != 0 {
			for r in 0 ..< 100 {
				pos := hres * r + (col - 1)
				pixel := mem.ptr_offset(base_address, pos)
				pixel^ = 0x00000000
			}
		}

		// Draw square
		for c in col ..< col + 100 {
			for r in 0 ..< 100 {
				pos := hres * r + c
				pixel := mem.ptr_offset(base_address, pos)
				pixel^ = 0x00FF0000
			}
		}

		col += 1

		if col == hres - 100 {
			col = 0
		}
	}
}
I am seeing tearing on my machine so I guess mission accomplished but what is surprising to me is that the square doesn't rip across the screen. The execution speed of the outer loop determines the speed of the square. And the square is moving quite slowly across the screen. Why is this? The outer loop to my knowledge is unbounded, it should be running as fast as it possibly can but it seems to be limited. I ran this code on a laptop and a desktop machine that both have decent processors. I know that this could be much faster, I'm doing a single memory write for each pixel in a 100x100 grid so I am fully expecting this to be slow compared to hardware accelerated drawing but not this slow.

I should also note, I did try removing the ReadKeyStroke() call incase that is a very slow function call and that didn't affect the speed.

When I write to the GOP framebuffer, maybe those writes are just really slow? Maybe this framebuffer memory I'm writing to isn't actually in my system RAM, it's in my graphics adapter's memory which is slow to write to? Or are the memory writes being limited by something else? I very curious on what is exactly going on here.
User avatar
Demindiro
Member
Member
Posts: 165
Joined: Fri Jun 11, 2021 6:02 am
Libera.chat IRC: demindiro
Location: Belgium
Contact:

Re: Slow GOP framebuffer

Post by Demindiro »

If you haven't: you should map the framebuffer as WC (Write-Combining). It will be much faster than either WT or UC, which I presume is the default.

You can set WC via PAT.
GeneSYS exokernel (Codeberg)
Lemmings! micro-/multikernel (Github, Codeberg)
Waddle container tool (Codeberg)
CasualKyle
Posts: 12
Joined: Fri Oct 24, 2025 2:38 pm

Re: Slow GOP framebuffer

Post by CasualKyle »

Sounds good, this may be why it is unexpectedly slow.
Post Reply