Ashen Aetna

— Rustily stumbling around on an ash-covered volcano

(A tutorial on/in/about/with 3D graphics, Rust, Vulkan, ash)

Cleanup and updates

It has been some time since the last update (and I don’t mean the slowness of the chapters, but the update of the Rust version on my computer).

rustup default 1.47.0

(Why not rustup update stable? We’ll get to that. Today this would, by the way, give Rust 1.48.0.)

After a bit of time for recompiling, the program still runs. Good.

Now let’s try the actually newest version:

rustup update stable
rustup default stable

(Now it’s set to the newest (stable) Rust, that is, 1.48.0.)

Let’s compile again:

thread 'main' panicked at 'attempted to zero-initialize type `ash::Device`, which is invalid', /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/mem/


This is some problem occurring within vk-mem. (And thanks go to xla for noticing and pointing out this issue.) This is fixed in a newer version of vk-mem. So, apparently, an update of Cargo.toml is in order:

vk-mem = "0.2.3"


error: failed to select a version for the requirement `vk-mem = "^0.2.3"`
candidate versions found which didn't match: 0.2.2, 0.2.0, 0.1.9, ...
location searched: index

The fix is not yet released. Well, it already exists in the repository. Let’s point cargo there:

vk-mem = { git = "", version = "0.2.3" }

… and …

More errors:

error[E0277]: the `?` operator can only be applied to values that implement `Try`

vk-mem’s changelog tells us the reason:

    Removed Result return values from functions that always returned Ok(())

That means: We can get rid of some question marks and some .expects. Let’s do so.

Okay, that makes these errors disappear. But:

error: linking with `cc` failed: exit code: 1


I think I now know better what “but master doesn’t seem to be building right now” in the response to vk-mem’s corresponding issue means. Next step: Downloading this repository and trying locally. In Cargo.toml that means something like

vk-mem = { path = "../../vk-mem-rs-master/", version = "0.2.3" }

with the path relative to Cargo.toml (and I must not forget to download the included vendor folder separately, as this is a different repository).

Of course, the error remains.

A bit of poking around (and thoughts of giving up – after all, I don’t really want to deal with C++ and linker errors) and helpful … pointers … by the VMA documentation, in line 4021 of vk_mem_alloc.h (in the CONFIGURATION SECTION) I write


(instead of 1).

And now, magically, it works again. (No problems with dynamically loading functions, if we decide not to load functions dynamically, I suppose.) I don’t know if this is “the right solution” or “a dirty hack”, but I don’t really care that much. (I just hope for a fix in the next version of vk-mem.)

There are also new versions of the crates my Cargo.toml lists. For example,

ash = "0.31.0"


   --> src/
361 |         .image(destination_image)
    |                ^^^^^^^^^^^^^^^^^ expected struct `ash::vk::Image`, found a different struct `ash::vk::Image`
    = note: perhaps two different versions of crate `ash` are being used?

I really love how the compiler directly tells me what the problem might be. And of course it is correct. cargo tree reveals that, inter alia,

├── vk-mem v0.2.3 (/home/joh/prog/foreign-rust/vk-mem-rs-master)
│   ├── ash v0.30.0

Well, vk-mem’s Cargo.toml only says

ash = ">= 0.27.1"

Actually, I’m a bit surprised that cargo does not resolve this (at least after some cargo clean); but I do have access to vk-mem’s Cargo.toml after having downloaded this repository anyway. Thus, it’s simple to change this to "= 0.31.0" (and possibly change it back after recompiling once …)

More updates:

vk-shader-macros = "0.2.2"

could, by now, be “0.2.6”. (And could have been, for several months.) Nothing breaks. That’s how I like updates.


fontdue = "0.2.4"

can be updated to "0.4.0". Haven’t I added that only last chapter? (Well, somewhere it must be visible that I had started writing that chapter quite some time before finishing and uploading it. In my defense: In between, I moved to a different city and obtained a new position at a different employer.)

error[E0061]: this function takes 1 argument but 0 arguments were supplied
   --> src/
163 |         let mut layout = fontdue::layout::Layout::new();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- supplied 0 arguments
    |                          |
    |                          expected 1 argument

error[E0599]: no method named `layout_horizontal` found for struct `fontdue::layout::Layout<_>` in the current scope
   --> src/
