Beginner Questions About XCB - xcb_alloc_color is too slow

Carsten Haitzler raster at rasterman.com
Sun Feb 21 12:12:22 UTC 2021


On Sat, 20 Feb 2021 18:37:38 -0800 Mark Solomonik <marik.solomonik at gmail.com>
said:

> Hello,
> I am trying to implement a 3D engine from scratch with XCB. To do this, I
> have to set the color of every individual pixel before drawing it.
> 
> I noticed that calling xcb_alloc_color and xcb_alloc_color_reply are
> bottlenecking the program and making it slow to draw individual triangles.
> 
> Below is how I am setting the color before drawing each pixel, straight
> from this tutorial
> <https://www.x.org/releases/X11R7.7/doc/libxcb/tutorial/index.html#usecolor>
> :
> 
> void set_drawing_color(xcb_connection_t *c, xcb_colormap_t colormap,
> xcb_gcontext_t gcontext, int r, int g, int b) {
>     xcb_alloc_color_reply_t *color_info;
>     uint32_t mask;
>     uint32_t value[3];
>     color_info = xcb_alloc_color_reply(c, xcb_alloc_color(c, colormap,
> r, g, b), NULL);
> 
>     if (!color_info)
>         return 0;
>     free(color_info);
> 
>     mask = XCB_GC_FOREGROUND;
>     value[0] = color_info->pixel;
>     xcb_change_gc(c, gcontext, mask, value);}
> 
> Is this an appropriate way to set the color?

No. Just hell-no. No NO Double-extra-no with a cherry on top.

> Is there a more efficient way to do so?

Yes.

> Slightly unrelated question: as someone completely new to Xorg and
> Linux graphics in general,
> how do you deal with the lack of documentation for XCB?

Use Xlib. Or just use a toolkit higher up the stack. XCB really is for people
who already know X backwards. It can buy some speedups in some corner-cases by
avoiding round trips to the server and back and putting the job of doing it
async in your hands. Don't start with XCB. It's a great way to drive yourself
insane.

> Thanks,

Allocating colors comes from a time when you had displays with limited numbers
of colors. Perhaps only 2 (black and white) and ylou needed to know what pixel
value is black or white, or a display with 4, 8, 16, 32, 64 etc. or 256 colors
only. These were mapped to a palette of colors able to be user-defined OR They
may have been a fixed values that could never be changed by software.

Allocating a color meant one of 2 things depending on the situation above. It
either would go and find a free color value and give it the color your asked
for, or if X had run out of colors, it'd pick the "closest match" and tell you
that. The days of color palettes or these displays is pretty much over. In
theory if you with to run on ancient or esoteric hardware you may still have
to do this, but I'd suggest then using something like a 666 color cube (6
values for red, green and blue so 0->5 each) and then "pick the closest" and do
dithering. There are other variation on this, but I won't get into that.

What you want to be doing is just filling in pixel values based on the RGB
masks in the visual you are using. you can query the visual for its RGb masks
and this then tells you if its RGB, BGR, 30 bit, 24 bit or 16/15bit (565/555)
or perhaps even 12 bit (444). etc. Reality is that there is a limited number of
these RGB arrangements in t he real world and some are much more common than
others. I'd optimize for the most common then have a "slow but works" path for
the rest. Print some debug and have users inform you they hit a slow path to
see if it's worth supporting.

The advantage of dealing with it all as RGB true color is the paletted case
where you would use XAllocColor to allocate a 666 colorcube is just a reduced
quality version of all the true color modes.

Now... You didn't mention this, but I imagine you are also doing something
really bad... and that is drawing pixel by pixel with something like
XDrawPoint()... DO NOT DO THIS!!!!! It's insane.

Given what you describe doing a 3D engine from scratch - you are explicitly
avoiding OpenGL because you want to learn how to do your own rendering or just
do it as a challenge. Lucky for you... I wrote one of these 3D engines about 25
years ago ... for X11. I did update it a bit to do true color etc. ... so the
code is old and crusty and has mistakes in it I'd not make after 25 years of
X11... but it should help inspire you:

http://www.rasterman.com/files/tex.tar.gz

Fun thing... these days it gets like 1000 fps - back in '96 I was getting
about 40fps... and it only runs on a single core so I never threaded it - ifI
could I might get a lot more FPS now (in 1996 more than 1 CPU was not something
you through about).

But the crux of the above is .. you want to use XShmPutImage (Note you will
need a fallback to XPutImage for remote XServers as XShmPutImage is for local
XServers only which is 99.9% of cases). You want have a shared memory blob
of pixels you are writing to (probably 2 of them actually for front and back
buffer), then fill in this blob of memory directly by just writing pixel
values, when done XShmPutImagae it to a window. You could do the latter in a
thread and have the "fill in the buffer" part also farmed off to one or more
threads - when it'd done it wakes up the "putimage" thread to throw it at the
screen. An XSync() after the putimage will do a round trip here and wait for
the xserver to finish this put so you don't run too far ahead. As this would be
a dedicated putimage thread this will be fine. Once this sync returns the shm
buffer is available for re-use. So keep 2 or 3 buffers around and put them to
the screen and then when those complete put them back in the pool/ring buffer
of "buffers" to be available for the rendering thread(s) to fill up.

You do not need the XRender protocol for any of this. It's not useful given
what I suspect you are doing (which is almost exactly what I did 25 years ago).

-- 
------------- Codito, ergo sum - "I code, therefore I am" --------------
Carsten Haitzler - raster at rasterman.com



More information about the xorg mailing list