Reorganize project to workspace

This commit is contained in:
Tony Klink 2024-04-01 20:51:39 -06:00
parent 92c0278ef0
commit 960e2f8a37
Signed by: klink
GPG key ID: 85175567C4D19231
39 changed files with 4420 additions and 1189 deletions

15
vendor/egui-vulkano/Cargo.toml vendored Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "egui-vulkano"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
egui = "0.27.1"
image = "0.25.0"
ahash = "0.8.11"
egui-winit = "0.27.1"
winit = { version = "0.29.15",features = ["rwh_05"] }

321
vendor/egui-vulkano/src/integration.rs vendored Normal file
View file

@ -0,0 +1,321 @@
// Copyright (c) 2021 Okko Hakola, 2024 Klink
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::sync::Arc;
use egui::{ClippedPrimitive, TexturesDelta};
use egui_winit::winit::event_loop::EventLoopWindowTarget;
use vulkano::{
command_buffer::CommandBuffer, device::Queue, format::{Format, NumericFormat}, image::{sampler::SamplerCreateInfo, view::ImageView, SampleCount}, render_pass::Subpass, swapchain::Surface, sync::GpuFuture
};
use winit::window::Window;
use super::{
renderer::{RenderResources, Renderer},
utils::{immutable_texture_from_bytes, immutable_texture_from_file},
};
pub struct GuiConfig {
/// Allows supplying sRGB ImageViews as render targets instead of just UNORM ImageViews, defaults to false.
/// **Using sRGB will cause minor discoloration of UI elements** due to blending in linear color space and not
/// sRGB as Egui expects.
///
/// If you would like to visually compare between UNORM and sRGB render targets, run the `demo_app` example of
/// this crate.
pub allow_srgb_render_target: bool,
/// Whether to render gui as overlay. Only relevant in the case of `Gui::new`, not when using
/// subpass. Determines whether the pipeline should clear the target image.
pub is_overlay: bool,
/// Multisample count. Defaults to 1. If you use more than 1, you'll have to ensure your
/// pipeline and target image matches that.
pub samples: SampleCount,
}
impl Default for GuiConfig {
fn default() -> Self {
GuiConfig {
allow_srgb_render_target: false,
is_overlay: false,
samples: SampleCount::Sample1,
}
}
}
impl GuiConfig {
pub fn validate(&self, output_format: Format) {
if output_format.numeric_format_color().unwrap() == NumericFormat::SRGB {
assert!(
self.allow_srgb_render_target,
"Using an output format with sRGB requires `GuiConfig::allow_srgb_render_target` \
to be set! Egui prefers UNORM render targets. Using sRGB will cause minor \
discoloration of UI elements due to blending in linear color space and not sRGB \
as Egui expects."
);
}
}
}
pub struct Gui {
pub egui_ctx: egui::Context,
pub egui_winit: egui_winit::State,
renderer: Renderer,
surface: Arc<Surface>,
shapes: Vec<egui::epaint::ClippedShape>,
textures_delta: egui::TexturesDelta,
}
impl Gui {
/// Creates new Egui to Vulkano integration by setting the necessary parameters
/// This is to be called once we have access to vulkano_win's winit window surface
/// and gfx queue. Created with this, the renderer will own a render pass which is useful to e.g. place your render pass' images
/// onto egui windows
pub fn new<T>(
event_loop: &EventLoopWindowTarget<T>,
surface: Arc<Surface>,
gfx_queue: Arc<Queue>,
output_format: Format,
config: GuiConfig,
) -> Gui {
config.validate(output_format);
let renderer = Renderer::new_with_render_pass(
gfx_queue,
output_format,
config.is_overlay,
config.samples,
);
Self::new_internal(event_loop, surface, renderer)
}
/// Same as `new` but instead of integration owning a render pass, egui renders on your subpass
pub fn new_with_subpass<T>(
event_loop: &EventLoopWindowTarget<T>,
surface: Arc<Surface>,
gfx_queue: Arc<Queue>,
subpass: Subpass,
output_format: Format,
config: GuiConfig,
) -> Gui {
config.validate(output_format);
let renderer = Renderer::new_with_subpass(gfx_queue, output_format, subpass);
Self::new_internal(event_loop, surface, renderer)
}
/// Same as `new` but instead of integration owning a render pass, egui renders on your subpass
fn new_internal<T>(
event_loop: &EventLoopWindowTarget<T>,
surface: Arc<Surface>,
renderer: Renderer,
) -> Gui {
let max_texture_side = renderer
.queue()
.device()
.physical_device()
.properties()
.max_image_dimension2_d as usize;
let egui_ctx: egui::Context = Default::default();
let egui_winit = egui_winit::State::new(
egui_ctx.clone(),
egui_ctx.viewport_id(),
event_loop,
Some(surface_window(&surface).scale_factor() as f32),
Some(max_texture_side),
);
Gui {
egui_ctx,
egui_winit,
renderer,
surface,
shapes: vec![],
textures_delta: Default::default(),
}
}
/// Returns the pixels per point of the window of this gui.
fn pixels_per_point(&self) -> f32 {
egui_winit::pixels_per_point(&self.egui_ctx, surface_window(&self.surface))
}
/// Returns a set of resources used to construct the render pipeline. These can be reused
/// to create additional pipelines and buffers to be rendered in a `PaintCallback`.
pub fn render_resources(&self) -> RenderResources {
self.renderer.render_resources()
}
/// Updates context state by winit window event.
/// Returns `true` if egui wants exclusive use of this event
/// (e.g. a mouse click on an egui window, or entering text into a text field).
/// For instance, if you use egui for a game, you want to first call this
/// and only when this returns `false` pass on the events to your game.
///
/// Note that egui uses `tab` to move focus between elements, so this will always return `true` for tabs.
pub fn update(&mut self, window: &Window, winit_event: &winit::event::WindowEvent) -> bool {
self.egui_winit
.on_window_event(window, winit_event)
.consumed
}
/// Begins Egui frame & determines what will be drawn later. This must be called before draw, and after `update` (winit event).
pub fn immediate_ui(&mut self, layout_function: impl FnOnce(&mut Self)) {
let raw_input = self
.egui_winit
.take_egui_input(surface_window(&self.surface));
self.egui_ctx.begin_frame(raw_input);
// Render Egui
layout_function(self);
}
/// If you wish to better control when to begin frame, do so by calling this function
/// (Finish by drawing)
pub fn begin_frame(&mut self) {
let raw_input = self
.egui_winit
.take_egui_input(surface_window(&self.surface));
self.egui_ctx.begin_frame(raw_input);
}
/// Renders ui on `final_image` & Updates cursor icon
/// Finishes Egui frame
/// - `before_future` = Vulkano's GpuFuture
/// - `final_image` = Vulkano's image (render target)
pub fn draw_on_image<F>(
&mut self,
before_future: F,
final_image: Arc<ImageView>,
) -> Box<dyn GpuFuture>
where
F: GpuFuture + 'static,
{
if !self.renderer.has_renderpass() {
panic!(
"Gui integration has been created with subpass, use `draw_on_subpass_image` \
instead"
)
}
let (clipped_meshes, textures_delta) = self.extract_draw_data_at_frame_end();
self.renderer.draw_on_image(
&clipped_meshes,
&textures_delta,
self.pixels_per_point(),
before_future,
final_image,
)
}
/// Creates commands for rendering ui on subpass' image and returns the command buffer for execution on your side
/// - Finishes Egui frame
/// - You must execute the secondary command buffer yourself
pub fn draw_on_subpass_image(
&mut self,
image_dimensions: [u32; 2],
) -> Arc<CommandBuffer> {
if self.renderer.has_renderpass() {
panic!(
"Gui integration has been created with its own render pass, use `draw_on_image` \
instead"
)
}
let (clipped_meshes, textures_delta) = self.extract_draw_data_at_frame_end();
self.renderer.draw_on_subpass_image(
&clipped_meshes,
&textures_delta,
self.pixels_per_point(),
image_dimensions,
)
}
fn extract_draw_data_at_frame_end(&mut self) -> (Vec<ClippedPrimitive>, TexturesDelta) {
self.end_frame();
let shapes = std::mem::take(&mut self.shapes);
let textures_delta = std::mem::take(&mut self.textures_delta);
let clipped_meshes = self.egui_ctx.tessellate(shapes, self.pixels_per_point());
(clipped_meshes, textures_delta)
}
fn end_frame(&mut self) {
let egui::FullOutput {
platform_output,
textures_delta,
shapes,
pixels_per_point: _,
viewport_output: _,
} = self.egui_ctx.end_frame();
self.egui_winit.handle_platform_output(
surface_window(&self.surface),
platform_output,
);
self.shapes = shapes;
self.textures_delta = textures_delta;
}
/// Registers a user image from Vulkano image view to be used by egui
pub fn register_user_image_view(
&mut self,
image: Arc<ImageView>,
sampler_create_info: SamplerCreateInfo,
) -> egui::TextureId {
self.renderer.register_image(image, sampler_create_info)
}
/// Registers a user image to be used by egui
/// - `image_file_bytes`: e.g. include_bytes!("./assets/tree.png")
/// - `format`: e.g. vulkano::format::Format::R8G8B8A8Unorm
pub fn register_user_image(
&mut self,
image_file_bytes: &[u8],
format: vulkano::format::Format,
sampler_create_info: SamplerCreateInfo,
) -> egui::TextureId {
let image = immutable_texture_from_file(
self.renderer.allocators(),
self.renderer.queue(),
image_file_bytes,
format,
)
.expect("Failed to create image");
self.renderer.register_image(image, sampler_create_info)
}
pub fn register_user_image_from_bytes(
&mut self,
image_byte_data: &[u8],
dimensions: [u32; 2],
format: vulkano::format::Format,
sampler_create_info: SamplerCreateInfo,
) -> egui::TextureId {
let image = immutable_texture_from_bytes(
self.renderer.allocators(),
self.renderer.queue(),
image_byte_data,
dimensions,
format,
)
.expect("Failed to create image");
self.renderer.register_image(image, sampler_create_info)
}
/// Unregisters a user image
pub fn unregister_user_image(&mut self, texture_id: egui::TextureId) {
self.renderer.unregister_image(texture_id);
}
/// Access egui's context (which can be used to e.g. set fonts, visuals etc)
pub fn context(&self) -> egui::Context {
self.egui_ctx.clone()
}
}
// Helper to retrieve Window from surface object
fn surface_window(surface: &Surface) -> &Window {
surface.object().unwrap().downcast_ref::<Window>().unwrap()
}

