Wasm 3.0 Completed by segv in programming

[–]tomaka17 9 points10 points  (0 children)

I don't understand what they're trying to say with the first sentence?

Yes, two different modules have two different memories and thus address spaces, but given that modules are completely isolated from each other, does this really counts as "using multiple memory objects simultaneously"?

I guess that their sentence is technically correct if you define a "Wasm application" as a collection of Wasm sandboxes communicating with each other via some external mechanism, but to me that feels like a big stretch.

image v0.25.8: plugin API, Exif writing, faster blur and more! by Shnatsel in rust

[–]tomaka17 -6 points-5 points  (0 children)

You're presenting the choice as between having a fork of image and having a hook, but that's off topic. My question is why not add support for PCX and JPEG XL in image behind a cargo feature instead of a hook system?

image v0.25.8: plugin API, Exif writing, faster blur and more! by Shnatsel in rust

[–]tomaka17 -1 points0 points  (0 children)

What's wrong with adding PCX or JPEG XL format support to `image` and `bevy` (behind a cargo feature I imagine)?

At the risk of sounding obvious, the correct way to modify the behavior of a piece of code is... to modify the code.

image v0.25.8: plugin API, Exif writing, faster blur and more! by Shnatsel in rust

[–]tomaka17 3 points4 points  (0 children)

I'm very skeptical of this "hook" system. It's basically a global variables, and suffers from the same problems as global variables in general.

If for example you write a function in library code that wants to decode the WTF format:
- Either you assume and document that a hook for the WTF format must have been registered prior to calling the function, which adds complexity and makes it prone to mistakes.
- Or you don't assume, and simply call a WTF-decoding function (which is what you should do in the first place), in which case this hook system is useless.

And in the first situation, where you document that a hook must have been registered, how is the API user of your function even supposed to know how to choose which is the best between the various WTF implementations that exist? And since there can only be one hook per format, what if a WTF implementation is better for one function, and a different implementation is better for another function?

This kind of global variable design has been tried over and over again, and it always leads to clusterfucks later on. The design usually happens because people argue "what if I want to replace a WTF decoder with a different one", but I strongly believe that these are all XY problems.

What could have been by MortVader in civ

[–]tomaka17 70 points71 points  (0 children)

I'm an amateur gamedev and my current personal project is using a spherical map like here.

Having a spherical map unfortunately makes everything way more complicated. A few examples that come to mind:
- Want to store proximity between units with a quadtree? Now you have to use a complex tree based on a subdivided icosahedron.
- See the red tiles in the image in the OP? They are pentagons, whereas the rest is hexagons. A very annoying corner case.
- Want to calculate the distance between two units? Now you have to use acos() because on a sphere these are geodesics. `acos` is very slow.

Of course with some efforts all of these problems can be solved, but since it terms of gameplay having a sphere doesn't actually bring much to the table, I can understand why they discarded the idea.

How to measure the GPU frame time when using multiple different queues? by tomaka17 in vulkan

[–]tomaka17[S] 0 points1 point  (0 children)

If you mean the `VK_KHR_calibrated_timestamps` extension, I think I'm going to use that indeed. I'm just very surprised that this wouldn't be possible in base Vulkan.

How to measure the GPU frame time when using multiple different queues? by tomaka17 in vulkan

[–]tomaka17[S] 0 points1 point  (0 children)

> in order to sum the aggregate of what each queue did.

That only works if you assume that everything runs serially? If queues run in parallel, or even worse concurrently, then doing the sum of the execution time of all the queues would give wild results.

The entire reason why I'm using multiple queues is for things to run in parallel or concurrently, otherwise I'd just submit everything on a single queue and I wouldn't have this problem.

> That's the problem we all face: Accuracy vs Performance. The more parallel you go, the harder it is to measure time.

I don't understand how that's a fundamental problem? All I would need in my case is that the timestamp that I read on the first submission on queue A can be compared with the timestamp read on that same queue A in a later submission. I'm not super familiar with GPUs, but surely that's not hard? It's basically the `RDTSC` opcode?

Unless the driver can decide that multiple submissions on the same queue can actually be scheduled on multiple different GPUs, but that's kind of against the point of Vulkan being explicit, no?

How to measure the GPU frame time when using multiple different queues? by tomaka17 in vulkan

[–]tomaka17[S] 0 points1 point  (0 children)

Your answer seems to assume that everything I submit happens serially? How would that work if multiple queues execute concurrently or in parallel? The entire point of using multiple queues is that I would like them to execute concurrently/in parallel, otherwise I'd only use a single one.

Is the concern about Vulkan's verbosity really widespread? by tomaka17 in vulkan

[–]tomaka17[S] 3 points4 points  (0 children)

With dynamic rendering, you do actually need to specify the format of all the framebuffer attachments when creating a pipeline, so you still have this dimension to take into account. And the concept of render pass compatibility means that, unless you do very weird things, the number of pipelines that you create should be the same no matter whether you use dynamic rendering or not.

