commit
27cf17d067
33 changed files with 2003 additions and 0 deletions
@ -0,0 +1,8 @@
|
||||
**/*.rs.bk |
||||
*.bat |
||||
*.user |
||||
.DS_Store |
||||
/out |
||||
/target |
||||
Cargo.lock |
||||
perf.data* |
@ -0,0 +1,46 @@
|
||||
[package] |
||||
name = "blonkus" |
||||
version = "0.1.0" |
||||
authors = ["Alison G. Watson <marrub@greyserv.net>", "Tae Matous"] |
||||
description = "Some bullshit." |
||||
homepage = "https://greyserv.net/blonkus/" |
||||
repository = "http://git.greyserv.net/marrub/BLONKUS" |
||||
readme = "README.md" |
||||
license = "CC0-1.0" |
||||
edition = "2018" |
||||
publish = false |
||||
build = "source/build.rs" |
||||
|
||||
[features] |
||||
default = ["color-log"] |
||||
color-log = ["termcolor"] |
||||
|
||||
[dependencies] |
||||
ash = "0.29" |
||||
sdl2 = "0.32" |
||||
serde_yaml = "0.8" |
||||
|
||||
[dependencies.failure] |
||||
version = "0.1" |
||||
features = ["std"] |
||||
|
||||
[dependencies.termcolor] |
||||
version = "1.0" |
||||
optional = true |
||||
|
||||
[dependencies.serde] |
||||
version = "1.0" |
||||
features = ["derive"] |
||||
|
||||
[profile.dev] |
||||
opt-level = 1 |
||||
|
||||
[profile.release] |
||||
codegen-units = 1 |
||||
lto = true |
||||
|
||||
[[bin]] |
||||
name = "blonkus" |
||||
path = "source/main.rs" |
||||
|
||||
# EOF |
@ -0,0 +1,126 @@
|
||||
To the extent possible under law, I, Alison G. Watson, and all other |
||||
contributors to BLONKUS, have waived all copyright and related or neighboring |
||||
rights to this Document as described by the Creative Commons Zero license as |
||||
follows: |
||||
|
||||
Creative Commons Legal Code |
||||
|
||||
CC0 1.0 Universal |
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE |
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN |
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS |
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES |
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS |
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM |
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED |
||||
HEREUNDER. |
||||
|
||||
Statement of Purpose |
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer |
||||
exclusive Copyright and Related Rights (defined below) upon the creator |
||||
and subsequent owner(s) (each and all, an "owner") of an original work of |
||||
authorship and/or a database (each, a "Work"). |
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for |
||||
the purpose of contributing to a commons of creative, cultural and |
||||
scientific works ("Commons") that the public can reliably and without fear |
||||
of later claims of infringement build upon, modify, incorporate in other |
||||
works, reuse and redistribute as freely as possible in any form whatsoever |
||||
and for any purposes, including without limitation commercial purposes. |
||||
These owners may contribute to the Commons to promote the ideal of a free |
||||
culture and the further production of creative, cultural and scientific |
||||
works, or to gain reputation or greater distribution for their Work in |
||||
part through the use and efforts of others. |
||||
|
||||
For these and/or other purposes and motivations, and without any |
||||
expectation of additional consideration or compensation, the person |
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she |
||||
is an owner of Copyright and Related Rights in the Work, voluntarily |
||||
elects to apply CC0 to the Work and publicly distribute the Work under its |
||||
terms, with knowledge of his or her Copyright and Related Rights in the |
||||
Work and the meaning and intended legal effect of CC0 on those rights. |
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be |
||||
protected by copyright and related or neighboring rights ("Copyright and |
||||
Related Rights"). Copyright and Related Rights include, but are not |
||||
limited to, the following: |
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display, |
||||
communicate, and translate a Work; |
||||
ii. moral rights retained by the original author(s) and/or performer(s); |
||||
iii. publicity and privacy rights pertaining to a person's image or |
||||
likeness depicted in a Work; |
||||
iv. rights protecting against unfair competition in regards to a Work, |
||||
subject to the limitations in paragraph 4(a), below; |
||||
v. rights protecting the extraction, dissemination, use and reuse of data |
||||
in a Work; |
||||
vi. database rights (such as those arising under Directive 96/9/EC of the |
||||
European Parliament and of the Council of 11 March 1996 on the legal |
||||
protection of databases, and under any national implementation |
||||
thereof, including any amended or successor version of such |
||||
directive); and |
||||
vii. other similar, equivalent or corresponding rights throughout the |
||||
world based on applicable law or treaty, and any national |
||||
implementations thereof. |
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention |
||||
of, applicable law, Affirmer hereby overtly, fully, permanently, |
||||
irrevocably and unconditionally waives, abandons, and surrenders all of |
||||
Affirmer's Copyright and Related Rights and associated claims and causes |
||||
of action, whether now known or unknown (including existing as well as |
||||
future claims and causes of action), in the Work (i) in all territories |
||||
worldwide, (ii) for the maximum duration provided by applicable law or |
||||
treaty (including future time extensions), (iii) in any current or future |
||||
medium and for any number of copies, and (iv) for any purpose whatsoever, |
||||
including without limitation commercial, advertising or promotional |
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each |
||||
member of the public at large and to the detriment of Affirmer's heirs and |
||||
successors, fully intending that such Waiver shall not be subject to |
||||
revocation, rescission, cancellation, termination, or any other legal or |
||||
equitable action to disrupt the quiet enjoyment of the Work by the public |
||||
as contemplated by Affirmer's express Statement of Purpose. |
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason |
||||
be judged legally invalid or ineffective under applicable law, then the |
||||
Waiver shall be preserved to the maximum extent permitted taking into |
||||
account Affirmer's express Statement of Purpose. In addition, to the |
||||
extent the Waiver is so judged Affirmer hereby grants to each affected |
||||
person a royalty-free, non transferable, non sublicensable, non exclusive, |
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and |
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the |
||||
maximum duration provided by applicable law or treaty (including future |
||||
time extensions), (iii) in any current or future medium and for any number |
||||
of copies, and (iv) for any purpose whatsoever, including without |
||||
limitation commercial, advertising or promotional purposes (the |
||||
"License"). The License shall be deemed effective as of the date CC0 was |
||||
applied by Affirmer to the Work. Should any part of the License for any |
||||
reason be judged legally invalid or ineffective under applicable law, such |
||||
partial invalidity or ineffectiveness shall not invalidate the remainder |
||||
of the License, and in such case Affirmer hereby affirms that he or she |
||||
will not (i) exercise any of his or her remaining Copyright and Related |
||||
Rights in the Work or (ii) assert any associated claims and causes of |
||||
action with respect to the Work, in either case contrary to Affirmer's |
||||
express Statement of Purpose. |
||||
|
||||
4. Limitations and Disclaimers. |
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned, |
||||
surrendered, licensed or otherwise affected by this document. |
||||
b. Affirmer offers the Work as-is and makes no representations or |
||||
warranties of any kind concerning the Work, express, implied, |
||||
statutory or otherwise, including without limitation warranties of |
||||
title, merchantability, fitness for a particular purpose, non |
||||
infringement, or the absence of latent or other defects, accuracy, or |
||||
the present or absence of errors, whether or not discoverable, all to |
||||
the greatest extent permissible under applicable law. |
||||
c. Affirmer disclaims responsibility for clearing rights of other persons |
||||
that may apply to the Work or any use thereof, including without |
||||
limitation any person's Copyright and Related Rights in the Work. |
||||
Further, Affirmer disclaims responsibility for obtaining any necessary |
||||
consents, permissions or other rights required for any use of the |
||||
Work. |
||||
d. Affirmer understands and acknowledges that Creative Commons is not a |
||||
party to this document and has no duty or obligation with respect to |
||||
this CC0 or use of the Work. |
@ -0,0 +1,8 @@
|
||||
layout(location = 0) in vec3 frag_color; |
||||
layout(location = 0) out vec4 out_color; |
||||
|
||||
void main() { |
||||
out_color = vec4(frag_color, 1.0); |
||||
} |
||||
|
||||
// EOF |
@ -0,0 +1,20 @@
|
||||
layout(location = 0) out vec3 frag_color; |
||||
|
||||
vec2 positions[3] = vec2[]( |
||||
vec2( 0.0, -0.5), |
||||
vec2( 0.5, 0.5), |
||||
vec2(-0.5, 0.5) |
||||
); |
||||
|
||||
vec3 colors[3] = vec3[]( |
||||
vec3(1.0, 0.0, 0.0), |
||||
vec3(0.0, 1.0, 0.0), |
||||
vec3(0.0, 0.0, 1.0) |
||||
); |
||||
|
||||
void main() { |
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); |
||||
frag_color = colors[gl_VertexIndex]; |
||||
} |
||||
|
||||
// EOF |
@ -0,0 +1,6 @@
|
||||
hard_tabs = true |
||||
max_width = 80 |
||||
newline_style = "Unix" |
||||
tab_spaces = 3 |
||||
use_field_init_shorthand = true |
||||
use_small_heuristics = "Off" |
@ -0,0 +1,25 @@
|
||||
fn main() { |
||||
let out_dir = std::env::var("OUT_DIR").unwrap(); |
||||
|
||||
for file in std::fs::read_dir("glsl").unwrap() { |
||||
let file = file.unwrap(); |
||||
let path = file.path(); |
||||
let file_name = path.file_name().unwrap(); |
||||
let out = format!("{}/{}.o", out_dir, file_name.to_str().unwrap()); |
||||
let path = path.to_str().unwrap(); |
||||
println!("rerun-if-changed={}", path); |
||||
std::process::Command::new("glslc") |
||||
.arg("-O") |
||||
.arg("-o") |
||||
.arg(&out) |
||||
.arg("-std=450core") |
||||
.arg("--target-env=vulkan1.0") |
||||
.arg("-Werror") |
||||
.arg("-xglsl") |
||||
.arg(&path) |
||||
.status() |
||||
.unwrap(); |
||||
} |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,222 @@
|
||||
#[macro_use] |
||||
mod util; |
||||
|
||||
mod render; |
||||
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::{render::*, util::{err, log, meta}}; |
||||
use std::convert::TryInto; |
||||
|
||||
serialize! { |
||||
conf: |
||||
#[derive(Default)] |
||||
struct Conf { |
||||
log: log::Conf, |
||||
render: render::Conf, |
||||
} |
||||
} |
||||
|
||||
const MAIN_VERT: &'static [u8] = |
||||
include_bytes!(concat!(env!("OUT_DIR"), "/main.vert.o")); |
||||
const MAIN_FRAG: &'static [u8] = |
||||
include_bytes!(concat!(env!("OUT_DIR"), "/main.frag.o")); |
||||
|
||||
fn draw_frame( |
||||
device: &Device, |
||||
swapchain: &Swapchain, |
||||
image_available_s: &Semaphore, |
||||
render_finished_s: &Semaphore, |
||||
cmd_buffers: &[CommandBuffer], |
||||
queue_graphics: &Queue, |
||||
queue_surface: &Queue, |
||||
) -> err::Result<()> { |
||||
let image_index = unsafe { |
||||
device.swapchain_ext.acquire_next_image( |
||||
**swapchain, |
||||
u64::max_value(), |
||||
**image_available_s, |
||||
vk::Fence::null(), |
||||
)?.0 |
||||
}; |
||||
|
||||
let wait_semaphores = [**image_available_s]; |
||||
let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; |
||||
|
||||
let signal_semaphores = [**render_finished_s]; |
||||
|
||||
let submit_cmd_buffers = [*cmd_buffers[image_index as usize]]; |
||||
|
||||
let submit_info = [ |
||||
vk::SubmitInfo { |
||||
wait_semaphore_count: wait_semaphores.len().try_into()?, |
||||
p_wait_semaphores: wait_semaphores.as_ptr(), |
||||
p_wait_dst_stage_mask: wait_stages.as_ptr(), |
||||
command_buffer_count: submit_cmd_buffers.len().try_into()?, |
||||
p_command_buffers: submit_cmd_buffers.as_ptr(), |
||||
signal_semaphore_count: signal_semaphores.len().try_into()?, |
||||
p_signal_semaphores: signal_semaphores.as_ptr(), |
||||
..Default::default() |
||||
} |
||||
]; |
||||
|
||||
unsafe { |
||||
device.queue_submit(**queue_graphics, &submit_info, vk::Fence::null())?; |
||||
} |
||||
|
||||
let swapchains = [**swapchain]; |
||||
let image_indices = [image_index]; |
||||
|
||||
let present_info = vk::PresentInfoKHR { |
||||
wait_semaphore_count: signal_semaphores.len().try_into()?, |
||||
p_wait_semaphores: signal_semaphores.as_ptr(), |
||||
swapchain_count: swapchains.len().try_into()?, |
||||
p_swapchains: swapchains.as_ptr(), |
||||
p_image_indices: image_indices.as_ptr(), |
||||
..Default::default() |
||||
}; |
||||
|
||||
unsafe { |
||||
device.swapchain_ext.queue_present(**queue_surface, &present_info)?; |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn fallback_main(conf: &Conf, lg: &log::Log) -> err::Result<()> { |
||||
let sdl_context = err::from_s(sdl2::init())?; |
||||
let sdl_video = err::from_s(sdl_context.video())?; |
||||
|
||||
let window = sdl_video.window(meta::name(), 640, 480).vulkan().build()?; |
||||
|
||||
let entry = ash::Entry::new()?; |
||||
|
||||
let instance = Instance::create(&conf.render, &entry, &window)?; |
||||
|
||||
let phys_device = PhysicalDevice::get(&instance, &conf.render)?; |
||||
|
||||
let surface = Surface::create(&instance, &window)?; |
||||
|
||||
let qf_info = QueueFamilyInfo::collect(&instance, &surface, &phys_device)?; |
||||
|
||||
let device = Device::create(&instance, &phys_device, &qf_info)?; |
||||
|
||||
let queue_graphics = Queue::get(&device, qf_info.gfx_index); |
||||
let queue_surface = Queue::get(&device, qf_info.srf_index); |
||||
|
||||
let swapchain = Swapchain::create( |
||||
&instance, |
||||
&device, |
||||
&phys_device, |
||||
&surface, |
||||
&conf.render, |
||||
&qf_info, |
||||
)?; |
||||
|
||||
let image_views = |
||||
swapchain |
||||
.get_images()? |
||||
.iter() |
||||
.map(|&image| ImageView::create(&device, image, swapchain.format)) |
||||
.collect::<Result<Vec<_>, _>>()?; |
||||
|
||||
let shader_vert = ShaderModule::create(&device, &Spir::read(MAIN_VERT))?; |
||||
let shader_frag = ShaderModule::create(&device, &Spir::read(MAIN_FRAG))?; |
||||
|
||||
let layout = PipelineLayout::create(&device)?; |
||||
let render_pass = RenderPass::create(&device, swapchain.format)?; |
||||
let pipelines = Pipeline::create( |
||||
&device, |
||||
&layout, |
||||
&render_pass, |
||||
&shader_vert, |
||||
&shader_frag, |
||||
swapchain.extent, |
||||
)?; |
||||
|
||||
let framebuffers = |
||||
image_views |
||||
.iter() |
||||
.map(|image_view| { |
||||
Framebuffer::create( |
||||
&device, |
||||
&render_pass, |
||||
image_view, |
||||
swapchain.extent, |
||||
) |
||||
}) |
||||
.collect::<Result<Vec<_>, _>>()?; |
||||
|
||||
let cmd_pool = CommandPool::create(&device, qf_info.gfx_index)?; |
||||
let cmd_buffers = cmd_pool.allocate_buffers(&framebuffers)?; |
||||
|
||||
cmd_buffers |
||||
.iter() |
||||
.zip(&framebuffers) |
||||
.map(|(cmd_buf, framebuffer)| { |
||||
cmd_buf.bind( |
||||
framebuffer, |
||||
&render_pass, |
||||
&pipelines[0], |
||||
swapchain.extent, |
||||
) |
||||
}) |
||||
.collect::<Result<(), _>>()?; |
||||
|
||||
let image_available_s = Semaphore::create(&device)?; |
||||
let render_finished_s = Semaphore::create(&device)?; |
||||
|
||||
let frame = draw_frame( |
||||
&device, |
||||
&swapchain, |
||||
&image_available_s, |
||||
&render_finished_s, |
||||
&cmd_buffers, |
||||
&queue_graphics, |
||||
&queue_surface, |
||||
); |
||||
if let Err(e) = frame { |
||||
lg!(lg, Error, "Error rendering frame: {}", e); |
||||
} |
||||
|
||||
std::thread::sleep(std::time::Duration::from_secs(3)); |
||||
|
||||
let seize = unsafe { device.device_wait_idle() }; |
||||
if let Err(e) = seize { |
||||
lg!(lg, Error, "Error seizing renderer state: {}", e); |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn main() { |
||||
let lg = |
||||
log::Log::new() |
||||
.insert_mode("Std", log::Std); |
||||
|
||||
#[cfg(feature = "color-log")] |
||||
let lg = lg.insert_mode("Color", log::Color).set_mode("Color").unwrap(); |
||||
|
||||
trace!(&lg); |
||||
|
||||
let (conf, lg) = if let Ok(file) = std::fs::File::open("blonkus.yaml") { |
||||
let conf: Result<Conf, _> = serde_yaml::from_reader(file); |
||||
match conf { |
||||
Ok(conf) => { |
||||
(conf, lg) |
||||
} |
||||
Err(e) => { |
||||
lg!(&lg, Error, "Error loading configuration: {}", e); |
||||
(Conf::default(), lg) |
||||
} |
||||
} |
||||
} else { |
||||
lg!(&lg, Info, "No configuration file available. Using defaults."); |
||||
(Conf::default(), lg) |
||||
}; |
||||
|
||||
if let Err(e) = fallback_main(&conf, &lg) { |
||||
lg!(&lg, Critical, "Uncaught error: {}", e); |
||||
} |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,65 @@
|
||||
mod cmd; |
||||
mod conf; |
||||
mod device; |
||||
mod framebuffer; |
||||
mod imageview; |
||||
mod instance; |
||||
mod pipeline; |
||||
mod queue; |
||||
mod semaphore; |
||||
mod shader; |
||||
mod spir; |
||||
mod surface; |
||||
mod swapchain; |
||||
|
||||
pub use self::{ |
||||
cmd::{CommandBuffer, CommandPool}, |
||||
conf::{Conf, PresentMode}, |
||||
device::{Device, PhysicalDevice}, |
||||
framebuffer::Framebuffer, |
||||
imageview::ImageView, |
||||
instance::Instance, |
||||
pipeline::{PipelineLayout, Pipeline, RenderPass}, |
||||
queue::{Queue, QueueFamilyInfo}, |
||||
semaphore::Semaphore, |
||||
shader::ShaderModule, |
||||
spir::Spir, |
||||
surface::Surface, |
||||
swapchain::Swapchain, |
||||
}; |
||||
|
||||
use std::{ffi::CStr, os::raw::c_char}; |
||||
|
||||
#[derive(Debug)] |
||||
pub struct ErrNoProperty { |
||||
name: String, |
||||
} |
||||
|
||||
impl std::fmt::Display for ErrNoProperty { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
write!(f, "{} not available", self.name) |
||||
} |
||||
} |
||||
|
||||
impl std::error::Error for ErrNoProperty {} |
||||
|
||||
pub fn ensure_properties<T, F>( |
||||
props: &[T], |
||||
names: &[*const c_char], |
||||
func: F, |
||||
) -> Result<(), ErrNoProperty> |
||||
where |
||||
F: Fn(&T) -> *const c_char, |
||||
{ |
||||
for name in names { |
||||
let name = unsafe { CStr::from_ptr(*name) }; |
||||
|
||||
if !props.iter().any(|x| unsafe { CStr::from_ptr(func(x)) == name }) { |
||||
return Err(ErrNoProperty { name: name.to_string_lossy().to_string() }); |
||||
} |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,127 @@
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::{render::{Device, Framebuffer, Pipeline, RenderPass}, util::err}; |
||||
use std::convert::TryInto; |
||||
|
||||
pub struct CommandPool<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::CommandPool, |
||||
} |
||||
|
||||
pub struct CommandBuffer<'a, 'b, 'c> { |
||||
pool: &'a CommandPool<'b, 'c>, |
||||
handle: vk::CommandBuffer, |
||||
} |
||||
|
||||
impl<'a, 'b> CommandPool<'a, 'b> { |
||||
pub fn create(device: &'a Device<'b>, index: u32) -> err::Result<Self> { |
||||
let handle = unsafe { |
||||
device.create_command_pool( |
||||
&vk::CommandPoolCreateInfo { |
||||
queue_family_index: index, |
||||
..Default::default() |
||||
}, |
||||
None, |
||||
)? |
||||
}; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
|
||||
pub fn allocate_buffers( |
||||
&self, |
||||
framebuffers: &[Framebuffer], |
||||
) -> err::Result<Vec<CommandBuffer>> { |
||||
let alloc_info = vk::CommandBufferAllocateInfo { |
||||
command_pool: **self, |
||||
level: vk::CommandBufferLevel::PRIMARY, |
||||
command_buffer_count: framebuffers.len().try_into()?, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let bufs = unsafe { |
||||
self |
||||
.device |
||||
.allocate_command_buffers(&alloc_info)? |
||||
.iter() |
||||
.map(|&handle| CommandBuffer { pool: self, handle }) |
||||
.collect() |
||||
}; |
||||
|
||||
Ok(bufs) |
||||
} |
||||
} |
||||
|
||||
impl CommandBuffer<'_, '_, '_> { |
||||
pub fn bind( |
||||
&self, |
||||
framebuffer: &Framebuffer, |
||||
render_pass: &RenderPass, |
||||
pipeline: &Pipeline, |
||||
image_extent: vk::Extent2D, |
||||
) -> err::Result<()> { |
||||
let begin_info = vk::CommandBufferBeginInfo::default(); |
||||
|
||||
unsafe { |
||||
self.pool.device.begin_command_buffer(self.handle, &begin_info)?; |
||||
} |
||||
|
||||
let clear_values = [ |
||||
vk::ClearValue { |
||||
color: vk::ClearColorValue { |
||||
float32: [0.0, 0.0, 0.0, 1.0], |
||||
} |
||||
} |
||||
]; |
||||
|
||||
let begin_info = vk::RenderPassBeginInfo { |
||||
render_pass: **render_pass, |
||||
framebuffer: **framebuffer, |
||||
render_area: vk::Rect2D { |
||||
offset: vk::Offset2D::default(), |
||||
extent: image_extent, |
||||
}, |
||||
clear_value_count: clear_values.len().try_into()?, |
||||
p_clear_values: clear_values.as_ptr(), |
||||
..Default::default() |
||||
}; |
||||
|
||||
unsafe { |
||||
self.pool.device.cmd_begin_render_pass( |
||||
self.handle, |
||||
&begin_info, |
||||
vk::SubpassContents::INLINE, |
||||
); |
||||
|
||||
self.pool.device.cmd_bind_pipeline( |
||||
self.handle, |
||||
vk::PipelineBindPoint::GRAPHICS, |
||||
**pipeline, |
||||
); |
||||
|
||||
self.pool.device.cmd_draw(self.handle, 3, 1, 0, 0); |
||||
|
||||
self.pool.device.cmd_end_render_pass(self.handle); |
||||
|
||||
self.pool.device.end_command_buffer(self.handle)?; |
||||
} |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
impl Drop for CommandPool<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_command_pool(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for CommandPool<'_, '_> { |
||||
type Target = vk::CommandPool; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
impl std::ops::Deref for CommandBuffer<'_, '_, '_> { |
||||
type Target = vk::CommandBuffer; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,44 @@
|
||||
use ash::vk; |
||||
|
||||
serialize! { |
||||
conf_enum: |
||||
#[derive(Clone, Copy)] |
||||
pub enum PresentMode { |
||||
Immediate, |
||||
Mailbox, |
||||
Fifo, |
||||
FifoRelaxed, |
||||
} |
||||
} |
||||
|
||||
serialize! { |
||||
conf: |
||||
pub struct Conf { |
||||
pub device: usize, |
||||
pub validation_layers: bool, |
||||
pub swap_mode: PresentMode, |
||||
} |
||||
} |
||||
|
||||
impl Into<vk::PresentModeKHR> for PresentMode { |
||||
fn into(self) -> vk::PresentModeKHR { |
||||
match self { |
||||
Self::Immediate => vk::PresentModeKHR::IMMEDIATE, |
||||
Self::Mailbox => vk::PresentModeKHR::MAILBOX, |
||||
Self::Fifo => vk::PresentModeKHR::FIFO, |
||||
Self::FifoRelaxed => vk::PresentModeKHR::FIFO_RELAXED, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Default for Conf { |
||||
fn default() -> Self { |
||||
Self { |
||||
device: 0, |
||||
validation_layers: cfg!(debug_assertions), |
||||
swap_mode: PresentMode::Mailbox, |
||||
} |
||||
} |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,111 @@
|
||||
use ash::{extensions::khr, version::{DeviceV1_0, InstanceV1_0}, vk}; |
||||
use crate::{ |
||||
render::{Conf, Instance, ensure_properties, QueueFamilyInfo}, |
||||
util::err |
||||
}; |
||||
use std::{convert::TryInto, marker::PhantomData, os::raw::c_char}; |
||||
|
||||
pub struct Device<'a> { |
||||
instance: PhantomData<&'a Instance>, |
||||
handle: ash::Device, |
||||
|
||||
pub swapchain_ext: khr::Swapchain, |
||||
} |
||||
|
||||
pub struct PhysicalDevice<'a> { |
||||
instance: PhantomData<&'a Instance>, |
||||
handle: vk::PhysicalDevice, |
||||
} |
||||
|
||||
fn enable_device_extensions( |
||||
instance: &ash::Instance, |
||||
phys_device: &PhysicalDevice, |
||||
extensions: &[*const c_char], |
||||
) -> err::Result<vk::DeviceCreateInfo> { |
||||
if extensions.len() > 0 { |
||||
let props = unsafe { |
||||
instance.enumerate_device_extension_properties(**phys_device)? |
||||
}; |
||||
|
||||
ensure_properties(&props, extensions, |x| x.extension_name.as_ptr())?; |
||||
} |
||||
|
||||
Ok(vk::DeviceCreateInfo { |
||||
enabled_extension_count: extensions.len().try_into()?, |
||||
pp_enabled_extension_names: extensions.as_ptr(), |
||||
..Default::default() |
||||
}) |
||||
} |
||||
|
||||
impl<'a> Device<'a> { |
||||
pub fn create( |
||||
instance: &'a Instance, |
||||
phys_device: &'a PhysicalDevice, |
||||
qf_info: &QueueFamilyInfo, |
||||
) -> err::Result<Self> { |
||||
const QUEUE_PRIORITY: [f32; 1] = [1.0]; |
||||
|
||||
let queue_create_info = [ |
||||
vk::DeviceQueueCreateInfo { |
||||
queue_family_index: qf_info.gfx_index, |
||||
queue_count: 1, |
||||
p_queue_priorities: QUEUE_PRIORITY.as_ptr(), |
||||
..Default::default() |
||||
}, |
||||
vk::DeviceQueueCreateInfo { |
||||
queue_family_index: qf_info.srf_index, |
||||
queue_count: 1, |
||||
p_queue_priorities: QUEUE_PRIORITY.as_ptr(), |
||||
..Default::default() |
||||
}, |
||||
]; |
||||
|
||||
let device_features = vk::PhysicalDeviceFeatures::default(); |
||||
|
||||
let extensions = [c_str!("VK_KHR_swapchain")]; |
||||
|
||||
let create_info = vk::DeviceCreateInfo { |
||||
queue_create_info_count: queue_create_info.len().try_into()?, |
||||
p_queue_create_infos: queue_create_info.as_ptr(), |
||||
p_enabled_features: &device_features, |
||||
..enable_device_extensions(instance, phys_device, &extensions)? |
||||
}; |
||||
|
||||
let handle = unsafe { |
||||
instance.create_device(**phys_device, &create_info, None)? |
||||
}; |
||||
|
||||
let swapchain_ext = khr::Swapchain::new(&**instance, &handle); |
||||
|
||||
Ok(Self { instance: PhantomData, handle, swapchain_ext }) |
||||
} |
||||
} |
||||
|
||||
impl<'a> PhysicalDevice<'a> { |
||||
pub fn get(instance: &'a Instance, conf: &Conf) -> err::Result<Self> { |
||||
let devs = unsafe { instance.enumerate_physical_devices()? }; |
||||
|
||||
if conf.device < devs.len() { |
||||
let handle = devs[conf.device]; |
||||
Ok(Self { instance: PhantomData, handle }) |
||||
} else { |
||||
Err(err::static_msg("no device for Vulkan")) |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Drop for Device<'_> { |
||||
fn drop(&mut self) { unsafe { self.handle.destroy_device(None); } } |
||||
} |
||||
|
||||
impl std::ops::Deref for Device<'_> { |
||||
type Target = ash::Device; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
impl std::ops::Deref for PhysicalDevice<'_> { |
||||
type Target = vk::PhysicalDevice; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,45 @@
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::{render::{Device, ImageView, RenderPass}, util::err}; |
||||
use std::convert::TryInto; |
||||
|
||||
pub struct Framebuffer<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::Framebuffer, |
||||
} |
||||
|
||||
impl<'a, 'b> Framebuffer<'a, 'b> { |
||||
pub fn create( |
||||
device: &'a Device<'b>, |
||||
render_pass: &RenderPass, |
||||
image_view: &ImageView, |
||||
image_extent: vk::Extent2D, |
||||
) -> err::Result<Self> { |
||||
let attachments = [**image_view]; |
||||
|
||||
let create_info = vk::FramebufferCreateInfo { |
||||
render_pass: **render_pass, |
||||
attachment_count: attachments.len().try_into()?, |
||||
p_attachments: attachments.as_ptr(), |
||||
width: image_extent.width, |
||||
height: image_extent.height, |
||||
layers: 1, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let handle = unsafe { device.create_framebuffer(&create_info, None)? }; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
} |
||||
|
||||
impl Drop for Framebuffer<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_framebuffer(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for Framebuffer<'_, '_> { |
||||
type Target = vk::Framebuffer; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,50 @@
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::{render::Device, util::err}; |
||||
|
||||
pub struct ImageView<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::ImageView, |
||||
} |
||||
|
||||
impl<'a, 'b> ImageView<'a, 'b> { |
||||
pub fn create( |
||||
device: &'a Device<'b>, |
||||
image: vk::Image, |
||||
format: vk::Format, |
||||
) -> err::Result<Self> { |
||||
let create_info = vk::ImageViewCreateInfo { |
||||
image, |
||||
format, |
||||
view_type: vk::ImageViewType::TYPE_2D, |
||||
components: vk::ComponentMapping { |
||||
r: vk::ComponentSwizzle::IDENTITY, |
||||
g: vk::ComponentSwizzle::IDENTITY, |
||||
b: vk::ComponentSwizzle::IDENTITY, |
||||
a: vk::ComponentSwizzle::IDENTITY, |
||||
}, |
||||
subresource_range: vk::ImageSubresourceRange { |
||||
aspect_mask: vk::ImageAspectFlags::COLOR, |
||||
base_mip_level: 0, |
||||
level_count: 1, |
||||
base_array_layer: 0, |
||||
layer_count: 1, |
||||
}, |
||||
..Default::default() |
||||
}; |
||||
let handle = unsafe { device.create_image_view(&create_info, None)? }; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
} |
||||
|
||||
impl Drop for ImageView<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_image_view(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for ImageView<'_, '_> { |
||||
type Target = vk::ImageView; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,75 @@
|
||||
use ash::{ |
||||
extensions::khr, |
||||
version::{EntryV1_0, InstanceV1_0}, |
||||
vk, |
||||
vk_make_version |
||||
}; |
||||
use crate::{render::{Conf, ensure_properties}, util::{err, ffi, meta}}; |
||||
use std::{convert::TryInto, os::raw::c_char}; |
||||
|
||||
pub struct Instance { |
||||
handle: ash::Instance, |
||||
|
||||
pub surface_ext: khr::Surface, |
||||
} |
||||
|
||||
fn enable_instance_layers( |
||||
layers: &[*const c_char], |
||||
entry: &ash::Entry, |
||||
) -> err::Result<vk::InstanceCreateInfo> { |
||||
if layers.len() > 0 { |
||||
let props = entry.enumerate_instance_layer_properties()?; |
||||
ensure_properties(&props, layers, |x| x.layer_name.as_ptr())?; |
||||
} |
||||
|
||||
Ok(vk::InstanceCreateInfo { |
||||
enabled_layer_count: layers.len().try_into()?, |
||||
pp_enabled_layer_names: layers.as_ptr(), |
||||
..Default::default() |
||||
}) |
||||
} |
||||
|
||||
impl Instance { |
||||
pub fn create( |
||||
conf: &Conf, |
||||
entry: &ash::Entry, |
||||
window: &sdl2::video::Window, |
||||
) -> err::Result<Self> { |
||||
let exts = err::from_s(window.vulkan_instance_extensions())?; |
||||
let exts = ffi::CStringVec::new_from_iter(exts.into_iter())?; |
||||
|
||||
let app_info = vk::ApplicationInfo { |
||||
p_engine_name: meta::ffi::name(), |
||||
api_version: vk_make_version!(1, 0, 0), |
||||
..Default::default() |
||||
}; |
||||
|
||||
let mut layers = Vec::new(); |
||||
|
||||
if conf.validation_layers { |
||||
layers.push(c_str!("VK_LAYER_KHRONOS_validation")); |
||||
} |
||||
|
||||
let create_info = vk::InstanceCreateInfo { |
||||
p_application_info: &app_info, |
||||
enabled_extension_count: exts.len().try_into()?, |
||||
pp_enabled_extension_names: exts.as_ptr(), |
||||
..enable_instance_layers(&layers, &entry)? |
||||
}; |
||||
|
||||
let handle = unsafe { entry.create_instance(&create_info, None)? }; |
||||
let surface_ext = khr::Surface::new(entry, &handle); |
||||
Ok(Self { handle, surface_ext }) |
||||
} |
||||
} |
||||
|
||||
impl Drop for Instance { |
||||
fn drop(&mut self) { unsafe { self.handle.destroy_instance(None); } } |
||||
} |
||||
|
||||
impl std::ops::Deref for Instance { |
||||
type Target = ash::Instance; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,251 @@
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::{render::{Device, ShaderModule}, util::err}; |
||||
use std::convert::TryInto; |
||||
|
||||
pub struct PipelineLayout<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::PipelineLayout, |
||||
} |
||||
|
||||
pub struct RenderPass<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::RenderPass, |
||||
} |
||||
|
||||
pub struct Pipeline<'a, 'b> { |
||||
device: &'a Device<'b>, |
||||
handle: vk::Pipeline, |
||||
} |
||||
|
||||
impl<'a, 'b> PipelineLayout<'a, 'b> { |
||||
pub fn create(device: &'a Device<'b>) -> err::Result<Self> { |
||||
let create_info = vk::PipelineLayoutCreateInfo::default(); |
||||
|
||||
let handle = unsafe { |
||||
device.create_pipeline_layout(&create_info, None)? |
||||
}; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
} |
||||
|
||||
impl<'a, 'b> RenderPass<'a, 'b> { |
||||
pub fn create( |
||||
device: &'a Device<'b>, |
||||
format: vk::Format, |
||||
) -> err::Result<Self> { |
||||
let attachments = [ |
||||
vk::AttachmentDescription { |
||||
format, |
||||
flags: vk::AttachmentDescriptionFlags::empty(), |
||||
samples: vk::SampleCountFlags::TYPE_1, |
||||
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, |
||||
} |
||||
]; |
||||
|
||||
let attachment_references = [ |
||||
vk::AttachmentReference { |
||||
attachment: 0, |
||||
layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, |
||||
} |
||||
]; |
||||
|
||||
let subpasses = [ |
||||
vk::SubpassDescription { |
||||
pipeline_bind_point: vk::PipelineBindPoint::GRAPHICS, |
||||
color_attachment_count: attachment_references.len().try_into()?, |
||||
p_color_attachments: attachment_references.as_ptr(), |
||||
..Default::default() |
||||
} |
||||
]; |
||||
|
||||
let dependencies = [ |
||||
vk::SubpassDependency { |
||||
src_subpass: vk::SUBPASS_EXTERNAL, |
||||
dst_subpass: 0, |
||||
src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, |
||||
dst_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, |
||||
src_access_mask: vk::AccessFlags::empty(), |
||||
dst_access_mask: |
||||
vk::AccessFlags::COLOR_ATTACHMENT_READ | |
||||
vk::AccessFlags::COLOR_ATTACHMENT_WRITE, |
||||
dependency_flags: vk::DependencyFlags::empty(), |
||||
} |
||||
]; |
||||
|
||||
let create_info = vk::RenderPassCreateInfo { |
||||
attachment_count: attachments.len().try_into()?, |
||||
p_attachments: attachments.as_ptr(), |
||||
subpass_count: subpasses.len().try_into()?, |
||||
p_subpasses: subpasses.as_ptr(), |
||||
dependency_count: dependencies.len().try_into()?, |
||||
p_dependencies: dependencies.as_ptr(), |
||||
..Default::default() |
||||
}; |
||||
|
||||
let handle = unsafe { device.create_render_pass(&create_info, None)? }; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
} |
||||
|
||||
impl<'a, 'b> Pipeline<'a, 'b> { |
||||
pub fn create( |
||||
device: &'a Device<'b>, |
||||
layout: &PipelineLayout, |
||||
render_pass: &RenderPass, |
||||
shader_vert: &ShaderModule, |
||||
shader_frag: &ShaderModule, |
||||
extent: vk::Extent2D, |
||||
) -> err::Result<Vec<Self>> { |
||||
let shader_stages = [ |
||||
vk::PipelineShaderStageCreateInfo { |
||||
stage: vk::ShaderStageFlags::VERTEX, |
||||
module: **shader_vert, |
||||
p_name: c_str!("main"), |
||||
..Default::default() |
||||
}, |
||||
vk::PipelineShaderStageCreateInfo { |
||||
stage: vk::ShaderStageFlags::FRAGMENT, |
||||
module: **shader_frag, |
||||
p_name: c_str!("main"), |
||||
..Default::default() |
||||
}, |
||||
]; |
||||
|
||||
let vertex_input_state = |
||||
vk::PipelineVertexInputStateCreateInfo::default(); |
||||
|
||||
let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo { |
||||
topology: vk::PrimitiveTopology::TRIANGLE_LIST, |
||||
primitive_restart_enable: vk::FALSE, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let viewports = [ |
||||
vk::Viewport { |
||||
x: 0.0, |
||||
y: 0.0, |
||||
width: extent.width as f32, |
||||
height: extent.height as f32, |
||||
min_depth: 0.0, |
||||
max_depth: 1.0, |
||||
} |
||||
]; |
||||
|
||||
let scissors = [ |
||||
vk::Rect2D { |
||||
extent, |
||||
offset: vk::Offset2D::default(), |
||||
} |
||||
]; |
||||
|
||||
let viewport_state = vk::PipelineViewportStateCreateInfo { |
||||
viewport_count: viewports.len().try_into()?, |
||||
p_viewports: viewports.as_ptr(), |
||||
scissor_count: scissors.len().try_into()?, |
||||
p_scissors: scissors.as_ptr(), |
||||
..Default::default() |
||||
}; |
||||
|
||||
let rasterization_state = vk::PipelineRasterizationStateCreateInfo { |
||||
polygon_mode: vk::PolygonMode::FILL, |
||||
line_width: 1.0, |
||||
cull_mode: vk::CullModeFlags::BACK, |
||||
front_face: vk::FrontFace::CLOCKWISE, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let multisample_state = vk::PipelineMultisampleStateCreateInfo { |
||||
sample_shading_enable: vk::FALSE, |
||||
rasterization_samples: vk::SampleCountFlags::TYPE_1, |
||||
min_sample_shading: 1.0, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let color_blend_attachments = [ |
||||
vk::PipelineColorBlendAttachmentState { |
||||
color_write_mask: vk::ColorComponentFlags::all(), |
||||
..Default::default() |
||||
} |
||||
]; |
||||
|
||||
let color_blend_state = vk::PipelineColorBlendStateCreateInfo { |
||||
attachment_count: color_blend_attachments.len().try_into()?, |
||||
p_attachments: color_blend_attachments.as_ptr(), |
||||
..Default::default() |
||||
}; |
||||
|
||||
let create_info = vk::GraphicsPipelineCreateInfo { |
||||
layout: **layout, |
||||
render_pass: **render_pass, |
||||
stage_count: shader_stages.len().try_into()?, |
||||
p_stages: shader_stages.as_ptr(), |
||||
p_vertex_input_state: &vertex_input_state, |
||||
p_input_assembly_state: &input_assembly_state, |
||||
p_viewport_state: &viewport_state, |
||||
p_rasterization_state: &rasterization_state, |
||||
p_multisample_state: &multisample_state, |
||||
p_color_blend_state: &color_blend_state, |
||||
..Default::default() |
||||
}; |
||||
|
||||
let pipelines = unsafe { |
||||
device |
||||
.create_graphics_pipelines( |
||||
vk::PipelineCache::null(), |
||||
&[create_info], |
||||
None, |
||||
) |
||||
.or_else(|(pipelines, r)| { |
||||
for pipeline in pipelines { |
||||
device.destroy_pipeline(pipeline, None); |
||||
} |
||||
Err(r) |
||||
})? |
||||
.iter() |
||||
.map(|&handle| Self { device, handle }) |
||||
.collect() |
||||
}; |
||||
|
||||
Ok(pipelines) |
||||
} |
||||
} |
||||
|
||||
impl Drop for PipelineLayout<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_pipeline_layout(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl Drop for RenderPass<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_render_pass(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl Drop for Pipeline<'_, '_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_pipeline(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for PipelineLayout<'_, '_> { |
||||
type Target = vk::PipelineLayout; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
impl std::ops::Deref for RenderPass<'_, '_> { |
||||
type Target = vk::RenderPass; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
impl std::ops::Deref for Pipeline<'_, '_> { |
||||
type Target = vk::Pipeline; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,67 @@
|
||||
use ash::{version::{DeviceV1_0, InstanceV1_0}, vk}; |
||||
use crate::{render::{Device, Instance, PhysicalDevice, Surface}, util::err}; |
||||
use std::{convert::TryInto, marker::PhantomData}; |
||||
|
||||
pub struct QueueFamilyInfo { |
||||
pub gfx_index: u32, |
||||
pub srf_index: u32, |
||||
} |
||||
|
||||
pub struct Queue<'a, 'b> { |
||||
device: PhantomData<&'a Device<'b>>, |
||||
handle: vk::Queue, |
||||
} |
||||
|
||||
impl QueueFamilyInfo { |
||||
pub fn collect( |
||||
instance: &Instance, |
||||
surface: &Surface, |
||||
phys_device: &PhysicalDevice, |
||||
) -> err::Result<Self> { |
||||
let mut gfx_index = None; |
||||
let mut srf_index = None; |
||||
|
||||
let queues = unsafe { |
||||
instance.get_physical_device_queue_family_properties(**phys_device) |
||||
}; |
||||
|
||||
for (i, queue) in queues.iter().enumerate() { |
||||
let i = i.try_into()?; |
||||
|
||||
let surface_support = unsafe { |
||||
instance.surface_ext.get_physical_device_surface_support( |
||||
**phys_device, |
||||
i, |
||||
**surface, |
||||
) |
||||
}; |
||||
|
||||
if queue.queue_flags.contains(vk::QueueFlags::GRAPHICS) { |
||||
gfx_index = Some(i); |
||||
} |
||||
|
||||
if surface_support { |
||||
srf_index = Some(i); |
||||
} |
||||
} |
||||
|
||||
Ok(Self { |
||||
gfx_index: gfx_index.ok_or(err::static_msg("no graphics queue"))?, |
||||
srf_index: srf_index.ok_or(err::static_msg("no surface queue"))?, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl<'a, 'b> Queue<'a, 'b> { |
||||
pub fn get(device: &'a Device<'b>, index: u32) -> Self { |
||||
let handle = unsafe { device.get_device_queue(index, 0) }; |
||||
Self { device: PhantomData, handle } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for Queue<'_, '_> { |
||||
type Target = vk::Queue; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|
@ -0,0 +1,28 @@
|
||||
use ash::{version::DeviceV1_0, vk}; |
||||
use crate::util::err; |
||||
|
||||
pub struct Semaphore<'a> { |
||||
device: &'a ash::Device, |
||||
handle: vk::Semaphore, |
||||
} |
||||
|
||||
impl<'a> Semaphore<'a> { |
||||
pub fn create(device: &'a ash::Device) -> err::Result<Self> { |
||||
let create_info = vk::SemaphoreCreateInfo::default(); |
||||
let handle = unsafe { device.create_semaphore(&create_info, None)? }; |
||||
Ok(Self { device, handle }) |
||||
} |
||||
} |
||||
|
||||
impl Drop for Semaphore<'_> { |
||||
fn drop(&mut self) { |
||||
unsafe { self.device.destroy_semaphore(self.handle, None); } |
||||
} |
||||
} |
||||
|
||||
impl std::ops::Deref for Semaphore<'_> { |
||||
type Target = vk::Semaphore; |
||||
fn deref(&self) -> &Self::Target { &self.handle } |
||||
} |
||||
|
||||
// EOF
|