13
votes
What programming/technical projects have you been working on?
This is a recurring post to discuss programming or other technical projects that we've been working on. Tell us about one of your recent projects, either at work or personal projects. What's interesting about it? Are you having trouble with anything?
I worked out a method to align toolheads on 3D printers where each toolhead has a nozzle probe. It can be found here. It seems that with a 20mm by 20mm plate that has around a 3mm bend in it (such that it resembles an upside down bowl), I can get an alignment with 3 microns of standard deviation using common nozzle probes (like the Voron Tap) over a 10 by 10 grid of samples.
As an exercise, I built out an empty app, as advised by Ship Software That Does Nothing. As described in the article, there's quite a lot involved in a deployed full-stack web app. Without any presentable visuals, and nearly no functionality, I've managed to put in: database (PostgreSQL), stack framework (Rails), frontend framework (React), authentication (rodauth), FE build process (Vite), Typescript (config, build scripting), production build scripts, testing frameworks (rspec and Jest), FE data store (Tanstack), JS linting (eslint), JSON API endpoint (BE) and consumer (FE), test data management (FactoryBot, DatabaseCleaner), FE UI library (Material UI), and CI/CD (git hooks).
This is probably one of the cleanest, smoothest app deployments I've had the pleasure of working with. Granted, the app has no usable functionality yet, but I feel confident that I'll have a comfortable, low-friction experience as I fill out the features. (It's not going to be just a completely useless app. If I devote enough time to it, it could turn out to be a home-grown language learning tool.)
I've been polishing up my just-in-time compiler project.
Next I plan to improve the safety and error reporting when running JIT code. The hope is I can add quick bounds checks within the compiled code without much of a performance loss. Then I can early-return all the way back up to the Rust code if there's an error and handle it cleanly there. A "Memory pointer has overflowed the allotted area" message is a lot nicer than a SIGSEGV/SIGBUS error. Hopefully I can even show relevant source code! Additionally, I'm working on adding guard pages around the R/W memory area. The memory pointer offsets are 16bit, so I only need 32KiB on either side to guarantee the JIT code can't affect other memory in the process.
At work, one of the services I'm responsible disappeared when the server when it was hosted on experienced a drive failure. We lost all the VMs on it. Most were dev or test environments, so NBD, but there were a couple prod VMs. Mine was one of them.
Luckily, I had asked the person who initially set up this service to enable nightly backups and to make sure they get replicated to another device. That's been going on for months.
Unfortunately, that person is out on vacation and I wasn't going to bother them. It's an important service, but is it absolutely mission critical? I don't think so.
So I went about trying to restore it on my own. It's hosted on a Linux server, which I don't have a whole lot of experience with. But I've been wanting to gain more experience with command line Linux, and this was a good opportunity to get my feet wet and hands dirty.
The person who initially set everything up left some good documentation, which I'm always grateful for, but there were some things missing. Probably because they assumed the reader had decent Linux experience. Which I didn't. So I had to go online and figure some things out. The vendor also has good documentation and guides, but even though I was able to get the platform back up, the restore didn't go smoothly. Some of our data and functionality was still missing.
So I got on a call with the vendor and we worked through the problems. All the while, I was able to smoothly navigate my way around the command line, edit configs as needed, etc, while these techs were watching via shared screen. And in the end, we got everything working again after twice blowing out the whole platform and trying again. I'd say it was even a bit of a learning experience for the vendor techs!
The last bit today was getting the backups restarted. Which I think I got properly set up; I'll see the results on Monday. Again, thank you to my coworker for their great documentation, even though I still had to look some stuff up. But I'm understanding what some of these commands do and how they work. I'm no Linux pro, and it's going to be awhile til that day comes along, but I'm definitely feeling more confident overall.
I started looking at my voxel project again after a bit of a break (it is largely an SDL + Vulkan app that renders voxels in service of an undefined goal). I didn't write it with portability in mind, but a couple days ago I ported it from my Arch desktop with an AMD GPU to a Windows desktop with an Nvidia GPU for reasons. It was not a very smooth process.
The first thing I encountered was just some real compilation errors that GCC was covering up. This was just basic silly things like missing some header includes. All of the ones reported by MSVC were correct to report and probably should have been reported by GCC. This wasn't much of an issue.
The next thing was that my graphics pipelines just couldn't be created. No real logs. No validation errors. Just failing to create a valid pipeline and emitting an error code that translates to just "unknown error". This was not cool. After way too long debugging I found that the issue was that one step, the mesh shader, is for some reason forbidden from having a pointer in it's input data payload. Perfectly legal to pack it into a uint64 and then unpack it into a pointer inside the mesh shader though. Very strange and only happens on Nvidia hardware, at least as far as I could tell. I'd be interested to know more, like how it behaves on Linux Nvidia or even just other Nvidia GPUs on Windows, but I don't have any such machines handy so for now it's a mystery. Maybe I should report this to Nvidia, but that sounds like effort.
Now that my pipeline is succeeding creation, time to run it. And a bunch of the triangles are just gone, so that's cool. This one I figured out reasonably quickly, but the error still feels nonsensical. I knew that my 64-thread wide task shader was at AMD's preferred size and that Nvidia preferred 32, but usually you can get away with just using 64 anyway as Nvidia just spawns twice as many work groups to compensate. This time though, nope. Switching it to a 32 thread version of the entry point, which I already had in anticipation of doing this as an optimization, made it just work.
At this point I technically had it all working, but I figured I should fix the fact that I was running a 32 thread task shader into a 64 thread mesh shader. It worked, but since I was now already to the stage that I was dynamically selecting the right compute size based on what the physical device reported as preferences in one case it seemed like a no brainer to do it for the other. This was a mistake. Like before I just changed the core logic into a function that I could call from multiple entry points and then made 32 and 64 thread entry points that would produce the same result. After ages of debugging I learned that every entry point, except the last in the file, would just be broken. I didn't have this realization until I'd stared at it so long that I just thought "I'm just going to paste the working one in a second time and rename it" only to find out that was then broken too! It turns out that only the last entry point in the file works and any earlier ones have broken results.
I'm now deep down a whole new rabbit hole: can I do this at all without making extra entry shader files? I assumed it would work if I had more shaders emitted, but I liked the simplicity of loading one spirv from disk and just picking the entry point dynamically. This is where I started to wonder if I could do it all with one entry point somehow, but the problem was that the number of threads in a pool has to be a constant. I got thinking though "specialization constants are things, can I use those?" AI told me it was impossible, but because it doesn't know shit I decided to ignore that answer and push forward anyway. I'm happy to report that yes, you can in fact use specialization constants to size your thread groups. This is actually kind of huge. It means that, assuming your shader logic accounts for it, you can use one shader that gets dynamically optimized based on runtime hardware querying and it is optimized during shader loading (when the driver is building the internal representation that gets used) so there should be no overhead to doing this beyond the driver-level optimizer having to do a tiny bit more work at startup. I don't even think I could accurately say it has a cost though. The driver-level optimizer is already going to be doing things like constant folding, constant branch elimination, dead code elimination, and so on. Due to how much complex work it is doing in some cases the supplied constants may make things like constant branches easier to find.
I'm working on an assignment of my master degree. It involves tons of financial modeling (Python and NumPy), and tiny bits of writing (explain if the generated numbers are plausible). It's both fun and frustrating, to be honest. My mood swung between "wow! such mathematics! so much clever programming" to "damn! why don't the numbers make sense here!? what went wrong!?" (in more details, recently, I'm trying to debug why is my generated American call price lower than the equivalent European call price). It doesn't help that it's a group project, and I tried to plan the work for my teammates, but they didn't seem to be able to match what I expected, and I had to do the work myself.
My JavaScript (Client) and Python (Server) based Virtual Tabletop for D&D, Call of Cthulhu and others is progressing very nicely. The server is running without issues, the update of positions on the canvas works nice and fast (tried it with friends from different continents), Fog of War collisions are handled on the server instead of the clients and I added some movement smoothing, so I wouldn't have to send updates the entire time and was able to space them out by a little bit to still look like natural movement of objects.
Added completely server-based position handling of objects attached to other tokens to give them a nice look of "following" the token they are attached to around instead of being glued to them. This is going to look pretty cool later on when parties have NPCs join their group.
The next steps would be to start working on the drawing tools like a basic line tool, circles with radius display, rectangles and triangles, potentially a free-draw tool but that's gonna come last. During that, I think the time has come to work on the actual UI above the HTML5 canvas to switch tools, see online players, edit / add tokens, etc.
Sent off the prototype case design to my friend to 3D print the enclosure for my audio player project. I had to learn some 3D modelling software to do so, but it was relatively simple for the simple enclosure I had to make. The enclosure is the last step for it to be easy to use, as right now I have just been moving it around on an end table, since it is still connected via my breadboard.