168 |         layout.layout_horizontal(&self.fonts, styles, &settings, &mut output);
    |                ^^^^^^^^^^^^^^^^^ method not found in `fontdue::layout::Layout<_>`

error: aborting due to 2 previous errors; 5 warnings emitted

Okay, some changes to layout. That means, create_letters should look a bit differently:

    pub fn create_letters(
        styles: &[&fontdue::layout::TextStyle],
        colour: [f32; 3],
    ) -> Vec<Letter> {
        let mut layout =
        let settings = fontdue::layout::LayoutSettings {
        for style in styles {
            layout.append(&self.fonts, style);
        let output = layout.glyphs();
        let mut letters: Vec<Letter> = vec![];
        for glyph in output {
            letters.push(Letter {
                position_and_shape: glyph.clone(),

That is: Layout::new receives some information on the direction of the y-axis. (Here I use what was the default in the previous version. Changes here should be accompanied by changes in the translation to positions in create_vertexdata.) And the previous

         layout.layout_horizontal(&self.fonts, styles, &settings, &mut output);

is changed: We first append the styles (each one separately) to the layout and finally receive the glyphs from layout.glyphs and not in a output variable that we firstly passed to the function. Better this way.

But what’s that?

thread 'main' panicked at 'creating vkImage for texture: Error { kind: Vulkan(ERROR_VALIDATION_FAILED_EXT) }', src/

This is in TextTexture::from_u8s. Printing the function arguments (by


) makes me suspect that the spaces are the problem: they now are a of size 0, not of size 1.

Ah, well, some new first lines for this function:

        if data.len() == 0 {
            return Err(Box::new(TextError::EmptyData));


pub enum TextError {
impl std::fmt::Display for TextError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        match self {
            TextError::EmptyData => {
                write!(f, "Empty Data");
impl std::error::Error for TextError {}

And I am rewarded with:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: EmptyData', src/

In that place we have

                id = self
                        metrics.width as u32,
                        metrics.height as u32,
                    .unwrap() as u32;

Maybe rather something like this:

                id = match self.new_texture_from_u8s(
                    metrics.width as u32,
                    metrics.height as u32,
                ) {
                    Ok(the_id) => the_id as u32,
                    Err(e) => {
                        if let Some(err) = e.downcast_ref::<TextError>() {
                            match err {
                                TextError::EmptyData => {
                        } else {

If there is an error, we check whether it’s a case of empty data (downcasting the Boxed trait object), which means: skipping this letter — or something else (no idea what), which results in a panic. That sounds reasonable to me.

Next crate to update: image, from 0.23.4 to 0.23.12. — — — No trouble here.

Next: nalgebra from 0.18.0 to 0.24.0. Again, no problems.

And then there is winit. 0.22.0 to 0.24.0. And once more, I spot no problem.

Enough of updates. But while I’m at it: There are a few messages “unused std::result::Result that must be used” that I receive. And since I wanted to document every change I make to the code, let me briefly mention it here: I’m going to add some question marks and .expect()s …

Also, there seems to be one unnecessary unsafe block. I’ll remove it.

AllText::clear_pipeline does not need its Allocator argument any more. (And removed it is.)

warning: use of deprecated associated function `image::DynamicImage::to_rgba`: replaced by `to_rgba8`
   --> src/
549 |     let screen_image = image::DynamicImage::ImageBgra8(screen).to_rgba();
    |                                                                ^^^^^^^
    = note: `#[warn(deprecated)]` on by default

warning: use of deprecated associated function `image::DynamicImage::to_rgba`: replaced by `to_rgba8`
  --> src/
24 |             .map(|img| img.to_rgba())
   |                            ^^^^^^^

Did I say “no trouble here”? That was the point where I should have noticed and fixed these. Well, it definitely is no trouble. I’ll insert these 8s.

One mut is unnecessary. (Easy to fix. And don’t worry if you don’t find it. It is a leftover from one of the previous experiments and could have been removed some time ago.)

And finally, there are a lot of “never read” fields, “unused imports” and “never used” functions. I do not want to remove these, as they could be helpful again: It is quite possible that I will want to have spheres or lights again, at some time.

In order to make the list of warnings shorter, I’ll therefore include some crate-level


The remaining warnings are some for vk-mem, and I don’t intend to look at them more closely.