chore: initial commit
This commit is contained in:
commit
9e4f531bd4
25 files changed
+4232
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
rustflags = ["-C", "link-args=/STACK:2000000"]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
Generated
+17
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="accountSettings">
|
||||||
|
<option name="activeProfile" value="profile:default" />
|
||||||
|
<option name="activeRegion" value="us-east-1" />
|
||||||
|
<option name="recentlyUsedProfiles">
|
||||||
|
<list>
|
||||||
|
<option value="profile:default" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="recentlyUsedRegions">
|
||||||
|
<list>
|
||||||
|
<option value="us-east-1" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
<option name="applicationTheme" value="default" />
|
||||||
|
<option name="iconsTheme" value="default" />
|
||||||
|
<option name="button1Title" value="" />
|
||||||
|
<option name="button1Url" value="" />
|
||||||
|
<option name="button2Title" value="" />
|
||||||
|
<option name="button2Url" value="" />
|
||||||
|
<option name="customApplicationId" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/vulkano-test.iml" filepath="$PROJECT_DIR$/.idea/vulkano-test.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+2800
File diff suppressed because it is too large.
Load diff
+10
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "vulkano-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image = "0.25.10"
|
||||||
|
vulkano = "^0.35"
|
||||||
|
vulkano-shaders = "^0.35"
|
||||||
|
winit = "0.30.13"
|
||||||
+600
@@ -0,0 +1,600 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer};
|
||||||
|
use vulkano::command_buffer::allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo};
|
||||||
|
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, SubpassEndInfo};
|
||||||
|
use vulkano::descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator};
|
||||||
|
use vulkano::device::{Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo, QueueFlags};
|
||||||
|
use vulkano::device::physical::{PhysicalDevice, PhysicalDeviceType};
|
||||||
|
use vulkano::image::{Image, ImageUsage};
|
||||||
|
use vulkano::image::view::ImageView;
|
||||||
|
use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions};
|
||||||
|
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter, StandardMemoryAllocator};
|
||||||
|
use vulkano::pipeline::{GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo};
|
||||||
|
use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState};
|
||||||
|
use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo;
|
||||||
|
use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
|
||||||
|
use vulkano::pipeline::graphics::multisample::MultisampleState;
|
||||||
|
use vulkano::pipeline::graphics::rasterization::RasterizationState;
|
||||||
|
use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition};
|
||||||
|
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
|
||||||
|
use vulkano::pipeline::layout::PipelineDescriptorSetLayoutCreateInfo;
|
||||||
|
use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass};
|
||||||
|
use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo};
|
||||||
|
use vulkano::{swapchain, sync, Validated, VulkanError, VulkanLibrary};
|
||||||
|
use vulkano::shader::ShaderModule;
|
||||||
|
use vulkano::sync::GpuFuture;
|
||||||
|
use winit::application::ApplicationHandler;
|
||||||
|
use winit::event::WindowEvent;
|
||||||
|
use winit::event_loop::ActiveEventLoop;
|
||||||
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
|
use crate::math::Vector2;
|
||||||
|
use crate::triangle;
|
||||||
|
|
||||||
|
/// The `Vertex2` is an intermediate structure used to represent the [`Vector2`] in the graphics
|
||||||
|
/// pipeline. It specifies a positional value and can be passed directly into the shaders.
|
||||||
|
/// Note that conversion between a `Vector2` and a `Vertex2` may take extra computation time.
|
||||||
|
///
|
||||||
|
/// Honestly, I'm still not sure if this should be handled on a per-shader basis, as the position
|
||||||
|
/// argument changes with each shader.
|
||||||
|
#[derive(BufferContents, Vertex)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Vertex2 {
|
||||||
|
#[format(R32G32_SFLOAT)]
|
||||||
|
position: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector2> for Vertex2 {
|
||||||
|
fn from(v: Vector2) -> Self {
|
||||||
|
Self {
|
||||||
|
position: [v.x, v.y],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BufferContents, Vertex)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Vertex3 {
|
||||||
|
#[format(R32G32B32_SFLOAT)]
|
||||||
|
position: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VulkanContext {
|
||||||
|
window: Arc<Window>,
|
||||||
|
device: Arc<Device>,
|
||||||
|
queue: Arc<Queue>,
|
||||||
|
swapchain: Arc<Swapchain>,
|
||||||
|
swapchain_images: Vec<Arc<Image>>,
|
||||||
|
mem_allocator: Arc<dyn MemoryAllocator>,
|
||||||
|
cmd_allocator: Arc<StandardCommandBufferAllocator>,
|
||||||
|
desc_allocator: Arc<dyn DescriptorSetAllocator>,
|
||||||
|
vert_shader: Arc<ShaderModule>,
|
||||||
|
frag_shader: Arc<ShaderModule>,
|
||||||
|
render_pass: Arc<RenderPass>,
|
||||||
|
framebuffers: Vec<Arc<Framebuffer>>,
|
||||||
|
pipeline: Arc<GraphicsPipeline>,
|
||||||
|
vertex_buffer: Arc<Subbuffer<[Vertex2]>>,
|
||||||
|
index_buffer: Arc<Subbuffer<[u32]>>,
|
||||||
|
cmd_buffers: Vec<Arc<PrimaryAutoCommandBuffer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanContext {
|
||||||
|
/// Initializes and returns a new `VulkanContext`. It may fail in all cases where a Vulkan object
|
||||||
|
/// may fail, specifically during initialization of shaders, devices, buffers, or any other such
|
||||||
|
/// structures.
|
||||||
|
pub fn init(instance: Arc<Instance>, event_loop: &ActiveEventLoop) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let attributes = WindowAttributes::default()
|
||||||
|
.with_title("Vulkano Example")
|
||||||
|
.with_resizable(true);
|
||||||
|
|
||||||
|
let window = Arc::new(event_loop.create_window(attributes)?);
|
||||||
|
|
||||||
|
let surface = Surface::from_window(instance.clone(), window.clone())?;
|
||||||
|
|
||||||
|
let dev_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a physical device. Fails if there is no suitable device found. The device must have a
|
||||||
|
// graphics queue and swapchain capabilities.
|
||||||
|
let (physical_device, queue_family_idx) = Self::get_physical_device(
|
||||||
|
instance.clone(),
|
||||||
|
surface.clone(),
|
||||||
|
&dev_extensions
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Create a Vulkan device. This is a handle to the connection between the program and the
|
||||||
|
// chosen physical device.
|
||||||
|
let (device, mut queues) = Self::create_device(
|
||||||
|
physical_device.clone(),
|
||||||
|
&dev_extensions,
|
||||||
|
queue_family_idx as usize
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Get the first queue. This is what we'll use for drawing.
|
||||||
|
let queue = queues.next()
|
||||||
|
.ok_or("Queues list was empty.")?;
|
||||||
|
|
||||||
|
let capabilities = physical_device
|
||||||
|
.surface_capabilities(
|
||||||
|
&surface,
|
||||||
|
Default::default()
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let dimensions = window
|
||||||
|
.inner_size();
|
||||||
|
let composite_alpha = capabilities
|
||||||
|
.supported_composite_alpha
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.ok_or("No composite alpha supported")?;
|
||||||
|
let image_format = physical_device
|
||||||
|
.surface_formats(&surface, Default::default())?
|
||||||
|
[0]
|
||||||
|
.0;
|
||||||
|
|
||||||
|
// Swapchain needs to be easily changeable, since resizing the window creates a whole new
|
||||||
|
// set of information. Putting this into a function would be good, but also the new
|
||||||
|
// information comes from the window event handler.
|
||||||
|
let (swapchain, images) = Swapchain::new(
|
||||||
|
device.clone(),
|
||||||
|
surface.clone(),
|
||||||
|
SwapchainCreateInfo {
|
||||||
|
min_image_count: capabilities.min_image_count + 1,
|
||||||
|
image_format,
|
||||||
|
image_extent: dimensions.into(),
|
||||||
|
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
|
composite_alpha,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mem_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
|
||||||
|
let cmd_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
||||||
|
device.clone(),
|
||||||
|
StandardCommandBufferAllocatorCreateInfo::default(),
|
||||||
|
));
|
||||||
|
let desc_allocator = Arc::new(StandardDescriptorSetAllocator::new(device.clone(), Default::default()));
|
||||||
|
|
||||||
|
let render_pass = vulkano::single_pass_renderpass!(
|
||||||
|
device.clone(),
|
||||||
|
attachments: {
|
||||||
|
color: {
|
||||||
|
format: swapchain.image_format(),
|
||||||
|
samples: 1,
|
||||||
|
load_op: Clear,
|
||||||
|
store_op: Store,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pass: {
|
||||||
|
color: [color],
|
||||||
|
depth_stencil: {},
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let vert_shader = triangle::load_vertex(device.clone())
|
||||||
|
.map_err(|e| format!("Failed to load vertex shader: {e}"))?;
|
||||||
|
let frag_shader = triangle::load_fragment(device.clone())
|
||||||
|
.map_err(|e| format!("Failed to load fragment shader: {e}"))?;
|
||||||
|
|
||||||
|
let framebuffers = Self::create_framebuffers(&images, &render_pass)?;
|
||||||
|
|
||||||
|
let image_extents = swapchain.image_extent();
|
||||||
|
let pipeline = Self::create_pipeline(
|
||||||
|
&device,
|
||||||
|
&vert_shader,
|
||||||
|
&frag_shader,
|
||||||
|
&render_pass,
|
||||||
|
[image_extents[0] as f32, image_extents[1] as f32]
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let vertex_buffer = Arc::new(Buffer::from_iter(
|
||||||
|
mem_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::VERTEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Vertex2 { position: [0.5, 0.5] },
|
||||||
|
Vertex2 { position: [-0.5, 0.5] },
|
||||||
|
Vertex2 { position: [-0.5, -0.5] },
|
||||||
|
Vertex2 { position: [0.5, -0.5] },
|
||||||
|
]
|
||||||
|
)?);
|
||||||
|
|
||||||
|
let index_buffer = Arc::new(Buffer::from_iter(
|
||||||
|
mem_allocator.clone(),
|
||||||
|
BufferCreateInfo {
|
||||||
|
usage: BufferUsage::INDEX_BUFFER,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AllocationCreateInfo {
|
||||||
|
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||||
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
0, 1, 2,
|
||||||
|
0, 2, 3,
|
||||||
|
]
|
||||||
|
)?);
|
||||||
|
|
||||||
|
let cmd_buffers = Self::get_command_buffers(
|
||||||
|
&cmd_allocator,
|
||||||
|
&queue,
|
||||||
|
&pipeline,
|
||||||
|
&framebuffers,
|
||||||
|
&vertex_buffer,
|
||||||
|
&index_buffer
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
window,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
swapchain,
|
||||||
|
swapchain_images: images,
|
||||||
|
mem_allocator,
|
||||||
|
cmd_allocator,
|
||||||
|
desc_allocator,
|
||||||
|
render_pass,
|
||||||
|
vert_shader,
|
||||||
|
frag_shader,
|
||||||
|
framebuffers,
|
||||||
|
pipeline,
|
||||||
|
vertex_buffer,
|
||||||
|
index_buffer,
|
||||||
|
cmd_buffers
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws a frame on the current [`Surface`].
|
||||||
|
pub fn draw_frame(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let (image_idx, suboptimal, acquire_future) =
|
||||||
|
match swapchain::acquire_next_image(self.swapchain.clone(), None)
|
||||||
|
.map_err(Validated::unwrap){
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(VulkanError::OutOfDate) => {
|
||||||
|
println!("Swapchain is out of date!");
|
||||||
|
self.recreate_swapchain(true)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => panic!("Failed to acquire next image: {:?}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Swapchain images may be suboptimal, which means that the swapchain is not in a good state.
|
||||||
|
// This is fixed by recreating the swapchain, but we'll have to skip this frame.
|
||||||
|
if suboptimal {
|
||||||
|
println!("Swapchain is suboptimal!");
|
||||||
|
self.recreate_swapchain(false)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let execution = sync::now(self.device.clone())
|
||||||
|
.join(acquire_future)
|
||||||
|
.then_execute(self.queue.clone(), self.cmd_buffers[image_idx as usize].clone())?
|
||||||
|
.then_swapchain_present(
|
||||||
|
self.queue.clone(),
|
||||||
|
SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_idx),
|
||||||
|
)
|
||||||
|
.then_signal_fence_and_flush();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the correct [`PhysicalDevice`], which needs to match these requirements:
|
||||||
|
/// - Must have the correct `khr_swapchain` extension
|
||||||
|
/// - Must contain a graphics queue
|
||||||
|
/// - Priority is given to a discrete GPU, then the CPU.
|
||||||
|
fn get_physical_device(
|
||||||
|
instance: Arc<Instance>,
|
||||||
|
surface: Arc<Surface>,
|
||||||
|
extensions: &DeviceExtensions
|
||||||
|
) -> Result<(Arc<PhysicalDevice>, u32), String> {
|
||||||
|
instance
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.map_err(|e| format!("Failed to enumerate physical devices: {e:?}"))?
|
||||||
|
.filter(|dev| dev.supported_extensions().contains(extensions))
|
||||||
|
.filter_map(|dev| {
|
||||||
|
dev.queue_family_properties()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.position(|(i, q)| {
|
||||||
|
q.queue_flags.contains(QueueFlags::GRAPHICS)
|
||||||
|
&& dev.surface_support(i as u32, &surface).unwrap_or(false)
|
||||||
|
})
|
||||||
|
.map(|q| (dev, q as u32))
|
||||||
|
})
|
||||||
|
.min_by_key(|(dev, _)| {
|
||||||
|
match dev.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::Cpu => 1,
|
||||||
|
_ => 2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or_else(|| "No physical devices found".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the Vulkan [`Device`] given a specific [`PhysicalDevice`].
|
||||||
|
fn create_device(
|
||||||
|
physical_device: Arc<PhysicalDevice>,
|
||||||
|
device_extensions: &DeviceExtensions,
|
||||||
|
graphics_index: usize
|
||||||
|
) -> Result<(Arc<Device>, impl ExactSizeIterator<Item = Arc<Queue>>), String> {
|
||||||
|
Device::new(
|
||||||
|
physical_device.clone(),
|
||||||
|
DeviceCreateInfo {
|
||||||
|
queue_create_infos: vec![QueueCreateInfo {
|
||||||
|
queue_family_index: graphics_index as u32,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
enabled_extensions: device_extensions.clone(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Failed to create device: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`Framebuffer`] for each swapchain image.
|
||||||
|
fn create_framebuffers(
|
||||||
|
images: &Vec<Arc<Image>>,
|
||||||
|
render_pass: &Arc<RenderPass>
|
||||||
|
) -> Result<Vec<Arc<Framebuffer>>, Box<dyn Error>> {
|
||||||
|
let framebuffers = images.iter()
|
||||||
|
.map(|image| {
|
||||||
|
let view = ImageView::new_default(image.clone()).unwrap();
|
||||||
|
|
||||||
|
Framebuffer::new(
|
||||||
|
render_pass.clone(),
|
||||||
|
FramebufferCreateInfo {
|
||||||
|
attachments: vec![view],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.collect::<Vec<Arc<Framebuffer>>>();
|
||||||
|
|
||||||
|
Ok(framebuffers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`GraphicsPipeline`] that corresponds to the given [`RenderPass`], vertex shader,
|
||||||
|
/// and fragment shader.
|
||||||
|
pub fn create_pipeline(
|
||||||
|
device: &Arc<Device>,
|
||||||
|
vert_shader: &Arc<ShaderModule>,
|
||||||
|
frag_shader: &Arc<ShaderModule>,
|
||||||
|
render_pass: &Arc<RenderPass>,
|
||||||
|
extents: [f32; 2],
|
||||||
|
) -> Result<Arc<GraphicsPipeline>, Box<dyn Error>> {
|
||||||
|
// Get shader stages
|
||||||
|
let vs = vert_shader.entry_point("main")
|
||||||
|
.ok_or("Failed to find vertex shader entry point")?;
|
||||||
|
let fs = frag_shader.entry_point("main")
|
||||||
|
.ok_or("Failed to find fragment shader entry point")?;
|
||||||
|
|
||||||
|
let vertex_input_state = Vertex2::per_vertex()
|
||||||
|
.definition(&vs)?;
|
||||||
|
|
||||||
|
let stages = vec![
|
||||||
|
PipelineShaderStageCreateInfo::new(vs),
|
||||||
|
PipelineShaderStageCreateInfo::new(fs),
|
||||||
|
];
|
||||||
|
|
||||||
|
let subpass = Subpass::from(render_pass.clone(), 0)
|
||||||
|
.ok_or("Failed to create pipeline subpass")?;
|
||||||
|
|
||||||
|
let layout = PipelineLayout::new(
|
||||||
|
device.clone(),
|
||||||
|
PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
|
||||||
|
.into_pipeline_layout_create_info(device.clone())?
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: extents,
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(GraphicsPipeline::new(
|
||||||
|
device.clone(),
|
||||||
|
None,
|
||||||
|
GraphicsPipelineCreateInfo {
|
||||||
|
flags: Default::default(),
|
||||||
|
stages: stages.into_iter().collect(),
|
||||||
|
vertex_input_state: Some(vertex_input_state),
|
||||||
|
input_assembly_state: Some(InputAssemblyState::default()),
|
||||||
|
viewport_state: Some(ViewportState {
|
||||||
|
viewports: [viewport].into_iter().collect(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
rasterization_state: Some(RasterizationState::default()),
|
||||||
|
multisample_state: Some(MultisampleState::default()),
|
||||||
|
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||||
|
subpass.num_color_attachments(),
|
||||||
|
ColorBlendAttachmentState::default()
|
||||||
|
)),
|
||||||
|
dynamic_state: Default::default(),
|
||||||
|
subpass: Some(subpass.into()),
|
||||||
|
..GraphicsPipelineCreateInfo::layout(layout)
|
||||||
|
}
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`PrimaryAutoCommandBuffer`] for each swapchain image.
|
||||||
|
fn get_command_buffers(
|
||||||
|
cmd_allocator: &Arc<StandardCommandBufferAllocator>,
|
||||||
|
queue: &Arc<Queue>,
|
||||||
|
pipeline: &Arc<GraphicsPipeline>,
|
||||||
|
framebuffers: &Vec<Arc<Framebuffer>>,
|
||||||
|
vertex_buffer: &Subbuffer<[Vertex2]>,
|
||||||
|
index_buffer: &Subbuffer<[u32]>
|
||||||
|
) -> Result<Vec<Arc<PrimaryAutoCommandBuffer>>, Box<dyn Error>> {
|
||||||
|
let buffers = framebuffers.iter()
|
||||||
|
.filter_map(|framebuffer| {
|
||||||
|
let mut builder = AutoCommandBufferBuilder::primary(
|
||||||
|
cmd_allocator.clone(),
|
||||||
|
queue.queue_family_index(),
|
||||||
|
CommandBufferUsage::MultipleSubmit
|
||||||
|
).ok()?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
builder
|
||||||
|
.begin_render_pass(
|
||||||
|
RenderPassBeginInfo {
|
||||||
|
clear_values: vec![Some([0.1, 0.1, 0.1, 1.0].into())],
|
||||||
|
..RenderPassBeginInfo::framebuffer(framebuffer.clone())
|
||||||
|
},
|
||||||
|
SubpassBeginInfo {
|
||||||
|
contents: SubpassContents::Inline,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
).ok()?
|
||||||
|
.bind_pipeline_graphics(pipeline.clone()).ok()?
|
||||||
|
.bind_vertex_buffers(0, vertex_buffer.clone()).ok()?
|
||||||
|
.bind_index_buffer(index_buffer.clone()).ok()?
|
||||||
|
// .draw(vertex_buffer.len() as u32, 1, 0, 0).ok()?
|
||||||
|
.draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0).ok()?
|
||||||
|
.end_render_pass(SubpassEndInfo::default()).ok()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build().ok()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(buffers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles recreation logic for the [`Swapchain`] by using the current information and any new
|
||||||
|
/// bounds. Will also handle resizing logic, as this requires extra information to be updated,
|
||||||
|
/// specifically the [`GraphicsPipeline`] and [`PrimaryAutoCommandBuffer`]. All information is
|
||||||
|
/// automatically set within the `VulkanContext`.
|
||||||
|
pub fn recreate_swapchain(&mut self, resized: bool) -> Result<(), Box<dyn Error>> {
|
||||||
|
unsafe { self.device.wait_idle()?; };
|
||||||
|
|
||||||
|
let (new_swapchain, new_images) = self.swapchain
|
||||||
|
.recreate(SwapchainCreateInfo {
|
||||||
|
image_extent: self.window.inner_size().into(),
|
||||||
|
..self.swapchain.create_info()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.swapchain = new_swapchain;
|
||||||
|
self.swapchain_images = new_images;
|
||||||
|
|
||||||
|
// Recreate the framebuffers, which depend on the swapchain.
|
||||||
|
let new_framebuffers = Self::create_framebuffers(&self.swapchain_images, &self.render_pass)?;
|
||||||
|
|
||||||
|
self.framebuffers = new_framebuffers;
|
||||||
|
|
||||||
|
if resized {
|
||||||
|
let image_extents = self.swapchain.image_extent();
|
||||||
|
let new_pipeline = Self::create_pipeline(
|
||||||
|
&self.device,
|
||||||
|
&self.vert_shader,
|
||||||
|
&self.frag_shader,
|
||||||
|
&self.render_pass,
|
||||||
|
[image_extents[0] as f32, image_extents[1] as f32],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let command_buffers = Self::get_command_buffers(
|
||||||
|
&self.cmd_allocator,
|
||||||
|
&self.queue,
|
||||||
|
&new_pipeline,
|
||||||
|
&self.framebuffers,
|
||||||
|
&self.vertex_buffer,
|
||||||
|
&self.index_buffer
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.pipeline = new_pipeline;
|
||||||
|
self.cmd_buffers = command_buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MainApplication {
|
||||||
|
instance: Arc<Instance>,
|
||||||
|
context: Option<VulkanContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MainApplication {
|
||||||
|
pub fn new(instance_extensions: InstanceExtensions) -> Result<Self, Box<dyn Error>> {
|
||||||
|
Ok(Self {
|
||||||
|
instance: Self::get_instance(instance_extensions)?,
|
||||||
|
context: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_frame(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Draw a frame on the surface. This requires a VulkanContext, which in turn will handle
|
||||||
|
// the rendering logic. We'll pass on the required information to the context, if it exists,
|
||||||
|
// or we'll try to initialize the context.
|
||||||
|
self.context
|
||||||
|
.as_mut()
|
||||||
|
.expect("Context is not initialized")
|
||||||
|
.draw_frame()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the Vulkan instance.
|
||||||
|
fn get_instance(
|
||||||
|
required_extensions: InstanceExtensions,
|
||||||
|
) -> Result<Arc<Instance>, String> {
|
||||||
|
let library = VulkanLibrary::new()
|
||||||
|
.map_err(|e| format!("Failed to find Vulkan library: {e}"))?;
|
||||||
|
|
||||||
|
Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: required_extensions,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Failed to create instance: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for MainApplication {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
self.context.is_none()
|
||||||
|
.then(|| {
|
||||||
|
// Initialization logic
|
||||||
|
let context = VulkanContext::init(
|
||||||
|
self.instance.clone(),
|
||||||
|
event_loop
|
||||||
|
).expect("Couldn't initialize VulkanContext");
|
||||||
|
|
||||||
|
self.context = Some(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
println!("Closing window.");
|
||||||
|
event_loop.exit();
|
||||||
|
}
|
||||||
|
WindowEvent::Resized(dimensions) => {
|
||||||
|
println!("Window was resized, recreating swapchain. New dimensions: {:?}", dimensions);
|
||||||
|
if let Some(context) = self.context.as_mut() {
|
||||||
|
context.recreate_swapchain(true).expect("Failed to recreate swapchain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
// This is where all redrawing logic goes.
|
||||||
|
self.draw_frame()
|
||||||
|
.unwrap_or_else(|e| println!("Failed to draw frame, dropping: {:?}", e));
|
||||||
|
|
||||||
|
// Queue the next frame for drawing.
|
||||||
|
self.context.as_ref().unwrap().window.request_redraw();
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
mod app;
|
||||||
|
pub mod math;
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use vulkano::swapchain::Surface;
|
||||||
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use crate::app::MainApplication;
|
||||||
|
|
||||||
|
// Compute shader compilation. Happens at runtime, but file contents are included during compilation.
|
||||||
|
mod cs {
|
||||||
|
vulkano_shaders::shader!{
|
||||||
|
ty: "compute",
|
||||||
|
path: "src/shaders/comp.glsl",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod mandelbrot_shader {
|
||||||
|
vulkano_shaders::shader!{
|
||||||
|
ty: "compute",
|
||||||
|
path: "src/shaders/mandelbrot.glsl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod triangle {
|
||||||
|
vulkano_shaders::shader!{
|
||||||
|
shaders: {
|
||||||
|
vertex: {
|
||||||
|
ty: "vertex",
|
||||||
|
path: "src/shaders/triangle_vert.glsl"
|
||||||
|
},
|
||||||
|
fragment: {
|
||||||
|
ty: "fragment",
|
||||||
|
path: "src/shaders/triangle_frag.glsl"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
println!("Beginning initialization of vulkano");
|
||||||
|
|
||||||
|
// Create the window that we'll use for graphics rendering with a swapchain.
|
||||||
|
let event_loop = EventLoop::new()?;
|
||||||
|
let required_extensions = Surface::required_extensions(&event_loop)?;
|
||||||
|
|
||||||
|
// Create and start the window.
|
||||||
|
let mut app = MainApplication::new(required_extensions)?;
|
||||||
|
event_loop.set_control_flow(ControlFlow::Wait);
|
||||||
|
event_loop.run_app(&mut app)?;
|
||||||
|
|
||||||
|
println!("All operations successful, exiting.");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
use std::ops::{Add, Div, Index, IndexMut, Mul, Sub};
|
||||||
|
use crate::math::traits::Matrix;
|
||||||
|
use crate::math::Vector2;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Copy, Default)]
|
||||||
|
pub struct Matrix2 {
|
||||||
|
row1: [f32; 2],
|
||||||
|
row2: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matrix2 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row_vector(&self, row: usize) -> Vector2 {
|
||||||
|
Vector2::from([
|
||||||
|
self[row][0],
|
||||||
|
self[row][1],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn column_vector(&self, col: usize) -> Vector2 {
|
||||||
|
Vector2::from([
|
||||||
|
self[0][col],
|
||||||
|
self[1][col],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Matrix2 {
|
||||||
|
type Output = [f32; 2];
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
if index == 0 {
|
||||||
|
&self.row1
|
||||||
|
} else {
|
||||||
|
&self.row2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Matrix2 {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
if index == 0 {
|
||||||
|
&mut self.row1
|
||||||
|
} else {
|
||||||
|
&mut self.row2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
row1: [self[0][0] + rhs[0][0], self[0][1] + rhs[0][1]],
|
||||||
|
row2: [self[1][0] + rhs[1][0], self[1][1] + rhs[1][1]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
row1: [self[0][0] - rhs[0][0], self[0][1] - rhs[0][1]],
|
||||||
|
row2: [self[1][0] - rhs[1][0], self[1][1] - rhs[1][1]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
row1: [self[0][0] * rhs, self[0][1] * rhs],
|
||||||
|
row2: [self[1][0] * rhs, self[1][1] * rhs],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Matrix2> for f32 {
|
||||||
|
type Output = Matrix2;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Matrix2) -> Self::Output {
|
||||||
|
rhs * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Matrix2) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
row1: [self[0][0] * rhs[0][0] + self[0][1] * rhs[1][0], self[0][0] * rhs[0][1] + self[0][1] * rhs[1][1]],
|
||||||
|
row2: [self[1][0] * rhs[0][0] + self[1][1] * rhs[1][0], self[1][0] * rhs[0][1] + self[1][1] * rhs[1][1]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div for Matrix2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matrix for Matrix2 {
|
||||||
|
fn identity() -> Self {
|
||||||
|
Self {
|
||||||
|
row1: [1.0, 0.0],
|
||||||
|
row2: [0.0, 1.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transpose(self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determinant(self) -> f32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minor(self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cofactor(self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjugate(self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inverse(self) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,14 @@
|
|||||||
|
mod matrix4;
|
||||||
|
mod vector3;
|
||||||
|
mod vector2;
|
||||||
|
mod traits;
|
||||||
|
mod matrix3;
|
||||||
|
mod matrix2;
|
||||||
|
mod vector4;
|
||||||
|
|
||||||
|
pub use matrix4::*;
|
||||||
|
pub use matrix3::*;
|
||||||
|
pub use matrix2::*;
|
||||||
|
pub use vector4::*;
|
||||||
|
pub use vector3::*;
|
||||||
|
pub use vector2::*;
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
|
||||||
|
pub trait Vector: Copy + Default + PartialEq + PartialOrd + Add + Sub + Div<f32, Output = Self> + Mul<f32, Output = Self> {
|
||||||
|
/// Gets the dot product of the two vectors.
|
||||||
|
fn dot(self, other: Self) -> f32;
|
||||||
|
|
||||||
|
/// Gets the signed length of the vector.
|
||||||
|
fn length(&self) -> f32;
|
||||||
|
|
||||||
|
/// Creates a normalized vector.
|
||||||
|
fn normalize(self) -> Self;
|
||||||
|
|
||||||
|
/// Projects the given vector onto the current vector. For example:
|
||||||
|
/// ```
|
||||||
|
/// let v = <some vector>;
|
||||||
|
/// let u = <another vector>;
|
||||||
|
/// let vu_projection = v.project(u); // projects u onto v
|
||||||
|
/// let uv_projection = u.project(v); // projects v onto u
|
||||||
|
/// ```
|
||||||
|
fn project(self, other: Self) -> Self {
|
||||||
|
let dot = self.dot(other);
|
||||||
|
let length = self.length();
|
||||||
|
let full_length = length * length;
|
||||||
|
|
||||||
|
self * (dot / full_length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Matrix: Copy + Default + PartialEq + PartialOrd + Add + Sub
|
||||||
|
+ Div<f32, Output = Self> + Div
|
||||||
|
+ Mul<f32, Output = Self> + Mul {
|
||||||
|
/// Creates an identity matrix.
|
||||||
|
fn identity() -> Self;
|
||||||
|
|
||||||
|
/// Gets the transpose of this matrix.
|
||||||
|
fn transpose(self) -> Self;
|
||||||
|
|
||||||
|
/// Gets the determinant of this matrix.
|
||||||
|
fn determinant(self) -> f32;
|
||||||
|
|
||||||
|
/// Finds the minor of this matrix. The element at location (`i`, `j`) of the minor is the
|
||||||
|
/// determinant of the submatrix obtained by removing row `i` and column `j` in the original
|
||||||
|
/// matrix.
|
||||||
|
fn minor(self) -> Self;
|
||||||
|
|
||||||
|
/// Finds the cofactor of this matrix. The element at location (`i`, `j`) of the cofactor is
|
||||||
|
/// defined as -1^ij * Mij, where Mij is the element at (`i`, `j`) in the minor of the matrix.
|
||||||
|
/// This is effectively flipping the sign of every other element in the minor of this matrix.
|
||||||
|
fn cofactor(self) -> Self;
|
||||||
|
|
||||||
|
/// Gets the adjugate of this matrix. This is the transpose of the cofactor of this matrix.
|
||||||
|
fn adjugate(self) -> Self;
|
||||||
|
|
||||||
|
/// Gets the inverse of this matrix. Note that this is a very expensive operation and should
|
||||||
|
/// be used as sparingly as possible.
|
||||||
|
fn inverse(self) -> Self;
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
use crate::math::traits::Vector;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||||
|
pub struct Vector2 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector2 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_values(x: f32, y: f32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Vector2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Vector2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x - other.x,
|
||||||
|
y: self.y - other.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vector2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, other: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * other,
|
||||||
|
y: self.y * other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vector2> for f32 {
|
||||||
|
type Output = Vector2;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vector2) -> Self::Output {
|
||||||
|
rhs * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Vector2 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, other: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x / other,
|
||||||
|
y: self.y / other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Vector2> for f32 {
|
||||||
|
type Output = Vector2;
|
||||||
|
|
||||||
|
fn div(self, rhs: Vector2) -> Self::Output {
|
||||||
|
rhs / self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector for Vector2 {
|
||||||
|
fn dot(self, other: Self) -> f32 {
|
||||||
|
self.x * other.x + self.y * other.y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(&self) -> f32 {
|
||||||
|
self.x.hypot(self.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize(self) -> Self {
|
||||||
|
let length = self.length();
|
||||||
|
Self {
|
||||||
|
x: self.x / length,
|
||||||
|
y: self.y / length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[f32; 2]> for Vector2 {
|
||||||
|
fn from(v: [f32; 2]) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v[0],
|
||||||
|
y: v[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for Vector2 {
|
||||||
|
fn from(v: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v,
|
||||||
|
y: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
use crate::math::traits::Vector;
|
||||||
|
use crate::math::vector2::Vector2;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||||
|
pub struct Vector3 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector3 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xy(&self) -> Vector2 {
|
||||||
|
Vector2::from([
|
||||||
|
self.x,
|
||||||
|
self.y
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yz(&self) -> Vector2 {
|
||||||
|
Vector2::from([
|
||||||
|
self.y,
|
||||||
|
self.z
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xz(&self) -> Vector2 {
|
||||||
|
Vector2::from([
|
||||||
|
self.x,
|
||||||
|
self.z
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xyz(&self) -> (f32, f32, f32) {
|
||||||
|
(self.x, self.y, self.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cross(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.y * other.z - self.z * other.y,
|
||||||
|
y: self.z * other.x - self.x * other.z,
|
||||||
|
z: self.x * other.y - self.y * other.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
z: self.z + other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x - other.x,
|
||||||
|
y: self.y - other.y,
|
||||||
|
z: self.z - other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, other: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * other,
|
||||||
|
y: self.y * other,
|
||||||
|
z: self.z * other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vector3> for f32 {
|
||||||
|
type Output = Vector3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vector3) -> Self::Output {
|
||||||
|
rhs * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, other: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x / other,
|
||||||
|
y: self.y / other,
|
||||||
|
z: self.z / other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Vector3> for f32 {
|
||||||
|
type Output = Vector3;
|
||||||
|
|
||||||
|
fn div(self, rhs: Vector3) -> Self::Output {
|
||||||
|
rhs / self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector for Vector3 {
|
||||||
|
fn dot(self, other: Self) -> f32 {
|
||||||
|
self.x * other.x + self.y * other.y + self.z * other.z
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(&self) -> f32 {
|
||||||
|
self.x.hypot(self.y.hypot(self.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize(self) -> Self {
|
||||||
|
let length = self.length();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
x: self.x / length,
|
||||||
|
y: self.y / length,
|
||||||
|
z: self.z / length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[f32; 3]> for Vector3 {
|
||||||
|
fn from(v: [f32; 3]) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v[0],
|
||||||
|
y: v[1],
|
||||||
|
z: v[2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector2> for Vector3 {
|
||||||
|
fn from(v: Vector2) -> Self {
|
||||||
|
Self {
|
||||||
|
z: 0.0,
|
||||||
|
..v.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
use crate::math::traits::Vector;
|
||||||
|
use crate::math::{Vector2, Vector3};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||||
|
pub struct Vector4 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
pub w: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector4 {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xyzw(&self) -> (f32, f32, f32, f32) {
|
||||||
|
(self.x, self.y, self.z, self.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xyz(&self) -> Vector3 {
|
||||||
|
Vector3::from([self.x, self.y, self.z])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xy(&self) -> Vector2 {
|
||||||
|
Vector2::from([self.x, self.y])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yz(&self) -> Vector2 {
|
||||||
|
Vector2::from([self.y, self.z])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xz(&self) -> Vector2 {
|
||||||
|
Vector2::from([self.x, self.z])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Vector4 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
z: self.z + other.z,
|
||||||
|
w: self.w + other.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Vector4 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x - other.x,
|
||||||
|
y: self.y - other.y,
|
||||||
|
z: self.z - other.z,
|
||||||
|
w: self.w - other.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vector4 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x * rhs,
|
||||||
|
y: self.y * rhs,
|
||||||
|
z: self.z * rhs,
|
||||||
|
w: self.w * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vector4> for f32 {
|
||||||
|
type Output = Vector4;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vector4) -> Self::Output {
|
||||||
|
rhs * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Vector4 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x / rhs,
|
||||||
|
y: self.y / rhs,
|
||||||
|
z: self.z / rhs,
|
||||||
|
w: self.w / rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Vector4> for f32 {
|
||||||
|
type Output = Vector4;
|
||||||
|
|
||||||
|
fn div(self, rhs: Vector4) -> Self::Output {
|
||||||
|
rhs / self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector for Vector4 {
|
||||||
|
fn dot(self, other: Self) -> f32 {
|
||||||
|
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(&self) -> f32 {
|
||||||
|
self.x.hypot(self.y.hypot(self.z.hypot(self.w)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize(self) -> Self {
|
||||||
|
self / self.length()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[f32; 4]> for Vector4 {
|
||||||
|
fn from(v: [f32; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v[0],
|
||||||
|
y: v[1],
|
||||||
|
z: v[2],
|
||||||
|
w: v[3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(Vector3, f32)> for Vector4 {
|
||||||
|
fn from((v, n): (Vector3, f32)) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v.x,
|
||||||
|
y: v.y,
|
||||||
|
z: v.z,
|
||||||
|
w: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(Vector2, f32, f32)> for Vector4 {
|
||||||
|
fn from((v, n1, n2): (Vector2, f32, f32)) -> Self {
|
||||||
|
Self {
|
||||||
|
x: v.x,
|
||||||
|
y: v.y,
|
||||||
|
z: n1,
|
||||||
|
w: n2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
layout(set = 0, binding = 0) buffer Data {
|
||||||
|
uint data[];
|
||||||
|
} buf;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint idx = gl_GlobalInvocationID.x;
|
||||||
|
buf.data[idx] *= 12;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||||
|
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D img;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Some normalized xy coord, plus 0.5, which gets the center of a pixel. Then it is
|
||||||
|
// normalized to the size of the image.
|
||||||
|
vec2 norm_coords = (gl_GlobalInvocationID.xy + vec2(0.5)) / vec2(imageSize(img));
|
||||||
|
// Need to look this up - not sure what this is.
|
||||||
|
// C is in the set when it is diverging when iterated from z = 0.
|
||||||
|
vec2 c = (norm_coords - vec2(0.5)) * 2.0 - vec2(1.0, 0.0);
|
||||||
|
vec2 z = vec2(0.0);
|
||||||
|
|
||||||
|
float i;
|
||||||
|
for (i = 0.0; i < 1.0; i += 0.005) {
|
||||||
|
z = vec2(
|
||||||
|
(z.x * z.x) - (z.y * z.y) + c.x,
|
||||||
|
(z.y * z.x) + (z.x * z.y) + c.y
|
||||||
|
);
|
||||||
|
|
||||||
|
if (length(z) > 4.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 to_write = vec4(vec3(i), 1.0);
|
||||||
|
imageStore(img, ivec2(gl_GlobalInvocationID.xy), to_write);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user