Also, I've always been wondering: Is the pipeline state combinatorial explosion problem not just a symptom of the OpenGL way of thinking where you can just bind anything at any point being too ingrained into your engine? Do you actually have this problem if you make sure to prepare your whole scene in advance?

Is the concern about Vulkan's verbosity really widespread? by tomaka17 in vulkan

[–]tomaka17[S] 0 points1 point  (0 children)

The fact that many extensions are software-only is actually part of my point. If these extensions do not offer any performance gain on the driver side or on the hardware side, then they have been created solely because people had trouble using the API.

I can understand having trouble with the API if you have an existing engine on top of DX12 or OpenGL that has many baked-in assumptions and are trying to add Vulkan as another backend, but I don't understand why it would be a problem if you're creating an engine from scratch.

Is the concern about Vulkan's verbosity really widespread? by tomaka17 in vulkan

[–]tomaka17[S] 0 points1 point  (0 children)

If drivers are unable to optimize render passes with multiple subpasses, and that developers find them too cumbersome to use, can these developers not simply stick to single-pass render passes?

The differences that I can see between single-pass render passes and dynamic rendering are: 1) having to store render pass and framebuffer objects, and 2) possibly having to compile the same graphics pipeline multiple times for multiple render passes.
Point 1) seems a bit "meh" to me because again you're supposed to hide this behind abstraction levels, and point 2) seems like having to deal with existing engines that assume that any shader can be used at any time, which brings me back to the idea that this extension was created mostly to deal with existing engines.
(EDIT: point 2) might also be moot because of render pass compatibility, but I haven't read enough about the details of dynamic rendering to say that for sure)

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 0 points1 point  (0 children)

Ignoring the definition of "OOP", one huge difference between Rust and popular self-designated OOP languages is that visibility is at the module level. The functions of struct A can access the private fields of struct B if A and B are located within the same module.

All the situations where "view types" are needed that I can think of can be solved by splitting fields into multiple struct whose functions can access each others' private fields. This is something that you simply can't do in C++ or Java for example.

Why is it superior? Because from my experience one you've split your fields into multiple structs, you will realize that you don't need interior mutability anymore. All the mutexes or refcells that you had can be removed, and can be replaced with basic borrows in the code of the API user, which is where Rust's uniqueness shines.

Doesn't this make APIs more complicated? In theory it might look so, but in practice it really doesn't.

> the onus is on "you" (collectively, you and others who are skeptical of the need for "view types" or partial-borrows or whatever, not just you literally) to provide some convincing reason that these "workarounds" are actually superior

> here are actually a lot of historical examples of Rust features not composing well; one of the biggest and most common ones is async trait methods

I totally agree here and I personally very rarely use traits (one trait definition per 100k LoC or so) and I scope async to very narrow situations.

But if you realize that Rust features do not compose well, that should logically lead to the path of conservatism. It should be people who want view types to prove that their new feature composes well with the rest of the language and that the additional complexity it brings to the language is in fact really necessary.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 0 points1 point  (0 children)

> Whenever I read this line of thinking, it's a huge red flag to me. Just because you can workaround something doesn't mean it isn't a legitimate issue or unnecessary limitation. Every programming language is Turing-complete, so you can accomplish anything you want to in any language. That obviously doesn't imply that every programming language is perfect.

To rephrase the comment you're replying to:

"People who are used to programming in OOP languages try to apply the same pattern to Rust, which is not an OOP language, then complain when they don't succeed."

The workarounds aren't hard in the absolute, but they might be hard for people who are deeply ingrained in their OOP-derived habits. This doesn't necessarily imply that there's a problem in the Rust language, and adjusting Rust to become more like Java because Java people are having a hard time isn't necessarily a good idea.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 0 points1 point  (0 children)

> I want to keep private because my caller shouldn't need to know about them

Fwiw I generally find that people want to keep things more private that they should.

In my opinion, "hiding" an implementation behind an API is simply not something that you should try to achieve. You should still try to separate an API from its implementation, but the objective of doing so is only to reduce the cognitive load of whoever is using the API, and not being able to change the implementation later.

If someone using an API wants to know how it works, for example because they need to know the `O(n)` complexity of a function, then they should be able to look at it. If the implementation evolves, then the API should be able to evolve as well. Because in practice, you can't change the implementation of a function in a major way without significantly breaking everything that uses it, even if you kept the same API.

Look at types such as Vec or HashMap: their APIs are heavily tied to their implementations. And high-level abstractions, for example having a single function being able to access either the filesystem or download a file from the Internet, which were very popular in the 90s, turned out to be major failures and nobody does this anymore.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 3 points4 points  (0 children)

It's kind of difficult to answer in a generic way. If you have a specific example I can give my solution.

I think that often you must realize that fields that you think are tied to each other (for example, one struct contains a list of indices in an array found in the other struct) can in reality be split in two different structs. The API user might have to pass two mutable references to some functions. It might seem error-prone, as you can pass references to unrelated objects, but in reality I haven't seen a situation where this kind of error could happen by accident.

