vulkano-rs / vulkano

Safe and rich Rust wrapper around the Vulkan API
Apache License 2.0
4.55k stars 437 forks source link

Proposal: replace vk-sys with ash #1500

Closed Rua closed 3 years ago

Rua commented 3 years ago

While searching for possible improvements to Vulkano design and maintainability, I came across the ash crate. This is a very lightweight wrapper around the raw Vulkan API, designed to make it more Rusty but in a very unopinionated way. Meanwhile, vk-sys is Vulkano's own version of the bindings, which is much more C-like, not always up-to-date, and attempts to auto-generate it have stalled (#89).

Therefore, I propose migrating Vulkano over to ash, and removing vk-sys entirely. It would make Vulkano's low-level code a little nicer, and also resolve the maintainance problem of vk-sys by building on top of the excellent work of others. We can also decide to re-export some of their types and functions, if that is convenient. In the case of #1496 that means we can directly use ash's bitflags types.

gurchetansingh commented 3 years ago

YES!!! The Rust community must leverage each other and work efficiently if we are ever to free C/C++ Vulkan developers from their mud huts.

I think that was the original plan too, but https://crates.io/crates/vk_generator lost traction. Makes sense to converge on Ash.

https://github.com/vulkano-rs/vulkano/issues/89

CC: @MaikKlein @kvark

gurchetansingh commented 3 years ago

In addition, I think we need to evaluate the long-term design of Vulkano too. Is it a validation/type safety layer? LunarG works all the time on that and tracks each VUID (valid usage ID) in spreadsheets:

https://github.com/KhronosGroup/Vulkan-ValidationLayers

Is it a nice utility wrapper for Rust developers who want to use Vulkan safely with strongly-typed objects? A design/roadmap from interested developers would be good to have. Maybe we can even bring out the legendary tomaka out of retirement to help with the long-term vision.

P.S: Vulkan and Rust adoption is still very low, so I still consider this an early stage project

Rua commented 3 years ago

Ultimately, Vulkano's goal is to be a safe wrapper. So it should be usable without any unsafe calls, it should refuse to use Vulkan incorrectly, no UB, no dangling references, etc. However, below this, Vulkano also has an "unsafe" layer. This doesn't do any validation, it just does whatever you tell it. Types in this category do not keep resources alive, except for Device and Instance for the most part. The utility of the unsafe layer is limited IMO, and in Vulkano it's primarily used to build safe wrappers on top of.

Validation incurs some runtime overhead, which is part of what Vulkan tries to eliminate, and why validation layers exist. So it could be argued that this part of Vulkano is less necessary, or perhaps should be optional. On the other hand, one of the strengths of Rust is the ability to make it impossible to use things incorrectly/unsafely at compile time thanks to its strong type system. So maybe more work could be done to enforce safe usage at the type level instead of at runtime. This kind of principle is known as typestate programming.

For example, instead of having a single AutoCommandBufferBuilder, there could be different types, with different subsets of methods, that are used based on what particular context your builder is in. As a concrete example, when you start the process of building a primary command buffer, you receive an object that lacks methods for draw operations. That way, it's impossible at compile time to add draw calls to the buffer, and no runtime checks are needed anymore. When you call a method to begin a render pass, you trade in ownership of your current builder for a new one that has draw operations, but lacks some other operations including build and begin_render_pass. You have to call end_render_pass to receive your original object back, which has the build method.

Using designs like this, some of the runtime cost of validation can be offloaded onto the compiler, speeding up the library. It does require some careful thinking, however.

gurchetansingh commented 3 years ago

Thank you Rua for this detailed explanation! Vulkano thus hopes to leverage typestate programming to enable safe and efficient use of the Vulkan API! Terrific!!

As for validation layers, Vulkano does validation in Rust. That from a purely ideological basis is superior to anything in C/C++!!

kvark commented 3 years ago

I find this goal highly aspirational and not realistic to achieve. You can either try to be safe, or fast, but getting both in the land of graphics is very tough. Safety requires you to take cuts from both performance and functionality. I.e. doing any weird stuff in your shader can end up overwriting the CPU memory, so you'll have to sanitize all the shader execution like WebGPU does.

If you expose Vulkan's binding model, you'd have to have run-time code for checking pipeline layout compatibility, and potentially rebinding some descriptor sets, which means both being less explicit and slower than Vulkan proper. Alternatively, you can construct a complex type system around them, and users will curse you for over-complicating the API.

The idea of a safe graphics API is almost the opposite of what Vulkan is. Vulkan was designed so that it's horribly unsafe, but there is a debug layer that can guarantee safety. Trying wrap this in a safe API is a uphill battle.

gurchetansingh commented 3 years ago

