[HN Gopher] Minimal Cross-Platform Graphics
___________________________________________________________________
Minimal Cross-Platform Graphics
Author : rrampage
Score : 184 points
Date : 2023-01-24 06:25 UTC (16 hours ago)
(HTM) web link (zserge.com)
(TXT) w3m dump (zserge.com)
| torstenvl wrote:
| Very, very cool. Already played doom on it. Maybe I can get my
| nephew interested in playing with this!
| jacereda wrote:
| Not so minimal, but if you need GPU rendering I wrote this:
| https://github.com/jacereda/glcv
| jacereda wrote:
| And a couple of wrappers (probably bitrotten)
| https://github.com/search?q=user%3Ajacereda+glcv
| brabel wrote:
| Wonderful. I really wanted something like this to do Graphic the
| "hard way", also the most fun way... I was trying to use Tcl-TK
| for that but this seems a lot lower level, perfect for what I
| wanted to do which is to write a small GUI toolkit for tiny apps.
| pavlov wrote:
| Writing pixels into an RGBA framebuffer in main memory is fun,
| but it's also the easy and slow way to do graphics. The hard
| way these days is to figure out how to use a modern GPU to do
| the rendering you need. It's almost always several orders of
| magnitude faster.
| kragen wrote:
| writing pixels into an rgba framebuffer in main memory is
| still plenty fast to do a full-screen animation at 60 hertz
| tho
|
| the benefit of the gpu is no longer (since maybe late last
| millennium) that you don't have the memory bandwidth to your
| framebuffer; it's that you can do a lot of computation per
| pixel
|
| typically the gpu advantage is only about an order of
| magnitude tho
| kefir wrote:
| Very similar to minifb:
|
| https://github.com/emoon/minifb,
| https://docs.rs/minifb/latest/minifb/,
| https://news.ycombinator.com/item?id=24172362
| imadr wrote:
| I did something similar to load an image in windows/linux
| https://imadr.me/cross-platform-window-in-c/
| HexDecOctBin wrote:
| Is there any similar tutorial for XCB? Most documentation only
| covers libx11, and trying to extrapolate libxcb usage from that
| is tricky at best and heisenbug-prone at worst.
| ibebrett86 wrote:
| If anyone is interested in getting into zig, we made a library
| that has similar goals (and can run on WASM in the browser):
| https://github.com/ibebrett/zigzag
| kragen wrote:
| since everyone else is posting links to their similar libraries,
| i thought i'd post mine too,
| https://gitlab.com/kragen/bubbleos/-/tree/master/yeso
|
| it's probably a bit more efficient than fenster on x-windows
| because it uses shared memory, and i think the programming
| interface is a little nicer (see the readme above for code
| examples)
|
| apps i've written in yeso include an adm-3a terminal emulator, a
| tetris game, a raytracer, an rpn calculator, a fractal explorer,
| and so on
|
| i haven't ported yeso to win32/64, macos, android, or the browser
| canvas yet, just x-windows, the linux framebuffer (partly), and a
| window system implemented in yeso called wercam
|
| it includes bindings for c, python (via cffi), and lua (via
| luajit's ffi), and presumably you could use it from zig or rust
| in the same way as fenster, but i haven't tried
| carapace wrote:
| I like BubbleOS. :)
| kragen wrote:
| yay :)
| enriquto wrote:
| This is extremely useful, and the page is beautifully written.
| Some people write absurd electron behemoths, and then there's
| this jewel of elegance.
|
| Could this be compiled as an ape? Such a thing would make many
| heads explode.
| [deleted]
| rootbear wrote:
| Alpha Rho Epsilon? Alpha Pee Epsilon? Google isn't helping me
| here...
| jstrieb wrote:
| I think they're referring to this:
|
| https://justine.lol/ape.html
|
| One of many HN discussions about the topic here:
|
| https://news.ycombinator.com/item?id=26273960
| rootbear wrote:
| Thank you! When I encounter an unknown term on HN, I try to
| find out for myself what it is before bugging everyone, but
| it doesn't always work.
| jstrieb wrote:
| Yup! Besides being hard to search, others have commented
| that the name might be bad for accessibility:
|
| https://news.ycombinator.com/item?id=24257503
| [deleted]
| abainbridge wrote:
| I think the Xlib dependency would prevent compilation as an
| ape. I put some effort into doing X11 from scratch (without
| Xlib or Xcb) to make this possible. Or at least, my aim was to
| be able to build with musl libc and generate a single
| executable that would run on many different Linuxes.
|
| https://github.com/abainbridge/deadfrog-
| lib/blob/master/src/.... It is janky though.
| 082349872349872 wrote:
| One advantage of speaking X directly for small projects is
| that one can allocate fixed ids.
| csdvrx wrote:
| Super interesting!
|
| > It is janky though
|
| So what? It can be improved :)
|
| Would you like to try to make an APE out of it? It would then
| be not just different Linuxes, but also MacOS and Windows, a
| bit like https://x410.dev/ or the free software vcxserv
| https://sourceforge.net/projects/vcxsrv/
|
| We have discord. You should join us!
|
| FWIW I'm currently working on a DNS server with nice extra
| features like Multicast for mDNS/Bonjour: it could be used to
| publish the X server DISPLAY and make it accessible via
| service-discovery
| bananaboy wrote:
| Very nice! I like that it's a single header. I wrote something
| similar although it's not single header, doesn't do audio, and no
| X11 backend https://github.com/samizzo/pixie. I use mine as a
| backend for Windows ports of MS-DOS demos that I make.
| detrites wrote:
| A similar x-platform library called TiGR (Tiny GRaphics):
|
| https://github.com/erkkah/tigr
|
| Brief discussion:
|
| https://news.ycombinator.com/item?id=34310208
| synergy20 wrote:
| Basically a bare-metal SDL2? well done.
| Const-me wrote:
| I think this needs much more complexity to be useful.
|
| For the rendering, ideally it needs GPU support.
|
| Input needs much more work, here's an overview for Windows:
| http://blog.ngedit.com/2005/06/13/whats-broken-in-the-wm_key...
|
| Windows' Sleep() function has default resolution 15.6ms, that's
| not enough for realtime rendering, and relatively hard to fix,
| ideally need a modern OS and a waitable timer created with high
| resolution flag.
|
| Here's my attempt at making something similar, couple years ago:
| https://github.com/Const-me/Vrmac
| bob1029 wrote:
| > Windows' Sleep() function has default resolution 15.6ms,
| that's not enough for realtime rendering, and relatively hard
| to fix
|
| Very easy to fix. Call this at the start of your real-time
| process:
|
| https://learn.microsoft.com/en-us/windows/win32/api/timeapi/...
|
| Use an argument of 1, and windows will come knocking at ~1Khz.
| This is extremely effective in my experience. Allows you to do
| some pretty crazy stuff on 1 thread.
| bitwize wrote:
| No direct access to the command buffers of the GPU? No matrix
| transform routines? No colorspaces besides RGB? Absolute non-
| starter.
| wokwokwok wrote:
| There's simple and there's naive.
|
| SDL already walked this path. So did pygame.
|
| It turns out without shaders, and all their complexity, you
| basically can't do anything useful at high resolutions.
|
| /shrug
|
| It's too slow; compositing, transformations, all the things
| people want to do are very much harder to implement in
| software on top of a naive graphics stack.
|
| Simple doesn't mean naive; you can have a simple api that is
| excellent.
|
| ...but _this_ is a naive implementation, and it won't work in
| any useful way at scale.
|
| People complain about ballooning complexity, but they often
| forget that stuff (eg. specialised graphics hardware) wasnt
| invented by a bunch of idiots.
|
| It was invented because the naive approach, that _they used
| before_ was found to be _in practice_ fundamentally limiting,
| inferior and failed to meet people's expectations.
|
| Of course, if you vastly lower your expectations, and target
| say, 320x240 at 8bit colour, you can happily have a naive
| implementation that works just fine
|
| (Don't believe me? Quote:
|
| > Having this we can now draw complex polygons and would
| probably need a "flood fill" algorithm. Typically it is
| implemented using a queue of pixels to check and paint, but
| we can use recursion, as long as the filled area remains
| small enough to not overflow the stack
|
| ^ This is the very definition of a naive implementation, and
| https://github.com/zserge/fenster/blob/e71d493fa6d544243dd60.
| .. settings one pixel at a time, is too. Delightfully
| charming as it might be to write your own functions that set
| one pixel at a time, it's a joke, really, if you expect to do
| anything serious)
| stephc_int13 wrote:
| For any graphic intensive application it would be obviously
| be necessary to use a GPU.
|
| But for quick hacking / porting old demos / writing
| emulators and also text based UI it can be fast enough.
|
| With the added benefit of small footprint, high
| compatibility and fast startup time.
|
| The Lite editor https://github.com/rxi/lite is using pure
| software rendering (on top of SDL) in a rather naive
| fashion but it still renders full 32bit colors at full
| resolution at more than 60FPS on my computer, not the best
| solution but still surprisingly fast given the simplicity
| of the renderer.
|
| This is typically a case where simple/naive can beat a
| juggernaut like Electron.
| flohofwoe wrote:
| > No direct access to the command buffers of the GPU?
|
| Lol, show me one library that does this without going through
| an abstraction layer like Vulkan. Details like this are not
| even documented by GPU vendors and you'd need to reverse
| engineer every supported GPU architecture yourself.
| bitwize wrote:
| I was being sardonic.
|
| Guy writes a library with the design goals of making basic,
| retro-like graphics functionality easy and fun with a
| minimum of code, and certain Hackernews dogpile him because
| it can't be integrated with an AAA pipeline or whatever.
| flohofwoe wrote:
| Ah, apologies then :)
| Const-me wrote:
| > direct access to the command buffers of the GPU
|
| Impossible given the platform limitations at that time, on
| Linux that code works on top of GLES 3.1. I didn't want too
| many Windows-only features there.
|
| > No matrix transform routines?
|
| There's no need for that. That library is for C#, which
| includes these routines in the standard library of the
| language for many years now, since .NET core 1.0:
| https://learn.microsoft.com/en-
| us/dotnet/api/system.numerics...
| bitwize wrote:
| My remark was about Fenster, and a critique of your comment
| that it somehow isn't good enough because it lacks GPU
| acceleration and that sort of thing. The point of Fenster
| is to be minimal and simple yet still allow programmers to
| get stuff on the screen in a way that's easy and fun. It
| performs superbly at the role it was conceived for. We can
| list features we want/think are table stakes for a modern
| graphics pipeline or whatever, and there are plenty of
| libraries that are up to those tasks. This one is doing
| something different.
| flohofwoe wrote:
| > For the rendering, ideally it needs GPU support.
|
| It's a 2D framebuffer library for placing single pixels like in
| mode 13 *eyerolling* (GPUs are hardly useful for this type of
| stuff).
|
| If you want something more complete, check out the sokol
| headers (shameless plug): https://floooh.github.io/sokol-html5/
|
| ...but that can hardly be called 'minimal' anymore.
| krapp wrote:
| 2d graphics are rendered on the GPU nowadays, and rightly so.
| Even SDL uses the GPU wherever it can, by default.
| flohofwoe wrote:
| That's a silly statement. If you want to place single
| pixels, a GPU won't help in any way. The most efficient way
| would be to write pixels with the CPU into a mapped GPU
| texture, and then render this texture as fullscreen quad.
| That's hardly more efficient than going through the
| system's windowing system.
|
| For applications like emulators, or making vintage games
| (like Doom) run on modern platforms, that approach makes a
| lot of sense.
| krapp wrote:
| >That's hardly more efficient than going through the
| system's windowing system.
|
| My intuition says otherwise but I admit I don't have math
| on hand to back it up.
| flohofwoe wrote:
| Ok it's most likely slightly slower, but not enough that
| it matters. Frame latency might actually be lower though
| if the result doesn't need to go through a swapchain AND
| the window system composer.
| CryZe wrote:
| Doing this through the GPU is actually somehow A LOT
| slower on at least Windows than going through the
| windowing system.
| greenbit wrote:
| But wouldn't the GPU help if you were mapping e.g.
| 256x192 virtual pixels to say 1024x768? I.e., each of the
| pixels from the low-res space being represented by a NxM
| patch of actual screen pixels, like a Win32 GDI
| StretchBlt() call.
|
| If you had a frame buffer for the actual screen and you
| tried to do even a 1 to 2x2 expansion on the CPU's time,
| that'd have to be a serious speed hit. Presumably GPU
| hardware can do that sort of thing.
| flohofwoe wrote:
| Yes, for fancy upscaling or applying pixel shader effects
| like CRT filters, doing the final pass on the GPU
| definitely makes sense. This would no longer be a
| "minimal" library though.
|
| Window system composers should also be able to upscale
| bitmaps on their own though, and nothing prevents them to
| use the GPU for this.
| zozbot234 wrote:
| Most vintage systems use something way more complex than
| a pure CPU-controlled framebuffer for graphics. They
| generally have some sorts of pre-defined "tiles" used to
| implement a fixed-width character mode, with the addition
| of a limited number of "sprites" overlaid in hardware.
| These video modes could be implemented efficiently by
| modern GPU's.
| flohofwoe wrote:
| Only if you don't have a cycle correct emulator
| inbetween. Those old school system relied on hard real
| time timings down to the clock cycle to let the CPU
| control the color palette, sprite properties etc... at
| the right raster position within a frame. Modern GPUs
| don't allow such a tight synchronization between the CPU
| and GPU, so the best way is to run the entire emulation
| single-threaded on the CPU, including the video decoding.
|
| (the resulting framebuffer can then of course be dumped
| into a GPU texture for rendering, but that just offers a
| bit more flexibility, eg embedding the emulated system
| into a 3D rendered world)
| zozbot234 wrote:
| It depends what you mean by "hard real time". In theory,
| user input you get while scanning out pixel _x_ might
| change pixel _x_ + 1, and this leaves you with no choice
| but rendering single pixels in a strictly serial way. In
| practice, no existing emulator cares about that.
| flohofwoe wrote:
| It's not about user input, but the CPU writing video
| hardware registers at just the right raster position mid-
| frame (to recycle sprites, change the color palette, or
| even the resolution). Home computer emulators for systems
| like the C64 or the Amstrad CPC need to do this at
| exactly the right clock cycle, otherwise modern (demo
| scene) demos wouldn't render correctly.
|
| PS: of course one could build a GPU command list to
| render such a video frame somehow, but I bet just
| building this command list is more expensive then just
| doing the video decode with the CPU. It would basically
| come down to one draw command per (emulated system) pixel
| in the worst case.
| panzerboiler wrote:
| Perfect companion for this C graphics library:
| https://github.com/tsoding/olive.c
| DrNosferatu wrote:
| "raylib" comes to mind, as it is a graphics library also inspired
| by the Borland BGI graphics library.
|
| - Anyone with experience in it / any possible synergies?
|
| [https://en.wikipedia.org/wiki/Raylib]
| unwind wrote:
| Very cool!
|
| This example I think needs editing:
| memset(f->buf, rgb, f->width*f->height*sizeoof(uint32_t));
|
| it's described as a way to "fill the complete framebuffer with a
| solid colour", but 'rgb' is shown in the previous snipped to be
| 'uint32_t'. That is not how 'memset()' [1] works, it will only
| use the least significant 8 bits of the 'int'-typed value
| argument. So it would use whatever blue bits where in 'rgb',
| only.
|
| For clearing (all zero) it's usable, and then I would recommend
| writing it as: memset(f->buf, 0, f->width *
| f->height * sizeof *f->buf);
|
| this both fixes the typo ("sizeoof" sounds like a C lecturer
| being punched in the guts), and avoids duplicating the type and
| instead inferring it from the actual object, which is more DRY
| and a style I've been advocating for ... years.
|
| [1]: https://man7.org/linux/man-pages/man3/memset.3.html
| aa-jv wrote:
| Fine, but at least fix this bug: .. (sizeof
| *f->buf) ..
|
| Because otherwise thats one stray space char away from a bad
| day ..
| dahart wrote:
| Or ... sizeof(*f->buf)? I'd forgotten that sizeof even allows
| dropping the parens, but isn't that asking for trouble?
| "sizeof a <op> b" is sometimes parsed as "sizeof(a) <op> b"
| and sometimes as "sizeof(a <op> b) depending on the
| precedence of operator <op>. Is there any downside to always
| using parens and writing sizeof() like a function call?
| unwind wrote:
| Sure, many people like that style, I don't.
|
| But no, I don't think there are any downsides, but there is
| this notion that things should look like what they are,
| which is popular in C.
|
| With parens, I would suggest spaces to make it
| sizeof (*f->buf)
|
| that makes it look less like a function call in many
| styles.
| nuc1e0n wrote:
| This is very cool, but if I were doing such things I'd just use
| SDL (https://www.libsdl.org/)
| abainbridge wrote:
| I used to use a timer to limit the frame rate to 60 Hz too. But
| on Windows, I found that DwmFlush() seems to act like
| WaitVSync(), so I've been using that in preference for years now.
| I think this is undocumented behaviour. I guess what it actually
| does is wait until the compositor is ready for another frame.
|
| To be able to call that function, I LoadLibrary("dwmapi.dll"),
| and then GetProcAddress(dwm, "DwmFlush").
| formerly_proven wrote:
| > Issues a flush call that blocks the caller until the next
| present, when all of the Microsoft DirectX surface updates that
| are currently outstanding have been made.
|
| DWM always runs with vsync so presents never happen more
| frequently than screen refreshes.
| suby wrote:
| Why not just use VSync?
| sylware wrote:
| If I am not too much mistaken, because on some hardware
| (mobile), there is no vsync feedback.
|
| vsync feedback support has to be queried (look at vulkan
| API/wayland API). Usually it is called "presentation"
| something.
| abainbridge wrote:
| How would I do that? I think there is any such function in
| the Win32 API.
| pavlov wrote:
| Yes, I think you basically have to use a DirectX swap chain
| to get vsync presentation.
___________________________________________________________________
(page generated 2023-01-24 23:02 UTC)