Compare commits

..

5 commits

Author SHA1 Message Date
janis cb02051cf3
agent-generated docs 2026-10-22 22:47:15 +02:00
janis 0e19b8d99b
use ash-formats 2026-10-22 22:47:03 +02:00
janis 6e9da846bb
fix warnings 2026-10-22 20:39:47 +02:00
janis cfc5f8594c
idk? 2026-10-22 20:14:07 +02:00
janis d942589108
update dependencies 2026-10-22 20:13:58 +02:00
23 changed files with 401 additions and 864 deletions

View file

@ -28,27 +28,28 @@ debug = true
[workspace.dependencies]
anyhow = "1.0.89"
thiserror = "2.0"
derive_more = { version = "1.0.0", features = ["deref", "debug", "deref_mut"] }
derive_more = { version = "2", features = ["deref", "debug", "deref_mut"] }
tracing = "0.1"
tracing-subscriber = {version ="0.3", features = ["env-filter"]}
glam = {version = "0.29.0", features = ["bytemuck"]}
rand = "0.9"
glam = { version = "0.33", features = ["bytemuck"] }
rand = "0.10"
bitflags = "2.6"
thread_local = "1.1.8"
ash = "0.38.0"
ash-formats = { git = "https://github.com/janis-bhm/ash-formats.git", version = "0.1.0" }
ash-window = "0.13.0"
vk-mem = "0.5.0"
# vk-mem = "0.5.0"
gpu-allocator = { git = "https://github.com/janis-bhm/gpu-allocator", branch = "main" }
rectangle-pack = "0.4.2"
vk-sync = "0.1.6"
# vk-sync = "0.1.6"
arrayvec = "0.7.6"
tinyvec = "1.8"
indexmap = "2"
petgraph = "0.7"
petgraph = "0.8"
itertools = "0.14.0"
ahash = "0.8"
@ -57,10 +58,10 @@ md-5 = "0.11.0"
parking_lot = "0.12.3"
tokio = "1.42"
futures = "0.3"
# tokio = "1.42"
# futures = "0.3"
smol = "2.0"
rayon = "1.10"
# rayon = "1.10"
winit = {version = "0.30.5", features = ["rwh_06"]}
raw-window-handle = "0.6"
@ -68,22 +69,22 @@ raw-window-handle = "0.6"
egui = "0.32"
egui_winit_platform = "0.27"
bevy = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_app = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_asset = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_derive = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_ecs = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy", features = ["multi_threaded", "trace"]}
bevy_hierarchy = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_log = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_math = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_platform = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_reflect = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_tasks = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_time = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_transform = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_utils = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_window = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy_winit = { version = "0.19.0-dev", git = "https://github.com/bevyengine/bevy" }
bevy = { path = "/var/code/bevy-engine/bevy" }
bevy_app = { path = "/var/code/bevy-engine/bevy/crates/bevy_app" }
bevy_asset = { path = "/var/code/bevy-engine/bevy/crates/bevy_asset" }
bevy_derive = { path = "/var/code/bevy-engine/bevy/crates/bevy_derive" }
bevy_ecs = { path = "/var/code/bevy-engine/bevy/crates/bevy_ecs", features = ["multi_threaded", "trace"]}
# bevy_hierarchy = { path = "/var/code/bevy-engine/bevy/crates/bevy_hierarchy" }
bevy_log = { path = "/var/code/bevy-engine/bevy/crates/bevy_log" }
# bevy_math = { path = "/var/code/bevy-engine/bevy/crates/bevy_math" }
bevy_platform = { path = "/var/code/bevy-engine/bevy/crates/bevy_platform" }
bevy_reflect = { path = "/var/code/bevy-engine/bevy/crates/bevy_reflect" }
bevy_tasks = { path = "/var/code/bevy-engine/bevy/crates/bevy_tasks" }
bevy_time = { path = "/var/code/bevy-engine/bevy/crates/bevy_time" }
# bevy_transform = { path = "/var/code/bevy-engine/bevy/crates/bevy_transform" }
bevy_utils = { path = "/var/code/bevy-engine/bevy/crates/bevy_utils" }
bevy_window = { path = "/var/code/bevy-engine/bevy/crates/bevy_window" }
# bevy_winit = { path = "/var/code/bevy-engine/bevy/crates/bevy_winit" }
[[example]]
name = "bevy_integration"

View file

@ -1 +1,3 @@
*.ttf
!Roboto/Roboto-VariableFont_wdth,wght.ttf
!Noto_Sans_SC/NotoSansSC-VariableFont_wght.ttf

View file

@ -26,3 +26,7 @@ for filename in "${!FONTS[@]}"; do
echo "Fetching $filename from $url"
curl -L "$url" -o "$filename"
done
mkdir -p Roboto Noto_Sans_SC
cp Roboto.ttf Roboto/Roboto-VariableFont_wdth,wght.ttf
cp NotoSansSC.ttf Noto_Sans_SC/NotoSansSC-VariableFont_wght.ttf

View file

@ -2,10 +2,9 @@ use crate::extract_plugin::MainWorld;
use bevy_ecs::{
change_detection::Tick,
prelude::*,
query::FilteredAccessSet,
system::{
ReadOnlySystemParam, SystemMeta, SystemParam, SystemParamItem, SystemParamValidationError,
SystemState,
ReadOnlySystemParam, SystemAccess, SystemMeta, SystemParam, SystemParamAccessConflict,
SystemParamItem, SystemParamValidationError, SystemState,
},
world::unsafe_world_cell::UnsafeWorldCell,
};
@ -83,15 +82,9 @@ where
fn init_access(
state: &Self::State,
system_meta: &mut SystemMeta,
component_access_set: &mut FilteredAccessSet,
world: &mut World,
) {
Res::<MainWorld>::init_access(
&state.main_world_state,
system_meta,
component_access_set,
world,
);
system_access: &mut SystemAccess,
) -> Result<(), SystemParamAccessConflict> {
Res::<MainWorld>::init_access(&state.main_world_state, system_meta, system_access)
}
#[inline]

View file

