Page 1 of 1

Weirdness when double-buffering

Posted: Fri Feb 05, 2021 12:00 am
by justinian
So I've been banging my head on this issue and can't figure it out. I'm wondering if any of you might have some insight. I've got a framebuffer (from GOP, on both real hardware and QEMU) and a userspace program that gets that framebuffer mapped into its address space. If I write directly to the framebuffer in this program, everything looks right. But if I malloc a back buffer and do double-buffering, things start looking skewed and over-drawn.

When I draw directly to the framebuffer, it looks like this:
Image

When I double-buffer, it looks like this:
Image

You can see the branch with my WIP code trying to figure this out in my GitHub repo, where the only difference is uncommenting the line here. So I'm still mallocing the back buffer, still drawing the same way, still even copying the frame (though then it's just copying the framebuffer to itself). I must just be forgetting something stupid here, but I can't for the life of me figure it out.

For ease of looking at, here's the most relevant code:

Code: Select all

screen::screen(volatile void *addr, unsigned hres, unsigned vres, unsigned scanline, pixel_order order) :
	m_fb(static_cast<volatile pixel_t *>(addr)),
	m_order(order),
	m_scanline(scanline),
	m_resx(hres),
	m_resy(vres)
{
	m_back = reinterpret_cast<pixel_t*>(malloc(scanline*vres*sizeof(pixel_t)));
	//m_back = const_cast<pixel_t*>(m_fb);   // <==== Uncommenting this to use the FB directly
}

void
screen::fill(pixel_t color)
{
	const size_t len = m_scanline * m_resy;
	asm volatile ( "rep stosl" : :
		"a"(color), "c"(len), "D"(m_back) );
}

void
screen::update()
{
	const size_t len = m_scanline * m_resy;
	for (size_t i = 0; i < len; ++i) {
		m_fb[i] = m_back[i];
	}
}

// this function is defined inline in the header
inline void draw_pixel(unsigned x, unsigned y, pixel_t color) {
	const size_t index = x + y * m_scanline;
	if (x > m_resx || y > m_resy)
		return;
	m_back[index] = color;
}
Any ideas? Please tell me I did something simple and obvious..

Re: Weirdness when double-buffering

Posted: Fri Feb 05, 2021 1:46 am
by Gigasoft
Looks like something is wrong with malloc, where some of the memory being allocated overlaps with video memory.

Re: Weirdness when double-buffering

Posted: Fri Feb 05, 2021 3:14 am
by justinian
Gigasoft wrote:Looks like something is wrong with malloc, where some of the memory being allocated overlaps with video memory.
Yeah, it sure does look like it's getting written over by something.. but if I follow the mappings, the lfb is at 0x90000000 (identity mapped) and the back buffer is at 0x1c0000000 (ends up mapped to somewhere around 0x1fc4f000). Though, that's what it starts at.. maybe there's some stack corruption and something is overwriting the pointer or something. I'll go look for that.

Re: Weirdness when double-buffering

Posted: Fri Feb 05, 2021 6:37 am
by Gigasoft
The asm statement is also wrong. It should be:

Code: Select all

uint64_t unused1, unused2;
asm volatile ( "rep stosl" : "=c"(unused1), "=D"(unused2) :
      "a"(color), "0"(len), "1"(m_back));

Re: Weirdness when double-buffering

Posted: Fri Feb 05, 2021 4:31 pm
by justinian
Gigasoft wrote:The asm statement is also wrong. It should be:

Code: Select all

uint64_t unused1, unused2;
asm volatile ( "rep stosl" : "=c"(unused1), "=D"(unused2) :
      "a"(color), "0"(len), "1"(m_back));
Interesting, why? It seems like this is just adding them to the output constraints list as well as the input constraints list, but then not using the output. The main difference in generated asm is storing them back in the local stack frame right before returning, which is pretty much what I'd expect.

Re: Weirdness when double-buffering

Posted: Fri Feb 05, 2021 6:21 pm
by Gigasoft
GCC assumes that input-only registers remain unchanged. However, if you are compiling with optimizations off, it doesn't matter.