우리는 여전히 점을 하나만 그리고 있습니다. (물론 지금은 애플리케이션 코드 내에서 위치, 크기, 색상을 조정할 수 있게 되었지만요.)
이제 몇 개 더 그려봅시다.
이를 위해 무엇을 해야 할까요?
우선 정점 버퍼에 더 많은 값을 넣어야 합니다. 예를 들어,
buffer1.fill(
&allocator,
&[
0.4f32, -0.2f32, 0.0f32, 1.0f32, 0.2f32, 0.0f32, 0.0f32, 1.0f32,
],
)?;
처럼 말이죠. 하지만 먼저 버퍼를 충분히 크게 만드는 것을 잊지 말아야 합니다.
let buffer1 = Buffer::new(
&allocator,
32,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
그리고 물론, 두 번째 버퍼에도 더 많은 데이터가 있어야 합니다.
let buffer2 = Buffer::new(
&allocator,
40,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer2.fill(
&allocator,
&[
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 3.0f32, 1.0f32, 0.4f32, 0.0f32, 1.0f32,
],
)?;
그런데 이게 뭘까요? 프로그램을 실행해도 여전히 점이 하나만 나옵니다.
draw 명령어 또한 수정해야 합니다. 거기서 “점 하나를 그리라”고 했기 때문에, 점이 하나만 그려지는 것입니다.
다음 코드를
logical_device.cmd_draw(commandbuffer, 1, 1, 0, 0);
이렇게 바꿔서
logical_device.cmd_draw(commandbuffer, 2, 1, 0, 0);
두 개의 정점을 그리도록 해야 합니다.
이제 좀 낫네요.
더 많은 점을 그려봅시다!
let buffer1 = Buffer::new(
&allocator,
96,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer1.fill(
&allocator,
&[
0.4f32, -0.2f32, 0.0f32, 1.0f32, 0.2f32, 0.0f32, 0.0f32, 1.0f32, -0.4f32, 0.2f32,
0.0f32, 1.0f32, 0.5f32, 0.0f32, 0.0f32, 1.0f32, 0.0f32, 0.2f32, 0.0f32, 1.0f32,
-0.5f32, 0.0f32, 0.0f32, 1.0f32,
],
)?;
let buffer2 = Buffer::new(
&allocator,
120,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer2.fill(
&allocator,
&[
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32,
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32,
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32,
],
)?;
그리고, 물론,
logical_device.cmd_draw(commandbuffer, 6, 1, 0, 0);
도 수정해야 합니다.
우리는 크기와 색상에 대한 동일한 데이터를 반복해서 보내고 있습니다. 이것을 동일한 3D 모델을 여러 곳에 그려야 하는 상황의 (과도한) 단순화 버전이라고 생각해보세요.
크기/색상 데이터는 한 번만 보내고, 각각의 위치에 모두 그리도록 할 수 있습니다.
이를 위한 도구가 바로 “인스턴스 렌더링”이며, 아이디어는 다음과 같습니다: 데이터를 정점별(per-vertex) 데이터와 인스턴스별(per-instance) 데이터로 나눕니다. 각 인스턴스에 대해 모든 정점을 그립니다.
따라서 위치를 인스턴스 데이터로 넣고, 크기 15.0과 녹색을 유일한 정점 데이터로 유지해볼 수 있습니다.
이렇게 하면 두 번째 버퍼가 더 작아집니다:
let buffer2 = Buffer::new(
&allocator,
20,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer2.fill(&allocator, &[15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32])?;
우리는 프로그램에게 위치 데이터가 정점마다 바뀌는 것이 아니라, 인스턴스마다 바뀐다고 알려줘야 합니다. 이는 바인딩 서술(binding description)의 일부입니다:
let vertex_binding_descs = [
vk::VertexInputBindingDescription {
binding: 0,
stride: 16,
input_rate: vk::VertexInputRate::INSTANCE, // 인스턴스 단위
},
vk::VertexInputBindingDescription {
binding: 1,
stride: 20,
input_rate: vk::VertexInputRate::VERTEX, // 정점 단위
},
];
그리고 마지막으로, 그리기 호출(draw call)을 수정합니다: 이제 정점은 하나, 인스턴스는 여섯 개입니다:
logical_device.cmd_draw(commandbuffer, 1, 6, 0, 0);
결과는 여전히 화면에 동일한 그림으로 나타납니다.
인스턴스당 두 개의 정점을 그리면 어떻게 될까요?
logical_device.cmd_draw(commandbuffer, 2, 6, 0, 0);
당연히, 이 요청을 하려면 정점별 데이터 버퍼에도 두 개의 정점을 넣어야 합니다:
let buffer2 = Buffer::new(
&allocator,
40,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer2.fill(
&allocator,
&[
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 5.0f32, 0.0f32, 0.5f32, 0.8f32, 1.0f32,
],
)?;
이제 녹색 점 위에 더 작은 파란색 점들이 보입니다. 파란색 점의 위치를 독립적으로 선택할 수 있는 능력은 잃었지만(이것은 인스턴스별 데이터에 해당하므로), GPU로 보내야 하는 바이트 양에 비해 훨씬 더 많은 것을 그리고 있습니다.
만약 여전히 파란색 점의 위치를 녹색 점과 독립적으로 제어해야 한다면, 두 번의 그리기 호출(그리고 총 네 개의 버퍼)을 사용하거나, 위치를 인스턴스별 데이터와 정점별 데이터 양쪽에 전달할 수 있습니다: 인스턴스별 오프셋과, 이 오프셋에 상대적인 정점별 좌표를 사용하는 것이죠. 지금 예제에서는 바이트를 절약할 수 없지만, 정점별 버퍼가 크고 (큰 모델) 많은 인스턴스에 재사용될 수 있다면, 이는 엄청난 이득이 될 수 있습니다.
성능에 관해서는: 늘 그렇듯이, 측정 없이는 확실히 알 수 없습니다.
하지만 인스턴싱은 큰 버퍼가 관련될 경우 모든 것을 더 빠르게 만들어야 합니다. 지금 예제에서는 차이가 없을 것입니다. 그리고 여기처럼 정점별 데이터가 적은 경우에는 “모든 것을 단일 정점 버퍼에 넣는” 더 쉬운 해결책이 오히려 약간 더 빠를 수도 있습니다.
지금은, 정점별 데이터만 사용하는 방식으로 돌아가겠습니다. 두 바인딩 모두 vk::VertexInputRate::VERTEX로 설정하고, 그리기 명령어는 인스턴스 하나만 사용합니다 (이번에는 점 세 개를 설정할 것이므로, logical_device.cmd_draw(commandbuffer, 3, 1, 0, 0); 입니다). 버퍼들도 그에 맞게 채웁니다.
전체 소스 코드를 다시 한번 첨부합니다 (모든 단계를 하나하나 따라오지 않은 분들을 위해):
use ash::version::DeviceV1_0;
use ash::version::EntryV1_0;
use ash::version::InstanceV1_0;
use ash::vk;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let eventloop = winit::event_loop::EventLoop::new();
let window = winit::window::Window::new(&eventloop)?;
let mut aetna = Aetna::init(window)?;
use winit::event::{Event, WindowEvent};
eventloop.run(move |event, _, controlflow| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*controlflow = winit::event_loop::ControlFlow::Exit;
}
Event::MainEventsCleared => {
// 여기서 작업을 수행합니다 (나중에)
aetna.window.request_redraw();
}
Event::RedrawRequested(_) => {
let (image_index, _) = unsafe {
aetna
.swapchain
.swapchain_loader
.acquire_next_image(
aetna.swapchain.swapchain,
std::u64::MAX,
aetna.swapchain.image_available[aetna.swapchain.current_image],
vk::Fence::null(),
)
.expect("image acquisition trouble")
};
unsafe {
aetna
.device
.wait_for_fences(
&[aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image]],
true,
std::u64::MAX,
)
.expect("fence-waiting");
aetna
.device
.reset_fences(&[
aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image]
])
.expect("resetting fences");
}
let semaphores_available =
[aetna.swapchain.image_available[aetna.swapchain.current_image]];
let waiting_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
let semaphores_finished =
[aetna.swapchain.rendering_finished[aetna.swapchain.current_image]];
let commandbuffers = [aetna.commandbuffers[image_index as usize]];
let submit_info = [vk::SubmitInfo::builder()
.wait_semaphores(&semaphores_available)
.wait_dst_stage_mask(&waiting_stages)
.command_buffers(&commandbuffers)
.signal_semaphores(&semaphores_finished)
.build()];
unsafe {
aetna
.device
.queue_submit(
aetna.queues.graphics_queue,
&submit_info,
aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image],
)
.expect("queue submission");
};
let swapchains = [aetna.swapchain.swapchain];
let indices = [image_index];
let present_info = vk::PresentInfoKHR::builder()
.wait_semaphores(&semaphores_finished)
.swapchains(&swapchains)
.image_indices(&indices);
unsafe {
aetna
.swapchain
.swapchain_loader
.queue_present(aetna.queues.graphics_queue, &present_info)
.expect("queue presentation");
};
aetna.swapchain.current_image =
(aetna.swapchain.current_image + 1) % aetna.swapchain.amount_of_images as usize;
}
_ => {}
});
}
unsafe extern "system" fn vulkan_debug_utils_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
_p_user_data: *mut std::ffi::c_void,
) -> vk::Bool32 {
let message = std::ffi::CStr::from_ptr((*p_callback_data).p_message);
let severity = format!("{:?}", message_severity).to_lowercase();
let ty = format!("{:?}", message_type).to_lowercase();
println!("[Debug][{}][{}] {:?}", severity, ty, message);
vk::FALSE
}
fn init_instance(
entry: &ash::Entry,
layer_names: &[&str],
) -> Result<ash::Instance, ash::InstanceError> {
let enginename = std::ffi::CString::new("UnknownGameEngine").unwrap();
let appname = std::ffi::CString::new("The Black Window").unwrap();
let app_info = vk::ApplicationInfo::builder()
.application_name(&appname)
.application_version(vk::make_version(0, 0, 1))
.engine_name(&enginename)
.engine_version(vk::make_version(0, 42, 0))
.api_version(vk::make_version(1, 0, 106));
let layer_names_c: Vec<std::ffi::CString> = layer_names
.iter()
.map(|&ln| std::ffi::CString::new(ln).unwrap())
.collect();
let layer_name_pointers: Vec<*const i8> = layer_names_c
.iter()
.map(|layer_name| layer_name.as_ptr())
.collect();
let extension_name_pointers: Vec<*const i8> = vec![
ash::extensions::ext::DebugUtils::name().as_ptr(),
ash::extensions::khr::Surface::name().as_ptr(),
ash::extensions::khr::XlibSurface::name().as_ptr(),
];
let mut debugcreateinfo = vk::DebugUtilsMessengerCreateInfoEXT::builder()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
)
.pfn_user_callback(Some(vulkan_debug_utils_callback));
let instance_create_info = vk::InstanceCreateInfo::builder()
.push_next(&mut debugcreateinfo)
.application_info(&app_info)
.enabled_layer_names(&layer_name_pointers)
.enabled_extension_names(&extension_name_pointers);
unsafe { entry.create_instance(&instance_create_info, None) }
}
struct DebugDongXi {
loader: ash::extensions::ext::DebugUtils,
messenger: vk::DebugUtilsMessengerEXT,
}
impl DebugDongXi {
fn init(entry: &ash::Entry, instance: &ash::Instance) -> Result<DebugDongXi, vk::Result> {
let debugcreateinfo = vk::DebugUtilsMessengerCreateInfoEXT::builder()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
)
.pfn_user_callback(Some(vulkan_debug_utils_callback));
let loader = ash::extensions::ext::DebugUtils::new(entry, instance);
let messenger = unsafe { loader.create_debug_utils_messenger(&debugcreateinfo, None)? };
Ok(DebugDongXi { loader, messenger })
}
}
impl Drop for DebugDongXi {
fn drop(&mut self) {
unsafe {
self.loader
.destroy_debug_utils_messenger(self.messenger, None)
};
}
}
struct SurfaceDongXi {
xlib_surface_loader: ash::extensions::khr::XlibSurface,
surface: vk::SurfaceKHR,
surface_loader: ash::extensions::khr::Surface,
}
impl SurfaceDongXi {
fn init(
window: &winit::window::Window,
entry: &ash::Entry,
instance: &ash::Instance,
) -> Result<SurfaceDongXi, vk::Result> {
use winit::platform::unix::WindowExtUnix;
let x11_display = window.xlib_display().unwrap();
let x11_window = window.xlib_window().unwrap();
let x11_create_info = vk::XlibSurfaceCreateInfoKHR::builder()
.window(x11_window)
.dpy(x11_display as *mut vk::Display);
let xlib_surface_loader = ash::extensions::khr::XlibSurface::new(entry, instance);
let surface = unsafe { xlib_surface_loader.create_xlib_surface(&x11_create_info, None) }?;
let surface_loader = ash::extensions::khr::Surface::new(entry, instance);
Ok(SurfaceDongXi {
xlib_surface_loader,
surface,
surface_loader,
})
}
fn get_capabilities(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<vk::SurfaceCapabilitiesKHR, vk::Result> {
unsafe {
self.surface_loader
.get_physical_device_surface_capabilities(physical_device, self.surface)
}
}
fn get_present_modes(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::PresentModeKHR>, vk::Result> {
unsafe {
self.surface_loader
.get_physical_device_surface_present_modes(physical_device, self.surface)
}
}
fn get_formats(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::SurfaceFormatKHR>, vk::Result> {
unsafe {
self.surface_loader
.get_physical_device_surface_formats(physical_device, self.surface)
}
}
fn get_physical_device_surface_support(
&self,
physical_device: vk::PhysicalDevice,
queuefamilyindex: usize,
) -> Result<bool, vk::Result> {
unsafe {
self.surface_loader.get_physical_device_surface_support(
physical_device,
queuefamilyindex as u32,
self.surface,
)
}
}
}
impl Drop for SurfaceDongXi {
fn drop(&mut self) {
unsafe {
self.surface_loader.destroy_surface(self.surface, None);
}
}
}
fn init_physical_device_and_properties(
instance: &ash::Instance,
) -> Result<(vk::PhysicalDevice, vk::PhysicalDeviceProperties), vk::Result> {
let phys_devs = unsafe { instance.enumerate_physical_devices()? };
let mut chosen = None;
for p in phys_devs {
let properties = unsafe { instance.get_physical_device_properties(p) };
if properties.device_type == vk::PhysicalDeviceType::DISCRETE_GPU {
chosen = Some((p, properties));
}
}
Ok(chosen.unwrap())
}
struct QueueFamilies {
graphics_q_index: Option<u32>,
transfer_q_index: Option<u32>,
}
impl QueueFamilies {
fn init(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
surfaces: &SurfaceDongXi,
) -> Result<QueueFamilies, vk::Result> {
let queuefamilyproperties =
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
let mut found_graphics_q_index = None;
let mut found_transfer_q_index = None;
for (index, qfam) in queuefamilyproperties.iter().enumerate() {
if qfam.queue_count > 0
&& qfam.queue_flags.contains(vk::QueueFlags::GRAPHICS)
&& surfaces.get_physical_device_surface_support(physical_device, index)?
{
found_graphics_q_index = Some(index as u32);
}
if qfam.queue_count > 0 && qfam.queue_flags.contains(vk::QueueFlags::TRANSFER) {
if found_transfer_q_index.is_none()
|| !qfam.queue_flags.contains(vk::QueueFlags::GRAPHICS)
{
found_transfer_q_index = Some(index as u32);
}
}
}
Ok(QueueFamilies {
graphics_q_index: found_graphics_q_index,
transfer_q_index: found_transfer_q_index,
})
}
}
struct Queues {
graphics_queue: vk::Queue,
transfer_queue: vk::Queue,
}
fn init_device_and_queues(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
queue_families: &QueueFamilies,
layer_names: &[&str],
) -> Result<(ash::Device, Queues), vk::Result> {
let layer_names_c: Vec<std::ffi::CString> = layer_names
.iter()
.map(|&ln| std::ffi::CString::new(ln).unwrap())
.collect();
let layer_name_pointers: Vec<*const i8> = layer_names_c
.iter()
.map(|layer_name| layer_name.as_ptr())
.collect();
let priorities = [1.0f32];
let queue_infos = [
vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_families.graphics_q_index.unwrap())
.queue_priorities(&priorities)
.build(),
vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_families.transfer_q_index.unwrap())
.queue_priorities(&priorities)
.build(),
];
let device_extension_name_pointers: Vec<*const i8> =
vec![ash::extensions::khr::Swapchain::name().as_ptr()];
let device_create_info = vk::DeviceCreateInfo::builder()
.queue_create_infos(&queue_infos)
.enabled_extension_names(&device_extension_name_pointers)
.enabled_layer_names(&layer_name_pointers);
let logical_device =
unsafe { instance.create_device(physical_device, &device_create_info, None)? };
let graphics_queue =
unsafe { logical_device.get_device_queue(queue_families.graphics_q_index.unwrap(), 0) };
let transfer_queue =
unsafe { logical_device.get_device_queue(queue_families.transfer_q_index.unwrap(), 0) };
Ok((
logical_device,
Queues {
graphics_queue,
transfer_queue,
},
))
}
struct SwapchainDongXi {
swapchain_loader: ash::extensions::khr::Swapchain,
swapchain: vk::SwapchainKHR,
images: Vec<vk::Image>,
imageviews: Vec<vk::ImageView>,
framebuffers: Vec<vk::Framebuffer>,
surface_format: vk::SurfaceFormatKHR,
extent: vk::Extent2D,
image_available: Vec<vk::Semaphore>,
rendering_finished: Vec<vk::Semaphore>,
may_begin_drawing: Vec<vk::Fence>,
amount_of_images: u32,
current_image: usize,
}
impl SwapchainDongXi {
fn init(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
logical_device: &ash::Device,
surfaces: &SurfaceDongXi,
queue_families: &QueueFamilies,
queues: &Queues,
) -> Result<SwapchainDongXi, vk::Result> {
let surface_capabilities = surfaces.get_capabilities(physical_device)?;
let extent = surface_capabilities.current_extent;
let surface_present_modes = surfaces.get_present_modes(physical_device)?;
let surface_format = *surfaces.get_formats(physical_device)?.first().unwrap();
let queuefamilies = [queue_families.graphics_q_index.unwrap()];
let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder()
.surface(surfaces.surface)
.min_image_count(
3.max(surface_capabilities.min_image_count)
.min(surface_capabilities.max_image_count),
)
.image_format(surface_format.format)
.image_color_space(surface_format.color_space)
.image_extent(extent)
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.queue_family_indices(&queuefamilies)
.pre_transform(surface_capabilities.current_transform)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.present_mode(vk::PresentModeKHR::FIFO);
let swapchain_loader = ash::extensions::khr::Swapchain::new(instance, logical_device);
let swapchain = unsafe { swapchain_loader.create_swapchain(&swapchain_create_info, None)? };
let swapchain_images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? };
let amount_of_images = swapchain_images.len() as u32;
let mut swapchain_imageviews = Vec::with_capacity(swapchain_images.len());
for image in &swapchain_images {
let subresource_range = vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1);
let imageview_create_info = vk::ImageViewCreateInfo::builder()
.image(*image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.subresource_range(*subresource_range);
let imageview =
unsafe { logical_device.create_image_view(&imageview_create_info, None) }?;
swapchain_imageviews.push(imageview);
}
let mut image_available = vec![];
let mut rendering_finished = vec![];
let mut may_begin_drawing = vec![];
let semaphoreinfo = vk::SemaphoreCreateInfo::builder();
let fenceinfo = vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
for _ in 0..amount_of_images {
let semaphore_available =
unsafe { logical_device.create_semaphore(&semaphoreinfo, None) }?;
let semaphore_finished =
unsafe { logical_device.create_semaphore(&semaphoreinfo, None) }?;
image_available.push(semaphore_available);
rendering_finished.push(semaphore_finished);
let fence = unsafe { logical_device.create_fence(&fenceinfo, None) }?;
may_begin_drawing.push(fence);
}
Ok(SwapchainDongXi {
swapchain_loader,
swapchain,
images: swapchain_images,
imageviews: swapchain_imageviews,
framebuffers: vec![],
surface_format,
extent,
amount_of_images,
current_image: 0,
image_available,
rendering_finished,
may_begin_drawing,
})
}
fn create_framebuffers(
&mut self,
logical_device: &ash::Device,
renderpass: vk::RenderPass,
) -> Result<(), vk::Result> {
for iv in &self.imageviews {
let iview = [*iv];
let framebuffer_info = vk::FramebufferCreateInfo::builder()
.render_pass(renderpass)
.attachments(&iview)
.width(self.extent.width)
.height(self.extent.height)
.layers(1);
let fb = unsafe { logical_device.create_framebuffer(&framebuffer_info, None) }?;
self.framebuffers.push(fb);
}
Ok(())
}
unsafe fn cleanup(&mut self, logical_device: &ash::Device) {
for fence in &self.may_begin_drawing {
logical_device.destroy_fence(*fence, None);
}
for semaphore in &self.image_available {
logical_device.destroy_semaphore(*semaphore, None);
}
for semaphore in &self.rendering_finished {
logical_device.destroy_semaphore(*semaphore, None);
}
for fb in &self.framebuffers {
logical_device.destroy_framebuffer(*fb, None);
}
for iv in &self.imageviews {
logical_device.destroy_image_view(*iv, None);
}
self.swapchain_loader
.destroy_swapchain(self.swapchain, None)
}
}
fn init_renderpass(
logical_device: &ash::Device,
physical_device: vk::PhysicalDevice,
format: vk::Format,
) -> Result<vk::RenderPass, vk::Result> {
let attachments = [vk::AttachmentDescription::builder()
.format(format)
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::STORE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.final_layout(vk::ImageLayout::PRESENT_SRC_KHR)
.samples(vk::SampleCountFlags::TYPE_1)
.build()];
let color_attachment_references = [vk::AttachmentReference {
attachment: 0,
layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
}];
let subpasses = [vk::SubpassDescription::builder()
.color_attachments(&color_attachment_references)
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.build()];
let subpass_dependencies = [vk::SubpassDependency::builder()
.src_subpass(vk::SUBPASS_EXTERNAL)
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
.dst_subpass(0)
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
)
.build()];
let renderpass_info = vk::RenderPassCreateInfo::builder()
.attachments(&attachments)
.subpasses(&subpasses)
.dependencies(&subpass_dependencies);
let renderpass = unsafe { logical_device.create_render_pass(&renderpass_info, None)? };
Ok(renderpass)
}
struct Pipeline {
pipeline: vk::Pipeline,
layout: vk::PipelineLayout,
}
impl Pipeline {
fn cleanup(&self, logical_device: &ash::Device) {
unsafe {
logical_device.destroy_pipeline(self.pipeline, None);
logical_device.destroy_pipeline_layout(self.layout, None);
}
}
fn init(
logical_device: &ash::Device,
swapchain: &SwapchainDongXi,
renderpass: &vk::RenderPass,
) -> Result<Pipeline, vk::Result> {
let vertexshader_createinfo = vk::ShaderModuleCreateInfo::builder().code(
vk_shader_macros::include_glsl!("./shaders/shader.vert", kind: vert),
);
let vertexshader_module =
unsafe { logical_device.create_shader_module(&vertexshader_createinfo, None)? };
let fragmentshader_createinfo = vk::ShaderModuleCreateInfo::builder()
.code(vk_shader_macros::include_glsl!("./shaders/shader.frag"));
let fragmentshader_module =
unsafe { logical_device.create_shader_module(&fragmentshader_createinfo, None)? };
let mainfunctionname = std::ffi::CString::new("main").unwrap();
let vertexshader_stage = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.module(vertexshader_module)
.name(&mainfunctionname);
let fragmentshader_stage = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(fragmentshader_module)
.name(&mainfunctionname);
let shader_stages = vec![vertexshader_stage.build(), fragmentshader_stage.build()];
let vertex_attrib_descs = [
vk::VertexInputAttributeDescription {
binding: 0,
location: 0,
offset: 0,
format: vk::Format::R32G32B32A32_SFLOAT,
},
vk::VertexInputAttributeDescription {
binding: 1,
location: 1,
offset: 0,
format: vk::Format::R32_SFLOAT,
},
vk::VertexInputAttributeDescription {
binding: 1,
location: 2,
offset: 4,
format: vk::Format::R32G32B32A32_SFLOAT,
},
];
let vertex_binding_descs = [
vk::VertexInputBindingDescription {
binding: 0,
stride: 16,
input_rate: vk::VertexInputRate::VERTEX,
},
vk::VertexInputBindingDescription {
binding: 1,
stride: 20,
input_rate: vk::VertexInputRate::VERTEX,
},
];
let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::builder()
.vertex_attribute_descriptions(&vertex_attrib_descs)
.vertex_binding_descriptions(&vertex_binding_descs);
let input_assembly_info = vk::PipelineInputAssemblyStateCreateInfo::builder()
.topology(vk::PrimitiveTopology::POINT_LIST);
let viewports = [vk::Viewport {
x: 0.,
y: 0.,
width: swapchain.extent.width as f32,
height: swapchain.extent.height as f32,
min_depth: 0.,
max_depth: 1.,
}];
let scissors = [vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: swapchain.extent,
}];
let viewport_info = vk::PipelineViewportStateCreateInfo::builder()
.viewports(&viewports)
.scissors(&scissors);
let rasterizer_info = vk::PipelineRasterizationStateCreateInfo::builder()
.line_width(1.0)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.cull_mode(vk::CullModeFlags::NONE)
.polygon_mode(vk::PolygonMode::FILL);
let multisampler_info = vk::PipelineMultisampleStateCreateInfo::builder()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let colourblend_attachments = [vk::PipelineColorBlendAttachmentState::builder()
.blend_enable(true)
.src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
.dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
.color_blend_op(vk::BlendOp::ADD)
.src_alpha_blend_factor(vk::BlendFactor::SRC_ALPHA)
.dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask(
vk::ColorComponentFlags::R
| vk::ColorComponentFlags::G
| vk::ColorComponentFlags::B
| vk::ColorComponentFlags::A,
)
.build()];
let colourblend_info =
vk::PipelineColorBlendStateCreateInfo::builder().attachments(&colourblend_attachments);
let pipelinelayout_info = vk::PipelineLayoutCreateInfo::builder();
let pipelinelayout =
unsafe { logical_device.create_pipeline_layout(&pipelinelayout_info, None) }?;
let pipeline_info = vk::GraphicsPipelineCreateInfo::builder()
.stages(&shader_stages)
.vertex_input_state(&vertex_input_info)
.input_assembly_state(&input_assembly_info)
.viewport_state(&viewport_info)
.rasterization_state(&rasterizer_info)
.multisample_state(&multisampler_info)
.color_blend_state(&colourblend_info)
.layout(pipelinelayout)
.render_pass(*renderpass)
.subpass(0);
let graphicspipeline = unsafe {
logical_device
.create_graphics_pipelines(
vk::PipelineCache::null(),
&[pipeline_info.build()],
None,
)
.expect("A problem with the pipeline creation")
}[0];
unsafe {
logical_device.destroy_shader_module(fragmentshader_module, None);
logical_device.destroy_shader_module(vertexshader_module, None);
}
Ok(Pipeline {
pipeline: graphicspipeline,
layout: pipelinelayout,
})
}
}
struct Pools {
commandpool_graphics: vk::CommandPool,
commandpool_transfer: vk::CommandPool,
}
impl Pools {
fn init(
logical_device: &ash::Device,
queue_families: &QueueFamilies,
) -> Result<Pools, vk::Result> {
let graphics_commandpool_info = vk::CommandPoolCreateInfo::builder()
.queue_family_index(queue_families.graphics_q_index.unwrap())
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
let commandpool_graphics =
unsafe { logical_device.create_command_pool(&graphics_commandpool_info, None) }?;
let transfer_commandpool_info = vk::CommandPoolCreateInfo::builder()
.queue_family_index(queue_families.transfer_q_index.unwrap())
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
let commandpool_transfer =
unsafe { logical_device.create_command_pool(&transfer_commandpool_info, None) }?;
Ok(Pools {
commandpool_graphics,
commandpool_transfer,
})
}
fn cleanup(&self, logical_device: &ash::Device) {
unsafe {
logical_device.destroy_command_pool(self.commandpool_graphics, None);
logical_device.destroy_command_pool(self.commandpool_transfer, None);
}
}
}
fn create_commandbuffers(
logical_device: &ash::Device,
pools: &Pools,
amount: u32,
) -> Result<Vec<vk::CommandBuffer>, vk::Result> {
let commandbuf_allocate_info = vk::CommandBufferAllocateInfo::builder()
.command_pool(pools.commandpool_graphics)
.command_buffer_count(amount);
unsafe { logical_device.allocate_command_buffers(&commandbuf_allocate_info) }
}
fn fill_commandbuffers(
commandbuffers: &[vk::CommandBuffer],
logical_device: &ash::Device,
renderpass: &vk::RenderPass,
swapchain: &SwapchainDongXi,
pipeline: &Pipeline,
vb1: &vk::Buffer,
vb2: &vk::Buffer,
) -> Result<(), vk::Result> {
for (i, &commandbuffer) in commandbuffers.iter().enumerate() {
let commandbuffer_begininfo = vk::CommandBufferBeginInfo::builder();
unsafe {
logical_device.begin_command_buffer(commandbuffer, &commandbuffer_begininfo)?;
}
let clearvalues = [vk::ClearValue {
color: vk::ClearColorValue {
float32: [0.0, 0.0, 0.08, 1.0],
},
}];
let renderpass_begininfo = vk::RenderPassBeginInfo::builder()
.render_pass(*renderpass)
.framebuffer(swapchain.framebuffers[i])
.render_area(vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: swapchain.extent,
})
.clear_values(&clearvalues);
unsafe {
logical_device.cmd_begin_render_pass(
commandbuffer,
&renderpass_begininfo,
vk::SubpassContents::INLINE,
);
logical_device.cmd_bind_pipeline(
commandbuffer,
vk::PipelineBindPoint::GRAPHICS,
pipeline.pipeline,
);
logical_device.cmd_bind_vertex_buffers(commandbuffer, 0, &[*vb1], &[0]);
logical_device.cmd_bind_vertex_buffers(commandbuffer, 1, &[*vb2], &[0]);
logical_device.cmd_draw(commandbuffer, 3, 1, 0, 0);
logical_device.cmd_end_render_pass(commandbuffer);
logical_device.end_command_buffer(commandbuffer)?;
}
}
Ok(())
}
struct Buffer {
buffer: vk::Buffer,
allocation: vk_mem::Allocation,
allocation_info: vk_mem::AllocationInfo,
}
impl Buffer {
fn new(
allocator: &vk_mem::Allocator,
size_in_bytes: u64,
usage: vk::BufferUsageFlags,
memory_usage: vk_mem::MemoryUsage,
) -> Result<Buffer, vk_mem::error::Error> {
let allocation_create_info = vk_mem::AllocationCreateInfo {
usage: memory_usage,
..Default::default()
};
let (buffer, allocation, allocation_info) = allocator.create_buffer(
&ash::vk::BufferCreateInfo::builder()
.size(size_in_bytes)
.usage(usage)
.build(),
&allocation_create_info,
)?;
Ok(Buffer {
buffer,
allocation,
allocation_info,
})
}
fn fill<T: Sized>(
&self,
allocator: &vk_mem::Allocator,
data: &[T],
) -> Result<(), vk_mem::error::Error> {
let data_ptr = allocator.map_memory(&self.allocation)? as *mut T;
unsafe { data_ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()) };
allocator.unmap_memory(&self.allocation)?;
Ok(())
}
}
struct Aetna {
window: winit::window::Window,
entry: ash::Entry,
instance: ash::Instance,
debug: std::mem::ManuallyDrop<DebugDongXi>,
surfaces: std::mem::ManuallyDrop<SurfaceDongXi>,
physical_device: vk::PhysicalDevice,
physical_device_properties: vk::PhysicalDeviceProperties,
queue_families: QueueFamilies,
queues: Queues,
device: ash::Device,
swapchain: SwapchainDongXi,
renderpass: vk::RenderPass,
pipeline: Pipeline,
pools: Pools,
commandbuffers: Vec<vk::CommandBuffer>,
allocator: vk_mem::Allocator,
buffers: Vec<Buffer>,
}
impl Aetna {
fn init(window: winit::window::Window) -> Result<Aetna, Box<dyn std::error::Error>> {
let entry = ash::Entry::new()?;
let layer_names = vec!["VK_LAYER_KHRONOS_validation"];
let instance = init_instance(&entry, &layer_names)?;
let debug = DebugDongXi::init(&entry, &instance)?;
let surfaces = SurfaceDongXi::init(&window, &entry, &instance)?;
let (physical_device, physical_device_properties) =
init_physical_device_and_properties(&instance)?;
let queue_families = QueueFamilies::init(&instance, physical_device, &surfaces)?;
let (logical_device, queues) =
init_device_and_queues(&instance, physical_device, &queue_families, &layer_names)?;
let mut swapchain = SwapchainDongXi::init(
&instance,
physical_device,
&logical_device,
&surfaces,
&queue_families,
&queues,
)?;
let renderpass = init_renderpass(
&logical_device,
physical_device,
swapchain.surface_format.format,
)?;
swapchain.create_framebuffers(&logical_device, renderpass)?;
let pipeline = Pipeline::init(&logical_device, &swapchain, &renderpass)?;
let pools = Pools::init(&logical_device, &queue_families)?;
let allocator_create_info = vk_mem::AllocatorCreateInfo {
physical_device,
device: logical_device.clone(),
instance: instance.clone(),
..Default::default()
};
let allocator = vk_mem::Allocator::new(&allocator_create_info)?;
let allocation_create_info = vk_mem::AllocationCreateInfo {
usage: vk_mem::MemoryUsage::CpuToGpu,
..Default::default()
};
let buffer1 = Buffer::new(
&allocator,
48,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer1.fill(
&allocator,
&[
0.5f32, 0.0f32, 0.0f32, 1.0f32, 0.0f32, 0.2f32, 0.0f32, 1.0f32, -0.5f32, 0.0f32,
0.0f32, 1.0f32,
],
)?;
let buffer2 = Buffer::new(
&allocator,
60,
vk::BufferUsageFlags::VERTEX_BUFFER,
vk_mem::MemoryUsage::CpuToGpu,
)?;
buffer2.fill(
&allocator,
&[
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32, 15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32,
15.0f32, 0.0f32, 1.0f32, 0.0f32, 1.0f32,
],
)?;
let commandbuffers =
create_commandbuffers(&logical_device, &pools, swapchain.amount_of_images)?;
fill_commandbuffers(
&commandbuffers,
&logical_device,
&renderpass,
&swapchain,
&pipeline,
&buffer1.buffer,
&buffer2.buffer,
)?;
Ok(Aetna {
window,
entry,
instance,
debug: std::mem::ManuallyDrop::new(debug),
surfaces: std::mem::ManuallyDrop::new(surfaces),
physical_device,
physical_device_properties,
queue_families,
queues,
device: logical_device,
swapchain,
renderpass,
pipeline,
pools,
commandbuffers,
allocator,
buffers: vec![buffer1, buffer2],
})
}
}
impl Drop for Aetna {
fn drop(&mut self) {
unsafe {
self.device
.device_wait_idle()
.expect("something wrong while waiting");
for b in &self.buffers {
self.allocator
.destroy_buffer(b.buffer, &b.allocation)
.expect("problem with buffer destruction");
}
self.allocator.destroy();
self.pools.cleanup(&self.device);
self.pipeline.cleanup(&self.device);
self.device.destroy_render_pass(self.renderpass, None);
self.swapchain.cleanup(&self.device);
self.device.destroy_device(None);
std::mem::ManuallyDrop::drop(&mut self.surfaces);
std::mem::ManuallyDrop::drop(&mut self.debug);
self.instance.destroy_instance(None)
};
}
}
이제 점 대신 삼각형을 그릴 때가 된 것 같습니다.