본문 바로가기

개발/Rust

Rust에서 DirectX12 개발하기(0) - 개발환경 준비하기

최근 Rust라는 언어가 부상하고 있고, 구글, MS와 같은 글로벌 IT회사에서 자사의 제품에 부분적으로 Rust를 도입하고 있습니다. 특히 MS는 자사의 OS인 Windows에서 지난 12년간 발견한 오류중 70% 정도가 메모리 안정성 오류라고 밝혔으며, Rust 도입을 통해 이런 오류의 대부분을 제거할 수 있을 것이라고 발표했습니다[각주:1].

 

또한 MS에서는 자사의 Win32API를 여러 언어에서 사용할 수 있도록 Win32 Metadata[각주:2] 프로젝트를 진행하고 있습니다. 이 Metadata 프로젝트를 바탕으로 나온것이 바로 Rust에서 윈도우 API를 호출할 수 있는 windows 크레이트입니다. 

 

https://crates.io/crates/windows

 

위 프로젝트에서 DirectX12 프로젝트의 샘플을 구할 수는 있지만, 샘플 코드 외에는 별다른 설명이 있지 않아서, 처음 Rust를 접하거나 익숙하지 않으신 분들에게는 잘 이해가 되지 않을 수가 있습니다. 그래서 MS의 Rust로 포팅된 DirectX12 프로젝트[각주:3]를 참고하여, DirectX12에 대한 간략한 설명과 Rust에서 Win32 API를 어떻게 사용하는지에 대해서 설명하고자 합니다. 

 

 


 

 

우선, Rust프로젝트를 만드는 것부터 시작합니다.  Windows Terminal, CMD 등 여러 프로그램을 통해 프로젝트를 생성하고자 하는 폴더에 아래와 같은 명령어를 입력합니다.

 

cargo new rust-d3d12
cd rust-d3d12
cargo new --lib bindings

 

우선 cargo new rust-d3d12라는 명령어는 Rust의 통합 패키지 매니저인 Cargo를 통해 rust-d3d12라는 프로젝트를 생성해줍니다. rust-d3d12라는 프로젝트를 생성을 완료했으면, 해당 폴더로 이동 후 cargo new --lib bindings라는 명령어를 통해 bindings라는 라이브러리를 생성해줍니다. bindings라는 라이브러리는 메타데이터를 통한 Win32 API를 빌드하는데에 사용됩니다. 자세한 사용법은 잠시 후 다시 설명하겠습니다. 위 명령어를 제대로 실행했다면, rust-d3d12 프로젝트 폴더 구조는 아래와 같을 것입니다. 

 

+---bindings
|   |   Cargo.toml
|   \---src
|           lib.rs
\---src
|       main.rs
|   .gitignore
|   Cargo.toml

 

 


 

 

위와 같은 형태로 프로젝트가 생성이 되었다면 이번에는 rust-d3d12 폴더 하위에 있는 Cargo.toml파일을 수정할 차례입니다. 처음 파일을 열면 아래와 비슷한 코드가 작성되어있을 것입니다.

 

