[HN Gopher] Olive.c: a simple graphics library that does not hav...
       ___________________________________________________________________
        
       Olive.c: a simple graphics library that does not have any
       dependencies
        
       Author : masterofsome
       Score  : 466 points
       Date   : 2022-09-21 22:50 UTC (1 days ago)
        
 (HTM) web link (tsoding.org)
 (TXT) w3m dump (tsoding.org)
        
       | treuks wrote:
       | Please do note that it is in an extremely early stage of
       | development, so if it may seem raw and unpolished, it is because
       | it is.
        
       | dezmou wrote:
       | Nice to see tsoding on the top of HN, this guy does amazing
       | streams and youtube video
        
         | mikedelago wrote:
         | I agree, he's pretty great. Really intelligent guy.
         | 
         | It's a damn shame that he's unable to get paid from Twitch and
         | Youtube due to the war.
        
         | NeutralForest wrote:
         | Dude is insanely talented and patient enough to learn new
         | stuff, it's always a pleasure to watch the videos even though
         | they're usually really long.
        
         | _qua wrote:
         | I only follow about 20% of what he does because of my limited
         | skill but he's certainly entertaining.
        
         | abainbridge wrote:
         | How can I find them?
        
           | sph wrote:
           | He's got a channel on the tube, Tsoding Daily. These days
           | he's really into WASM and Jai, but he's always trying
           | something new.
           | 
           | https://youtube.com/c/TsodingDaily
        
           | SiempreViernes wrote:
           | Using the search field on youtube is probably easiest.
        
             | abainbridge wrote:
             | Gee thanks. I tried typing olive.c there. That didn't help.
             | The string "tsoding" is the magic I needed.
        
       | WoodenChair wrote:
       | I started something superficially similar but for SVG in an intro
       | C class: https://github.com/davecom/SuperSimpleGraphics
        
       | two_almonds wrote:
       | _The name is pronounced as "olivets" which is a Ukrainian word
       | for "pencil" ("olivets'")._
       | 
       | From the readme.
        
         | SotCodeLaureate wrote:
         | Neat.. of course it seems obvious now, but that's funny - I'm
         | Ukrainian and I think that without explicit explanation it
         | would have never occurred to me to read, let alone pronounce it
         | this way - the dot before the extension somehow puts an
         | insurmountable boundary after "olive".
        
           | lioeters wrote:
           | As a long-time beginner student of several Slavic languages,
           | I like that word play too, with ".c" being "ts". It makes me
           | wonder what missed opportunities there are for C library
           | names with a funny pun.
        
       | sylware wrote:
        
       | arriu wrote:
       | Any plans for text rendering?
        
         | tyingq wrote:
         | There's a fairly simple olivec_text() function there that uses
         | a built-in bitmap font.
        
       | an1sotropy wrote:
       | The TODO section at the end of olive.c shows that there's lots
       | more to do, and decisions to make about how much to do (e.g. what
       | about bezier curves?), but that's quibbling: it is refreshing
       | (and educational) to see everything here done in such a perfectly
       | self-contained way. Godspeed!
        
         | shultays wrote:
         | Hell, half the letters in alphabet is not implemented in that
         | glyph list. That is a weird place to leave a work halfway done.
         | I wonder if he only added the letters for his hello world
        
       | bearon wrote:
       | Nice! An awesome retrospective.
        
       | tleb_ wrote:
       | Idea for improvement: avoiding color blending in sRGB. A first
       | step would be to work in linear space.
       | 
       | https://github.com/tsoding/olive.c/blob/43a67dd76760f915516a...
        
       | simonw wrote:
       | This is so nice. It feels a bit like Logo using C syntax - I love
       | that it's totally safe to run software written in this in a
       | browser thanks to WebAssembly.
        
       | erwincoumans wrote:
       | Cool use of Webassemly! See also the 500 line
       | https://github.com/ssloy/tinyrenderer or the 100 line
       | Python/numpy version https://github.com/rougier/tiny-renderer
       | 
       | Both cpu renderers with texture mapping and Wavefront obj import
       | without further dependencies.
        
       | billsix wrote:
       | Nice! PortableGL is also great, OpenGL api that is defined in
       | entirely in C and writes the "framebuffer" into an SDL window
       | 
       | https://github.com/rswinkle/PortableGL
        
         | rswinkle wrote:
         | Minor correction/clarification, the library doesn't depend on
         | SDL, it just writes into a 32 bit color frame buffer, but all
         | the demos/examples use SDL for getting it to the screen since
         | that's the best option. The test suite just writes frames to
         | disk as png images to compare against expected for example.
        
         | jstimpfle wrote:
         | It's an implementation, not an API.
        
           | sylware wrote:
           | ... and it is opengl-ish, namely seriously castrated
           | (shaders). Borrowing maths from Mr. Bellard's tinygl.
        
             | rswinkle wrote:
             | Depends on how you define castrated. It's not like using C
             | or C++ is really limiting and using a C++ math library
             | (like my own rsw_math or more complete glm) lets you
             | basically write it in GLSL. It's not like there's anything
             | you can't do.
             | 
             | And the only math from Bellard's TinyGL that I use is his
             | clipping code, maybe 80 lines of code give or take. Not to
             | diminish Bellard at all, if anything I'm saying any
             | problems with PortableGL are mine not his.
        
               | sylware wrote:
               | My bad, I used "castrated", it is not what I really
               | wanted to say. I should have said a subset of opengl
               | features. Namely, it is not a software GL implementation
               | you can run an opengl4 3D game on (only those horrible
               | c++ diarehas which are llvm with things like llvmpipe or
               | the other one from intel are supposed to be able to do
               | so).
        
       | osener wrote:
       | I also found this Cairo-like vector graphics library that runs in
       | a shader interesting: https://www.shadertoy.com/view/lslXW8
        
         | stoeckley wrote:
         | That is truly one of the coolest things I've seen in a while.
        
       | TazeTSchnitzel wrote:
       | It's cool that you can get away with software rendering these
       | days, but it's a shame to not make use of the power of the GPU.
        
         | jesse__ wrote:
         | That's the point. Also, not all devices have dedicated graphics
         | hardware
        
           | nottorp wrote:
           | And even if they do, if they're portable please think of my
           | battery :)
        
           | TazeTSchnitzel wrote:
           | Only embedded systems and servers lack it these days. Server-
           | side rendering is an interesting use-case, I guess.
        
       | urjjhjx wrote:
        
       | dlivingston wrote:
       | Cool project! What's the motivation? Since it eschews any
       | graphics API, is it safe to assume this only does CPU-based
       | rendering?
        
         | feiss wrote:
         | yes, this is only cpu based
        
         | skulk wrote:
         | From demos/triangle.c (https://github.com/tsoding/olive.c/blob/
         | master/demos/triangl...)                   // This idea is that
         | you can take this code and compile it to different platforms
         | with different rendering machanisms:         // native with
         | SDL, WebAssembly with HTML5 canvas, etc.
         | 
         | The goal seems to be to maximize portability.
        
           | p1necone wrote:
           | Unfortunately only portable to systems with fast CPUs though.
           | Although I guess rendering simple 3d shapes probably runs
           | just fine on low end hardware.
        
             | keeglin wrote:
             | Uh, right? The demo page renders fine on this decade old
             | core i5-3337u, with all the vulnerabilities patched.
        
             | unsafecast wrote:
             | It depends how fast "fast" is. PCs in the 90s could run
             | games like quake in lower quality mode with lower
             | framerates, but they ran. CPUs that we have now are many
             | orders of magnitude more powerful, so imagine what you can
             | do with that.
             | 
             | Though most games don't bother so we don't really have
             | actual evidence.
        
               | mysterydip wrote:
               | I was thinking about this just yesterday, it would be
               | neat to have a game jam to make something run in dosbox
               | at modern cpu speeds.
        
               | Someone wrote:
               | Quake used hand-coded assembly for blitting pixels. That
               | could be a bit less efficient when using this engine.
               | 
               | I would expect multi+GHz CPUs hardware to be able to
               | overcome that, though, especially if you're happy with
               | the tiny (for today) screen resolution of Quake.
               | 
               | Quake also did a few hacks to stay performant (https://en
               | .wikipedia.org/wiki/Quake_engine#Engine_design_and...).
               | 
               | If necessary you would have to do the same for something
               | that uses this library.
        
               | unsafecast wrote:
               | I'm not giving this library as an example of very fast
               | code. I mean, it's really cool, but at the moment it
               | doesn't even use SIMD (which _dramatically_ improves
               | performance).
               | 
               | Also, I think handmade hero started with a software
               | renderer. Not sure how far they went with that, but I
               | remember Casey mentioning once that software rendering is
               | viable if you do it right. Certain art styles are easier
               | to do as well -- I'm not expecting PBR to be fast, but
               | you could pull off a cel shaded look with simple
               | lighting, and make it look good.
               | 
               | It's also worth mentioning that even with portable APIs
               | like OpenGL, drivers are terrible and porting to other
               | operating systems (and even other GPUs!) can be a chore.
               | Software rendering can be an asset in those cases.
        
           | nspattak wrote:
           | i understand that it is more portable but if it is cpu based,
           | i guess its power performance is terrible since it is not
           | using gpu acceleration which is a priori more efficient
        
       | quickthrower2 wrote:
       | The combination of mapping pixels to memory and rendering
       | individually and then that appearing on webgl and looking great
       | on a not too expensive phone, where I am pinch zooming while it
       | flawlessly animates, sort of blows my mind in how much technology
       | has come in 25 years. Connects the old with the new in a way I
       | haven't seen for a while (and yeah seen loads of C64 emulators
       | and such)
        
         | sriram_malhar wrote:
         | At first I thought the demos were gifs! After seeing your
         | comment, I went back and remained slack-jawed!
        
         | darkwinx wrote:
         | those are canvas with 2d context, not webgl
        
       | tambourine_man wrote:
       | Amazing that software rendering can be this smooth on a phone.
        
       | altilunium wrote:
       | Years ago, i made something similar as a coursework (Computer
       | Graphics)
       | 
       | https://github.com/rtnF14/ComputerGraphics
        
       | jandrese wrote:
       | It pains me to see #include <olive.c> at the top of the file. It
       | makes the project feel like a toy.
        
         | kazinator wrote:
         | It looks like olive.c is a "single header" library. They chose
         | a .c suffix rather than .h.
         | 
         | If OLIVE_IMPLEMENTATION is defined before including "olive.c",
         | then the full implementation is produced. Otherwise, just
         | declarations, so that it behaves like a header file.
         | 
         | It's a valid technique. If you have a library all in one source
         | file, the requirement to have a separate .h doubles your file
         | count.
         | 
         | One small reason to have a .c suffix is might be that your
         | editor can then choose a more specific syntax scheme. A .h file
         | could be C++. Another one is that it can be used as a source
         | file. You can pop it into a Makefile project, and just make
         | sure you have -DOLIVE_IMPLEMENTATION on the compiler command
         | line for that file. Other files using it just include "olive.c"
         | to get the declarations. Because it has a .c suffix, make will
         | handle it via its .c.o rule.
        
           | jandrese wrote:
           | This feels far messier to me than just having a .h file.
        
             | worthless-trash wrote:
             | Its part of the spec, and not even obscure. this is not
             | even scratching the surface of nightmare level C.
        
             | Brian_K_White wrote:
             | In this case, 2 files is a failure and blows the whole
             | point. 2 files means a tgz and might as well be 50 files.
        
             | kazinator wrote:
             | Sure, but let's look at the larger picture. If you have one
             | file, then that's your deliverable. You can host that file
             | somewhere, send it as an e-mail body, drop it into a paste-
             | bin or whatever. It is one self-contained unit.
             | 
             | If you have two files: .h and .c, the use scenario may be
             | simpler. Not a lot though. And now youu have two files
             | which have to stay together somehow, yet remain distinct.
             | If you combine them in one body of text, you have to
             | indicate: oh, please snip out this part as a .h file and
             | then the rest as a .c file. In e-mail you can have it as
             | two separate MIME attachments. You can use an archive file
             | --- have you looked into the formats? Not so simple.
             | 
             | It's not hard to understand the attraction to the one file
             | deployment, even if you don't do it yourself.
        
               | mrweasel wrote:
               | Can you do something like what SQLite does an squash the
               | .h and .c into one file, but also have multiple files for
               | those who are doing development or just like separate
               | files?
        
               | kazinator wrote:
               | Yes, I can, and so can you, without a doubt.
               | 
               | If I wanted to distribute a single file library, but have
               | the two file option for users, I'd make it so that a
               | specific Awk one-liner produces the two. For instance,
               | the file might look like this:                  #ifndef
               | FOOBAR_LIB_H_D3B94F3C        #define
               | FOOBAR_LIB_H_D3B94F3C             // header stuff here
               | #endif // FOOBAR_LIB_H_D3B94F3             #if
               | FOOBAR_LIB_IMPL             // impl here
               | #endif
               | 
               | Then to people who want two, I would say, just run this
               | command in your shell and paste the content into it:
               | awk 'BEGIN { print "#include \"foobar.h\"" > "foobar.c"
               | print "#define FOOBAR_LIB_IMPL" > "foobar.c" }
               | /#ifndef FOOBAR_LIB/,/#endif.*FOOBAR_LIB/ { print >
               | "foobar.h"; next }             { print > "foobar.c" }'
               | 
               | that way I wouldn't need a build step on my end to
               | generate parallel files.
               | 
               | I would have the extra build step if it were a large
               | project of multiple .c files that I wanted to deploy as a
               | single file for the users who want that. (Not only would
               | I build the single file out of multiple files, but also
               | have some test cases which actually use it. Things have
               | ways of breaking when you combine files, like giving the
               | same name two two static variables or functions in
               | different files.)
        
         | KMnO4 wrote:
         | Just wait till you find out that you can do:
         | 
         | const char *s =
         | 
         | #include "test.txt"
         | 
         | ;
        
           | unwind wrote:
           | Sure, but only if the text file looks like a C string
           | literal, i.e. starts and ends with double quotes (which would
           | make it into a weird text file).
           | 
           | Doing                   const char *s = "         #inclued
           | "test.txt"         ";
           | 
           | won't work, since the preprocessor won't interpret directives
           | inside string literals of course.
           | 
           | In many assemblers, there is a directive called "incbin"
           | which pastes in unstructured binary data at the point of
           | usage. I just found a very clever C and C++ wrapper [1] for
           | that, which gives you an INCBIN() macro. Nice!
           | 
           | [1]: https://github.com/graphitemaster/incbin
        
             | a_e_k wrote:
             | An incbin-like facility is coming to the standard:
             | https://thephd.dev/finally-embed-in-c23
        
               | unwind wrote:
               | Yeah, I actually had heard about that but it didn't show
               | up in my first search. Thanks!
        
             | iforgotpassword wrote:
             | Yeah, ignoring incbin hackery, a better example than
             | strings might be                 float vertices[] = {
             | #include "stuff.txt"       };
        
             | cygx wrote:
             | Note that C23 will include a variant of _incbin_ spelled
             | _#embed_ : Semantically, the preprocessor will insert a
             | list of integers which you can use to initialize an array.
        
               | jsmith45 wrote:
               | Also, for clarity, it is fully expected that the compiler
               | will use the as-if rule to optimize it. Most likely by
               | having a dedicated token/ast node that only decomposes to
               | comma separated values if if actually needs to, with
               | common usage in initalizers being handled by simply
               | copying the data directly into a static data segment
               | without ever creating an integer list at all.
        
         | LastTrain wrote:
         | Then you made an incorrect assumption without enough
         | investigation.
        
         | mcmuffin6o wrote:
         | Sorry, why does it make the project feel like a toy? I'm not
         | trying to argue, I just don't understand. Is it because you're
         | including a source file instead of a header file?
        
           | teraflop wrote:
           | The function _declarations_ and _definitions_ are all
           | commingled in a single file. So if you want to use this
           | library in a project that contains multiple source files, you
           | have to include a duplicate copy of the implementation
           | (including e.g. the static font data) in every single
           | compilation unit.
           | 
           | It's possible that your linker would be smart enough to
           | identify and remove the duplicates, but it's still inelegant,
           | unidiomatic and needlessly inefficient.
        
             | jandrese wrote:
             | gcc or clang would crash on the linking step complaining
             | about duplicate symbols. This library is only good for a
             | project that is built in a single .c file.
        
               | esrauch wrote:
               | That's not true in this case: if you look at the .c file
               | you can see it actually has no definitions by default and
               | is a relatively normal .h file when it's included.
               | 
               | You can #include it in any number of files as long as
               | exactly one has #define OLIVEC_IMPLEMENTATION.
        
               | anthk wrote:
               | False.
        
               | dundarious wrote:
               | There are many single-file C "libraries" that work
               | perfectly fine as both "header" and "implementation", and
               | that do not require unity builds (building everything as
               | a single translation unit, e.g., a single .c file). Here
               | is but one famous collection of them:
               | https://github.com/nothings/stb
        
             | bangonkeyboard wrote:
             | Incorrect. The implementation is #define'd out of every
             | compilation unit save one.
        
               | teraflop wrote:
               | Ah, I missed that, but I don't think your comment is
               | precisely correct either.
               | 
               | All of the function definitions are declared with
               | OLIVECDEF, which by default is #defined as `static
               | inline`. So if you want to only get a single copy of the
               | implementation, you would have to choose one compilation
               | unit that defines OLIVEC_IMPLEMENTATION, _and_ you would
               | have to define OLIVECDEF as something else (like the
               | empty string) that causes the functions to be non-static.
               | 
               | Still kind of hacky, but not as bad as I thought.
               | 
               | EDIT: I just noticed that only _some_ of the functions
               | are marked with OLIVECDEF, so you _have_ to do this trick
               | if you want to reference the library in multiple
               | compilation units, or else you would get duplicate
               | symbols. The default behavior doesn 't seem like it would
               | ever be useful.
        
               | hactually wrote:
               | All good - I struggled when I first started with C.
               | 
               | The macro preprocessor is like a Schrodinger's Cat of
               | helpful and painful!
        
           | collegeburner wrote:
           | single-file libraries almost always feel like toys because
           | most serious projects i'd want them integrated into a real
           | build system. vcpkg is a good one these days for c/cpp.
           | nothing wrong with a toy library ofc.
        
             | TillE wrote:
             | Unless it contains C++ templates, it's fairly trivial to
             | compile a "header-only" library and use it as normal, even
             | in unusual cases where the author has made no effort to
             | support that.
        
             | ctrlmeta wrote:
             | Just saying that these "feel like toys" is a very low-
             | effort comment. Header-only libraries exist even if
             | uncommon and they work fine when written properly.
             | 
             | If you actually use a header-only library and it fails to
             | link properly or be used in a project with multiple
             | compilation units, then that would be a bug in the header-
             | only library worth discussing and fixing. That would be a
             | good-effort and interesting comment.
        
             | butterisgood wrote:
             | sqlite has amalgamated source too. It's used EVERYWHERE.
             | This opinion doesn't seem like it's based on anything valid
             | or real.
             | 
             | Note you get some opportunities for better compiler
             | optimizations when the entire compilation unit is the
             | entire project. In fact, sqlite claims the code runs 5-10%
             | faster when built as an amalgamation
             | (https://www.sqlite.org/amalgamation.html)
        
       | m00dy wrote:
       | I have been wondering what he is going to do under the recently
       | declared partial mobilisation. I hope he could find a way to get
       | out the country. He needs to know he is always welcomed in
       | Turkiye.
        
       | synergy20 wrote:
       | this is very impressive, how is this done? the executable is only
       | a few hundred KB and it runs for me, wasm is doing the magic here
       | but I don't know too much about how the process works.
       | 
       | I added -I/usr/include/SDL2 to build.sh, made sure wasm-ld is in
       | place, and it builds and runs smoothly.
        
         | Jasper_ wrote:
         | It's a simple software rasterizer. If you're interested in
         | writing your own, I've written a tutorial explaining the
         | basics. https://magcius.github.io/xplain/article/rast1.html
        
           | dazhbog wrote:
           | Nice website. Looking forward to the next rasterization
           | article!
        
           | sowbug wrote:
           | Also look at the source for original Quake
           | (https://github.com/id-Software/Quake), one of the last pure
           | software-rasterizing AAA 3D PC games. Michael Abrash's
           | Graphics Programming Black Book
           | (https://github.com/jagregory/abrash-black-book) explains
           | many of the critical parts of the rendering pipeline.
           | 
           | By the way, quake.exe for DOS was 404,480 bytes.
        
       | andai wrote:
       | Does this use WebGL2? It doesn't run on my phone (which does
       | support WebGL 1).
       | 
       | Edit: According to the JS source file it appears to be a plain 2D
       | canvas. Now I am even more confused, those should definitely
       | load!
       | 
       | https://github.com/tsoding/olive.c/blob/master/js/vc.js
        
         | jeroenhd wrote:
         | Depending on your privacy settings, your anti fingerprinting
         | configuration may block canvas operations. I know some
         | stringent Firefox fingerprinting protection used to do this,
         | not sure if that has been refined since.
        
         | MobiusHorizons wrote:
         | If you read the linked article, it is a c cpu rendering library
         | compiled to web assembly.
        
         | unsafecast wrote:
         | Does your browser version support webassembly?
        
       | ofrzeta wrote:
       | From the build.sh I figured out that all it takes is to compile
       | with the wasm target, I think:
       | 
       | clang $COMMON_CFLAGS -Os -fno-builtin --target=wasm32 ...
        
       ___________________________________________________________________
       (page generated 2022-09-22 23:02 UTC)