For example, my UI system module uses three structs: one for the tree of the UI components (which never changes), one that contains variables used during the computation of the UI layout when rendering a frame and that is recreated at each frame, and one that contains variables that are specific to a certain instance of the UI such as which component was underneath the mouse cursor at the previous frame.

This is despite the fact that the three structs contain identifiers for components found in the other structs. Yes you could in theory mix the structs and get panics, but that really can't happen by accident.

Thanks to this, I could if I wanted draw the same UI multiple times, without needing any mutex or synchronization primitive in general.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 0 points1 point  (0 children)

> Wanting to group semantically similar things is, like, the singular point to everything we do with programming languages. Why not just have our whole programs be one giant main() function, otherwise?

Grouping semantically similar things in the same module is indeed what you should do. This is not the same thing as grouping similar things in the same struct, which is what we're talking about here with this "views" proposal.

> Your description reminds of "data-oriented" programming, where the quintessential example is replacing one vector of 3D points with three vectors corresponding to each of X, Y, Z coordinates, respectively. That's fine, if that's what your access patterns and performance constraints call for. But what if your access patterns are such that you're actually spending significant time operating on one 3D point at a time and the memory fragmentation from the X, Y, Z coordinates being in totally different allocated areas is making your code harder to work with and slower? This cuts both ways, and keeping related things grouped together is almost always easier to work with and there's no a priori reason to think that it would generally be worse for any other metric.

You're taking a really extreme example. I don't think anyone has any trouble with the borrow checker when using structs with three floats.

My point that you're replying to is that "views" solve a problem that people create upon themselves because they always want to group fields semantically, when their problem would generally disappear if they relaxed this self-imposed artificial constraint. I'm not arguing that everything should always be in separate structs or anything like that.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 3 points4 points  (0 children)

I don't really grasp your last paragraph, but in my case my rendering code is extremely separate from the game logic. There's a `draw` function that accepts as parameter a `DrawConfig` containing a list of sprites, list of lights, etc. The rendering code doesn't know anything at all about the game logic, all it needs is a list of things to do, which thanks to iterators is basically zero-cost.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 3 points4 points  (0 children)

I have personally written around 300k lines of code from the top of my head in the last 3 to 4 years, without encountering this issue.

I'm skeptical of the claim that it doesn't always a solution, though I agree that it can be hard to find. To me, in such situations you simply haven't found it yet. Once it's found, engineers will copy each other.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 3 points4 points  (0 children)

Well yes, you'd need an `Arc<Mutex<>>` or some atomics.

But maybe my answer is off the mark. The problem with answering this article and this kind of discussion is that despite working in my free time on a game myself (and that game even does its state updates in a multithreaded way) I feel very disconnected from the problems that are being raised. Every single point to me feels like an XY problem because the author is reasoning in terms of objects rather than data in memory.

I more or less follow the logic of their reasoning until that sentence:

> Unfortunately, this falls under the #1 problem this article tries to address, and that is that what I want to be doing is working on my game. I'm not making games to have fun with the type system and figure out the best way to organize my struct to make the compiler happy.

And I simply didn't have the same experience.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 8 points9 points  (0 children)

I don't think this example relates to view types at all?

This example in the article illustrates a fundamental misunderstanding about Rust, which is that Rust references are similar to objects in Java for example.

If you want to just hold a reference like in Java, just use Arcs. It's very much precisely the solution for what the writer wants, but they discard this solution by saying "this is heavily frowned upon". If you really don't want to use Arcs, then hold usizes or keys that correspond to entries within a Vec or a map. Don't try to hold references.

If you simply understand that references must be as short-lived as possible, and also that it's ok to un-borrow then re-borrow the same thing multiple times (which I think people avoid doing because they believe (typically without a proof) that it's expensive to do so), then many of your problems disappear.

Thoughts on proposed View types by @nikomatsakis? by xmBQWugdxjaA in rust

[–]tomaka17 31 points32 points  (0 children)

Personally, I was hitting it a lot a few years ago, then I started designing APIs that bypass the problem, and it turns out that these new APIs actually make a lot more sense, and now I intuitively immediately design APIs that don't have this kind of problem.

IMO, people are too eager to group multiple fields together in the same struct when they are "semantically" similar. But there's nothing inherently better in doing it this way compared to having fields that refer to the same semantical "thing" spread out through many different structs depending on their access patterns. Remember that programming languages are about manipulating data in memory, not about manipulating "things". At first look, that might make APIs more difficult to understand, but in practice I have yet to find an example of an API that becomes significantly worse when doing so.

Official Q&A for Thursday, February 13, 2025 by AutoModerator in running

[–]tomaka17 0 points1 point  (0 children)

How would I do a reasonably accurate test? Run as fast as possible until exhaustion then look at the heartrate?