Slow GOP framebuffer
Posted: Fri Nov 14, 2025 2:03 pm
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.
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.
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 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.