@kvark thank for your analysis! I generally agree with you about Vulkan being horribly designed for safety (every invalid usage is undefined behavior!). I do agree WebGPU is looking awfully superior too in many ways:

http://kvark.github.io/web/gpu/native/2020/05/03/point-of-webgpu-native.html

But still, I think Vulkano fills some niche that other projects simply don't. We want to use Vulkan in our apps, and follow idiomatic Rust. Speed is less important than ease of use and shared wrappers (without Vulkano, I would have to write wrappers over Ash anyways). You are 100% correct that that the binding model is not what Vulkan devs would expect as well.

It may be helpful for Vulkano developers to write a formal design document on project direction. Regardless of where this ends up, using Ash is definitely the right call.

Eliah-Lakhin commented 3 years ago

@Rua First, about the topic on migration from vk-sys. I like this idea and would certainly support it as much as I can. Low-level bindings as far as I understand was not a primary goal from the beginning, but there were no good alternatives 5 years ago when Vulkano begins. Now we have options in the Rust ecosystem that passed test of time, and that we can consider to migrate to. Ash seem to be a good option. I would also suggest looking at Erupt as another alternative. It is slightly younger than Ash, but I have heard good feedback about it from the very advanced Rust-Vulkan developers who at some point migrated from Ash to Erupt. Just as an idea to consider. I will left the choice completely to your hands.

And also regarding the topic #1496 about bitflags. Yes, let's proceed with this migration too. Especially if we are going to replace vk-sys with Ash or Erupt(both are using bitlfags, afaik).

Eliah-Lakhin commented 3 years ago

A bit of my personal thoughts(as an active user, not maintainer) about Vulkano and Rust computer graphics in general.

First of all I want to say that I'm very satisfied with Vulkano. It has nice and very thoughtful API design from the beginning thanks to Pierre Krieger, the author of this project. The API is on one hand is quite safe(maybe not always in the Rust soundness terms, but in more general sense) such that it finely guards the end-user to misuse it's parts, and it also usually guides the users in right directions to achieve desirable goals of their applications. On the other hand Vulkano's high-level API concepts and introduced terms are quite transparently represent of what is going on under the hood from the original Vulkan API's point of view too. It wraps, but it is not trying to rename/reinvent too much to what is happening on the Vulkan's side. So, one can just read Vulkan's docs and map it's concepts to Vulkano more or less directly.

In general my personal vision on ultimate goals and typesafety of Vulkano are very much close to @Rua's explanation. While I agree with @kvark that an attempt to reach this goal in it's ultimate form might be an uphill battle, I believe that Pierre did great job to establish reasonable balance between safety, performance costs and API flexibility. And we, the Vulkano contributors, are trying to develop the project in the same direction too. I believe that Vulkano in it's current form can be recommended to a wide range of audience as a basis to build a higher level and more specialized projects, such as for example @AustinJ235's Basalt GUI framework, or the end computer games engines.

Of course any attempt to make a safe API around Vulkan has it's edge cases. WGPU supervised by @kvark proposing another good alternative to Vulkano in the current Rust ecosystem with the different design model and different goals. For example WGPU is more oriented to usage of runtime shaders, and as such they paid more attention in making of the shaders loading soundness on CPU side(in Rust terms). This approach has some small performance GPU runtime penalties, but it also brings a number of benefits. In contrast Vulkano's shaders are mostly compile time buildable, and are not checked for data range access in GLSL code which is in theory could be unsafe. On the other hand this approach allows Vulkano Shaders to generate correct Rust types from GLSL types with all paddings and requested derives generated automatically too, which is in practice might be useful from the code maintainability point of view. And is easy to start working with.

To conclude, in my opinion it is beneficial for the Rust ecosystem when the users can make a choice between various alternatives whether it be safe/semisafe high level wrapper APIs such as Vulkano or WGPU, or maybe using low level unsafe APIs as Ash or Erupt, depending on the needs and particular project goals.

Rua commented 3 years ago

I've been working with Ash a little while, submitting a few PRs, and I also had a look at Erupt. Overall they both seem good, and are quite similar, but they also have some design choices that affect Vulkano one way or another.

gurchetansingh commented 3 years ago

Erupt and Ash both look quite terrific, and we can't go wrong either way. If I had to choose, I would choose Ash for two reasons:

  1. Larger community and it was the first-in-class of autogenerated bindings (I think?). The Rust community is small, and we need to coalesce if we want to hit real world products, so since Ash is already used by the WebGPU Rust initiative, it makes sense for Vulkano to back that too.
  2. Slightly lower runtime cost. There may be a --features=super-unsafe-disable-validation build of Vulkano in the future, where you get speed and still use Rust objects/style. Any little bit helps in that scenario.

Though the decision, IMO, is typically in the hands of the implementer. Just my 2 cents.