The name comes from the fact that I was going to play with writing a toy browser. It has since gone in several very different directions, all of which are tending to lower and lower levels.
modules/DataStructures is a persistent data structure library (mostly a partial copy of Clojure’s data structures). The initial implementation worked well but performed abysmally, recent rewrites have improved things, but performance is acceptable at best. This subproject alternates between developing the language I want to program a game in and low level fiddling to make it fast enough to use in a game engine.
modules/HLVK (High Level Vulkan) is a toy game engine. It works, most of the time, and performs reasonably when working. The demos are all very simple, and there is an unbounded list of features that are useful, or would be informative to implement.
src/browser is the humblest beginnings of a working browser. It can fetch HTML and dump it to a terminal. That’s about it. I didn’t want to use GTK, and I’ve always wanted to learn Vulkan properly, so this hobby project took a hard turn.
Internal modules are in the “modules” directory and included in the main project
as dev dependencies. This isn’t exactly idiomatic, but I’m still experimenting
with ways modularise the code that doesn’t involve include
statements lying
all over the place. I’m alergic to include
statements.
Demos of the graphics portion are in the “src/graphics” folder. Each file has a
main
entry point.
Note, you must have libglfw and libvulkan in your $LD_LIBRARY_PATH for julia to find them. These are generally installed via your OS package manager as GLFW and Vulkan respectively. I may one day bundle them, but not now.
To run one of the demos, run a julia repl and enter the following:
# First time project setup
import Pkg
Pkg.activate("./") # Assumes you're running the repl from project root.
Pkg.instantiate()
# Optional, takes a while but speeds up subsequent runs
Pkg.precompile()
# Run a demo
include("src/graphics/quad.jl")
Running a demo will create a window. Closing that window will break out of the render loop and return control to the repl. You can thus use the same repl to try different demos.
This initial JIT compile time is substantial, so do try and reuse the repl whenever possible. I’ve briefly looked into profiling the compile time, but can’t find any useful information. That’ll be an adventure when I get around to it.
Warning: the cleanup code currently contains a race condition which sometimes frees objects in the wrong order and causes a segfault. I apologise if that ruins your morning. I’m looking into it.
Vulkan doesn’t rememeber anything for you, which makes life unduly difficult.
Segfault when shrinking windows. Resizing is just disabled at present.
Specifically the swapchain destructor segfaults.
Almost never the first time, but always within 5 or 6 tries. Handles aren’t being freed in the correct order most likely.
currently images are generally sharing mode exclusive and there’s transition logic to load them then hand them off to graphics. It’s not bad.
So why are all buffers relegated to concurrent access? The speed degradation is not an issue yet, but it’s not a hard thing to fix.
Note: use VkBufferMemoryBarrier(2)
Having a fixed struct sort of violates my basic principle of accumulating state to create a uniform API.
But I’m starting to run into some issues with handling hashmaps in hot spots like the render loop.
Something like a clj record could be a nice interim solution, but really, if you want to work with C you need closed types, which clojure rejects pretty uniformly. You also need C compatible memory layouts.
So why not embrace C structs, but wrap them in logic so that they can be accessed and maybe even extended as if they were maps (of course they won’t still be structs then).
The dream is to be able to spot maps with stable structure and transform them into structs as part of jit compilation. This is probably only really useful in hotspots, but would be a really cool optimisation to pull off.
When interacting with the graphic long enough, eventually the return channel from the compute pipeline never receives a value and the render loop hangs.
No errors appear to happen on any of the threads, though I could be failing to catch something.
© 2024 Thomas Getgood