19
vendor/egui-vulkano/src/lib.rs vendored Normal file
View file

@ -0,0 +1,19 @@
// Copyright (c) 2021 Okko Hakola, 2024 Klink
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
mod integration;
mod renderer;
mod utils;
pub use egui;
pub use integration::*;
#[allow(unused_imports)]
pub use renderer::{CallbackContext, CallbackFn, RenderResources};
#[allow(unused_imports)]
pub use utils::{immutable_texture_from_bytes, immutable_texture_from_file};

1208
vendor/egui-vulkano/src/renderer.rs vendored Normal file

File diff suppressed because it is too large Load diff

144
vendor/egui-vulkano/src/utils.rs vendored Normal file
View file

@ -0,0 +1,144 @@
// Copyright (c) 2021 Okko Hakola, 2024 Klink
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::sync::Arc;
use image::RgbaImage;
use vulkano::{
buffer::{AllocateBufferError, Buffer, BufferCreateInfo, BufferUsage},
command_buffer::{
allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo}, CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, CopyBufferToImageInfo, RecordingCommandBuffer
},
descriptor_set::allocator::StandardDescriptorSetAllocator,
device::{Device, Queue},
image::{view::ImageView, AllocateImageError, Image, ImageCreateInfo, ImageType, ImageUsage},
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
Validated, ValidationError, VulkanError,
};
#[derive(Debug)]
pub enum ImageCreationError {
Vulkan(Validated<VulkanError>),
AllocateImage(Validated<AllocateImageError>),
AllocateBuffer(Validated<AllocateBufferError>),
Validation(Box<ValidationError>),
}
pub fn immutable_texture_from_bytes(
allocators: &Allocators,
queue: Arc<Queue>,
byte_data: &[u8],
dimensions: [u32; 2],
format: vulkano::format::Format,
) -> Result<Arc<ImageView>, ImageCreationError> {
let mut cbb = RecordingCommandBuffer::new(
allocators.command_buffer.clone(),
queue.queue_family_index(),
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage: CommandBufferUsage::OneTimeSubmit,
..Default::default()
}
)
.map_err(ImageCreationError::Vulkan)?;
let texture_data_buffer = Buffer::from_iter(
allocators.memory.clone(),
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
byte_data.iter().cloned(),
)
.map_err(ImageCreationError::AllocateBuffer)?;
let texture = Image::new(
allocators.memory.clone(),
ImageCreateInfo {
image_type: ImageType::Dim2d,
format,
extent: [dimensions[0], dimensions[1], 1],
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
..Default::default()
},
AllocationCreateInfo::default(),
)
.map_err(ImageCreationError::AllocateImage)?;
cbb.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
texture_data_buffer,
texture.clone(),
))
.map_err(ImageCreationError::Validation)?;
let _fut = cbb.end().unwrap().execute(queue).unwrap();
Ok(ImageView::new_default(texture).unwrap())
}
pub fn immutable_texture_from_file(
allocators: &Allocators,
queue: Arc<Queue>,
file_bytes: &[u8],
format: vulkano::format::Format,
) -> Result<Arc<ImageView>, ImageCreationError> {
use image::GenericImageView;
let img = image::load_from_memory(file_bytes).expect("Failed to load image from bytes");
let rgba = if let Some(rgba) = img.as_rgba8() {
rgba.to_owned().to_vec()
} else {
// Convert rgb to rgba
let rgb = img.as_rgb8().unwrap().to_owned();
let mut raw_data = vec![];
for val in rgb.chunks(3) {
raw_data.push(val[0]);
raw_data.push(val[1]);
raw_data.push(val[2]);
raw_data.push(255);
}
let new_rgba = RgbaImage::from_raw(rgb.width(), rgb.height(), raw_data).unwrap();
new_rgba.to_vec()
};
let dimensions = img.dimensions();
immutable_texture_from_bytes(
allocators,
queue,
&rgba,
[dimensions.0, dimensions.1],
format,
)
}
pub struct Allocators {
pub memory: Arc<StandardMemoryAllocator>,
pub descriptor_set: Arc<StandardDescriptorSetAllocator>,
pub command_buffer: Arc<StandardCommandBufferAllocator>,
}
impl Allocators {
pub fn new_default(device: &Arc<Device>) -> Self {
Self {
memory: Arc::new(StandardMemoryAllocator::new_default(device.clone())),
descriptor_set: Arc::new(StandardDescriptorSetAllocator::new(device.clone(), Default::default())),
command_buffer: Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
StandardCommandBufferAllocatorCreateInfo {
secondary_buffer_count: 32,
..Default::default()
},
)),
}
}
}