stuff: slight refactoring
This commit is contained in:
parent
9802bec8b0
commit
c70c1591db
|
@ -6,6 +6,8 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = { workspace = true }
|
winit = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
smol = { workspace = true }
|
||||||
|
glam = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
|
|
||||||
|
|
|
@ -444,12 +444,12 @@ impl SingleUseCommand {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn bind_vertices(&self, buffer: vk::Buffer, offset: u64) {
|
pub fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_bind_vertex_buffers(self.buffer(), 0, &[buffer], &[offset]);
|
.cmd_bind_vertex_buffers(self.buffer(), 0, buffers, offsets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {
|
pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {
|
||||||
|
|
|
@ -245,6 +245,9 @@ impl Device {
|
||||||
pub fn graphics_queue(&self) -> &Queue {
|
pub fn graphics_queue(&self) -> &Queue {
|
||||||
&self.0.main_queue
|
&self.0.main_queue
|
||||||
}
|
}
|
||||||
|
pub fn transfer_queue(&self) -> &Queue {
|
||||||
|
&self.0.transfer_queue
|
||||||
|
}
|
||||||
pub fn present_queue(&self) -> &Queue {
|
pub fn present_queue(&self) -> &Queue {
|
||||||
&self.0.present_queue
|
&self.0.present_queue
|
||||||
}
|
}
|
||||||
|
|
|
@ -712,7 +712,7 @@ pub fn egui_pass(
|
||||||
|
|
||||||
cmd.bind_pipeline(&pipeline);
|
cmd.bind_pipeline(&pipeline);
|
||||||
cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32);
|
cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32);
|
||||||
cmd.bind_vertices(vertices.buffer(), 0);
|
cmd.bind_vertex_buffers(&[vertices.buffer()], &[0]);
|
||||||
cmd.push_constants(
|
cmd.push_constants(
|
||||||
&pipeline_layout,
|
&pipeline_layout,
|
||||||
vk::ShaderStageFlags::VERTEX,
|
vk::ShaderStageFlags::VERTEX,
|
||||||
|
|
|
@ -43,9 +43,9 @@ mod egui_pass;
|
||||||
mod images;
|
mod images;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod render_graph;
|
mod render_graph;
|
||||||
mod rendering;
|
pub mod rendering;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod util;
|
pub mod util;
|
||||||
|
|
||||||
use device::{Device, DeviceOwned, DeviceQueueFamilies};
|
use device::{Device, DeviceOwned, DeviceQueueFamilies};
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ pub enum Error {
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
struct VkNameList<'a> {
|
struct VkNameList<'a> {
|
||||||
names: Vec<*const i8>,
|
names: Vec<*const i8>,
|
||||||
|
@ -1072,7 +1072,7 @@ impl Drop for Surface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SamplerCache {
|
pub struct SamplerCache {
|
||||||
device: Device,
|
device: Device,
|
||||||
samplers: HashMap<pipeline::SamplerDesc, pipeline::Sampler>,
|
samplers: HashMap<pipeline::SamplerDesc, pipeline::Sampler>,
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1101,7 @@ impl SamplerCache {
|
||||||
|
|
||||||
pub struct Vulkan {
|
pub struct Vulkan {
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Device,
|
pub device: Device,
|
||||||
samplers: SamplerCache,
|
samplers: SamplerCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,6 +1124,14 @@ impl Vulkan {
|
||||||
const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr =
|
const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr =
|
||||||
c"VK_LAYER_NV_nomad_release_public_2021_4_2";
|
c"VK_LAYER_NV_nomad_release_public_2021_4_2";
|
||||||
|
|
||||||
|
pub fn samplers(&self) -> &SamplerCache {
|
||||||
|
&self.samplers
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn samplers_mut(&mut self) -> &mut SamplerCache {
|
||||||
|
&mut self.samplers
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
instance_layers: &[&CStr],
|
instance_layers: &[&CStr],
|
||||||
|
@ -1232,6 +1240,7 @@ impl Vulkan {
|
||||||
.features10(
|
.features10(
|
||||||
vk::PhysicalDeviceFeatures::default()
|
vk::PhysicalDeviceFeatures::default()
|
||||||
.sampler_anisotropy(true)
|
.sampler_anisotropy(true)
|
||||||
|
.fill_mode_non_solid(true)
|
||||||
.multi_draw_indirect(true),
|
.multi_draw_indirect(true),
|
||||||
)
|
)
|
||||||
.features11(vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true))
|
.features11(vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true))
|
||||||
|
@ -2049,7 +2058,7 @@ pub struct Renderer<W> {
|
||||||
// thinkw: want renderer linked with display? then no (real) headless
|
// thinkw: want renderer linked with display? then no (real) headless
|
||||||
display: RawDisplayHandle,
|
display: RawDisplayHandle,
|
||||||
pub window_contexts: HashMap<W, WindowContext>,
|
pub window_contexts: HashMap<W, WindowContext>,
|
||||||
vulkan: Vulkan,
|
pub vulkan: Vulkan,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use vk::Extent2D;
|
pub use vk::Extent2D;
|
||||||
|
@ -2066,447 +2075,59 @@ impl<W> Renderer<W> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_egui(&mut self, ctx: &egui::Context, output: egui::FullOutput) -> Result<()> {
|
pub async fn draw_with_graph<K, F, G, T>(
|
||||||
let pool = commands::SingleUseCommandPool::new(
|
&mut self,
|
||||||
self.vulkan.device.clone(),
|
window: &K,
|
||||||
self.vulkan.device.graphics_queue().clone(),
|
pre_present_cb: F,
|
||||||
)
|
cb: G,
|
||||||
.unwrap();
|
) -> Result<T>
|
||||||
let cmd = pool.alloc().unwrap();
|
where
|
||||||
|
K: core::hash::Hash + Eq,
|
||||||
let cmd_objects = output
|
W: core::hash::Hash + Eq + Borrow<K>,
|
||||||
.textures_delta
|
F: FnOnce(),
|
||||||
.set
|
G: FnOnce(&mut Self, &mut render_graph::RenderGraph) -> T,
|
||||||
.iter()
|
|
||||||
.map(|(egui_id, delta)| {
|
|
||||||
let size = delta.image.size();
|
|
||||||
let byte_size = size[0] * size[1] * 4;
|
|
||||||
|
|
||||||
let mut staging = buffers::Buffer::new(
|
|
||||||
self.vulkan.device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some(format!("egui-{egui_id:?}-staging-buf").into()),
|
|
||||||
size: byte_size as u64,
|
|
||||||
usage: vk::BufferUsageFlags::TRANSFER_SRC,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferHost,
|
|
||||||
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
|
|
||||||
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
|
|
||||||
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("staging buffer");
|
|
||||||
{
|
{
|
||||||
let mut mem = staging.map().expect("mapping staging buffer");
|
let dev = self.vulkan.device.clone();
|
||||||
match &delta.image {
|
|
||||||
egui::ImageData::Color(arc) => {
|
let ctx = self.window_contexts.get(window).unwrap();
|
||||||
let slice = unsafe {
|
let (frame, suboptimal) = ctx.current_swapchain.read().clone().acquire_image().await?;
|
||||||
core::slice::from_raw_parts(
|
|
||||||
arc.pixels.as_ptr().cast::<u8>(),
|
if suboptimal {
|
||||||
arc.pixels.len() * size_of::<Color32>(),
|
tracing::warn!(
|
||||||
)
|
"swapchain ({:?}) is suboptimal!",
|
||||||
};
|
ctx.current_swapchain.read().swapchain
|
||||||
mem[..slice.len()].copy_from_slice(slice);
|
);
|
||||||
}
|
|
||||||
egui::ImageData::Font(font_image) => {
|
|
||||||
for (i, c) in font_image.srgba_pixels(None).enumerate() {
|
|
||||||
let bytes = c.to_array();
|
|
||||||
mem[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sampled = if delta.is_whole() {
|
let [r, g, b] =
|
||||||
vk::ImageUsageFlags::SAMPLED
|
rand::prelude::StdRng::seed_from_u64(ctx.surface.surface.as_raw()).gen::<[f32; 3]>();
|
||||||
} else {
|
let clear_color = Rgba([r, g, b, 1.0]);
|
||||||
vk::ImageUsageFlags::TRANSFER_SRC
|
|
||||||
};
|
|
||||||
|
|
||||||
let texture = Arc::new(
|
let mut rg = render_graph::RenderGraph::new();
|
||||||
images::Image::new(
|
let framebuffer = rg.import_image(frame.image.clone(), Access::undefined());
|
||||||
self.vulkan.device.clone(),
|
rg.mark_as_output(framebuffer, Access::present());
|
||||||
images::ImageDesc {
|
rg.framebuffer = Some(framebuffer);
|
||||||
name: Some(format!("egui-texture-{egui_id:?}").into()),
|
|
||||||
format: vk::Format::R8G8B8A8_UNORM,
|
|
||||||
extent: vk::Extent3D {
|
|
||||||
width: delta.image.width() as u32,
|
|
||||||
height: delta.image.height() as u32,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
usage: sampled | vk::ImageUsageFlags::TRANSFER_DST,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("image creation"),
|
|
||||||
);
|
|
||||||
|
|
||||||
cmd.image_barrier(
|
render_graph::clear_pass(&mut rg, clear_color, framebuffer);
|
||||||
texture.image(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::empty(),
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
cmd.copy_buffer_to_image(
|
|
||||||
staging.buffer(),
|
|
||||||
texture.image(),
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
&[vk::BufferImageCopy {
|
|
||||||
buffer_offset: 0,
|
|
||||||
buffer_row_length: delta.image.width() as u32,
|
|
||||||
buffer_image_height: delta.image.height() as u32,
|
|
||||||
image_subresource: vk::ImageSubresourceLayers::default()
|
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
|
||||||
.base_array_layer(0)
|
|
||||||
.mip_level(0)
|
|
||||||
.layer_count(1),
|
|
||||||
image_offset: vk::Offset3D { x: 0, y: 0, z: 0 },
|
|
||||||
image_extent: vk::Extent3D {
|
|
||||||
width: texture.size().width,
|
|
||||||
height: texture.size().height,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
let id = self.egui_state.lookup_texture(*egui_id).unwrap_or_else(|| {
|
let t = cb(self, &mut rg);
|
||||||
let id = texture::TextureId::new();
|
|
||||||
|
|
||||||
self.egui_state.textures.insert(
|
let cmds = rg.resolve(dev.clone())?;
|
||||||
*egui_id,
|
|
||||||
EguiTextureInfo {
|
|
||||||
id,
|
|
||||||
options: delta.options,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
id
|
let future = cmds.submit(
|
||||||
});
|
Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
|
||||||
|
Some(frame.release),
|
||||||
if let Some(pos) = delta.pos {
|
Arc::new(sync::Fence::create(dev.clone())?),
|
||||||
// SAFETY: must exist because image is not whole.
|
|
||||||
let existing_texture = self.texture_handler.textures.get(&id).cloned().unwrap();
|
|
||||||
|
|
||||||
cmd.image_barrier(
|
|
||||||
texture.image(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_READ,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
cmd.image_barrier(
|
|
||||||
existing_texture.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::empty(),
|
|
||||||
vk::AccessFlags2::empty(),
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
cmd.blit_images(
|
|
||||||
&texture,
|
|
||||||
util::Rect2D::new(0, 0, texture.width() as i32, texture.height() as i32),
|
|
||||||
&existing_texture,
|
|
||||||
util::Rect2D::new_from_size(
|
|
||||||
glam::ivec2(pos[0] as i32, pos[1] as i32),
|
|
||||||
glam::ivec2(texture.width() as i32, texture.height() as i32),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
cmd.image_barrier(
|
|
||||||
existing_texture.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_SAMPLED_READ,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
cmd.image_barrier(
|
|
||||||
texture.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_SAMPLED_READ,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
self.texture_handler
|
|
||||||
.insert_image_with_id(id, texture.clone());
|
|
||||||
tracing::trace!("new texture for egui: {egui_id:?} -> {id:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
(staging, texture)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let draw_data = ctx.tessellate(output.shapes, output.pixels_per_point);
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
struct Vertex {
|
|
||||||
pos: glam::Vec2,
|
|
||||||
uv: glam::Vec2,
|
|
||||||
color: egui::epaint::Color32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
struct DrawCall(vk::DrawIndexedIndirectCommand);
|
|
||||||
unsafe impl bytemuck::Zeroable for DrawCall {}
|
|
||||||
unsafe impl bytemuck::Pod for DrawCall {}
|
|
||||||
|
|
||||||
let mut vertices = Vec::new();
|
|
||||||
let mut indices = Vec::new();
|
|
||||||
let mut draw_calls = Vec::new();
|
|
||||||
let mut textures = IndexMap::new();
|
|
||||||
let mut textures_indices = Vec::new();
|
|
||||||
for draw in draw_data {
|
|
||||||
let egui::epaint::Primitive::Mesh(mesh) = draw.primitive else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
draw_calls.push(DrawCall(vk::DrawIndexedIndirectCommand {
|
|
||||||
index_count: mesh.indices.len() as u32,
|
|
||||||
instance_count: 1,
|
|
||||||
first_index: indices.len() as u32,
|
|
||||||
vertex_offset: vertices.len() as i32,
|
|
||||||
first_instance: 0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
vertices.extend(mesh.vertices.iter().map(|v| Vertex {
|
|
||||||
pos: glam::vec2(v.pos.x, v.pos.y),
|
|
||||||
uv: glam::vec2(v.uv.x, v.uv.y),
|
|
||||||
color: v.color,
|
|
||||||
}));
|
|
||||||
|
|
||||||
indices.extend(mesh.indices);
|
|
||||||
let texture = self
|
|
||||||
.egui_state
|
|
||||||
.textures
|
|
||||||
.get(&mesh.texture_id)
|
|
||||||
.cloned()
|
|
||||||
.unwrap();
|
|
||||||
if !textures.contains_key(&texture.id) {
|
|
||||||
textures.insert(texture.id, texture);
|
|
||||||
}
|
|
||||||
let idx = textures.get_index_of(&texture.id).unwrap();
|
|
||||||
textures_indices.push(idx as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_draw_calls = draw_calls.len();
|
|
||||||
let device = self.vulkan.device.clone();
|
|
||||||
let (draw_staging, vertices, indices, draw_calls, texture_ids) = {
|
|
||||||
let vertices_size = vertices.len() * size_of::<Vertex>();
|
|
||||||
let indices_size = indices.len() * size_of::<u32>();
|
|
||||||
let draw_calls_size = draw_calls.len() * size_of::<vk::DrawIndexedIndirectCommand>();
|
|
||||||
|
|
||||||
let staging_size = vertices_size + indices_size + draw_calls_size;
|
|
||||||
|
|
||||||
let mut staging = buffers::Buffer::new(
|
|
||||||
device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some("egui-draw-staging".into()),
|
|
||||||
size: staging_size as u64,
|
|
||||||
usage: vk::BufferUsageFlags::TRANSFER_SRC,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferHost,
|
|
||||||
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
|
|
||||||
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
|
|
||||||
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
{
|
// call pre_present_notify
|
||||||
let mut map = staging.map()?;
|
pre_present_cb();
|
||||||
|
|
||||||
let (st_vertices, rest) = map.split_at_mut(vertices_size);
|
future.await;
|
||||||
let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size);
|
let wait = Some(frame.release);
|
||||||
st_vertices.copy_from_slice(bytemuck::cast_slice(&vertices));
|
frame.present(wait)?;
|
||||||
st_indices.copy_from_slice(bytemuck::cast_slice(&indices));
|
|
||||||
st_drawcalls.copy_from_slice(bytemuck::cast_slice(&draw_calls));
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = buffers::Buffer::new(
|
Ok(t)
|
||||||
device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some("egui-draw-vertices".into()),
|
|
||||||
size: vertices_size as u64,
|
|
||||||
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let indices = buffers::Buffer::new(
|
|
||||||
device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some("egui-draw-indices".into()),
|
|
||||||
size: indices_size as u64,
|
|
||||||
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let draw_calls = buffers::Buffer::new(
|
|
||||||
device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some("egui-draw-draw_calls".into()),
|
|
||||||
size: draw_calls_size as u64,
|
|
||||||
usage: vk::BufferUsageFlags::TRANSFER_DST
|
|
||||||
| vk::BufferUsageFlags::INDIRECT_BUFFER,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
cmd.copy_buffers(
|
|
||||||
staging.buffer(),
|
|
||||||
vertices.buffer(),
|
|
||||||
&[vk::BufferCopy {
|
|
||||||
src_offset: 0,
|
|
||||||
dst_offset: 0,
|
|
||||||
size: vertices_size as u64,
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
cmd.copy_buffers(
|
|
||||||
staging.buffer(),
|
|
||||||
indices.buffer(),
|
|
||||||
&[vk::BufferCopy {
|
|
||||||
src_offset: vertices_size as u64,
|
|
||||||
dst_offset: 0,
|
|
||||||
size: indices_size as u64,
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
cmd.copy_buffers(
|
|
||||||
staging.buffer(),
|
|
||||||
draw_calls.buffer(),
|
|
||||||
&[vk::BufferCopy {
|
|
||||||
src_offset: (vertices_size + indices_size) as u64,
|
|
||||||
dst_offset: 0,
|
|
||||||
size: draw_calls_size as u64,
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut texture_ids = buffers::Buffer::new(
|
|
||||||
device.clone(),
|
|
||||||
buffers::BufferDesc {
|
|
||||||
name: Some("egui-draw-texture_ids".into()),
|
|
||||||
size: (textures_indices.len() * size_of::<u32>()) as u64,
|
|
||||||
usage: vk::BufferUsageFlags::STORAGE_BUFFER,
|
|
||||||
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
|
|
||||||
alloc_flags: vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
{
|
|
||||||
let mut map = texture_ids.map()?;
|
|
||||||
map.copy_from_slice(bytemuck::cast_slice(&textures_indices));
|
|
||||||
}
|
|
||||||
|
|
||||||
(staging, vertices, indices, draw_calls, texture_ids)
|
|
||||||
};
|
|
||||||
|
|
||||||
let descriptor_infos = textures
|
|
||||||
.values()
|
|
||||||
.map(|entry| {
|
|
||||||
let texture = self.texture_handler.get_texture(entry.id).unwrap();
|
|
||||||
let info = vk::DescriptorImageInfo {
|
|
||||||
sampler: self
|
|
||||||
.vulkan
|
|
||||||
.samplers
|
|
||||||
.get_sampler(entry.into_sampler_desc())
|
|
||||||
.unwrap(),
|
|
||||||
image_view: texture
|
|
||||||
.get_view(images::ImageViewDesc {
|
|
||||||
kind: vk::ImageViewType::TYPE_2D,
|
|
||||||
format: texture.format(),
|
|
||||||
aspect: vk::ImageAspectFlags::COLOR,
|
|
||||||
mip_range: (0..1).into(),
|
|
||||||
layer_range: (0..1).into(),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
info
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let uniform_info = vk::DescriptorBufferInfo {
|
|
||||||
buffer: texture_ids.buffer(),
|
|
||||||
offset: 0,
|
|
||||||
range: texture_ids.len(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let descriptor_writes = descriptor_infos
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, info)| {
|
|
||||||
vk::WriteDescriptorSet::default()
|
|
||||||
.image_info(core::slice::from_ref(info))
|
|
||||||
.descriptor_count(1)
|
|
||||||
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
|
|
||||||
.dst_set(self.egui_state.descriptor_set)
|
|
||||||
.dst_binding(EguiState::TEXTURE_BINDING)
|
|
||||||
.dst_array_element(i as u32)
|
|
||||||
})
|
|
||||||
.chain(core::iter::once({
|
|
||||||
vk::WriteDescriptorSet::default()
|
|
||||||
.buffer_info(core::slice::from_ref(&uniform_info))
|
|
||||||
.descriptor_count(1)
|
|
||||||
.dst_binding(EguiState::UNIFORM_BINDING)
|
|
||||||
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
|
|
||||||
.dst_array_element(0)
|
|
||||||
.dst_set(self.egui_state.descriptor_set)
|
|
||||||
}))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
device.dev().update_descriptor_sets(&descriptor_writes, &[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let to_remove_tex_ids = output
|
|
||||||
.textures_delta
|
|
||||||
.free
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| self.egui_state.textures.get(id).cloned())
|
|
||||||
.map(|entry| entry.id)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
self.egui_state.render_state = Some(EguiRenderState {
|
|
||||||
vertices,
|
|
||||||
indices,
|
|
||||||
draw_calls,
|
|
||||||
num_draw_calls,
|
|
||||||
texture_ids,
|
|
||||||
textures_to_free: to_remove_tex_ids,
|
|
||||||
});
|
|
||||||
|
|
||||||
let fence = Arc::new(sync::Fence::create(device.clone()).unwrap());
|
|
||||||
let future = cmd.submit_async(None, None, fence).unwrap();
|
|
||||||
|
|
||||||
future.block()?;
|
|
||||||
|
|
||||||
black_box((cmd_objects, draw_staging));
|
|
||||||
// free after drawing
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_draw_egui<K, F>(
|
pub fn debug_draw_egui<K, F>(
|
||||||
|
@ -2588,159 +2209,6 @@ impl<W> Renderer<W> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_draw<K, F: FnOnce()>(&mut self, window: &K, pre_present_cb: F) -> Result<()>
|
|
||||||
where
|
|
||||||
K: core::hash::Hash + Eq,
|
|
||||||
W: core::hash::Hash + Eq + Borrow<K>,
|
|
||||||
{
|
|
||||||
let dev = self.vulkan.device.clone();
|
|
||||||
let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.graphics_queue().clone())?;
|
|
||||||
|
|
||||||
if let Some(ctx) = self.window_contexts.get(window) {
|
|
||||||
let (frame, suboptimal) =
|
|
||||||
smol::block_on(ctx.current_swapchain.read().clone().acquire_image())?;
|
|
||||||
|
|
||||||
if suboptimal {
|
|
||||||
tracing::warn!(
|
|
||||||
"swapchain ({:?}) is suboptimal!",
|
|
||||||
ctx.current_swapchain.read().swapchain
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let [r, g, b] = rand::prelude::StdRng::seed_from_u64(ctx.surface.surface.as_raw())
|
|
||||||
.gen::<[f32; 3]>();
|
|
||||||
let clear_color = Rgba([r, g, b, 1.0]);
|
|
||||||
|
|
||||||
let egui_ctx = self.egui_state.render_state.take();
|
|
||||||
|
|
||||||
let cmd = util::timed("record command buffer", || {
|
|
||||||
let cmd = pool.alloc()?;
|
|
||||||
cmd.image_barrier(
|
|
||||||
frame.image.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::empty(),
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
cmd.clear_color_image(
|
|
||||||
frame.image.handle(),
|
|
||||||
frame.format,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
clear_color,
|
|
||||||
&[images::SUBRESOURCERANGE_COLOR_ALL],
|
|
||||||
);
|
|
||||||
cmd.image_barrier(
|
|
||||||
frame.image.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_WRITE,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(ctx) = egui_ctx.as_ref() {
|
|
||||||
_ = &ctx.texture_ids;
|
|
||||||
|
|
||||||
let color_attachment = &vk::RenderingAttachmentInfo::default()
|
|
||||||
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
|
||||||
.image_view(frame.view)
|
|
||||||
.load_op(vk::AttachmentLoadOp::LOAD)
|
|
||||||
.store_op(vk::AttachmentStoreOp::STORE);
|
|
||||||
|
|
||||||
cmd.begin_rendering(
|
|
||||||
vk::RenderingInfo::default()
|
|
||||||
.color_attachments(core::slice::from_ref(color_attachment))
|
|
||||||
.layer_count(1)
|
|
||||||
.render_area(vk::Rect2D::default().extent(frame.swapchain.extent)),
|
|
||||||
);
|
|
||||||
|
|
||||||
cmd.set_scissors(&[vk::Rect2D::default()
|
|
||||||
.offset(vk::Offset2D::default())
|
|
||||||
.extent(frame.swapchain.extent)]);
|
|
||||||
|
|
||||||
cmd.set_viewport(&[vk::Viewport::default()
|
|
||||||
.x(0.0)
|
|
||||||
.y(0.0)
|
|
||||||
.min_depth(0.0)
|
|
||||||
.max_depth(1.0)
|
|
||||||
.width(frame.swapchain.extent.width as f32)
|
|
||||||
.height(frame.swapchain.extent.height as f32)]);
|
|
||||||
|
|
||||||
cmd.bind_pipeline(&self.egui_state.pipeline);
|
|
||||||
cmd.bind_indices(ctx.indices.buffer(), 0, vk::IndexType::UINT32);
|
|
||||||
cmd.bind_vertices(ctx.vertices.buffer(), 0);
|
|
||||||
cmd.push_constants(
|
|
||||||
&self.egui_state.pipeline_layout,
|
|
||||||
vk::ShaderStageFlags::VERTEX,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(
|
|
||||||
&[
|
|
||||||
frame.swapchain.extent.width as f32,
|
|
||||||
frame.swapchain.extent.height as f32,
|
|
||||||
]
|
|
||||||
.map(|f| f.to_bits()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
cmd.bind_descriptor_sets(
|
|
||||||
&self.egui_state.pipeline_layout,
|
|
||||||
vk::PipelineBindPoint::GRAPHICS,
|
|
||||||
&[self.egui_state.descriptor_set],
|
|
||||||
);
|
|
||||||
cmd.draw_indexed_indirect(
|
|
||||||
ctx.draw_calls.buffer(),
|
|
||||||
0,
|
|
||||||
ctx.num_draw_calls as u32,
|
|
||||||
size_of::<vk::DrawIndexedIndirectCommand>() as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
cmd.end_rendering();
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.image_barrier(
|
|
||||||
frame.image.handle(),
|
|
||||||
vk::ImageAspectFlags::COLOR,
|
|
||||||
vk::PipelineStageFlags2::FRAGMENT_SHADER,
|
|
||||||
vk::AccessFlags2::SHADER_WRITE,
|
|
||||||
vk::PipelineStageFlags2::BOTTOM_OF_PIPE,
|
|
||||||
vk::AccessFlags2::empty(),
|
|
||||||
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
|
|
||||||
vk::ImageLayout::PRESENT_SRC_KHR,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
Result::Ok(cmd)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let future = cmd.submit_async(
|
|
||||||
Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
|
|
||||||
Some(frame.release),
|
|
||||||
Arc::new(sync::Fence::create(dev.clone())?),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// call pre_present_notify
|
|
||||||
pre_present_cb();
|
|
||||||
|
|
||||||
let wait = Some(frame.release);
|
|
||||||
frame.present(wait)?;
|
|
||||||
future.block()?;
|
|
||||||
|
|
||||||
egui_ctx.map(|ctx| {
|
|
||||||
for id in ctx.textures_to_free {
|
|
||||||
self.texture_handler.remove_texture(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_window_context(
|
pub fn new_window_context(
|
||||||
&mut self,
|
&mut self,
|
||||||
extent: vk::Extent2D,
|
extent: vk::Extent2D,
|
||||||
|
|
|
@ -43,7 +43,7 @@ impl From<GraphResourceDesc> for GraphResource {
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
pub enum GraphResource {
|
pub enum GraphResource {
|
||||||
Framebuffer(Arc<SwapchainFrame>),
|
Framebuffer(Arc<Image>),
|
||||||
ImportedImage(Arc<Image>),
|
ImportedImage(Arc<Image>),
|
||||||
ImportedBuffer(Arc<Buffer>),
|
ImportedBuffer(Arc<Buffer>),
|
||||||
Image(Arc<Image>),
|
Image(Arc<Image>),
|
||||||
|
@ -63,7 +63,7 @@ impl GraphResource {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
GraphResource::Framebuffer(swapchain_frame) => {
|
GraphResource::Framebuffer(swapchain_frame) => {
|
||||||
(swapchain_frame.index, swapchain_frame.image.handle()).hash(&mut state)
|
(swapchain_frame.handle()).hash(&mut state)
|
||||||
}
|
}
|
||||||
GraphResource::ImportedImage(image) => image.handle().hash(&mut state),
|
GraphResource::ImportedImage(image) => image.handle().hash(&mut state),
|
||||||
GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state),
|
GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state),
|
||||||
|
@ -95,6 +95,7 @@ pub struct RenderContext<'a> {
|
||||||
pub device: device::Device,
|
pub device: device::Device,
|
||||||
pub cmd: commands::SingleUseCommand,
|
pub cmd: commands::SingleUseCommand,
|
||||||
pub resources: &'a [GraphResource],
|
pub resources: &'a [GraphResource],
|
||||||
|
pub framebuffer: Option<GraphResourceId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderContext<'_> {
|
impl RenderContext<'_> {
|
||||||
|
@ -102,7 +103,7 @@ impl RenderContext<'_> {
|
||||||
self.resources.get(id.0 as usize).and_then(|res| match res {
|
self.resources.get(id.0 as usize).and_then(|res| match res {
|
||||||
GraphResource::ImportedImage(arc) => Some(arc),
|
GraphResource::ImportedImage(arc) => Some(arc),
|
||||||
GraphResource::Image(image) => Some(image),
|
GraphResource::Image(image) => Some(image),
|
||||||
GraphResource::Framebuffer(fb) => Some(&fb.image),
|
GraphResource::Framebuffer(fb) => Some(fb),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -113,6 +114,14 @@ impl RenderContext<'_> {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn get_framebuffer(&self) -> Option<&Image> {
|
||||||
|
self.framebuffer
|
||||||
|
.and_then(|rid| self.resources.get(rid.0 as usize))
|
||||||
|
.and_then(|res| match res {
|
||||||
|
GraphResource::Framebuffer(arc) => Some(arc.as_ref()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -349,6 +358,7 @@ pub struct RenderGraph {
|
||||||
/// the rendergraph produces these resources. Any passes on which these
|
/// the rendergraph produces these resources. Any passes on which these
|
||||||
/// outputs do not depend are pruned.
|
/// outputs do not depend are pruned.
|
||||||
outputs: BTreeMap<GraphResourceId, Access>,
|
outputs: BTreeMap<GraphResourceId, Access>,
|
||||||
|
pub(crate) framebuffer: Option<GraphResourceId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
|
@ -360,9 +370,14 @@ impl RenderGraph {
|
||||||
resources: Vec::new(),
|
resources: Vec::new(),
|
||||||
pass_descs,
|
pass_descs,
|
||||||
outputs: BTreeMap::new(),
|
outputs: BTreeMap::new(),
|
||||||
|
framebuffer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_framebuffer(&self) -> Option<GraphResourceId> {
|
||||||
|
self.framebuffer
|
||||||
|
}
|
||||||
|
|
||||||
fn get_next_resource_id(&mut self) -> GraphResourceId {
|
fn get_next_resource_id(&mut self) -> GraphResourceId {
|
||||||
GraphResourceId(self.resources.len() as u32)
|
GraphResourceId(self.resources.len() as u32)
|
||||||
}
|
}
|
||||||
|
@ -406,8 +421,14 @@ impl RenderGraph {
|
||||||
self.import_resource(res, access)
|
self.import_resource(res, access)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_framebuffer(&mut self, frame: Arc<SwapchainFrame>) -> GraphResourceId {
|
pub fn import_framebuffer(&mut self, frame: &SwapchainFrame) -> GraphResourceId {
|
||||||
self.import_resource(GraphResource::Framebuffer(frame), Access::undefined())
|
let rid = self.import_resource(
|
||||||
|
GraphResource::Framebuffer(frame.image.clone()),
|
||||||
|
Access::undefined(),
|
||||||
|
);
|
||||||
|
self.mark_as_output(rid, Access::present());
|
||||||
|
self.framebuffer = Some(rid);
|
||||||
|
rid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_pass(&mut self, pass: PassDesc) {
|
pub fn add_pass(&mut self, pass: PassDesc) {
|
||||||
|
@ -500,6 +521,7 @@ impl RenderGraph {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
cmd,
|
cmd,
|
||||||
resources,
|
resources,
|
||||||
|
framebuffer: self.framebuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
for pass in passes {
|
for pass in passes {
|
||||||
|
@ -538,7 +560,7 @@ impl RenderGraph {
|
||||||
) {
|
) {
|
||||||
let barrier: Barrier = match res {
|
let barrier: Barrier = match res {
|
||||||
GraphResource::Framebuffer(arc) => {
|
GraphResource::Framebuffer(arc) => {
|
||||||
image_barrier(arc.image.handle(), arc.image.format(), from, to, None).into()
|
image_barrier(arc.handle(), arc.format(), from, to, None).into()
|
||||||
}
|
}
|
||||||
GraphResource::ImportedImage(arc) => {
|
GraphResource::ImportedImage(arc) => {
|
||||||
image_barrier(arc.handle(), arc.format(), from, to, None).into()
|
image_barrier(arc.handle(), arc.format(), from, to, None).into()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! def_monotonic_id {
|
macro_rules! def_monotonic_id {
|
||||||
|
@ -350,8 +351,13 @@ pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct Rgba(pub [f32; 4]);
|
pub struct Rgba(pub [f32; 4]);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Rgba8(pub [u8; 4]);
|
||||||
|
|
||||||
impl std::hash::Hash for Rgba {
|
impl std::hash::Hash for Rgba {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.0.map(|f| hash_f32(state, f));
|
self.0.map(|f| hash_f32(state, f));
|
||||||
|
@ -369,6 +375,9 @@ impl Rgba {
|
||||||
pub fn into_f32(&self) -> [f32; 4] {
|
pub fn into_f32(&self) -> [f32; 4] {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
pub fn into_u8(&self) -> Rgba8 {
|
||||||
|
Rgba8(self.0.map(|f| (f.clamp(0.0, 1.0) * 255.0) as u8))
|
||||||
|
}
|
||||||
pub fn into_snorm(&self) -> [f32; 4] {
|
pub fn into_snorm(&self) -> [f32; 4] {
|
||||||
self.0.map(|f| (f - 0.5) * 2.0)
|
self.0.map(|f| (f - 0.5) * 2.0)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue