1.2 万一手滑了怎么办?
我之前可能有提到过Vulkan自带一个非常强大的验证层(Validation Layer),可以帮我们打打log,看看内存泄漏,之类的,总之是贤妻良母级别的.
首先呢,之前我们一直是在main.rs
中创建EventsLoop的,我们可以把这个加入到App
中,反正也就有一个loop
use super::super::events_handler::*;
use super::vki::*;
use winit::EventsLoop;
use super::super::window::*;
pub const WINDOW_TITLE: &'static str = "When Vulkan meets Rust";
pub struct App {
pub window: winit::Window,
pub vki: VKI,
pub events_loop: EventsLoop,
}
impl App {
pub fn new() -> App {
let ev = EventsLoop::new();
let window = window_create(WINDOW_TITLE, 800, 600, &ev);
return App {
window: window,
vki: VKI::new(),
events_loop: ev,
};
}
pub fn main_loop(&mut self) {
self.events_loop.run_forever(event_handler);
}
}
然后把 window.rs
里的String
换成&'static str
这样能提升性能(忽略不计的)
我们需要准备一个叫vk_to_string
的函数,因为vulkan的log都是C语言的string,我们需要把他转换到Rust的String
新建目录utils\tools.rs
自行补全lib.rs
和mod.rs
use std::ffi::CStr;
use std::os::raw::c_char;
use std::path::Path;
//请大家不要忘记 char* 他可是我最喜欢的C语言类型
pub fn vk_to_string(raw_string_array: &[c_char]) -> String {
let raw_string = unsafe {
let pointer = raw_string_array.as_ptr();
CStr::from_ptr(pointer)
};
raw_string
.to_str()
.expect("转换失败!")
.to_owned()
}
现在开始重头戏
在app
下创建validation.rs
use super::super::platforms::required_extension::*;
use super::super::utils::tools::*;
use ash::version::EntryV1_0;
use ash::version::InstanceV1_0;
use ash::vk;
use ash::vk_make_version;
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_void};
use std::ptr;
pub struct ValidationInfo {
pub is_enable: bool,
pub required_validation_layers: [&'static str; 1],
}
//下面这个常数是方便开关validation的,有点 #ifdef 的意思
pub const VALIDATION: ValidationInfo = ValidationInfo {
is_enable: true,
required_validation_layers: ["VK_LAYER_LUNARG_standard_validation"],
//我们需要拿过来LUNARG写的标准的验证层
};
pub fn check_validation_layer_support(entry: &ash::Entry) -> bool {
// 这个函数检查支持验证层的情况
//如果支持会返回一个true
let layer_properties = entry
.enumerate_instance_layer_properties() //枚举实例层属性
.expect("枚举实例层失败!");
if layer_properties.len() <= 0 {
eprintln!("没有可用层.");
return false;
} else {
println!("可用实例层: ");
for layer in layer_properties.iter() { //把可用的实例层都打印出来
let layer_name = vk_to_string(&layer.layer_name);
println!("\t{}", layer_name);
}
}
//把所有的layer翻个遍,找找咱要的那个layer,没找着就return false了
for required_layer_name in VALIDATION.required_validation_layers.iter() {
let mut is_layer_found = false;
for layer_property in layer_properties.iter() {
let test_layer_name = vk_to_string(&layer_property.layer_name);
if (*required_layer_name) == test_layer_name {
is_layer_found = true;
break;
}
}
if is_layer_found == false {
return false;
}
}
true
}
pub unsafe extern "system" fn vk_debug_callback( //unsafe extern "system" 一看就是给C用的
_: vk::DebugReportFlagsEXT,
_: vk::DebugReportObjectTypeEXT,
_: u64,
_: usize,
_: i32,
_: *const c_char,
p_message: *const c_char,
_: *mut c_void,
) -> u32 {
println!("{:?}", CStr::from_ptr(p_message)); //打印一下回调信息
vk::FALSE
}
pub fn setup_debug_callback(
entry: &ash::Entry,
instance: &ash::Instance,
) -> (
ash::extensions::ext::DebugReport,
vk::DebugReportCallbackEXT,
) {
let debug_report_loader = ash::extensions::ext::DebugReport::new(entry, instance);
//创建调试反馈
if VALIDATION.is_enable == false { //看看实例层开着没有
(debug_report_loader, ash::vk::DebugReportCallbackEXT::null())
} else {
let debug_create_info = vk::DebugReportCallbackCreateInfoEXT { //debug的创建信息,下面这个结构体是不是跟某个结构体很像?
s_type: vk::StructureType::DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
p_next: ptr::null(),
//flags标注了这个东西大概需要干什么
flags: vk::DebugReportFlagsEXT::ERROR //报个错
| vk::DebugReportFlagsEXT::INFORMATION //给个信息
//| vk::DebugReportFlagsEXT::DEBUG
| vk::DebugReportFlagsEXT::WARNING //报个警
| vk::DebugReportFlagsEXT::PERFORMANCE_WARNING, //性能不足报个警
pfn_callback: Some(vk_debug_callback), //回调
p_user_data: ptr::null_mut(),
};
let debug_call_back = unsafe {
debug_report_loader
.create_debug_report_callback(&debug_create_info, None)
.expect("创建调试回调失败 原因:")
};
(debug_report_loader, debug_call_back) //返回值是一个报告和一个回调
}
}
现在我们重新回到vki.rs
我们需要把我们写的验证层加入到我们的vulkan实例中去
所以我决定在impl VKI
里面加入一个create_instance
,在VKI
结构体里塞一个debug_report
和 debug_callback
pub struct VKI {
pub entry: ash::Entry,
pub instance: ash::Instance,
pub debug_report: ash::extensions::ext::DebugReport,//新增
pub debug_callback: vk::DebugReportCallbackEXT,//新增
}
impl VKI{
pub fn new() -> VKI {
let entry = ash::Entry::new().unwrap();
let instance = VKI::create_instance(&entry);
let (debug_report, debug_callback) = setup_debug_callback(&entry, &instance); //新增
return VKI {
entry: entry,
instance: instance,
debug_report: debug_report, //新增
debug_callback: debug_callback, //新增
};
}
fn create_instance(entry: &ash::Entry) -> ash::Instance {
if VALIDATION.is_enable && check_validation_layer_support(entry) == false {
panic!("验证层不可用!");
}
let app_name = CString::new(WINDOW_TITLE).unwrap();
let engine_name = CString::new("Vulkan Engine").unwrap();
let app_info = vk::ApplicationInfo { //我发现Vulkan特别喜欢让我们填表
p_application_name: app_name.as_ptr(),
s_type: vk::StructureType::APPLICATION_INFO,
p_next: ptr::null(),
application_version: APPLICATION_VERSION,
p_engine_name: engine_name.as_ptr(),
engine_version: ENGINE_VERSION,
api_version: API_VERSION,
};
//收集一下扩展的名字.
let extension_names = required_extension_names();
//便利一下我们常量需要的扩展
let requred_validation_layer_raw_names: Vec<CString> = VALIDATION
.required_validation_layers
.iter()
.map(|layer_name| CString::new(*layer_name).unwrap())
.collect();
//便利一下我们开着的扩展
let enable_layer_names: Vec<*const i8> = requred_validation_layer_raw_names
.iter()
.map(|layer_name| layer_name.as_ptr())
.collect();
let create_info = vk::InstanceCreateInfo {//填表不解释
s_type: vk::StructureType::INSTANCE_CREATE_INFO,
p_next: ptr::null(),
flags: vk::InstanceCreateFlags::empty(),
p_application_info: &app_info,
pp_enabled_layer_names: if VALIDATION.is_enable {//如果开着验证层把名字告诉他,要不啥没有
enable_layer_names.as_ptr()
} else {
ptr::null()
},
enabled_layer_count: if VALIDATION.is_enable {//开这几个层
enable_layer_names.len()
} else {
0
} as u32,
pp_enabled_extension_names: extension_names.as_ptr(),//扩展的名字们
enabled_extension_count: extension_names.len() as u32,//开着几个扩展
};
let instance: ash::Instance = unsafe {
entry
.create_instance(&create_info, None)
.expect("创建实例失败!")
};
instance
}
}
现在看着补补use
,补完之后,我们来讲一下为什么会发生之前的那个错误
可能就是因为我们drop
的方法不太对
现在我们来重新写一下drop
因为这此我们还带了一个验证层,所以,有什么错误他会告诉咱的
我相信大家知道要改的是那个文件
impl Drop for VKI {
fn drop(&mut self) {
unsafe {
if VALIDATION.is_enable {
self.debug_report
.destroy_debug_report_callback(self.debug_callback, None);
}
self.instance.destroy_instance(None);
}
}
}
现在我们尝试运行一下看看有什么会被打印出来cargo run
差不多以下内容就证明你写的还凑合
Instance Available Layers:
VK_LAYER_NV_optimus
VK_LAYER_VALVE_steam_overlay
VK_LAYER_VALVE_steam_fossilize
VK_LAYER_LUNARG_standard_validation
关闭窗口后,没有报错 我觉得这章写到这里就行了,大家赶紧去抄抄代码
后面的章节将会越来越难理解,还是鼓励一下吧