마지막 업데이트 이후로 꽤 시간이 흘렀습니다 (제가 챕터를 느리게 올리는 것을 말하는 게 아니라, 제 컴퓨터의 Rust 버전 업데이트를 말하는 겁니다).
rustup default 1.47.0
(왜 rustup update stable이 아니냐고요? 곧 설명하겠습니다. 참고로 오늘 이 명령어를 실행하면 Rust 1.48.0이 설치될 겁니다.)
잠시 재컴파일하는 시간을 거치니, 프로그램은 여전히 잘 실행됩니다. 좋습니다.
이제 정말 최신 버전을 시도해 봅시다:
rustup update stable
rustup default stable
(이제 최신(stable) Rust, 즉 1.48.0으로 설정되었습니다.)
다시 컴파일해 봅시다:
thread 'main' panicked at 'attempted to zero-initialize type `ash::Device`, which is invalid', /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/mem/mod.rs:622:9
이런.
이것은 vk-mem 내에서 발생하는 문제입니다. (그리고 이 이슈를 발견하고 지적해준 xla님께 감사를 전합니다.) 이 문제는 vk-mem의 새로운 버전에서 수정되었습니다. 그러니 Cargo.toml을 업데이트해야 할 것 같습니다:
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: crates.io index
수정사항이 아직 릴리스되지 않았네요. 뭐, 이미 저장소에는 존재합니다. cargo가 그 저장소를 가리키도록 해봅시다:
vk-mem = { git = "https://github.com/gwihlidal/vk-mem-rs", version = "0.2.3" }
… 그리고 …
더 많은 에러:
error[E0277]: the `?` operator can only be applied to values that implement `Try`
vk-mem의 변경 로그가 그 이유를 알려줍니다:
Removed Result return values from functions that always returned Ok(())
(항상 Ok(())를 반환하던 함수에서 Result 반환 값을 제거함)
즉, 물음표(?)와 .expect를 몇 개 제거할 수 있다는 뜻입니다. 그렇게 해봅시다.
좋아요, 이제 이 에러들은 사라졌습니다. 하지만:
error: linking with `cc` failed: exit code: 1
으으음.
이제야 vk-mem의 해당 이슈에 대한 답변에 있던 “하지만 master 브랜치가 지금 당장은 빌드되지 않는 것 같다”는 말이 무슨 뜻인지 더 잘 알 것 같습니다. 다음 단계: 이 저장소를 다운로드해서 로컬에서 시도해보기. Cargo.toml에서는 다음과 같이 설정합니다.
vk-mem = { path = "../../vk-mem-rs-master/", version = "0.2.3" }
경로는 Cargo.toml에 대한 상대 경로입니다 (그리고 포함된 vendor 폴더는 다른 저장소이므로 별도로 다운로드하는 것을 잊지 말아야 합니다).
물론, 에러는 그대로입니다.
조금 더 살펴보고 (포기할까 하는 생각도 들었지만 – 결국 C++과 링커 에러를 다루고 싶지는 않았으니까요) VMA 문서에서 도움이 되는… 지침(pointers) 덕분에,
vk_mem_alloc.h의 4021번째 줄(CONFIGURATION SECTION)에 다음과 같이 작성합니다.
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
(1 대신에요).
그러자 마법처럼 다시 작동합니다. (동적으로 함수를 로드하지 않기로 결정하면 동적 로딩에 아무런 문제가 없겠죠, 아마도요.) 이게 “올바른 해결책”인지 “더러운 꼼수”인지는 모르겠습니다만, 별로 신경 쓰지 않습니다. (그저 다음 vk-mem 버전에서 수정되기를 바랄 뿐입니다.)
제 Cargo.toml에 나열된 크레이트들의 새 버전도 있습니다. 예를 들면,
ash = "0.31.0"
오:
--> src/main.rs:361:16
|
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?
컴파일러가 문제의 원인을 직접 알려주는 점이 정말 마음에 듭니다. 그리고 물론 컴파일러가 맞았습니다. cargo tree는 여러 정보 중에서도 다음을 보여줍니다.
├── vk-mem v0.2.3 (/home/joh/prog/foreign-rust/vk-mem-rs-master)
│ ├── ash v0.30.0
음, vk-mem의 Cargo.toml에는 단지 다음과 같이만 되어 있습니다.
ash = ">= 0.27.1"
사실 cargo가 이 문제를 해결하지 못하는 게 좀 놀랍습니다 (적어도 cargo clean을 한 후에는요). 하지만 어쨌든 이 저장소를 다운로드했으니 vk-mem의 Cargo.toml에 접근할 수 있습니다. 따라서 이 부분을 "= 0.31.0"으로 바꾸는 건 간단합니다 (그리고 한번 재컴파일한 후에 다시 원래대로 되돌려 놓을 수도 있겠죠…)
더 많은 업데이트:
vk-shader-macros = "0.2.2"
는 이제 “0.2.6”이 될 수 있습니다. (그리고 이미 몇 달 전부터 가능했습니다.) 아무것도 깨지지 않습니다. 제가 좋아하는 방식의 업데이트네요.
게다가,
fontdue = "0.2.4"
는 "0.4.0"으로 업데이트될 수 있습니다. 이거 바로 지난 챕터에서 추가하지 않았나요? (음, 제가 그 챕터를 쓰기 시작하고 나서 완성해서 올리기까지 꽤 시간이 걸렸다는 게 어디선가는 드러나야겠죠. 변명하자면, 그 사이에 다른 도시로 이사했고 다른 직장에서 새로운 직책을 맡게 되었습니다.)
error[E0061]: this function takes 1 argument but 0 arguments were supplied
--> src/text.rs:163:26
|
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/text.rs:168:16
|
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
네, 레이아웃에 몇 가지 변경이 있었군요. 즉, create_letters가 좀 다르게 보여야 합니다:
pub fn create_letters(
&self,
styles: &[&fontdue::layout::TextStyle],
colour: [f32; 3],
) -> Vec<Letter> {
let mut layout =
fontdue::layout::Layout::new(fontdue::layout::CoordinateSystem::PositiveYUp);
let settings = fontdue::layout::LayoutSettings {
..fontdue::layout::LayoutSettings::default()
};
layout.reset(&settings);
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 {
colour,
position_and_shape: glyph.clone(),
});
}
letters
}
즉, Layout::new는 y축 방향에 대한 정보를 받습니다. (여기서는 이전 버전의 기본값을 사용했습니다. 여기서 변경이 있다면 create_vertexdata에서 위치로 변환하는 부분도 변경되어야 합니다.) 그리고 이전의
layout.layout_horizontal(&self.fonts, styles, &settings, &mut output);
는 다음과 같이 변경됩니다: 먼저 스타일들을 (각각 따로) 레이아웃에 추가하고, 마지막에 layout.glyphs로부터 글리프들을 받습니다. 이전에 함수에 전달했던 output 변수에서 받는 것이 아니고요. 이 편이 더 낫네요.
그런데 이건 뭐죠?
thread 'main' panicked at 'creating vkImage for texture: Error { kind: Vulkan(ERROR_VALIDATION_FAILED_EXT) }', src/text.rs:398:14
이건 TextTexture::from_u8s 안에서 발생합니다. 함수 인자들을 출력해보니 (다음과 같이)
dbg!(&data);
dbg!(&width);
dbg!(&height);
공백(space) 문자가 문제일 거라고 의심하게 만듭니다: 이제 크기가 1이 아니라 0이 되었네요.
아, 이 함수에 새로운 첫 몇 줄을 추가해야겠습니다:
if data.len() == 0 {
return Err(Box::new(TextError::EmptyData));
}
여기서 TextError는 다음과 같습니다.
#[derive(Debug)]
pub enum TextError {
EmptyData,
}
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");
}
}
Ok(())
}
}
impl std::error::Error for TextError {}
그리고 제가 받은 보상은 이것입니다:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: EmptyData', src/text.rs:215:22
그 위치에는 다음 코드가 있습니다.
id = self
.new_texture_from_u8s(
&bitmap,
metrics.width as u32,
metrics.height as u32,
device,
allocator,
commandpool_graphics,
graphics_queue,
)
.unwrap() as u32;
아마도 이런 식으로 바꾸는 게 낫겠습니다:
id = match self.new_texture_from_u8s(
&bitmap,
metrics.width as u32,
metrics.height as u32,
device,
allocator,
commandpool_graphics,
graphics_queue,
) {
Ok(the_id) => the_id as u32,
Err(e) => {
if let Some(err) = e.downcast_ref::<TextError>() {
match err {
TextError::EmptyData => {
continue;
}
}
} else {
panic!();
}
}
};
에러가 발생하면, 그것이 빈 데이터 경우인지 확인하고(Box에 담긴 트레이트 객체를 다운캐스팅하여), 그렇다면 이 글자는 건너뜁니다. 다른 경우라면(무엇일지는 모르겠지만), 패닉을 발생시킵니다. 저에게는 합리적으로 들립니다.
다음 업데이트할 크레이트: image, 0.23.4에서 0.23.12로. — — — 여기서는 문제가 없네요.
다음: nalgebra, 0.18.0에서 0.24.0으로. 다시, 문제없습니다.
그리고 winit이 있습니다. 0.22.0에서 0.24.0으로. 그리고 또 한 번, 아무 문제도 발견하지 못했습니다.
업데이트는 이쯤 하고요. 하지만 이왕 하는 김에: 제가 받는 “사용해야 할 std::result::Result가 사용되지 않았습니다”라는 메시지가 몇 개 있습니다. 그리고 코드에 가하는 모든 변경 사항을 기록하고 싶었으니, 여기에 잠시 언급하겠습니다: 물음표(?)와 .expect()를 몇 개 추가할 겁니다…
또한, 불필요한 unsafe 블록이 하나 있는 것 같습니다. 제거하겠습니다.
AllText::clear_pipeline은 더 이상 Allocator 인자가 필요 없습니다. (그리고 제거되었습니다.)
warning: use of deprecated associated function `image::DynamicImage::to_rgba`: replaced by `to_rgba8`
--> src/main.rs:549:64
|
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/texture.rs:24:28
|
24 | .map(|img| img.to_rgba())
| ^^^^^^^
“문제없다”고 말했던가요? 이 시점에서 이걸 발견하고 수정했어야 했습니다. 뭐, 이건 확실히 문제 될 게 없습니다. 이 8들을 추가하겠습니다.
mut 하나가 불필요합니다. (고치기 쉽습니다. 그리고 못 찾더라도 걱정 마세요. 이전 실험에서 남은 찌꺼기라 진작에 제거될 수도 있었습니다.)
그리고 마지막으로, “읽히지 않는” 필드, “사용되지 않는 임포트”, “사용되지 않는” 함수들이 많이 있습니다. 이것들을 제거하고 싶지는 않습니다, 다시 유용할 수 있으니까요: 언젠가 다시 구나 조명을 사용하고 싶을 가능성이 꽤 있습니다.
따라서 경고 목록을 줄이기 위해, 크레이트 수준에서 다음과 같이 추가할 겁니다.
#![allow(unused)]
남은 경고는 vk-mem에 대한 것들 몇 개인데, 더 자세히 들여다볼 생각은 없습니다.