Looking at the code examples (e.g. the cube[0]), I can’t get away from the idea that this looks very similar to standard OpenGL bindings, just with a different style of boilerplate.
At some point (for basic rendering examples at least), you’re always going to have to define a data layout of some sort, have that reproduced or reflected between the cpu/gpu environments, and iterate through each vertex. How exactly that turns into GLSL’s in/out variables feels to me like a relatively unimportant implementation detail that should be assessed on performance alone rather than usability[†]. I wonder if what we’re ultimately heading toward is better shader compilation, directly to SPIR-V, to support both bound and unbound use cases.
Sort of related, but Gabriel Dechichi’s engine [1] reflects shader vertex layouts back to C, so you get compile-time error handling if the struct layouts don’t match between cpu/shader.
> unimportant implementation detail that should be assessed on performance alone rather than usability
I disagree. Usability is important, or you end up with garbage like Vulkan. I'd rather have 99% of the performance for 100% usability, than 100% of the performance for 5% of the usability.
Unfortunately when it comes to graphics APIs, the choices are between bad and worse, which is why I switched to doing real time rendering in CUDA. Easier to write a fast software rasterizer in cuda, than to draw a single triangle in Vulkan
Sorry, I wasn't clear (and have edited the post), I absolutely do believe that usability is important, and to be honest I feel like it's the main problem with graphics APIs (at least for my skill level).
Where I'm at now is that Vulkan is great because it's removed so many of the hard-coded assumptions that were previously hidden in the driver layer, allowing much greater CPU and GPU utilisation and performance when you need it (if you can get past the ugly APIs).
Where the usability stuff comes in is that it allows for the creation of alternative, higher-level shader-reflection setups that work brilliantly for simple use cases, all in user-space, which can open up shader programming for creative coders and a broader spectrum of developers. The lower-level APIs are then still accessible to those who absolutely need those performance levers.
Generally I agree, but the bindless approach can give you both ergonomics and performance in some cases (not all). Certainly it can reduce CPU overhead - passing a single pointer to a shader is much faster than heavy bind group updates. The trade off is that you might have more pointer indirection on the GPU, which can introduce stalls.
Being able to use pointers and u64s for resources also makes having a shared header between shader and c++ source easier, though you still probably need some macro trickery to smooth over language differences. Metal is really good for this since the shading language is closest to C++.
> but the bindless approach can give you both ergonomics and performance in some cases (not all). Certainly it can reduce CPU overhead - passing a single pointer to a shader is much faster than heavy bind group updates. The trade off is that you might have more pointer indirection on the GPU, which can introduce stalls.
Yeah I'm totally with you there, it just feels like the interfaces aren't that pleasant at the moment (although I've not tried Metal)
Edit: didn't realise you were the project author, I think it's great by the way!
It allows you to do some remote control and automation for kernel loading and debugging where you get a very thin layer in between the real hardware and the kernel, without affecting the hardware I/O behaviour.
The dot syntax used everywhere really confuses me. I get its use in struct fields, or for defining anonymous structs, but what is this one for? (Some kind of module-level enum space, where .sampler and .unknown are defined previously?)
Yeah, after looking it up, it looks like it is basically only used as either field access or an 'infer operator', is that right?
I thought it was used in four completely separate ways:
· normal struct field access
· anonymous struct definition
· field definition within structs (for reasons to do with the parser)
· an extra 'infer operator' for syntactic sugar
But there's no support for anonymous structs/fields, and all structs and fields require a type somewhere for it to be inferred. Which is why this is invalid zig:
const test = .{ .x = 0, .y = 1 };
(It would need the type to be specified in the called function definition, or inline when assigning)
I don't think that "infer operator" is a special case of field access, to me it feels like regular known-type elision similar to how C# and C++ use the var keyword if the data type can be inferred from the rhs expression:
const Enum = enum {one, two, five};
const t: Enum = .one; // Enum.one, but the type was inferred from lhs
std.debug.print("{t}\n", .{t});
Defining an anonymous struct is valid in zig; your example is only invalid because "test" is a reserved keyword. But you are correct that it reifies into a concrete type, and after initialization it doesn't coerce into other types because zig doesn't do structural typing:
const anonymous = .{ .x = 0, .y = 1 };
std.debug.print("{}\n", .{@TypeOf(anonymous)}); // will output something like test_0__struct_45138
const Point = struct { x: i32, y: i32 };
const p1 = Point{ .x = 0, .y = 1 }; // valid, explicit struct literal
const p2: Point = .{ .x = 0, .y = 1 }; // valid, anonymous struct will coerce to Point
//const pt: Point = anonymous; // error: expected type 'test_0.Point', found 'test_0__struct_45138'
And then there's fieldless anonymous structs aka tuples. I'm including them because they were used in the print statements above:
All this does for me is raise the question of why they chose to use the `.` for so many different uses. I'd be fine if it was just to infer the type, but it seems very overloaded.
This looks great. I've had a quick look through the docs and can't see anything about bikes under transport, or, under housing, anything relating to modes of tenure that aren't private rentals (e.g. Vienna-style social housing, housing co-operatives, or private ownership). Would be lovely to play around with those variables.
Also, for finance, is there a particular reason why the finance sector calculations look quite simple (on the face of it, at least)? They seem only to be a percentage lift based on aura, rather than anything resembling any number of complex sectoral dynamics, from full rentier-financial domination to prudent support of industry/services.
Regarding the unit economics for finance sector, you are right, the calculations are way too simple, and I'm not entirely happy with it, so I'm looking to improve it, but it does need to be a sweet spot between 1. realistic enough to be educatioal and 2. simple enough you can use it as a gameplay mechanic.
Before the end of the year there will be mod support, and I will pay special attention to making sure that modders can swap the economic formulas for more sophisticated models.
I’ve been doing something very similar recently[0] with slightly different goals (still a tiny bundle size, but fully typed uniforms, deeper control over buffer bytes and layout, and less setup than raw gl).
Some in the comments seem to think GL is dead, but for me I think it’s just an easier shader language for beginners and that’s most important for dabbling and many small web use cases.
> The entire point of civilization and society is that we are all "addicted" to technology and progress.
Technology is like much of material reality, in that we can think whatever the hell we like about its various forms, especially so if we’re surrounded by it.
It’s not insane. They are correct that is the point of civilization which carries information from generation to generation outside the oral tradition in a systematic organized reliable way.
The point of civilisation, however loose that idea may be, if it’s anything at all, is determined by people.
Technology exists today in a way that feels like it could be defining its own path in a sense, but much like oral tradition, neither are large enough concepts to describe civilisation.
Is there a general preference for serifs, or a local style in Finland for this reason?
German certainly has typographic preferences that err toward taller x-heights and narrower forms due to heavy use of portmanteaus. It’d be interesting to know of other language-specific typographic styles too.
> Every Noise at Once was a long-running attempt at an algorithmically-generated, readability-adjusted scatter-plot of the musical genre-space, based on data tracked and analyzed for 6,291 genre-shaped distinctions at Spotify through 2023-11-19. The calibration is fuzzy, but in general down is more organic, up is more mechanical and electric; left is denser and more atmospheric, right is spikier and bouncier.
> Click anything to hear an example of what it sounds like.
> Click the » on a genre to see a map of its artists.
> Be calmly aware that this may periodically expand, contract or combust.
I’d go further than this and say globally-scoped CSS is fine as long as you’re using a decent naming pattern (e.g. BEM), or @layers.
For me, back in the JQuery days, the problem was always globally-scoped JS/DOM, rather than CSS. The big revolution was simply co-locating/importing styles in JS modules during the compile step, which works surprisingly well even outside of any framework.
Just using vanilla DOM or a small wrapper around web components for connectedCallback/disconnectedCallback logic is refreshingly simple. It’s quite sad that most SSR frameworks don’t allow this sort of approach, and lock you in to a specific front-end library.
> I’d go further than this and say globally-scoped CSS is fine as long as you’re using a decent naming pattern (e.g. BEM), or @layers.
Hear hear. Started using BEM in like 2016, haven't had collision issues since. Not sure why people are so hellbent on doing CSS inside JavaScript when CSS by itself can do almost anything you'd want at this point, even handling basic interactions.
At some point (for basic rendering examples at least), you’re always going to have to define a data layout of some sort, have that reproduced or reflected between the cpu/gpu environments, and iterate through each vertex. How exactly that turns into GLSL’s in/out variables feels to me like a relatively unimportant implementation detail that should be assessed on performance alone rather than usability[†]. I wonder if what we’re ultimately heading toward is better shader compilation, directly to SPIR-V, to support both bound and unbound use cases.
Sort of related, but Gabriel Dechichi’s engine [1] reflects shader vertex layouts back to C, so you get compile-time error handling if the struct layouts don’t match between cpu/shader.
[0] https://github.com/rkevingibson/loon_gpu/blob/main/examples/...
[1] https://m.youtube.com/watch?v=NTuLfB2ex5Q&ra=m
[†] Edit: Specifically talking about the final compilation unit here, not the shader language or library interface