@ -101,7 +101,50 @@ impl Plugin for RenderPlugin {
}
fn init_vulkan(app: &mut App) -> Option<()> {
let device = renderer::device::Device::new_from_default_desc(None, &[]).ok()?;
let instance_desc = renderer::instance::InstanceDesc {
app_name: Some("Vidya"),
layers: &[],
layer_settings: &[],
..Default::default()
};
let instance = renderer::instance::Instance::new(&instance_desc).ok()?;
let required_extensions = [
renderer::make_extension!(renderer::ash::ext::mesh_shader),
renderer::make_extension!(renderer::ash::ext::index_type_uint8),
renderer::make_extension!(renderer::ash::khr::spirv_1_4),
];
let features = renderer::PhysicalDeviceFeatures {
core: renderer::ash::vk::PhysicalDeviceFeatures::default()
.sampler_anisotropy(true)
.fill_mode_non_solid(true)
.multi_draw_indirect(true),
core11: renderer::ash::vk::PhysicalDeviceVulkan11Features::default()
.shader_draw_parameters(true),
core12: renderer::ash::vk::PhysicalDeviceVulkan12Features::default()
.shader_int8(true)
.runtime_descriptor_array(true)
.descriptor_binding_partially_bound(true)
.shader_sampled_image_array_non_uniform_indexing(true)
.descriptor_binding_sampled_image_update_after_bind(true)
.storage_buffer8_bit_access(true),
core13: renderer::ash::vk::PhysicalDeviceVulkan13Features::default()
.synchronization2(true)
.dynamic_rendering(true)
.maintenance4(true),
mesh_shader: Some(
renderer::ash::vk::PhysicalDeviceMeshShaderFeaturesEXT::default()
.mesh_shader(true)
.task_shader(true),
),
};
let adapter = instance
.choose_adapter_default(None, &required_extensions, Some(&features))
.ok()?;
let device = adapter
.create_logical_device(&instance, &required_extensions, features, None)
.ok()?;
let device = RenderDevice { device };
app.insert_resource(device.clone());

View file

@ -93,12 +93,12 @@ impl Plugin for SyncWorldPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.init_resource::<PendingSyncEntity>();
app.add_observer(
|add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|add: On<Add<SyncToRenderWorld>>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(add.entity));
},
);
app.add_observer(
|remove: On<Remove, SyncToRenderWorld>,
|remove: On<Remove<SyncToRenderWorld>>,
mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| {
if let Ok(e) = query.get(remove.entity) {
@ -278,15 +278,15 @@ mod render_entities_world_query_impls {
use super::{MainEntity, RenderEntity};
use bevy_ecs::{
archetype::Archetype,
archetype::{Archetype, ArchetypeId},
change_detection::Tick,
component::{ComponentId, Components},
entity::Entity,
query::{
ArchetypeQueryData, FilteredAccess, IterQueryData, QueryData, ReadOnlyQueryData,
ReleaseStateQueryData, SingleEntityQueryData, WorldQuery,
ArchetypeQueryData, FilteredAccess, FilteredAccessSet, IterQueryData, QueryData,
ReadOnlyQueryData, ReleaseStateQueryData, SingleEntityQueryData, WorldQuery,
},
storage::{Table, TableRow},
storage::{Table, TableId, TableRow},
world::{World, unsafe_world_cell::UnsafeWorldCell},
};
@ -305,14 +305,12 @@ mod render_entities_world_query_impls {
#[inline]
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
component_id: &'s ComponentId,
state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
unsafe {
<&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
}
unsafe { <&RenderEntity as WorldQuery>::init_fetch(world, state, last_run, this_run) }
}
const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
@ -320,28 +318,45 @@ mod render_entities_world_query_impls {
#[inline]
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
component_id: &'s ComponentId,
state: &'s Self::State,
archetype_id: ArchetypeId,
archetype: &'w Archetype,
table_id: TableId,
table: &'w Table,
) {
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
unsafe {
<&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
<&RenderEntity as WorldQuery>::set_archetype(
fetch,
state,
archetype_id,
archetype,
table_id,
table,
);
}
}
#[inline]
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
&component_id: &'s ComponentId,
state: &'s Self::State,
table_id: TableId,
table: &'w Table,
) {
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, state, table_id, table) }
}
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
<&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {
<&RenderEntity as WorldQuery>::update_component_access(state, access);
}
fn init_nested_access(
_state: &Self::State,
_component_access_set: &mut FilteredAccessSet,
) -> Result<(), FilteredAccessSet> {
Ok(())
}
fn init_state(world: &mut World) -> ComponentId {
@ -353,11 +368,13 @@ mod render_entities_world_query_impls {
}
fn matches_component_set(
&state: &ComponentId,
state: &Self::State,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
<&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
<&RenderEntity as WorldQuery>::matches_component_set(state, set_contains_id)
}
fn update_archetypes(_state: &mut Self::State, _world: UnsafeWorldCell) {}
}
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
@ -425,14 +442,12 @@ mod render_entities_world_query_impls {
#[inline]
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
component_id: &'s ComponentId,
state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
unsafe {
<&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
}
unsafe { <&MainEntity as WorldQuery>::init_fetch(world, state, last_run, this_run) }
}
const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
@ -440,28 +455,45 @@ mod render_entities_world_query_impls {
#[inline]
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId,
state: &'s Self::State,
archetype_id: ArchetypeId,
archetype: &'w Archetype,
table_id: TableId,
table: &'w Table,
) {
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
unsafe {
<&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
<&MainEntity as WorldQuery>::set_archetype(
fetch,
state,
archetype_id,
archetype,
table_id,
table,
);
}
}
#[inline]
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
&component_id: &'s ComponentId,
state: &'s Self::State,
table_id: TableId,
table: &'w Table,
) {
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
unsafe { <&MainEntity as WorldQuery>::set_table(fetch, state, table_id, table) }
}
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
<&MainEntity as WorldQuery>::update_component_access(&component_id, access);
fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {
<&MainEntity as WorldQuery>::update_component_access(state, access);
}
fn init_nested_access(
_state: &Self::State,
_component_access_set: &mut FilteredAccessSet,
) -> Result<(), FilteredAccessSet> {
Ok(())
}
fn init_state(world: &mut World) -> ComponentId {
@ -473,11 +505,13 @@ mod render_entities_world_query_impls {
}
fn matches_component_set(
&state: &ComponentId,
state: &Self::State,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
<&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
<&MainEntity as WorldQuery>::matches_component_set(state, set_contains_id)
}
fn update_archetypes(_state: &mut Self::State, _world: UnsafeWorldCell) {}
}
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
@ -558,12 +592,12 @@ mod tests {
main_world.init_resource::<PendingSyncEntity>();
main_world.add_observer(
|add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
|add: On<Add<SyncToRenderWorld>>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(add.entity));
},
);
main_world.add_observer(
|remove: On<Remove, SyncToRenderWorld>,
|remove: On<Remove<SyncToRenderWorld>>,
mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| {
if let Ok(e) = query.get(remove.entity) {

View file

@ -5,7 +5,10 @@ use std::{
use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_window::{PrimaryWindow, RawHandleWrapper, Window, WindowClosing};
use renderer::{ash::vk, swapchain::WindowSurface};
use renderer::{
ash::vk,
swapchain::{Surface, SwapchainConfiguration},
};
use crate::{RenderDevice, extract_param::Extract};
@ -39,7 +42,7 @@ impl DerefMut for ExtractedWindows {
}
}
#[derive(Default, Resource)]
#[derive(Default)]
pub struct Surfaces {
surfaces: EntityHashMap<SurfaceData>,
configured: HashSet<Entity>,
@ -57,11 +60,11 @@ impl Surfaces {
}
pub struct SurfaceData {
pub surface: WindowSurface,
pub surface: Surface,
}
impl Deref for SurfaceData {
type Target = WindowSurface;
type Target = Surface;
fn deref(&self) -> &Self::Target {
&self.surface
@ -74,7 +77,7 @@ pub(crate) fn extract_windows(
mut closing: Extract<MessageReader<WindowClosing>>,
windows: Extract<Query<(Entity, &RawHandleWrapper, &Window, Option<&PrimaryWindow>)>>,
mut removed: Extract<RemovedComponents<RawHandleWrapper>>,
mut surfaces: ResMut<Surfaces>,
mut surfaces: NonSendMut<Surfaces>,
) {
for (entity, handle, window, primary) in windows.iter() {
if primary.is_some() {
@ -116,7 +119,7 @@ pub(crate) fn extract_windows(
pub(crate) fn need_surface_configuration(
windows: Res<ExtractedWindows>,
surfaces: Res<Surfaces>,
surfaces: NonSend<Surfaces>,
) -> bool {
windows
.windows
@ -126,7 +129,7 @@ pub(crate) fn need_surface_configuration(
pub(crate) fn configure_surfaces(
windows: Res<ExtractedWindows>,
mut surfaces: ResMut<Surfaces>,
mut surfaces: NonSendMut<Surfaces>,
device: Res<RenderDevice>,
) {
for window in windows.windows.values() {
@ -136,27 +139,49 @@ pub(crate) fn configure_surfaces(
window.handle.get_window_handle(),
);
let surface = WindowSurface::new(
(**device).clone(),
vk::Extent2D {
let surface = unsafe {
Surface::new_from_raw_window_handle(
device.instance(),
display_handle,
window_handle,
)
}
.expect("Failed to create window surface");
surface
.configure(
&**device,
SwapchainConfiguration {
extent: vk::Extent2D {
width: window.physical_width,
height: window.physical_height,
},
window_handle,
display_handle,
..Default::default()
},
)
.expect("Failed to create window surface");
.expect("Failed to configure window surface");
SurfaceData { surface }
});
if window.size_changed {
let config = surface
.surface
.swapchain()
.as_ref()
.map(|swapchain| swapchain.config().clone())
.unwrap_or_default();
surface
.surface
.recreate_with(Some(vk::Extent2D {
.configure(
&**device,
SwapchainConfiguration {
extent: vk::Extent2D {
width: window.physical_width,
height: window.physical_height,
}))
},
..config
},
)
.expect("Failed to recreate surface");
}
surfaces.configured.insert(window.entity);

View file

@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use rand::{Rng, SeedableRng};
use rand::{RngExt, SeedableRng};
use renderer::{
Renderer2, render_graph,
swapchain::{Surface, SwapchainConfiguration},
@ -106,7 +106,8 @@ impl WinitState {
use renderer::ash::vk::Handle;
let [r, g, b]: [f32; 3] =
rand::prelude::StdRng::seed_from_u64(window.surface.raw().as_raw()).random();
rand::prelude::StdRng::seed_from_u64(window.surface.raw().as_raw())
.random::<[f32; 3]>();
render_graph::clear_pass(rg, renderer::util::Rgba([r, g, b, 1.0]), framebuffer);
egui_pre_pass(
&dev,

View file

@ -10,7 +10,7 @@ indexmap = { workspace = true }
petgraph = { workspace = true }
itertools = { workspace = true }
ahash = { workspace = true }
rand = { workspace = true }
# rand = { workspace = true }
parking_lot = { workspace = true }
glam = { workspace = true }
bitflags = { workspace = true }
@ -21,8 +21,9 @@ anyhow = { workspace = true }
tracing = { workspace = true }
ash = { workspace = true }
ash-formats = { workspace = true }
ash-window = { workspace = true }
vk-mem = { workspace = true }
# vk-mem = { workspace = true }
gpu-allocator = { workspace = true }
rectangle-pack = { workspace = true }
@ -30,13 +31,13 @@ md-5 = { workspace = true }
raw-window-handle = { workspace = true }
egui = { workspace = true , features = ["bytemuck"]}
egui_winit_platform = { workspace = true }
# egui_winit_platform = { workspace = true }
futures = { workspace = true }
#futures = { workspace = true }
smol = { workspace = true }
# tokio = {workspace = true, features = ["rt", "sync"]}
bevy_tasks = { workspace = true }
# bevy_tasks = { workspace = true }
derive_more = { workspace = true , features = ["debug"] }

View file

@ -6,11 +6,12 @@ use crate::{
images::{Image, QueueOwnership},
pipeline::{Pipeline, PipelineLayout},
sync::{self, FenceFuture},
util::{self, FormatExt, MutexExt},
util::{self, MutexExt},
};
use super::{Device, queue::Queue};
use ash::{prelude::*, vk};
use ash_formats::FormatHelpers as _;
use parking_lot::Mutex;
#[derive(Debug)]
@ -475,17 +476,7 @@ pub mod traits {
subresources: &[vk::ImageSubresourceRange],
) {
// assert_eq!(self.state(), CommandBufferState::Recording);
let clear_colors = match format.get_component_kind() {
crate::util::FormatComponentKind::Float => vk::ClearColorValue {
float32: color.into_f32(),
},
crate::util::FormatComponentKind::UInt => vk::ClearColorValue {
uint32: color.into_u32(),
},
crate::util::FormatComponentKind::SInt => vk::ClearColorValue {
int32: color.into_i32(),
},
};
let clear_colors = format.clear_value(color.into_f32());
unsafe {
self.device().dev().cmd_clear_color_image(

View file

@ -4,6 +4,7 @@ use std::{
};
use ash::vk;
use ash_formats::FormatHelpers as _;
use gpu_allocator::MemoryLocation;
use indexmap::IndexMap;
@ -133,7 +134,7 @@ pub fn egui_pre_pass(
dev.clone(),
BufferDesc {
name: Some("egui-prepass-staging-buffer".into()),
size: (extent.0 * extent.1 * 4) as u64, // RGBA8
size: vk::Format::R8G8B8A8_UNORM.layer_size([extent.0, extent.1, 1]),
usage: vk::BufferUsageFlags::TRANSFER_SRC,
queue_families: device::QueueFlags::empty(),
mem_location: MemoryLocation::CpuToGpu,

View file

@ -16,6 +16,7 @@ use crate::{
use super::Device;
use ash::vk::{self, Handle};
use ash_formats::FormatExt;
use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme};
use parking_lot::Mutex;
@ -510,8 +511,7 @@ fn validate_image_view_format(image: &ImageDesc, view_format: vk::Format) -> boo
let mutable = image.flags.contains(vk::ImageCreateFlags::MUTABLE_FORMAT);
if mutable {
image.format == view_format
|| FormatClass::from(image.format) == FormatClass::from(view_format)
image.format == view_format || image.format.format_class() == view_format.format_class()
} else {
image.format == view_format
}
@ -957,500 +957,3 @@ pub const SUBRESOURCERANGE_COLOR_ALL: vk::ImageSubresourceRange = vk::ImageSubre
base_array_layer: 0,
layer_count: vk::REMAINING_ARRAY_LAYERS,
};
// copilot generated from spec:
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FormatClass {
Bits8,
Bits16,
Alpha8,
Bits24,
Bits32,
Bits48,
Bits64,
Bits96,
Bits128,
Bits192,
Bits256,
D16,
D24,
D32,
S8,
D16S8,
D24S8,
D32S8,
Bc1Rgb,
Bc1Rgba,
Bc2,
Bc3,
Bc4,
Bc5,
Bc6h,
Bc7,
Etc2Rgb,
Etc2Rgba,
Etc2EacRgba,
EacR,
EacRg,
Astc4x4,
Astc5x4,
Astc5x5,
Astc6x5,
Astc6x6,
Astc8x5,
Astc8x6,
Astc8x8,
Astc10x5,
Astc10x6,
Astc10x8,
Astc10x10,
Astc12x10,
Astc12x12,
YuvG8B8G8R8_422,
YuvB8G8R8G8_422,
YuvG8B8R8Triplane420,
YuvG8B8R8Biplane420,
YuvG8B8R8Triplane422,
YuvG8B8R8Biplane422,
YuvG8B8R8Triplane444,
YuvG10X6B10X6R10X6Triplane420,
YuvG10X6B10X6R10X6Biplane420,
YuvG10X6B10X6R10X6Triplane422,
YuvG10X6B10X6R10X6Biplane422,
YuvG10X6B10X6R10X6Triplane444,
YuvG12X4B12X4R12X4Triplane420,
YuvG12X4B12X4R12X4Biplane420,
YuvG12X4B12X4R12X4Triplane422,
YuvG12X4B12X4R12X4Biplane422,
YuvG12X4B12X4R12X4Triplane444,
YuvG16B16R16Triplane420,
YuvG16B16R16Biplane420,
YuvG16B16R16Triplane422,
YuvG16B16R16Biplane422,
YuvG16B16R16Triplane444,
YuvG8B8R8Biplane444,
YuvG10X6B10X6R10X6Biplane444,
YuvG12X4B12X4R12X4Biplane444,
YuvG16B16R16Biplane444,
Bits64R10G10B10A10,
Bits64G10B10G10R10_422,
Bits64B10G10R10G10_422,
Bits64R12G12B12A12,
Bits64G12B12G12R12_422,
Bits64B12G12R12G12_422,
Bits64G16B16G16R16_422,
Bits64B16G16R16G16_422,
Bits64R14G14B14A14,
Pvrtc1_2bpp,
Pvrtc1_4bpp,
Pvrtc2_2bpp,
Pvrtc2_4bpp,
Astc3x3x3,
Astc4x3x3,
Astc4x4x3,
Astc4x4x4,
Astc5x4x4,
Astc5x5x4,
Astc5x5x5,
Astc6x5x5,
Astc6x6x5,
Astc6x6x6,
YuvG14X2B14X2R14X2Biplane420,
YuvG14X2B14X2R14X2Biplane422,
}
impl From<vk::Format> for FormatClass {
fn from(format: vk::Format) -> Self {
use vk::Format as F;
match format {
// 8-bit
F::R4G4_UNORM_PACK8
| F::R8_UNORM
| F::R8_SNORM
| F::R8_USCALED
| F::R8_SSCALED
// | F::R8_BOOL_ARM
// | F::R8_SFLOAT_FPENCODING_FLOAT8E4M3_ARM
// | F::R8_SFLOAT_FPENCODING_FLOAT8E5M2_ARM
| F::R8_UINT
| F::R8_SINT
| F::R8_SRGB => FormatClass::Bits8,
// 16-bit
F::A1B5G5R5_UNORM_PACK16_KHR
| F::R10X6_UNORM_PACK16
| F::R12X4_UNORM_PACK16
| F::A4R4G4B4_UNORM_PACK16
| F::A4B4G4R4_UNORM_PACK16
// | F::R10X6_UINT_PACK16_ARM
// | F::R12X4_UINT_PACK16_ARM
// | F::R14X2_UINT_PACK16_ARM
// | F::R14X2_UNORM_PACK16_ARM
// | F::R16_SFLOAT_FPENCODING_BFLOAT16_ARM
| F::R4G4B4A4_UNORM_PACK16
| F::B4G4R4A4_UNORM_PACK16
| F::R5G6B5_UNORM_PACK16
| F::B5G6R5_UNORM_PACK16
| F::R5G5B5A1_UNORM_PACK16
| F::B5G5R5A1_UNORM_PACK16
| F::A1R5G5B5_UNORM_PACK16
| F::R8G8_UNORM
| F::R8G8_SNORM
| F::R8G8_USCALED
| F::R8G8_SSCALED
| F::R8G8_UINT
| F::R8G8_SINT
| F::R8G8_SRGB
| F::R16_UNORM
| F::R16_SNORM
| F::R16_USCALED
| F::R16_SSCALED
| F::R16_UINT
| F::R16_SINT
| F::R16_SFLOAT => FormatClass::Bits16,
// 8-bit alpha
F::A8_UNORM_KHR => FormatClass::Alpha8,
// 24-bit
F::R8G8B8_UNORM
| F::R8G8B8_SNORM
| F::R8G8B8_USCALED
| F::R8G8B8_SSCALED
| F::R8G8B8_UINT
| F::R8G8B8_SINT
| F::R8G8B8_SRGB
| F::B8G8R8_UNORM
| F::B8G8R8_SNORM
| F::B8G8R8_USCALED
| F::B8G8R8_SSCALED
| F::B8G8R8_UINT
| F::B8G8R8_SINT
| F::B8G8R8_SRGB => FormatClass::Bits24,
// 32-bit
F::R10X6G10X6_UNORM_2PACK16
| F::R12X4G12X4_UNORM_2PACK16
// | F::R16G16_SFIXED5_NV
// | F::R10X6G10X6_UINT_2PACK16_ARM
// | F::R12X4G12X4_UINT_2PACK16_ARM
// | F::R14X2G14X2_UINT_2PACK16_ARM
// | F::R14X2G14X2_UNORM_2PACK16_ARM
| F::R8G8B8A8_UNORM
| F::R8G8B8A8_SNORM
| F::R8G8B8A8_USCALED
| F::R8G8B8A8_SSCALED
| F::R8G8B8A8_UINT
| F::R8G8B8A8_SINT
| F::R8G8B8A8_SRGB
| F::B8G8R8A8_UNORM
| F::B8G8R8A8_SNORM
| F::B8G8R8A8_USCALED
| F::B8G8R8A8_SSCALED
| F::B8G8R8A8_UINT
| F::B8G8R8A8_SINT
| F::B8G8R8A8_SRGB
| F::A8B8G8R8_UNORM_PACK32
| F::A8B8G8R8_SNORM_PACK32
| F::A8B8G8R8_USCALED_PACK32
| F::A8B8G8R8_SSCALED_PACK32
| F::A8B8G8R8_UINT_PACK32
| F::A8B8G8R8_SINT_PACK32
| F::A8B8G8R8_SRGB_PACK32
| F::A2R10G10B10_UNORM_PACK32
| F::A2R10G10B10_SNORM_PACK32
| F::A2R10G10B10_USCALED_PACK32
| F::A2R10G10B10_SSCALED_PACK32
| F::A2R10G10B10_UINT_PACK32
| F::A2R10G10B10_SINT_PACK32
| F::A2B10G10R10_UNORM_PACK32
| F::A2B10G10R10_SNORM_PACK32
| F::A2B10G10R10_USCALED_PACK32
| F::A2B10G10R10_SSCALED_PACK32
| F::A2B10G10R10_UINT_PACK32
| F::A2B10G10R10_SINT_PACK32
| F::R16G16_UNORM
| F::R16G16_SNORM
| F::R16G16_USCALED
| F::R16G16_SSCALED
| F::R16G16_UINT
| F::R16G16_SINT
| F::R16G16_SFLOAT
| F::R32_UINT
| F::R32_SINT
| F::R32_SFLOAT
| F::B10G11R11_UFLOAT_PACK32
| F::E5B9G9R9_UFLOAT_PACK32 => FormatClass::Bits32,
// 48-bit
F::R16G16B16_UNORM
| F::R16G16B16_SNORM
| F::R16G16B16_USCALED
| F::R16G16B16_SSCALED
| F::R16G16B16_UINT
| F::R16G16B16_SINT
| F::R16G16B16_SFLOAT => FormatClass::Bits48,
// 64-bit
F::R16G16B16A16_UNORM
| F::R16G16B16A16_SNORM
| F::R16G16B16A16_USCALED
| F::R16G16B16A16_SSCALED
| F::R16G16B16A16_UINT
| F::R16G16B16A16_SINT
| F::R16G16B16A16_SFLOAT
| F::R32G32_UINT
| F::R32G32_SINT
| F::R32G32_SFLOAT
| F::R64_UINT
| F::R64_SINT
| F::R64_SFLOAT => FormatClass::Bits64,
// 96-bit
F::R32G32B32_UINT | F::R32G32B32_SINT | F::R32G32B32_SFLOAT => FormatClass::Bits96,
// 128-bit
F::R32G32B32A32_UINT
| F::R32G32B32A32_SINT
| F::R32G32B32A32_SFLOAT
| F::R64G64_UINT
| F::R64G64_SINT
| F::R64G64_SFLOAT => FormatClass::Bits128,
// 192-bit
F::R64G64B64_UINT | F::R64G64B64_SINT | F::R64G64B64_SFLOAT => FormatClass::Bits192,
// 256-bit
F::R64G64B64A64_UINT | F::R64G64B64A64_SINT | F::R64G64B64A64_SFLOAT => {
FormatClass::Bits256
}
// Depth / Stencil
F::D16_UNORM => FormatClass::D16,
F::X8_D24_UNORM_PACK32 => FormatClass::D24,
F::D32_SFLOAT => FormatClass::D32,
F::S8_UINT => FormatClass::S8,
F::D16_UNORM_S8_UINT => FormatClass::D16S8,
F::D24_UNORM_S8_UINT => FormatClass::D24S8,
F::D32_SFLOAT_S8_UINT => FormatClass::D32S8,
// BCn
F::BC1_RGB_UNORM_BLOCK | F::BC1_RGB_SRGB_BLOCK => FormatClass::Bc1Rgb,
F::BC1_RGBA_UNORM_BLOCK | F::BC1_RGBA_SRGB_BLOCK => FormatClass::Bc1Rgba,
F::BC2_UNORM_BLOCK | F::BC2_SRGB_BLOCK => FormatClass::Bc2,
F::BC3_UNORM_BLOCK | F::BC3_SRGB_BLOCK => FormatClass::Bc3,
F::BC4_UNORM_BLOCK | F::BC4_SNORM_BLOCK => FormatClass::Bc4,
F::BC5_UNORM_BLOCK | F::BC5_SNORM_BLOCK => FormatClass::Bc5,
F::BC6H_UFLOAT_BLOCK | F::BC6H_SFLOAT_BLOCK => FormatClass::Bc6h,
F::BC7_UNORM_BLOCK | F::BC7_SRGB_BLOCK => FormatClass::Bc7,
// ETC2 / EAC
F::ETC2_R8G8B8_UNORM_BLOCK | F::ETC2_R8G8B8_SRGB_BLOCK => FormatClass::Etc2Rgb,
F::ETC2_R8G8B8A1_UNORM_BLOCK | F::ETC2_R8G8B8A1_SRGB_BLOCK => FormatClass::Etc2Rgba,
F::ETC2_R8G8B8A8_UNORM_BLOCK | F::ETC2_R8G8B8A8_SRGB_BLOCK => FormatClass::Etc2EacRgba,
F::EAC_R11_UNORM_BLOCK | F::EAC_R11_SNORM_BLOCK => FormatClass::EacR,
F::EAC_R11G11_UNORM_BLOCK | F::EAC_R11G11_SNORM_BLOCK => FormatClass::EacRg,
// ASTC 2D
F::ASTC_4X4_SFLOAT_BLOCK | F::ASTC_4X4_UNORM_BLOCK | F::ASTC_4X4_SRGB_BLOCK => {
FormatClass::Astc4x4
}
F::ASTC_5X4_SFLOAT_BLOCK | F::ASTC_5X4_UNORM_BLOCK | F::ASTC_5X4_SRGB_BLOCK => {
FormatClass::Astc5x4
}
F::ASTC_5X5_SFLOAT_BLOCK | F::ASTC_5X5_UNORM_BLOCK | F::ASTC_5X5_SRGB_BLOCK => {
FormatClass::Astc5x5
}
F::ASTC_6X5_SFLOAT_BLOCK | F::ASTC_6X5_UNORM_BLOCK | F::ASTC_6X5_SRGB_BLOCK => {
FormatClass::Astc6x5
}
F::ASTC_6X6_SFLOAT_BLOCK | F::ASTC_6X6_UNORM_BLOCK | F::ASTC_6X6_SRGB_BLOCK => {
FormatClass::Astc6x6
}
F::ASTC_8X5_SFLOAT_BLOCK | F::ASTC_8X5_UNORM_BLOCK | F::ASTC_8X5_SRGB_BLOCK => {
FormatClass::Astc8x5
}
F::ASTC_8X6_SFLOAT_BLOCK | F::ASTC_8X6_UNORM_BLOCK | F::ASTC_8X6_SRGB_BLOCK => {
FormatClass::Astc8x6
}
F::ASTC_8X8_SFLOAT_BLOCK | F::ASTC_8X8_UNORM_BLOCK | F::ASTC_8X8_SRGB_BLOCK => {
FormatClass::Astc8x8
}
F::ASTC_10X5_SFLOAT_BLOCK | F::ASTC_10X5_UNORM_BLOCK | F::ASTC_10X5_SRGB_BLOCK => {
FormatClass::Astc10x5
}
F::ASTC_10X6_SFLOAT_BLOCK | F::ASTC_10X6_UNORM_BLOCK | F::ASTC_10X6_SRGB_BLOCK => {
FormatClass::Astc10x6
}
F::ASTC_10X8_SFLOAT_BLOCK | F::ASTC_10X8_UNORM_BLOCK | F::ASTC_10X8_SRGB_BLOCK => {
FormatClass::Astc10x8
}
F::ASTC_10X10_SFLOAT_BLOCK | F::ASTC_10X10_UNORM_BLOCK | F::ASTC_10X10_SRGB_BLOCK => {
FormatClass::Astc10x10
}
F::ASTC_12X10_SFLOAT_BLOCK | F::ASTC_12X10_UNORM_BLOCK | F::ASTC_12X10_SRGB_BLOCK => {
FormatClass::Astc12x10
}
F::ASTC_12X12_SFLOAT_BLOCK | F::ASTC_12X12_UNORM_BLOCK | F::ASTC_12X12_SRGB_BLOCK => {
FormatClass::Astc12x12
}
// Packed/YCbCr-ish (as listed)
F::G8B8G8R8_422_UNORM => FormatClass::YuvG8B8G8R8_422,
F::B8G8R8G8_422_UNORM => FormatClass::YuvB8G8R8G8_422,
F::G8_B8_R8_3PLANE_420_UNORM => FormatClass::YuvG8B8R8Triplane420,
F::G8_B8R8_2PLANE_420_UNORM => FormatClass::YuvG8B8R8Biplane420,
F::G8_B8_R8_3PLANE_422_UNORM => FormatClass::YuvG8B8R8Triplane422,
F::G8_B8R8_2PLANE_422_UNORM => FormatClass::YuvG8B8R8Biplane422,
F::G8_B8_R8_3PLANE_444_UNORM => FormatClass::YuvG8B8R8Triplane444,
F::G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Triplane420
}
F::G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Biplane420
}
F::G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Triplane422
}
F::G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Biplane422
}
F::G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Triplane444
}
F::G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Triplane420
}
F::G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Biplane420
}
F::G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Triplane422
}
F::G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Biplane422
}
F::G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Triplane444
}
F::G16_B16_R16_3PLANE_420_UNORM => FormatClass::YuvG16B16R16Triplane420,
F::G16_B16R16_2PLANE_420_UNORM => FormatClass::YuvG16B16R16Biplane420,
F::G16_B16_R16_3PLANE_422_UNORM => FormatClass::YuvG16B16R16Triplane422,
F::G16_B16R16_2PLANE_422_UNORM => FormatClass::YuvG16B16R16Biplane422,
F::G16_B16_R16_3PLANE_444_UNORM => FormatClass::YuvG16B16R16Triplane444,
// 2-plane 444 (additional section)
F::G8_B8R8_2PLANE_444_UNORM => FormatClass::YuvG8B8R8Biplane444,
F::G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 => {
FormatClass::YuvG10X6B10X6R10X6Biplane444
}
F::G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 => {
FormatClass::YuvG12X4B12X4R12X4Biplane444
}
F::G16_B16R16_2PLANE_444_UNORM => FormatClass::YuvG16B16R16Biplane444,
// 64-bit special grouped headings from the markdown
F::R10X6G10X6B10X6A10X6_UNORM_4PACK16 /* | F::R10X6G10X6B10X6A10X6_UINT_4PACK16_ARM */
=> {
FormatClass::Bits64R10G10B10A10
}
F::G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 => FormatClass::Bits64G10B10G10R10_422,
F::B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 => FormatClass::Bits64B10G10R10G10_422,
F::R12X4G12X4B12X4A12X4_UNORM_4PACK16 /*| F::R12X4G12X4B12X4A12X4_UINT_4PACK16_ARM */
=> {
FormatClass::Bits64R12G12B12A12
}
F::G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 => FormatClass::Bits64G12B12G12R12_422,
F::B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 => FormatClass::Bits64B12G12R12G12_422,
F::G16B16G16R16_422_UNORM => FormatClass::Bits64G16B16G16R16_422,
F::B16G16R16G16_422_UNORM => FormatClass::Bits64B16G16R16G16_422,
// F::R14X2G14X2B14X2A14X2_UINT_4PACK16_ARM
// | F::R14X2G14X2B14X2A14X2_UNORM_4PACK16_ARM => FormatClass::Bits64_R14G14B14A14,
// PVRTC
F::PVRTC1_2BPP_UNORM_BLOCK_IMG | F::PVRTC1_2BPP_SRGB_BLOCK_IMG => {
FormatClass::Pvrtc1_2bpp
}
F::PVRTC1_4BPP_UNORM_BLOCK_IMG | F::PVRTC1_4BPP_SRGB_BLOCK_IMG => {
FormatClass::Pvrtc1_4bpp
}
F::PVRTC2_2BPP_UNORM_BLOCK_IMG | F::PVRTC2_2BPP_SRGB_BLOCK_IMG => {
FormatClass::Pvrtc2_2bpp
}
F::PVRTC2_4BPP_UNORM_BLOCK_IMG | F::PVRTC2_4BPP_SRGB_BLOCK_IMG => {
FormatClass::Pvrtc2_4bpp
}
// ASTC 3D (EXT)
// F::ASTC_3X3X3_UNORM_BLOCK_EXT
// | F::ASTC_3X3X3_SRGB_BLOCK_EXT
// | F::ASTC_3X3X3_SFLOAT_BLOCK_EXT => FormatClass::Astc3x3x3,
// F::ASTC_4X3X3_UNORM_BLOCK_EXT
// | F::ASTC_4X3X3_SRGB_BLOCK_EXT
// | F::ASTC_4X3X3_SFLOAT_BLOCK_EXT => FormatClass::Astc4x3x3,
// F::ASTC_4X4X3_UNORM_BLOCK_EXT
// | F::ASTC_4X4X3_SRGB_BLOCK_EXT
// | F::ASTC_4X4X3_SFLOAT_BLOCK_EXT => FormatClass::Astc4x4x3,
// F::ASTC_4X4X4_UNORM_BLOCK_EXT
// | F::ASTC_4X4X4_SRGB_BLOCK_EXT
// | F::ASTC_4X4X4_SFLOAT_BLOCK_EXT => FormatClass::Astc4x4x4,
// F::ASTC_5X4X4_UNORM_BLOCK_EXT
// | F::ASTC_5X4X4_SRGB_BLOCK_EXT
// | F::ASTC_5X4X4_SFLOAT_BLOCK_EXT => FormatClass::Astc5x4x4,
// F::ASTC_5X5X4_UNORM_BLOCK_EXT
// | F::ASTC_5X5X4_SRGB_BLOCK_EXT
// | F::ASTC_5X5X4_SFLOAT_BLOCK_EXT => FormatClass::Astc5x5x4,
// F::ASTC_5X5X5_UNORM_BLOCK_EXT
// | F::ASTC_5X5X5_SRGB_BLOCK_EXT
// | F::ASTC_5X5X5_SFLOAT_BLOCK_EXT => FormatClass::Astc5x5x5,
// F::ASTC_6X5X5_UNORM_BLOCK_EXT
// | F::ASTC_6X5X5_SRGB_BLOCK_EXT
// | F::ASTC_6X5X5_SFLOAT_BLOCK_EXT => FormatClass::Astc6x5x5,
// F::ASTC_6X6X5_UNORM_BLOCK_EXT
// | F::ASTC_6X6X5_SRGB_BLOCK_EXT
// | F::ASTC_6X6X5_SFLOAT_BLOCK_EXT => FormatClass::Astc6x6x5,
// F::ASTC_6X6X6_UNORM_BLOCK_EXT
// | F::ASTC_6X6X6_SRGB_BLOCK_EXT
// | F::ASTC_6X6X6_SFLOAT_BLOCK_EXT => FormatClass::Astc6x6x6,
// // 14-bit 2-plane
// F::G14X2_B14X2R14X2_2PLANE_420_UNORM_3PACK16_ARM => {
// FormatClass::YuvG14X2_B14X2R14X2_2Plane_420
// }
// F::G14X2_B14X2R14X2_2PLANE_422_UNORM_3PACK16_ARM => {
// FormatClass::YuvG14X2_B14X2R14X2_2Plane_422
// }
_ => todo!(),
}
}
}
pub fn format_to_aspect_mask(format: vk::Format) -> vk::ImageAspectFlags {
use vk::Format as F;
match format {
F::D16_UNORM | F::X8_D24_UNORM_PACK32 | F::D32_SFLOAT => vk::ImageAspectFlags::DEPTH,
F::D16_UNORM_S8_UINT | F::D24_UNORM_S8_UINT | F::D32_SFLOAT_S8_UINT => {
vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL
}
F::S8_UINT => vk::ImageAspectFlags::STENCIL,
_ => vk::ImageAspectFlags::COLOR,
}
}

View file

@ -338,7 +338,7 @@ impl Instance {
})
}
pub(crate) fn choose_adapter_default(
pub fn choose_adapter_default(
&self,
surface: Option<&Surface>,
required_extensions: &[Extension],

View file

@ -1,8 +1,9 @@
#![feature(
negative_impls,
map_try_insert,
debug_closure_helpers,
core_io,
core_io_borrowed_buf,
debug_closure_helpers,
slice_partition_dedup
)]
@ -35,7 +36,7 @@ pub mod sync;
mod text;
pub mod util;
use device::{Device, DeviceOwned};
use device::Device;
mod texture {
use std::{collections::BTreeMap, sync::Arc};

View file

@ -15,6 +15,7 @@ use crate::{
util::{self, Rgba, WithLifetime},
};
use ash::vk;
use ash_formats::FormatExt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
@ -1267,7 +1268,7 @@ pub fn image_barrier(
.old_layout(before_access.layout)
.new_layout(after_access.layout)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: util::image_aspect_from_format(format),
aspect_mask: format.aspect_flags(),
..images::SUBRESOURCERANGE_ALL
})
.src_queue_family_index(

View file

@ -337,7 +337,7 @@ impl<'cmd> CommandList<'cmd> {
// construct barriers
// TODO: need to know each images aspects at this point
for barrier in barriers {
for _barrier in barriers {
// stuff
}

View file

@ -1,7 +1,10 @@
use std::sync::Arc;
use ash::vk;
use glam::{f32::Mat4, vec3};
use glam::{
camera::rh::{proj, view},
vec3,
};
use gpu_allocator::MemoryLocation;
pub use crate::egui_pass::{egui_pass, egui_pre_pass};
@ -319,14 +322,14 @@ impl Wireframe {
let cmd = &ctx.cmd;
//let model = Mat4::from_scale(vec3(0.01, 0.01, 0.01));
let proj = Mat4::perspective_lh(
let proj = proj::vulkan::perspective(
core::f32::consts::FRAC_PI_4,
target.width() as f32 / target.height() as f32,
0.1,
100.0,
);
let pos = vec3(0.0, 0.0, -5.0);
let view = Mat4::look_at_rh(pos, pos + vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0));
let view = view::look_at_mat4(pos, pos + vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0));
let to_clip = proj * view; // * model;
let color_attachment = &vk::RenderingAttachmentInfo::default()

View file

@ -62,7 +62,7 @@ impl SyncThreadpool {
use std::sync::atomic::Ordering;
match self
.num_threads
.fetch_update(Ordering::Release, Ordering::Acquire, |i| {
.try_update(Ordering::Release, Ordering::Acquire, |i| {
if i < self.max_threads {
Some(i + 1)
} else {

View file

@ -4,7 +4,8 @@ use std::{
sync::{Arc, Weak},
};
use ash::vk::Extent2D;
use ash::vk::{self, Extent2D};
use ash_formats::FormatHelpers as _;
use cosmic_text::{CacheKey, FontSystem, PhysicalGlyph, SwashCache};
use glam::IVec2;
use guillotiere::size2;
@ -189,7 +190,7 @@ impl std::fmt::Debug for FontAtlas {
impl FontAtlas {
fn new(size: u32) -> Self {
let num_bytes = size * size * 4;
let num_bytes = vk::Format::R8G8B8A8_UNORM.layer_size([size, size, 1]);
Self {
image: Arc::new(Image(parking_lot::RwLock::new(ImageInner {
image: vec![0; num_bytes as usize],
@ -234,7 +235,7 @@ impl FontAtlas {
let y = y + line_y;
let image_offset = 4 * y as u32 * image.width() + 4 * x as u32;
let glyph_offset = 4 * line_y * width;
let len = 4 * width as usize;
let len = vk::Format::R8G8B8A8_UNORM.row_pitch(width as u32) as usize;
let dst = &mut image.bytes_mut()[image_offset as usize..][..len];
let src = &data[glyph_offset as usize..][..len];
dst.copy_from_slice(src);

View file

@ -199,229 +199,6 @@ impl<'a, T: 'a> MutexExt<'a, T> for parking_lot::Mutex<T> {
}
}
#[allow(dead_code)]
pub trait FormatExt {
fn get_component_kind(&self) -> FormatComponentKind;
fn is_f32(&self) -> bool;
fn is_u32(&self) -> bool;
fn is_i32(&self) -> bool;
}
#[derive(Debug, PartialEq, Eq)]
pub enum FormatComponentKind {
Float,
UInt,
SInt,
}
fn format_to_primitive(format: vk::Format) -> FormatComponentKind {
use FormatComponentKind::*;
match format {
vk::Format::BC6H_SFLOAT_BLOCK => Float,
vk::Format::D32_SFLOAT => Float,
vk::Format::R16G16B16A16_SFLOAT => Float,
vk::Format::R16G16B16_SFLOAT => Float,
vk::Format::R16G16_SFLOAT => Float,
vk::Format::R16_SFLOAT => Float,
vk::Format::R32G32B32A32_SFLOAT => Float,
vk::Format::R32G32B32_SFLOAT => Float,
vk::Format::R32G32_SFLOAT => Float,
vk::Format::R32_SFLOAT => Float,
vk::Format::R64G64B64A64_SFLOAT => Float,
vk::Format::R64G64B64_SFLOAT => Float,
vk::Format::R64G64_SFLOAT => Float,
vk::Format::R64_SFLOAT => Float,
vk::Format::D32_SFLOAT_S8_UINT => Float,
vk::Format::A2B10G10R10_SINT_PACK32 => SInt,
vk::Format::A2R10G10B10_SINT_PACK32 => SInt,
vk::Format::A8B8G8R8_SINT_PACK32 => SInt,
vk::Format::B8G8R8A8_SINT => SInt,
vk::Format::B8G8R8_SINT => SInt,
vk::Format::R16G16B16A16_SINT => SInt,
vk::Format::R16G16B16_SINT => SInt,
vk::Format::R16G16_SINT => SInt,
vk::Format::R16_SINT => SInt,
vk::Format::R32G32B32A32_SINT => SInt,
vk::Format::R32G32B32_SINT => SInt,
vk::Format::R32G32_SINT => SInt,
vk::Format::R32_SINT => SInt,
vk::Format::R64G64B64A64_SINT => SInt,
vk::Format::R64G64B64_SINT => SInt,
vk::Format::R64G64_SINT => SInt,
vk::Format::R64_SINT => SInt,
vk::Format::R8G8B8A8_SINT => SInt,
vk::Format::R8G8B8_SINT => SInt,
vk::Format::R8G8_SINT => SInt,
vk::Format::R8_SINT => SInt,
vk::Format::A2B10G10R10_SNORM_PACK32 => Float,
vk::Format::A2R10G10B10_SNORM_PACK32 => Float,
vk::Format::A8B8G8R8_SNORM_PACK32 => Float,
vk::Format::B8G8R8A8_SNORM => Float,
vk::Format::B8G8R8_SNORM => Float,
vk::Format::BC4_SNORM_BLOCK => Float,
vk::Format::BC5_SNORM_BLOCK => Float,
vk::Format::EAC_R11G11_SNORM_BLOCK => Float,
vk::Format::EAC_R11_SNORM_BLOCK => Float,
vk::Format::R16G16B16A16_SNORM => Float,
vk::Format::R16G16B16_SNORM => Float,
vk::Format::R16G16_SNORM => Float,
vk::Format::R16_SNORM => Float,
vk::Format::R8G8B8A8_SNORM => Float,
vk::Format::R8G8B8_SNORM => Float,
vk::Format::R8G8_SNORM => Float,
vk::Format::R8_SNORM => Float,
vk::Format::A8B8G8R8_SRGB_PACK32 => Float,
vk::Format::ASTC_10X10_SRGB_BLOCK => Float,
vk::Format::ASTC_10X5_SRGB_BLOCK => Float,
vk::Format::ASTC_10X6_SRGB_BLOCK => Float,
vk::Format::ASTC_10X8_SRGB_BLOCK => Float,
vk::Format::ASTC_12X10_SRGB_BLOCK => Float,
vk::Format::ASTC_12X12_SRGB_BLOCK => Float,
vk::Format::ASTC_4X4_SRGB_BLOCK => Float,
vk::Format::ASTC_5X4_SRGB_BLOCK => Float,
vk::Format::ASTC_5X5_SRGB_BLOCK => Float,
vk::Format::ASTC_6X5_SRGB_BLOCK => Float,
vk::Format::ASTC_6X6_SRGB_BLOCK => Float,
vk::Format::ASTC_8X5_SRGB_BLOCK => Float,
vk::Format::ASTC_8X6_SRGB_BLOCK => Float,
vk::Format::ASTC_8X8_SRGB_BLOCK => Float,
vk::Format::B8G8R8A8_SRGB => Float,
vk::Format::B8G8R8_SRGB => Float,
vk::Format::BC1_RGBA_SRGB_BLOCK => Float,
vk::Format::BC1_RGB_SRGB_BLOCK => Float,
vk::Format::BC2_SRGB_BLOCK => Float,
vk::Format::BC3_SRGB_BLOCK => Float,
vk::Format::BC7_SRGB_BLOCK => Float,
vk::Format::ETC2_R8G8B8A1_SRGB_BLOCK => Float,
vk::Format::ETC2_R8G8B8A8_SRGB_BLOCK => Float,
vk::Format::ETC2_R8G8B8_SRGB_BLOCK => Float,
vk::Format::R8G8B8A8_SRGB => Float,
vk::Format::R8G8B8_SRGB => Float,
vk::Format::R8G8_SRGB => Float,
vk::Format::R8_SRGB => Float,
vk::Format::A2B10G10R10_SSCALED_PACK32 => Float,
vk::Format::A2R10G10B10_SSCALED_PACK32 => Float,
vk::Format::A8B8G8R8_SSCALED_PACK32 => Float,
vk::Format::B8G8R8A8_SSCALED => Float,
vk::Format::B8G8R8_SSCALED => Float,
vk::Format::R16G16B16A16_SSCALED => Float,
vk::Format::R16G16B16_SSCALED => Float,
vk::Format::R16G16_SSCALED => Float,
vk::Format::R16_SSCALED => Float,
vk::Format::R8G8B8A8_SSCALED => Float,
vk::Format::R8G8B8_SSCALED => Float,
vk::Format::R8G8_SSCALED => Float,
vk::Format::R8_SSCALED => Float,
vk::Format::B10G11R11_UFLOAT_PACK32 => Float,
vk::Format::BC6H_UFLOAT_BLOCK => Float,
vk::Format::E5B9G9R9_UFLOAT_PACK32 => Float,
vk::Format::A2B10G10R10_UINT_PACK32 => UInt,
vk::Format::A2R10G10B10_UINT_PACK32 => UInt,
vk::Format::A8B8G8R8_UINT_PACK32 => UInt,
vk::Format::B8G8R8A8_UINT => UInt,
vk::Format::B8G8R8_UINT => UInt,
vk::Format::R16G16B16A16_UINT => UInt,
vk::Format::R16G16B16_UINT => UInt,
vk::Format::R16G16_UINT => UInt,
vk::Format::R16_UINT => UInt,
vk::Format::R32G32B32A32_UINT => UInt,
vk::Format::R32G32B32_UINT => UInt,
vk::Format::R32G32_UINT => UInt,
vk::Format::R32_UINT => UInt,
vk::Format::R64G64B64A64_UINT => UInt,
vk::Format::R64G64B64_UINT => UInt,
vk::Format::R64G64_UINT => UInt,
vk::Format::R64_UINT => UInt,
vk::Format::R8G8B8A8_UINT => UInt,
vk::Format::R8G8B8_UINT => UInt,
vk::Format::R8G8_UINT => UInt,
vk::Format::R8_UINT => UInt,
vk::Format::S8_UINT => UInt,
vk::Format::A1R5G5B5_UNORM_PACK16 => Float,
vk::Format::A2B10G10R10_UNORM_PACK32 => Float,
vk::Format::A2R10G10B10_UNORM_PACK32 => Float,
vk::Format::A8B8G8R8_UNORM_PACK32 => Float,
vk::Format::ASTC_10X10_UNORM_BLOCK => Float,
vk::Format::ASTC_10X5_UNORM_BLOCK => Float,
vk::Format::ASTC_10X6_UNORM_BLOCK => Float,
vk::Format::ASTC_10X8_UNORM_BLOCK => Float,
vk::Format::ASTC_12X10_UNORM_BLOCK => Float,
vk::Format::ASTC_12X12_UNORM_BLOCK => Float,
vk::Format::ASTC_4X4_UNORM_BLOCK => Float,
vk::Format::ASTC_5X4_UNORM_BLOCK => Float,
vk::Format::ASTC_5X5_UNORM_BLOCK => Float,
vk::Format::ASTC_6X5_UNORM_BLOCK => Float,
vk::Format::ASTC_6X6_UNORM_BLOCK => Float,
vk::Format::ASTC_8X5_UNORM_BLOCK => Float,
vk::Format::ASTC_8X6_UNORM_BLOCK => Float,
vk::Format::ASTC_8X8_UNORM_BLOCK => Float,
vk::Format::B4G4R4A4_UNORM_PACK16 => Float,
vk::Format::B5G5R5A1_UNORM_PACK16 => Float,
vk::Format::B5G6R5_UNORM_PACK16 => Float,
vk::Format::B8G8R8A8_UNORM => Float,
vk::Format::B8G8R8_UNORM => Float,
vk::Format::BC1_RGBA_UNORM_BLOCK => Float,
vk::Format::BC1_RGB_UNORM_BLOCK => Float,
vk::Format::BC2_UNORM_BLOCK => Float,
vk::Format::BC3_UNORM_BLOCK => Float,
vk::Format::BC4_UNORM_BLOCK => Float,
vk::Format::BC5_UNORM_BLOCK => Float,
vk::Format::BC7_UNORM_BLOCK => Float,
vk::Format::D16_UNORM => Float,
vk::Format::D16_UNORM_S8_UINT => Float,
vk::Format::D24_UNORM_S8_UINT => Float,
vk::Format::EAC_R11G11_UNORM_BLOCK => Float,
vk::Format::EAC_R11_UNORM_BLOCK => Float,
vk::Format::ETC2_R8G8B8A1_UNORM_BLOCK => Float,
vk::Format::ETC2_R8G8B8A8_UNORM_BLOCK => Float,
vk::Format::ETC2_R8G8B8_UNORM_BLOCK => Float,
vk::Format::R16G16B16A16_UNORM => Float,
vk::Format::R16G16B16_UNORM => Float,
vk::Format::R16G16_UNORM => Float,
vk::Format::R16_UNORM => Float,
vk::Format::R4G4B4A4_UNORM_PACK16 => Float,
vk::Format::R4G4_UNORM_PACK8 => Float,
vk::Format::R5G5B5A1_UNORM_PACK16 => Float,
vk::Format::R5G6B5_UNORM_PACK16 => Float,
vk::Format::R8G8B8A8_UNORM => Float,
vk::Format::R8G8B8_UNORM => Float,
vk::Format::R8G8_UNORM => Float,
vk::Format::R8_UNORM => Float,
vk::Format::X8_D24_UNORM_PACK32 => Float,
vk::Format::A2B10G10R10_USCALED_PACK32 => Float,
vk::Format::A2R10G10B10_USCALED_PACK32 => Float,
vk::Format::A8B8G8R8_USCALED_PACK32 => Float,
vk::Format::B8G8R8A8_USCALED => Float,
vk::Format::B8G8R8_USCALED => Float,
vk::Format::R16G16B16A16_USCALED => Float,
vk::Format::R16G16B16_USCALED => Float,
vk::Format::R16G16_USCALED => Float,
vk::Format::R16_USCALED => Float,
vk::Format::R8G8B8A8_USCALED => Float,
vk::Format::R8G8B8_USCALED => Float,
vk::Format::R8G8_USCALED => Float,
vk::Format::R8_USCALED => Float,
_ => unimplemented!(),
}
}
impl FormatExt for vk::Format {
fn get_component_kind(&self) -> FormatComponentKind {
format_to_primitive(*self)
}
fn is_f32(&self) -> bool {
format_to_primitive(*self) == FormatComponentKind::Float
}
fn is_u32(&self) -> bool {
format_to_primitive(*self) == FormatComponentKind::UInt
}
fn is_i32(&self) -> bool {
format_to_primitive(*self) == FormatComponentKind::SInt
}
}
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
pub struct Rect2D {
top_left: glam::IVec2,
@ -618,21 +395,6 @@ impl Rgba {
}
}
#[allow(dead_code)]
pub fn image_aspect_from_format(format: vk::Format) -> vk::ImageAspectFlags {
use vk::{Format, ImageAspectFlags};
match format {
Format::D32_SFLOAT | Format::X8_D24_UNORM_PACK32 | Format::D16_UNORM => {
ImageAspectFlags::DEPTH
}
Format::S8_UINT => ImageAspectFlags::STENCIL,
Format::D32_SFLOAT_S8_UINT | Format::D16_UNORM_S8_UINT | Format::D24_UNORM_S8_UINT => {
ImageAspectFlags::DEPTH | ImageAspectFlags::STENCIL
}
_ => ImageAspectFlags::COLOR,
}
}
#[repr(transparent)]
pub struct WithLifetime<'a, T>(T, std::marker::PhantomData<&'a ()>);

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
ash-formats = { workspace = true }
thiserror = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true }

View file

@ -1,6 +1,7 @@
#![feature(debug_closure_helpers)]
#![allow(dead_code)]
use ash_formats::FormatHelpers as _;
use bevy_asset::{Asset, AssetLoader};
use bevy_ecs::prelude::*;
use bevy_reflect::TypePath;
@ -42,7 +43,7 @@ impl std::fmt::Debug for FontData {
}
}
#[derive(Debug, Default)]
#[derive(Debug, Default, TypePath)]
struct FontLoader;
#[derive(thiserror::Error, Debug)]
@ -241,7 +242,7 @@ impl std::fmt::Debug for FontAtlas {
impl FontAtlas {
fn new(size: u32) -> Self {
let num_bytes = size * size * 4;
let num_bytes = renderer::ash::vk::Format::R8G8B8A8_UNORM.layer_size([size, size, 1]);
Self {
image: Arc::new(Image(parking_lot::RwLock::new(ImageInner {
image: vec![0; num_bytes as usize],
@ -289,7 +290,7 @@ impl FontAtlas {
let y = y + line_y;
let image_offset = 4 * y as u32 * image.width() + 4 * x as u32;
let glyph_offset = 4 * line_y * width;
let len = 4 * width as usize;
let len = renderer::ash::vk::Format::R8G8B8A8_UNORM.row_pitch(width as u32) as usize;
let dst = &mut image.bytes_mut()[image_offset as usize..][..len];
let src = &data[glyph_offset as usize..][..len];
dst.copy_from_slice(src);

168
docs/renderer.md Normal file
View file

@ -0,0 +1,168 @@
# Renderer and repository guide
## Repository overview
Vidya is a Rust 2024 workspace containing a low-level Vulkan renderer, a Bevy-facing integration crate, small game/window crates, and a separate Bevy text crate. The root package is named `vidya`; the current runnable entry point is the `bevy_integration` example. The renderer is primarily an `ash`-based Vulkan layer, while `bevy_vulkan_render` sets up Bevy render-world infrastructure and currently contains a mock render system rather than a finished Bevy rendering backend.
Line counts below are `wc -l` counts over Rust source files in each package (not generated/dependency code); for the renderer the 22 files total 14,100 lines. This source inventory differs from the supplied starting estimate of about 24,600 lines across 23 files: the current `crates/renderer/src` glob has 22 `.rs` files and their measured total is 14,100 lines.
| Workspace member | Approx. Rust LOC | Purpose |
|---|---:|---|
| `crates/renderer` | 14,100 | Vulkan instance/device/resources, command recording, swapchains, render graph, egui and glyph-atlas code. |
| `crates/bevy_vulkan_render` | 1,518 | Bevy render sub-app, world synchronization/extraction, threaded scheduling, and window surfaces. |
| `crates/game` | 315 | Standalone winit/egui demo using `renderer::Renderer2`. |
| `crates/text` | 484 | Bevy asset/ECS-oriented text and font-atlas implementation built on `cosmic-text`. |
| `crates/window` | 1 | Minimal winit dependency crate; its `src/lib.rs` is empty. |
| root `vidya` package | 45 | Example host and dependency on `bevy_vulkan_render`; three lines of `src/lib.rs` plus the 42-line example. |
The code has two overlapping integration paths: `crates/game` directly creates the renderer and surfaces; the root example constructs a Bevy `App` with Bevy's standard plugins plus `bevy_vulkan_render::RenderPlugin`.
## Dependencies and toolchain
| Area | Repository configuration |
|---|---|
| Rust | `rust-toolchain` pins `nightly`; packages use edition 2024. |
| Local Bevy | Workspace dependencies point to `/var/code/bevy-engine/bevy` and its crate directories, not crates.io: `bevy`, `bevy_app`, `bevy_asset`, `bevy_derive`, `bevy_ecs`, `bevy_hierarchy`, `bevy_log`, `bevy_math`, `bevy_platform`, `bevy_reflect`, `bevy_tasks`, `bevy_time`, `bevy_transform`, `bevy_utils`, `bevy_window`, and `bevy_winit`. This checkout is Bevy `0.20.0-dev` as recorded in the project setup. `bevy_ecs` enables `multi_threaded` and `trace`. |
| Vulkan/window | `ash 0.38.0`, `ash-formats` for Vulkan format metadata/helpers, including the adopted `FormatHelpers` sizing and clear-value helpers, `ash-window 0.13.0`, `vk-mem 0.5.0`, `gpu-allocator` from the `janis-bhm/gpu-allocator` GitHub `main` branch, `winit 0.30.5`, and `raw-window-handle 0.6`. Renderer allocation code specifically uses `gpu-allocator`; `vk-mem` is declared but is not the allocation path in `device.rs`, `buffers.rs`, or `images.rs`. |
| Shared crates | `glam 0.33` with `bytemuck`, `rand 0.10`, `derive_more 2`, `petgraph 0.8`, `egui 0.32`, `tracing`, `parking_lot`, among others. See root `[workspace.dependencies]` for the complete list. |
| Text | `cosmic-text 0.12.1`, `guillotiere 0.6.2`, and `sys-locale 0.3.2` are used in the renderer and/or text crate. |
`crates/renderer/src/lib.rs` enables these nightly features (the `text` crate separately opts in to `debug_closure_helpers`):
| Feature gate | Use visible in the code |
|---|---|
| `negative_impls` | Explicitly marks command pools/buffers and single-use command objects as `!Send`/`!Sync` where Vulkan command-pool thread ownership is constrained (`commands.rs`). |
| `map_try_insert` | Uses `BTreeMap::try_insert`/`HashMap::try_insert` in graph and resource bookkeeping. |
| `core_io` | Imports `core::io::BorrowedBuf` in the graph-builder egui demo. |
| `core_io_borrowed_buf` | Enables the borrowed-buffer fill APIs used alongside `BorrowedBuf` in that demo. |
| `slice_partition_dedup` | Deduplicates selected queue family indices in-place (`queue.rs`). |
## Renderer crate
### Public surface and code map
`renderer/src/lib.rs` owns core device/feature types and exports `ash`, `instance`, `device`, `queue`, `commands`, `render_graph`, `rendering`, `swapchain`, `sync`, and `util`. `buffers`, `images`, `pipeline`, `text`, `debug`, and the egui implementation are declared as private modules. `egui.rs` is loaded as the private `egui_pass` module; `rendering` re-exports its two egui pass functions. The `render_graph` submodules are also private; `render_graph/mod.rs` currently re-exports the legacy graph API only.
| Source | Responsibilities and key types | Relationship / visibility |
|---|---|---|
| [`lib.rs`](../crates/renderer/src/lib.rs) | Crate root, feature/property representations (`PhysicalDeviceFeatures`, `PhysicalDeviceProperties`, `PhysicalDeviceInfo`), `Error`/`Result`, `SamplerCache`, `EguiState`, `Renderer2`, and the internal texture manager. `Renderer2::new`, `create_surface`, and async `draw_graph` are the higher-level convenience flow. | Public exports and renderer assembly; creates the instance, adapter, device, graph, and submission/presentation flow. |
| [`instance.rs`](../crates/renderer/src/instance.rs) | Vulkan loader/instance setup (`Instance`, `InstanceDesc`, `DebugUtilsCreateInfo`), layer and extension selection, physical-device enumeration, adapter ranking via `choose_adapter_default`, and surface capability queries. | `Instance` is re-exported from crate root and hands `PhysicalDeviceInfo` to `device.rs`. |
| [`device.rs`](../crates/renderer/src/device.rs) | Logical `Device`/`DeviceInner`, required extensions and feature chain, GPU allocator, device object lifetime helpers (`DeviceOwned`, `DeviceObject` family), resource allocation types (`Allocation`, `AllocationStrategy`), and reusable device pools. | Adapter creation calls into `queue.rs`, sets up queues and `gpu_allocator`, and is held by buffers, images, commands, and sync objects. |
| [`queue.rs`](../crates/renderer/src/queue.rs) | `Queue`, `QueueFamily`, `DeviceQueues`, `DeviceQueueInfos`, queue capability flags and family selection. | Called by logical device initialization; supplies graphics/main, compute and transfer queues to the device. |
| [`buffers.rs`](../crates/renderer/src/buffers.rs) | `BufferDesc` and `Buffer`: Vulkan buffer creation, queue-sharing mode, memory allocation/binding, optional mapped `map`/`map_mut`, raw handle and length. | Private module; used by commands, renderer helpers, graph implementations and egui uploads. |
| [`images.rs`](../crates/renderer/src/images.rs) | `ImageDesc`, `Image`, `ImageViewDesc`, `ImageView`, `Extent`, `Offset`, `MipRange`, `QueueOwnership`. Uses `ash-formats` format metadata. Handles owned and swapchain images, memory, views, extents and subresources. | Private module; swapchains wrap their external images here. Used by render graph and egui. |
| [`memory.rs`](../crates/renderer/src/memory.rs) | Empty placeholder (one blank line). | Not an allocation implementation; allocation is in `device.rs` and resource constructors. |
| [`pipeline.rs`](../crates/renderer/src/pipeline.rs) | Descriptor/pipeline descriptions and objects: `DescriptorPool`, `DescriptorSetLayout`, `PipelineLayout`, `Sampler`, `ShaderModule`, `Pipeline`, `PipelineCache`; supports compute and graphics pipeline construction. `ShaderModule::new_from_path` reads SPIR-V words and `new_from_memory` accepts words directly. | Private module; `commands.rs` binds pipelines/descriptors; `EguiState` and rendering helpers build concrete pipelines. No shader compiler/build pipeline is defined here. |
| [`commands.rs`](../crates/renderer/src/commands.rs) | Low-level command-pool and command-buffer abstractions (`SingleUseCommandPool`, `CommandList`, command-buffer types/state, `CommandBufferExt`), recording/copy/barrier operations and queue submission/futures. | The legacy graph and one-off uploads use these command buffers; synchronization objects come from `sync.rs`. |
| [`swapchain.rs`](../crates/renderer/src/swapchain.rs) | `Surface`, `Swapchain`, `SwapchainConfiguration`, `SwapchainImage`, `SwapchainFrame`; creates/configures surfaces and swapchains, acquires images, and presents them. | Surface owns a replaceable optional swapchain. Swapchain images are represented by `images::Image`/`ImageView`; uses `Fence` and semaphores. |
| [`sync.rs`](../crates/renderer/src/sync.rs) | `Fence`, binary/timeline `Semaphore`, `SyncThreadpool`, `FenceFuture`, and pooled synchronization objects. | The swapchain and command submission code use fences/semaphores; `DeviceInner` owns a sync waiter pool and device pools. |
| [`render_graph/mod.rs`](../crates/renderer/src/render_graph/mod.rs) | Declares `legacy`, `commands`, `graph_builder`, `recorder`, and `resources`; re-exports `legacy::*`. | Despite the internal new graph files, the externally reachable `RenderGraph` is the `legacy.rs` implementation. `#![allow(dead_code)]` covers the module tree. |
| [`render_graph/legacy.rs`](../crates/renderer/src/render_graph/legacy.rs) | Current reachable graph API: `RenderGraph`, `GraphResourceId`, `GraphResourceDesc`, `GraphResource`, `PassDesc`, `RenderContext`, `Access`, load/store ops and helpers such as `clear_pass`/`present_pass`. Stores imported/created resources, pass access declarations and output resources; `resolve` orders passes and records a `CommandList` for its caller to submit. | Re-exported by `render_graph/mod.rs`; used by `Renderer2::draw_graph`, `rendering/mod.rs` and `egui.rs`. Although named legacy, this is the path called by the current rendering code. |
| [`render_graph/resources.rs`](../crates/renderer/src/render_graph/resources.rs) | Newer command-graph resource abstraction: `Resource`, `ResourceId`, ranges/access records, `Buffer`/`Texture` handles and slices/regions, `Read`/`Write`/`ReadWrite` wrappers. | Internal to newer `commands.rs`/`recorder.rs`; not re-exported from `render_graph/mod.rs`. |
| [`render_graph/commands.rs`](../crates/renderer/src/render_graph/commands.rs) | New command API (`Command`, `ImportResource`, `Copy`, `ClearTexture`, `UpdateBuffer`, rendering/pipeline/buffer/descriptor/draw commands, `PipelineBarrier`); each command declares resource side effects and implements recording. | Uses newer resource wrappers and `CommandRecorder`, separate from the currently re-exported legacy `PassDesc` graph. Several commands remain incomplete; see current state. |
| [`render_graph/recorder.rs`](../crates/renderer/src/render_graph/recorder.rs) | New `CommandRecorder`, `CommandList`, `RenderPass`, side-effect maps, command metadata and access/barrier collection. | Intended to translate new graph commands to Vulkan command buffers; recorder resource lookups and barrier application contain explicit unimplemented code. |
| [`render_graph/graph_builder.rs`](../crates/renderer/src/render_graph/graph_builder.rs) | New `GraphResourceRegistry` and `GraphBuilder`, wrappers for buffer and staging resources, plus an `api_demo::egui_graph` sample that packs vertices/indices/draw data with `BorrowedBuf`. | Not a wired-up implementation of the current legacy graph: the resource-creation methods are `todo!()` and part of the sample is `unimplemented!()`. |
| [`rendering/mod.rs`](../crates/renderer/src/rendering/mod.rs) | `Wireframe` GPU data/pipeline example and `Wireframe::pass`; exports `egui_pre_pass` and `egui_pass` from the egui module. | Builds passes on the legacy `RenderGraph`; uses `Buffer`, `Pipeline`, graph access declarations, and commands. |
| [`egui.rs`](../crates/renderer/src/egui.rs) | `egui_pre_pass` uploads egui texture deltas (including staging/atlas packing) and `egui_pass` adds drawing work; uses `EguiState`, texture IDs, buffers, images, samplers, pipeline, and graph commands. | Private module imported by `rendering/mod.rs`; current code exercises the legacy graph, not the in-progress typed graph API. |
| [`text.rs`](../crates/renderer/src/text.rs) | Private glyph rasterization and atlas data structures (`FontId`, font stores/atlas sets, `get_outlined_glyph_texture`) based on `cosmic-text` and `guillotiere`. Embeds `assets/fonts/Roboto.ttf` and `NotoSansSC.ttf`. | Renderer-local/private text implementation; not the same module as the public Bevy-oriented `crates/text` crate. Contains an unfinished subpixel-mask case. |
| [`util.rs`](../crates/renderer/src/util.rs) | Shared Vulkan helpers and data types (`DebugName`, `Rect2D`, `F32`, `Rgba`, `Rgba8`, bit iterators, `CStringList`, drop/lifetime helpers and internal `WeakVec`). | Used across resource, debug, image and graph code. |
| [`debug.rs`](../crates/renderer/src/debug.rs) | Vulkan debug messenger callback, translating validation severities into `tracing` levels. | Used by instance setup; intentionally private. |
### Initialization, resources, and commands
The normal setup path begins with `Instance::new(&InstanceDesc)` (`instance.rs`), which loads Vulkan through `ash`, enables supported requested/WSI extensions and creates debug-utils state. The default description requests surface support and the Khronos validation layer/settings (unsupported layers are logged and filtered). Adapter enumeration exposes device properties/features as `PhysicalDeviceInfo`.
`Instance::choose_adapter_default(surface, required_extensions, required_features)` ranks adapters. Its comparator attempts to prefer swapchain/required-extension support and requested `PhysicalDeviceFeatures`, requires preference for Vulkan 1.3 `synchronization2`, `dynamic_rendering`, and `maintenance4`, then prefers discrete GPUs, newer API versions, more device-local memory, and larger 2D image limits. It is ranking logic, not a robust suitability filter: required extension checks return `Less` for the left-hand candidate rather than first excluding every unsuitable device, and `create_logical_device` warns and omits unsupported requested extensions. An important limitation visible in code: the `surface` argument is currently ignored at the `// TODO` surface-compatibility check; choosing an adapter does not actually validate that it can present to that surface. `PhysicalDeviceFeatures` is a chain of core and version/extension feature structs; `create_logical_device` pushes those into `vk::DeviceCreateInfo`.
`PhysicalDeviceInfo::create_logical_device` asks `DeviceQueueInfos::select_queue_families` for queue families, creates the logical device, retrieves queues, creates extension loaders, then creates `gpu_allocator::vulkan::Allocator` and resource pools. Queue selection chooses the first family containing both `GRAPHICS` and `COMPUTE`, decrements its available queue count, and asserts that this selected family has `PRESENT`. It then takes another available compute family and transfer family if found; missing compute/transfer queues alias the graphics queue. Therefore a display handle that did not produce presentation support can reach the explicit `Selected graphics queue family does not support presentation` assertion. The presentation flags are derived from a display handle, and the queue-selection path is not a general strategy for selecting a distinct present-only family.
The allocator is stored behind a mutex on `DeviceInner`. Both `Buffer::new` and `Image::new` create the Vulkan object, query memory requirements, allocate using `AllocationStrategy::{AllocatorManaged,Dedicated}`, bind the returned allocation, and retain it for destruction. `BufferDesc`/`ImageDesc` include memory location, usage, queue family flags and allocation strategy. Buffers may expose mapped bytes when the allocator returns a mapped allocation. Images also support externally owned swapchain images and cached weak image-view references. `memory.rs` itself has no implementation.
There is no universal upload subsystem in `buffers.rs`/`images.rs`; upload paths are explicit at call sites. For example, `Wireframe::from_triangles_indexed` writes positions/indices/colors to a `CpuToGpu` staging buffer, copies into `GpuOnly` vertex/index buffers via a one-off command buffer, and adds transfer-to-vertex/index barriers. The egui pre-pass similarly writes texture data to mapped staging storage and records graph copy work. Shader modules load SPIR-V from a path or word slice; pipeline creation consumes `GraphicsPipelineDesc` or `ComputePipelineDesc`, descriptor layouts/pools, and optional `PipelineCache`. Shader source compilation is outside this crate's code.
### Surfaces, frames, and synchronization
`Surface::new_from_raw_window_handle` creates a Vulkan surface from raw handles; `Surface::headless` is also defined. `Surface::configure` avoids recreation for an equal config on the same device, otherwise creates a replacement swapchain using the old swapchain handle. `SwapchainConfiguration` includes extent/format/present mode/usage and image-count options, while capability validation chooses compatible settings. Acquiring a frame asynchronously reserves a per-frame acquire semaphore, performs `acquire_next_image` through `smol::unblock` under the swapchain lock, creates an `Image` and view for the acquired swapchain image, and waits on a fence. `SwapchainImage` and `SwapchainFrame` are `must_use` and expose `present`.
Swapchain synchronization is not just a generic frame fence ring: the implementation creates acquire semaphores based on the number of in-flight frames and a release semaphore for every swapchain image. Submission waits on the acquire semaphore and signals the release semaphore; presentation waits on the release semaphore. `Swapchain::max_in_flight_images` is image count minus the surface minimum image count. The general `sync.rs` API also supports dedicated/pooled fences, binary/timeline semaphores, and fence futures serviced by a bounded waiter thread pool. `Renderer2::draw_graph` demonstrates acquire → import framebuffer → graph resolve → submit using acquire/release semaphores → await → present.
### Render graph: current path and in-progress path
The currently wired graph is `render_graph::legacy::RenderGraph`, re-exported by `render_graph/mod.rs`. It owns resources and `PassDesc`s with declared read/write `Access` values and optional one-shot `RecordFn`s. Imported framebuffer/images/buffers and created resource descriptions receive graph IDs. Outputs are marked explicitly; resolution determines which passes feed outputs and prepares resources/barriers before executing recorded passes. `Renderer2::draw_graph` constructs this graph and calls `resolve`; the wireframe and egui rendering functions add passes to it. In `legacy.rs`, comments discuss future resource aliasing, but should not be read as proof that aliasing is fully implemented.
The sibling `resources.rs`, `commands.rs`, `recorder.rs`, and `graph_builder.rs` form a newer typed command/resource design, but are private submodules and are not re-exported or connected to the root `RenderGraph` API. `resources.rs` tracks buffer/image ranges and stage/access/layout side effects; `commands.rs` describes copies, clears, updates, rendering scopes, binding, draws and barriers; `recorder.rs` is intended to lower command lists and insert transitions; `graph_builder.rs` contains the graph-resource registry and a sample upload/build path. That path uses staging buffers and `core::io::BorrowedBuf` for writing several egui draw-data arrays into one mapped region. However, the builder methods that create command lists and make/import buffers are `todo!()`, and the recorder still has unimplemented handle accessors/barrier code. Treat this as an experimental/incomplete path, not as the graph used by `draw_graph`.
### Text, egui, and orchestration
The renderer's private `text.rs` embeds two font files and manages per-font/per-size glyph atlases; `get_outlined_glyph_texture` rasterizes glyphs through `cosmic-text`/Swash and packs masks/color glyphs. It is separate from `crates/text/src/lib.rs`, which defines a Bevy `FontData` asset, font loader, ECS text component and its own atlas machinery. Both source trees embed font paths from `assets/fonts`.
The renderer egui path in `egui.rs` consumes `egui::FullOutput`: texture deltas are converted to image/atlas staging uploads, and tessellated primitives are emitted as rendering passes using the egui pipeline and descriptor state. `rendering/mod.rs` re-exports `egui_pre_pass` and `egui_pass` alongside the `Wireframe` graph pass. `Renderer2` is a direct-use convenience owner for instance/device/samplers/textures/egui state and surfaces; its `draw_graph` orchestrates one frame. Bevy has a separate plugin/schedule integration and currently does not call this orchestration as an active render backend.
## Bevy integration crate
`crates/bevy_vulkan_render/src/lib.rs` declares `RenderPlugin`, the `RenderApp` sub-app label, `Render` and `RenderStartup` schedules, and ordered `RenderSystems` sets (`ExtractCommands`, `Prepare`, `Render`, `Cleanup`, `PostCleanup`). `RenderPlugin::build` adds `ExtractPlugin`, initializes render schedules on the sub-app and installs `mock_render_system`. `RenderDevice` is a Bevy resource wrapper around `renderer::device::Device`. An `init_vulkan` function exists and requests sampler anisotropy, non-solid fill, multi-draw-indirect, Vulkan 1.1/1.2/1.3 features and mesh shaders, but it is unused according to both source and compiler dead-code warnings.
`extract_plugin.rs` creates the `RenderApp` `SubApp`, installs `ExtractSchedule`, and arranges sync/extract each frame. It moves the main `World` into a temporary `MainWorld` resource for extraction, restores it afterward, and delays `ExtractSchedule` deferred-command application into the render schedule's `ExtractCommands` set. This leaves the main thread free earlier for pipelining.
`extract_param.rs` defines `Extract<'w, 's, P>`, a `SystemParam` wrapper for applying a read-only system parameter to `MainWorld` during `ExtractSchedule`. It registers read-only main-world access and intentionally disallows contained mutating system params.
`sync_world.rs` implements `SyncWorldPlugin` and the `SyncToRenderWorld` marker. Observer records are processed by `entity_sync_system` to maintain the main/render entity link: `RenderEntity` on the main side points to the render entity; `MainEntity` points back. Custom `QueryData` implementations let `Query<RenderEntity>` and `Query<MainEntity>` return the wrapped `Entity` directly. Temporary render entities are despawned in post-cleanup. A TODO calls out batching pending sync records.
`window/mod.rs` holds extracted physical window metadata in `ExtractedWindows`, keyed by entity, and stores a `Surfaces` map keyed per window entity. `extract_windows` tracks primary window, dimensions, closing/removal, and initial presentation state. `need_surface_configuration` detects new/unconfigured or resized windows; `configure_surfaces` creates a `Surface` from the raw handles and configures/reconfigures its swapchain. These functions and resources are currently reported unused by the compiler, so their source structure is present but not yet integrated into active plugin systems.
`rendering.rs` defines `RenderingPlugin` and `RenderExtractApp`. When installed after `RenderPlugin`, it transfers the `RenderApp` between the main app and a spawned render thread using bounded async channels, executes render-app updates there, and extracts/sends it back each frame. The root example installs `RenderPlugin`, but does not install `RenderingPlugin`; the current `RenderPlugin` only installs a mock render function.
## Current state and known unfinished work
### Build and warnings
On the checked-out tree, `cargo check --workspace --all-targets` completes successfully. It emits warnings, including deprecated `glam::Mat4::perspective_lh` and `Mat4::look_at_rh` calls in `rendering/mod.rs`, a deprecated standard-library atomic `fetch_update` use, unused imports/variables, and dead code. Specifically, compiler warnings expose that `init_vulkan`, the window extraction/configuration functions, `render_start`/`render_startup`, and parts of sync tracking are unused. This is a compile check, not evidence that a Vulkan device/surface runs successfully on a target machine.
### Marker inventory (source scan)
Across `crates/renderer/src`, `crates/bevy_vulkan_render/src`, `crates/game/src`, and `crates/text/src`, a literal scan finds **21 `TODO`/`FIXME` occurrences** and **19 `todo!`/`unimplemented!` occurrences**. The latter includes test-only code; it does not imply 19 active runtime paths. The locations and notable behaviors are:
| File / lines | Finding |
|---|---|
| `renderer/src/instance.rs:368` | Surface compatibility in default adapter ranking is a TODO; `:474` XCB presentation support is `todo!("xcb")`. |
| `renderer/src/queue.rs:326-329` | Explicit assertion requires selected graphics+compute family to present. |
| `renderer/src/render_graph/graph_builder.rs:41,44,53,59` | New builder methods are `todo!()`; `:266` has an `unimplemented!()` factory in the demo path. |
| `renderer/src/render_graph/recorder.rs:23,27,31,35,39,43,344` | `CommandRecorder` accessors and image barrier recording are unimplemented/todo. `:339` also notes missing image aspect information. |
| `renderer/src/render_graph/commands.rs:316,592` | Render load/store operations are noted as TODO; `PipelineBarrier::apply` is `todo!()`. |
| `renderer/src/render_graph/resources.rs:46,250` | TODOs flag buffer-row range units and possible write-order conflict handling. |
| `renderer/src/render_graph/legacy.rs:799,809` | TODOs in graph hazard/dependency handling for first-pass checks. |
| `renderer/src/text.rs:71,162,281-282` | Atlas naming, host-visible/coherent image considerations, and `SubpixelMask` glyph content remain TODO/todo. |
| `renderer/src/sync.rs:361,373` | `vk::Handle::from_raw` for binary and timeline semaphore wrappers is unimplemented. |
| `renderer/src/device.rs:1107-1113` | A `#[cfg(test)]` helper contains a `summon` stub with `unimplemented!()`; test-only. |
| `renderer/src/lib.rs:610`; `swapchain.rs:593`; `commands.rs:749`; `egui.rs:320` | Additional behavior/design TODO comments; swapchain present-id/wait support is noted as optional work. |
| `bevy_vulkan_render/src/sync_world.rs:217` | TODO to batch pending sync records. |
| `game/src/main.rs:49,58,85` | TODOs call out error handling, resize handling and unfinished draw-request logging/path. |
| `text/src/lib.rs:123,214,336-337` | Separate Bevy text implementation has similar atlas/image TODOs and an unfinished subpixel-mask case. |
The largest architectural caveat is that the typed `render_graph` command/recorder/builder files are not active wiring: the legacy graph is still the sole graph re-export, and the new recorder contains explicit stubs. The Bevy crate likewise has substantial extraction/window/thread infrastructure, but its current installed render schedule uses a mock system and its Vulkan initializer/surface functions are unused. Bevy-facing rendering should therefore be described as scaffolding/integration work in progress rather than a complete rendered-frame backend.
## Build and run
1. Use the repository's nightly toolchain (`rustup` will honor `rust-toolchain`). The renderer has compile-time `include_bytes!` references to font files; the repository currently contains the generated flat and directory layouts. To regenerate the expected layouts, run the fetch script from the font directory:
```sh
cd assets/fonts
./fetch.sh
cd ../..
```
`fetch.sh` downloads the upstream Google Fonts files; network access is needed only for this prerequisite. It creates `Roboto/Roboto-VariableFont_wdth,wght.ttf` and `Noto_Sans_SC/NotoSansSC-VariableFont_wght.ttf` from flat files. Renderer-local text also includes flat `Roboto.ttf` and `NotoSansSC.ttf`.
2. Check the workspace and all targets:
```sh
cargo check --workspace --all-targets
```
3. Run the Bevy example:
```sh
cargo run --example bevy_integration
```
The example creates a Bevy app with minimal, asset, window/winit, camera/mesh/text and `VulkanRenderPlugin` plugins. It currently prints `Hello, world!` before running; the installed Vulkan render plugin uses a mock render system. For direct renderer/egui experimentation, `crates/game/src/main.rs` is the standalone winit entry point, though it too has TODO-marked draw/resize work and requires a Vulkan-capable system/display.