[package]
name = "rust-d3d12"
version = "0.1.0"
authors = ["Your Name <Your e-mail>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

 

이 파일은 프로젝트에 대한 설명을 작성하거나 프로젝트의 종속성을 설정하는데 사용되는 파일입니다. 여기서 우리가 수정해줘야하는 코드는 [dependencies] 부분입니다. [dependencies]는 해당 프로젝트가 다른 프로젝트에 대한 종속성을 가져야할 때 사용됩니다.

 

이곳에 Rust에서 Win32 API를 사용할 수 있게 해주는 windows 크레이트와 아까 생성한 bindings 라이브러리를 추가합니다. 

 

[dependencies]
bindings = {path = "bindings"}
windows = "0.10.0

 

코드를 이렇게 수정하게 되면, 이제 rust-d3d12는 bindings 라이브러리와 windows 크레이트 안에 정의된 내용들을 사용할 수 있습니다. 

 

이번에는 bindings 폴더 안에 있는 Cargo.toml 파일을 수정할 차례입니다.  아래와 같이 수정하면 됩니다.

 

[dependencies]
windows = "0.10.0"

[build-dependencies]
windows = "0.10.0

 

이전과 다르게 [build-dependencies]라는 코드가 추가되었는데, 이 부분은 빌드 스크립트를 작성할 때 종속성을 설정해주는 부분입니다. 빌드 스크립트란 Cargo가 프로젝트를 빌드하기 전에 컴파일하는 스크립트를 의미합니다. 

 

 


 

 

Rust의 windows 크레이트는 단순히 [dependencies]에 windows 크레이트를 추가해서, Win32 API를 사용하는 것은 불가능합니다. Rust에서 Win32 API를 사용하기 위해서는 아래와 같은 과정을 거쳐야합니다.

 

  1. 빌드 스크립트 내부에서 사용하고자 하는 API를 추가하기(binding)
  2. 바인딩된 API를 bindings 라이브러리에 포함하기(include)
  3. 원래의 프로젝트(rust-d3d12)에서 bindings 라이브러리를 추가하기(dependencies)
  4. use 구문을 통해서 bindings 라이브러리에 생성된 API를 범위로 가져오기(use)

 

그렇다면 우선, bindings 라이브러리에 빌드 스크립트를 작성하는 것부터 시작하겠습니다. 

 

우선 빌드 스크립트를 작성할 build.rs 파일을 bindings 폴더 밑에 생성해주세요.  build.rs 파일을 생성했다면 프로젝트 구조는 아래와 같을 것입니다.

 

+---bindings
|   |   build.rs
|   |   Cargo.toml
|   \---src
|           lib.rs
\---src
|       main.rs    
|   .gitignore
|   Cargo.toml

 

그 다음에 build.rs 파일에 아래와 같은 코드를 작성합니다.

 

fn main() {
	windows::build!();
}

 

build!() 매크로는 인수로 전달하는 메타데이터를 통해 사용하고자 하는 Win32 API를 생성합니다. 만약 HWND 구조체를 프로젝트에서 사용하고 싶으면, 아래와 같이 작성하면 됩니다. 

 

fn main() {
	windows::build!(
    	Windows::Win32::UI::WindowsAndMessaging::HWND,
    );
}

 

어떤 메타데이터를 추가해야하는지는 아래의 사이트에서 쉽게 검색할 수 있습니다. 

 

bindings::Windows - Rust (microsoft.github.io)

 

bindings::Windows - Rust

 

microsoft.github.io

 

 


 

 

이렇게 추가한 메타데이터를 라이브러리로 가져오기 위해서는 /bindings/src/lib.rs에 아래와 같은 코드를 작성해주면 됩니다. 

 

windows::include_bindings!();

 

inlcude_bindgins!() 매크로를 통해 빌드 스크립트에서 추가한 메타데이터를 bindings 라이브러리에 포함시키게 됩니다. 

 

최종적으로는 rust-d3d12 프로젝트는 bindings 라이브러리에 대한 종속성을 가지고 있기 때문에, bindings 네임스페이스를 통해 추가한 메타데이터의 API에 접근할 수 있게 됩니다. 

 

이번 편에서는 Rust에서 Win32 API를 사용하기 위한 기본적인 프로젝트 세팅을 알아보았습니다. 다음 편에서는 간단한 윈도우 창을 생성하는 프로그램을 작성해보겠습니다.

 

샘플 프로젝트 코드는 github에 올렸습니다.

 

seonhwi07jp/rust-d3d12 (github.com)

 

seonhwi07jp/rust-d3d12

Contribute to seonhwi07jp/rust-d3d12 development by creating an account on GitHub.

github.com

 

해당 코드는 ver1 브랜치로 이동하면 볼 수 있습니다.