scissor attempt
This commit is contained in:
@@ -23,7 +23,7 @@ use wgui::{
|
|||||||
EventListenerCollection, MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent,
|
EventListenerCollection, MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent,
|
||||||
MouseWheelEvent,
|
MouseWheelEvent,
|
||||||
},
|
},
|
||||||
gfx::WGfx,
|
gfx::{WGfx, cmd::WGfxClearMode},
|
||||||
renderer_vk::{self},
|
renderer_vk::{self},
|
||||||
};
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
@@ -32,12 +32,16 @@ use winit::{
|
|||||||
keyboard::{KeyCode, PhysicalKey},
|
keyboard::{KeyCode, PhysicalKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::testbed::{
|
use crate::{
|
||||||
TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric,
|
rate_limiter::RateLimiter,
|
||||||
|
testbed::{
|
||||||
|
TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod profiler;
|
mod profiler;
|
||||||
|
mod rate_limiter;
|
||||||
mod testbed;
|
mod testbed;
|
||||||
mod timestep;
|
mod timestep;
|
||||||
mod vulkan;
|
mod vulkan;
|
||||||
@@ -117,6 +121,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let mut timestep = Timestep::new();
|
let mut timestep = Timestep::new();
|
||||||
timestep.set_tps(60.0);
|
timestep.set_tps(60.0);
|
||||||
|
|
||||||
|
let mut limiter = RateLimiter::new();
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
event_loop.run(move |event, elwt| {
|
event_loop.run(move |event, elwt| {
|
||||||
elwt.set_control_flow(ControlFlow::Poll);
|
elwt.set_control_flow(ControlFlow::Poll);
|
||||||
@@ -291,6 +297,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
log::trace!("drawing frame {frame_index}");
|
log::trace!("drawing frame {frame_index}");
|
||||||
frame_index += 1;
|
frame_index += 1;
|
||||||
|
|
||||||
|
limiter.start(120); // max 120 fps
|
||||||
profiler.start();
|
profiler.start();
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -301,7 +308,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
recreate = true;
|
recreate = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => panic!("failed to acquire next image: {e}"),
|
Err(e) => {
|
||||||
|
log::error!("failed to acquire next image: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tgt = images[image_index as usize].clone();
|
let tgt = images[image_index as usize].clone();
|
||||||
@@ -309,7 +319,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let mut cmd_buf = gfx
|
let mut cmd_buf = gfx
|
||||||
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cmd_buf.begin_rendering(tgt).unwrap();
|
cmd_buf
|
||||||
|
.begin_rendering(tgt, WGfxClearMode::Clear([0.0, 0.0, 0.0, 0.1]))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let primitives = wgui::drawing::draw(&testbed.layout().borrow_mut()).unwrap();
|
let primitives = wgui::drawing::draw(&testbed.layout().borrow_mut()).unwrap();
|
||||||
render_context
|
render_context
|
||||||
@@ -334,6 +346,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
profiler.end();
|
profiler.end();
|
||||||
|
limiter.end();
|
||||||
}
|
}
|
||||||
Event::AboutToWait => {
|
Event::AboutToWait => {
|
||||||
// should be limited to vsync
|
// should be limited to vsync
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
use std::{sync::LazyLock, time::Instant};
|
use crate::timestep::get_micros;
|
||||||
|
|
||||||
static TIME_START: LazyLock<Instant> = LazyLock::new(Instant::now);
|
|
||||||
|
|
||||||
pub fn get_micros() -> u64 {
|
|
||||||
TIME_START.elapsed().as_micros() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Profiler {
|
pub struct Profiler {
|
||||||
interval_us: u64,
|
interval_us: u64,
|
||||||
|
|||||||
37
uidev/src/rate_limiter.rs
Normal file
37
uidev/src/rate_limiter.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::timestep::get_micros;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct RateLimiter {
|
||||||
|
rate: u16,
|
||||||
|
start_us: u64,
|
||||||
|
end_us: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RateLimiter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
rate: 0,
|
||||||
|
end_us: 0,
|
||||||
|
start_us: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self, rate: u16) {
|
||||||
|
self.rate = rate;
|
||||||
|
self.start_us = get_micros();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&mut self) {
|
||||||
|
if self.rate == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.end_us = get_micros();
|
||||||
|
let microseconds = self.end_us - self.start_us;
|
||||||
|
let frametime_microseconds = ((1000.0 / self.rate as f32) * 1000.0) as u64;
|
||||||
|
let delay = frametime_microseconds as i64 - microseconds as i64;
|
||||||
|
if delay > 0 {
|
||||||
|
std::thread::sleep(std::time::Duration::from_micros(delay as u64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,6 +111,14 @@ pub struct Rectangle {
|
|||||||
pub round_units: u8,
|
pub round_units: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Scissor {
|
||||||
|
pub x: u32,
|
||||||
|
pub y: u32,
|
||||||
|
pub w: u32,
|
||||||
|
pub h: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RenderPrimitive {
|
pub struct RenderPrimitive {
|
||||||
pub(super) boundary: Boundary,
|
pub(super) boundary: Boundary,
|
||||||
pub(super) transform: Mat4,
|
pub(super) transform: Mat4,
|
||||||
@@ -122,6 +130,7 @@ pub enum PrimitivePayload {
|
|||||||
Rectangle(Rectangle),
|
Rectangle(Rectangle),
|
||||||
Text(Rc<RefCell<Buffer>>),
|
Text(Rc<RefCell<Buffer>>),
|
||||||
Sprite(Option<CustomGlyph>), //option because we want as_slice
|
Sprite(Option<CustomGlyph>), //option because we want as_slice
|
||||||
|
Scissor(Scissor),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_widget(
|
fn draw_widget(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use vulkano::{
|
|||||||
PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
||||||
},
|
},
|
||||||
device::Queue,
|
device::Queue,
|
||||||
format::Format,
|
format::{ClearValue, Format},
|
||||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||||
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
||||||
@@ -44,14 +44,26 @@ impl<T> WCommandBuffer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum WGfxClearMode {
|
||||||
|
Keep,
|
||||||
|
Clear([f32; 4]),
|
||||||
|
}
|
||||||
|
|
||||||
impl WCommandBuffer<CmdBufGfx> {
|
impl WCommandBuffer<CmdBufGfx> {
|
||||||
pub fn begin_rendering(&mut self, render_target: Arc<ImageView>) -> anyhow::Result<()> {
|
pub fn begin_rendering(&mut self, render_target: Arc<ImageView>, clear_mode: WGfxClearMode) -> anyhow::Result<()> {
|
||||||
self.command_buffer.begin_rendering(RenderingInfo {
|
self.command_buffer.begin_rendering(RenderingInfo {
|
||||||
contents: SubpassContents::SecondaryCommandBuffers,
|
contents: SubpassContents::SecondaryCommandBuffers,
|
||||||
color_attachments: vec![Some(RenderingAttachmentInfo {
|
color_attachments: vec![Some(RenderingAttachmentInfo {
|
||||||
load_op: AttachmentLoadOp::Clear,
|
load_op: match &clear_mode {
|
||||||
|
WGfxClearMode::Keep => AttachmentLoadOp::Load,
|
||||||
|
WGfxClearMode::Clear(_) => AttachmentLoadOp::Clear,
|
||||||
|
},
|
||||||
store_op: AttachmentStoreOp::Store,
|
store_op: AttachmentStoreOp::Store,
|
||||||
clear_value: Some([0.0, 0.0, 0.0, 0.0].into()),
|
clear_value: match &clear_mode {
|
||||||
|
WGfxClearMode::Keep => None,
|
||||||
|
WGfxClearMode::Clear(color) => Some(ClearValue::Float(*color)),
|
||||||
|
},
|
||||||
..RenderingAttachmentInfo::image_view(render_target)
|
..RenderingAttachmentInfo::image_view(render_target)
|
||||||
})],
|
})],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};
|
|||||||
use cosmic_text::Buffer;
|
use cosmic_text::Buffer;
|
||||||
use glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
use slotmap::{SlotMap, new_key_type};
|
use slotmap::{SlotMap, new_key_type};
|
||||||
|
use vulkano::{buffer::view, pipeline::graphics::viewport};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing,
|
drawing::{self, Scissor},
|
||||||
gfx::{WGfx, cmd::GfxCommandBuffer},
|
gfx::{WGfx, cmd::GfxCommandBuffer},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,10 +25,11 @@ struct RendererPass<'a> {
|
|||||||
text_areas: Vec<TextArea<'a>>,
|
text_areas: Vec<TextArea<'a>>,
|
||||||
text_renderer: TextRenderer,
|
text_renderer: TextRenderer,
|
||||||
rect_renderer: RectRenderer,
|
rect_renderer: RectRenderer,
|
||||||
|
scissor: Option<Scissor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RendererPass<'_> {
|
impl RendererPass<'_> {
|
||||||
fn new(text_atlas: &mut TextAtlas, rect_pipeline: RectPipeline) -> anyhow::Result<Self> {
|
fn new(text_atlas: &mut TextAtlas, rect_pipeline: RectPipeline, scissor: Option<Scissor>) -> anyhow::Result<Self> {
|
||||||
let text_renderer = TextRenderer::new(text_atlas)?;
|
let text_renderer = TextRenderer::new(text_atlas)?;
|
||||||
let rect_renderer = RectRenderer::new(rect_pipeline)?;
|
let rect_renderer = RectRenderer::new(rect_pipeline)?;
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ impl RendererPass<'_> {
|
|||||||
text_renderer,
|
text_renderer,
|
||||||
rect_renderer,
|
rect_renderer,
|
||||||
text_areas: Vec::new(),
|
text_areas: Vec::new(),
|
||||||
|
scissor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +52,19 @@ impl RendererPass<'_> {
|
|||||||
if self.submitted {
|
if self.submitted {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let vk_scissor = if let Some(scissor) = self.scissor {
|
||||||
|
viewport::Scissor {
|
||||||
|
offset: [scissor.x, scissor.y],
|
||||||
|
extent: [scissor.w, scissor.h],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewport::Scissor::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO?
|
||||||
|
// cmd_buf.command_buffer.set_scissor(0, smallvec::smallvec![vk_scissor])?;
|
||||||
|
|
||||||
self.submitted = true;
|
self.submitted = true;
|
||||||
self.rect_renderer.render(gfx, viewport, cmd_buf)?;
|
self.rect_renderer.render(gfx, viewport, cmd_buf)?;
|
||||||
|
|
||||||
@@ -152,10 +168,7 @@ impl Context {
|
|||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = Vec2::new(
|
let size = Vec2::new(resolution[0] as f32 / pixel_scale, resolution[1] as f32 / pixel_scale);
|
||||||
resolution[0] as f32 / pixel_scale,
|
|
||||||
resolution[1] as f32 / pixel_scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
let fov = 0.4;
|
let fov = 0.4;
|
||||||
let aspect_ratio = size.x / size.y;
|
let aspect_ratio = size.x / size.y;
|
||||||
@@ -187,22 +200,28 @@ impl Context {
|
|||||||
|
|
||||||
let atlas = shared.atlas_map.get_mut(self.shared_ctx_key).unwrap();
|
let atlas = shared.atlas_map.get_mut(self.shared_ctx_key).unwrap();
|
||||||
|
|
||||||
let mut passes = vec![RendererPass::new(
|
let mut passes = Vec::<RendererPass>::new();
|
||||||
&mut atlas.text_atlas,
|
let mut needs_new_pass = true;
|
||||||
shared.rect_pipeline.clone(),
|
let mut next_scissor: Option<Scissor> = None;
|
||||||
)?];
|
|
||||||
|
|
||||||
for primitive in primitives {
|
for primitive in primitives {
|
||||||
|
if needs_new_pass {
|
||||||
|
passes.push(RendererPass::new(
|
||||||
|
&mut atlas.text_atlas,
|
||||||
|
shared.rect_pipeline.clone(),
|
||||||
|
next_scissor,
|
||||||
|
)?);
|
||||||
|
next_scissor = None;
|
||||||
|
needs_new_pass = false;
|
||||||
|
}
|
||||||
|
|
||||||
let pass = passes.last_mut().unwrap(); // always safe
|
let pass = passes.last_mut().unwrap(); // always safe
|
||||||
|
|
||||||
match &primitive.payload {
|
match &primitive.payload {
|
||||||
drawing::PrimitivePayload::Rectangle(rectangle) => {
|
drawing::PrimitivePayload::Rectangle(rectangle) => {
|
||||||
pass.rect_renderer.add_rect(
|
pass
|
||||||
primitive.boundary,
|
.rect_renderer
|
||||||
*rectangle,
|
.add_rect(primitive.boundary, *rectangle, &primitive.transform, primitive.depth);
|
||||||
&primitive.transform,
|
|
||||||
primitive.depth,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
drawing::PrimitivePayload::Text(text) => {
|
drawing::PrimitivePayload::Text(text) => {
|
||||||
pass.text_areas.push(TextArea {
|
pass.text_areas.push(TextArea {
|
||||||
@@ -230,16 +249,16 @@ impl Context {
|
|||||||
transform: primitive.transform,
|
transform: primitive.transform,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
drawing::PrimitivePayload::Scissor(scissor) => {
|
||||||
|
next_scissor = Some(*scissor);
|
||||||
|
needs_new_pass = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pass = passes.last_mut().unwrap();
|
for mut pass in passes {
|
||||||
pass.submit(
|
pass.submit(&shared.gfx, &mut self.viewport, cmd_buf, &mut atlas.text_atlas)?
|
||||||
&shared.gfx,
|
}
|
||||||
&mut self.viewport,
|
|
||||||
cmd_buf,
|
|
||||||
&mut atlas.text_atlas,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ impl ModelBuffer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub const fn begin(&mut self) {
|
||||||
self.models.clear(); // note: capacity is being preserved here
|
|
||||||
self.idx = 0;
|
self.idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,9 +82,7 @@ impl ModelBuffer {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
if self.idx == self.models.len() as u32 {
|
if self.idx == self.models.len() as u32 {
|
||||||
self
|
self.models.resize((self.models.len() * 2).max(1), Default::default());
|
||||||
.models
|
|
||||||
.resize(self.models.len() * 2, Default::default());
|
|
||||||
//log::info!("ModelBuffer: resized to {}", self.models.len());
|
//log::info!("ModelBuffer: resized to {}", self.models.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,12 +93,7 @@ impl ModelBuffer {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_pos_size(
|
pub fn register_pos_size(&mut self, pos: &glam::Vec2, size: &glam::Vec2, transform: &Mat4) -> u32 {
|
||||||
&mut self,
|
|
||||||
pos: &glam::Vec2,
|
|
||||||
size: &glam::Vec2,
|
|
||||||
transform: &Mat4,
|
|
||||||
) -> u32 {
|
|
||||||
let mut model = glam::Mat4::from_translation(Vec3::new(pos.x, pos.y, 0.0));
|
let mut model = glam::Mat4::from_translation(Vec3::new(pos.x, pos.y, 0.0));
|
||||||
model *= *transform;
|
model *= *transform;
|
||||||
model *= glam::Mat4::from_scale(Vec3::new(size.x, size.y, 1.0));
|
model *= glam::Mat4::from_scale(Vec3::new(size.x, size.y, 1.0));
|
||||||
|
|||||||
@@ -75,12 +75,11 @@ pub struct RectRenderer {
|
|||||||
|
|
||||||
impl RectRenderer {
|
impl RectRenderer {
|
||||||
pub fn new(pipeline: RectPipeline) -> anyhow::Result<Self> {
|
pub fn new(pipeline: RectPipeline) -> anyhow::Result<Self> {
|
||||||
const BUFFER_SIZE: usize = 128;
|
const BUFFER_SIZE: usize = 32;
|
||||||
|
|
||||||
let vert_buffer = pipeline.gfx.empty_buffer(
|
let vert_buffer = pipeline
|
||||||
BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
|
.gfx
|
||||||
BUFFER_SIZE as _,
|
.empty_buffer(BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST, BUFFER_SIZE as _)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
model_buffer: ModelBuffer::new(&pipeline.gfx)?,
|
model_buffer: ModelBuffer::new(&pipeline.gfx)?,
|
||||||
@@ -92,17 +91,15 @@ impl RectRenderer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rect(
|
pub fn begin(&mut self) {
|
||||||
&mut self,
|
self.rect_vertices.clear();
|
||||||
boundary: Boundary,
|
self.model_buffer.begin();
|
||||||
rectangle: Rectangle,
|
}
|
||||||
transform: &Mat4,
|
|
||||||
depth: f32,
|
pub fn add_rect(&mut self, boundary: Boundary, rectangle: Rectangle, transform: &Mat4, depth: f32) {
|
||||||
) {
|
let in_model_idx = self
|
||||||
let in_model_idx =
|
.model_buffer
|
||||||
self
|
.register_pos_size(&boundary.pos, &boundary.size, transform);
|
||||||
.model_buffer
|
|
||||||
.register_pos_size(&boundary.pos, &boundary.size, transform);
|
|
||||||
|
|
||||||
self.rect_vertices.push(RectVertex {
|
self.rect_vertices.push(RectVertex {
|
||||||
in_model_idx,
|
in_model_idx,
|
||||||
@@ -123,10 +120,10 @@ impl RectRenderer {
|
|||||||
fn upload_verts(&mut self) -> anyhow::Result<()> {
|
fn upload_verts(&mut self) -> anyhow::Result<()> {
|
||||||
if self.vert_buffer_size < self.rect_vertices.len() {
|
if self.vert_buffer_size < self.rect_vertices.len() {
|
||||||
let new_size = self.vert_buffer_size * 2;
|
let new_size = self.vert_buffer_size * 2;
|
||||||
self.vert_buffer = self.pipeline.gfx.empty_buffer(
|
self.vert_buffer = self
|
||||||
BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
|
.pipeline
|
||||||
new_size as _,
|
.gfx
|
||||||
)?;
|
.empty_buffer(BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST, new_size as _)?;
|
||||||
self.vert_buffer_size = new_size;
|
self.vert_buffer_size = new_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user