<div dir="ltr">Hi Pekka,<div><br></div><div>Thanks for the detailed insight.  That's exactly the kind of info I was after about how to manage some of this stuff.  I still haven't had a chance to dig into this in any detail but hope to in the next day or so...</div><div><br></div><div>Brad</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jun 22, 2020 at 6:46 PM Pekka Paalanen <<a href="mailto:ppaalanen@gmail.com">ppaalanen@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Fri, 19 Jun 2020 11:21:34 +0100<br>
Carsten Haitzler (The Rasterman) <<a href="mailto:raster@rasterman.com" target="_blank">raster@rasterman.com</a>> wrote:<br>
<br>
> On Fri, 19 Jun 2020 13:24:12 +1000 Brad Robinson <<a href="mailto:brobinson@toptensoftware.com" target="_blank">brobinson@toptensoftware.com</a>><br>
> said:<br>
> <br>
> > Hi All,<br>
> > <br>
> > I'm fairly new to Wayland and Linux GUI programming in general, but doing<br>
> > some experiments to figure out how to integrate it into my custom UI<br>
> > toolkit library and have a couple of questions about client side buffer<br>
> > management.<br>
> > <br>
> > Firstly, this is how I'm allocating the backing memory for client side<br>
> > buffer pools.  This is C# p-invoking to libc, and basically it's using<br>
> > mkstemp() to get a temp file, ftruncate() to set its length, mmap() to map<br>
> > it and then unlink() once mapped so temp files aren't left behind.  Any<br>
> > issues with this approach?<br>
> > <br>
> >             // Get temp file<br>
> >             var sb = new<br>
> > StringBuilder(System.IO.Path.Join(System.IO.Path.GetTempPath(),<br>
> > "mmXXXXXX"));<br>
> >             int fd = mkstemp(sb);<br>
> >             ftruncate(fd, (ulong)capacity);  <br>
> <br>
> i assume GetTempPath() will be looking at /tmp ... and /tmp may not be a<br>
> ramdisk. it may be a real disk... in which case your buffers may be getting<br>
> written to an actual disk. don't use /tmp.<br>
<br>
Hi,<br>
<br>
that's true. The trick we have been using is create the file in<br>
$XDG_RUNTIME_DIR which is practically always a tmpfs, but OTOH it might<br>
not be the best place to store large pixel buffers.<br>
<br>
> you might wan to to loop at shm_open or memfd <br>
<br>
Right.<br>
<br>
> or libdrm for specific drivr<br>
> allocation calls like drm_intel_bo_alloc_tiled, drm_intel_bo_map/unmap etc. the<br>
<br>
No, please do not do that!<br>
<br>
That will make your program specific to a certain hardware driver,<br>
which makes it specific to that hardware. Plus, you will have to learn how<br>
to program specifically for that driver. It's not worth it.<br>
<br>
The wl_shm Wayland interface is for software rendering anyway. No-one<br>
expects a wl_shm-based buffer to be directly usable by a display or a<br>
GPU driver, it will always be copied by a compositor. So trying to<br>
shove "hardware buffers" through wl_shm is only picking the worst of<br>
all options, as direct CPU access to them is often sub-optimal, perhaps<br>
even prohibitively slow.<br>
<br>
> latter libdrm ones wo9uld allow your buffers to possibly be scanned out<br>
> directly to the screen or used as textures directly without copies, but will<br>
> need careful handling, so do this only as an advanced step.<br>
<br>
If you want to use hardware acceleration or allow direct scanout, use<br>
the proper APIs intended for them: GBM, EGL, Vulkan; if you need to<br>
pass hardware buffers manually through Wayland, use zwp_linux_dmabuf_v1<br>
extension instead of wl_shm.<br>
<br>
> <br>
> >             // Map it<br>
> >             var addr = mmap(IntPtr.Zero, (ulong)capacity, Prot.Read |<br>
> > Prot.Write, Map.Shared, fd, 0);<br>
> > <br>
> >             // Unlink it (so temp files not left behind)<br>
> >             unlink(sb.ToString());<br>
> > <br>
> > Secondly I'm wondering about practical strategies for managing client side<br>
> > buffers.  The toolkit in question basically needs arbitrarily sized buffers<br>
> > to render whatever size the window happens to be.  Seems like to use a<br>
> > buffer pool for this would require some sort of heap manager to manage<br>
> > what's in each pool.  I'm wondering if there's any recommendations or best<br>
> > practices for how to deal with this.  eg: create one big pool and<br>
> > explicitly manage what's in there as a heap, use lots of little pools with<br>
> > one buffer in each, a combination of both, something else?  <br>
> <br>
> resizes of windows are less common (in general) than rendering to them. here<br>
> i'd go for a scheme of N buffers in a ring per window. so you have buffers A,<br>
> B, C and you first render to A then display it, then next frame B, then C, then<br>
> A, then B, then C. You could get away without C. as the buffers retain their<br>
> state you can take advantage of this and only partially render part of a<br>
> buffer for updates "since 1 or 2 frames ago" (depending if you do double or<br>
> triple buffering). as its predictable ABCABCABC you can just keep a "Sliding<br>
> window of the update regions of the past N frames" and merge those into the<br>
> "current amount to update" but always store per-frame update rectangle regions<br>
> before this merge-at-render-time. 3 buffers allows you to be rendering a 3rd<br>
> buffer while 1 buffer is queued to be displayed and one is still being<br>
> displayed. if you find you need a 4th buffer, perhaps just overdraw the 3rd on<br>
> you just did and "update it" ... or just block or don't update yet as you are<br>
> getting too far ahead of the compositor.<br>
<br>
Such a strict sequence of buffers is wasteful and not necessary. You<br>
don't need that to keep track of damage for doing partial updates. The<br>
slight complication in tracking the damage is very much worth it over<br>
the use of more buffers than you really need.<br>
<br>
Instead, allocate buffers on-demand, and re-use buffers as soon as the<br>
compositor you are connected to releases them. If you have more than one<br>
buffer idle, you can destroy the extra ones, perhaps after some<br>
timeout. This will automatically let your application to use the<br>
minimum required number of buffers at all times.<br>
<br>
In general:<br>
<br>
If you have a) something new to show, and b) wl_surface.frame callback<br>
has returned (unless you want to override an earlier update), and c)<br>
you have a buffer available or you have not exceeded the maximum number<br>
of buffers you are willing to allocate (4 is a good number), then you<br>
can repaint.<br>
<br>
When you repaint, you first need an available buffer. If you don't have<br>
any ready, allocate a new one. Repaint, submit the update via Wayland,<br>
and mark the buffer as busy in your own bookkeeping.<br>
<br>
When the compositor replies with wl_buffer.release, mark the buffer as<br>
available again. You may have to submit one or more further updates<br>
with other buffers before this happens. In some cases it might happen<br>
before you need to repaint again, in which case you'll only need one<br>
buffer in total.<br>
<br>
When you submit a buffer to a Wayland compositor, it gives the<br>
compositor permission to read the buffer at any time, as many times as<br>
it wants to, until it tells you with wl_buffer.release that it will not<br>
be looking at that buffer again. You must not write to a buffer that<br>
might be read by the compositor, as that can cause misrendering on<br>
screen (e.g. your repaint is shown unfinished).<br>
<br>
You also should not destroy a wl_buffer that is being kept busy by the<br>
compositor. If you are closing a window, destroy the wl_surface first,<br>
and wait for the wl_buffer.release events before destroying the wl_buffers.<br>
<br>
Also, every buffer you submit must be fully drawn, also outside of the<br>
areas you mark as damage. The compositor may be reading more than just<br>
the damage you submit.<br>
<br>
<br>
Thanks,<br>
pq<br>
</blockquote></div>