It's a damn shame that he's unable to get paid from Twitch and Youtube due to the war.
https://github.com/tsoding/olive.c/blob/43a67dd76760f915516a...
From the readme.
// 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.I'd personally be interested in a graphics API that do not try to render the whole buffer every frame, but update it given a list of changes (e.g. sprite moving/rotating). I believe that such approach could work very well for a 2D software renderer given the likeliness of spatial redundancy, and possibly for video encoding without going through a clueless encoder querying pixels (and having the ability to exploit hardware efficiency to decode/render the stream).
Both cpu renderers with texture mapping and Wavefront obj import without further dependencies.
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!
clang $COMMON_CFLAGS -Os -fno-builtin --target=wasm32 ...
I added -I/usr/include/SDL2 to build.sh, made sure wasm-ld is in place, and it builds and runs smoothly.
By the way, quake.exe for DOS was 404,480 bytes.
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.
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.
const char *s =
#include "test.txt"
;
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!
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.
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)
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.
Actually a few days ago someone sent me a pull request adding an interesting project to my README
https://github.com/rswinkle/PortableGL/commit/e0652b4dff266d...
So now if I were to try to sum up all the OpenGL software implementations I can think of,
TinyGL (and modern improved forks) = OpenGL 1.1-1.3 ish
osmesa = OpenGL 2.1 using Mesa 7.0.4's swrast
PortableGL = OpenGL 3.x-ish
Mesa = 2 software renderers still included, gallium based softpipe and llvmpipe and I think one or both support the latest OpenGL 4.6 but I could be wrong. swrast and Intel's gallium/llvm based OpenSWR have both been removed from mainline Mesa, and the latter only supports 3.3 core-ish (https://www.openswr.org/)
I'm sure there are others out there. I've actually never tried to use "Stand alone" Mesa. I really should to see how it performs if nothing else, but I still say nothing beats the single header library for ease of use.
Though most games don't bother so we don't really have actual evidence.
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.
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.
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.
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.)
float vertices[] = {
#include "stuff.txt"
};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.
The macro preprocessor is like a Schrodinger's Cat of helpful and painful!
You can #include it in any number of files as long as exactly one has #define OLIVEC_IMPLEMENTATION.