mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-21 03:36:41 +01:00
Compare commits
10 Commits
eb724e8785
...
gpu-window
| Author | SHA1 | Date | |
|---|---|---|---|
| 57a92ba79a | |||
| 7ec1e68a00 | |||
| 539d19e0d7 | |||
| e9a5f702b4 | |||
| ba0b037ec2 | |||
| 0130d1c906 | |||
| 379ece5cbf | |||
| c9c5cb76bd | |||
| a6d6615231 | |||
| fbc86fa78d |
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "uw8"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -223,7 +223,9 @@ impl State {
|
||||
let time = (now - instance.start_time).as_millis() as i32;
|
||||
{
|
||||
let offset = ((time as u32 as i64 * 6) % 100 - 50) / 6;
|
||||
result = Ok(now + Duration::from_millis((16 - offset) as u64));
|
||||
let max = now + Duration::from_millis(17);
|
||||
let next_center = now + Duration::from_millis((16 - offset) as u64);
|
||||
result = Ok(next_center.min(max));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
143
uw8-window/src/gpu/crt.rs
Normal file
143
uw8-window/src/gpu/crt.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use super::Filter;
|
||||
|
||||
pub struct CrtFilter {
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
bind_group: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl CrtFilter {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
screen: &wgpu::TextureView,
|
||||
resolution: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
) -> CrtFilter {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(resolution),
|
||||
};
|
||||
|
||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
contents: bytemuck::cast_slice(&[uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let crt_bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let crt_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &crt_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&screen),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let crt_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("crt.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&crt_bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &crt_shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &crt_shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: Default::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
CrtFilter {
|
||||
uniform_buffer,
|
||||
bind_group: crt_bind_group,
|
||||
pipeline: render_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter for CrtFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||
}
|
||||
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..6, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||
[
|
||||
res.width as f32 / scale,
|
||||
res.height as f32 / scale,
|
||||
2.0 / scale,
|
||||
0.0,
|
||||
]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Uniforms {
|
||||
texture_scale: [f32; 4],
|
||||
}
|
||||
@@ -14,10 +14,11 @@ fn vs_main(
|
||||
@builtin(vertex_index) in_vertex_index: u32,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let x = (1.0 - f32(in_vertex_index)) * 3.0;
|
||||
let y = f32(in_vertex_index & 1u) * 3.0 - 1.0;
|
||||
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
|
||||
out.tex_coords = vec2<f32>(x, y) * uniforms.texture_scale.xy + vec2<f32>(160.0, 120.0);
|
||||
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||
let x = -1.0 + f32(i % 2u) * 322.0;
|
||||
let y = -1.0 + f32(i / 2u) * 242.0;
|
||||
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) / uniforms.texture_scale.xy, 0.0, 1.0);
|
||||
out.tex_coords = vec2<f32>(x, y);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ fn sample_pixel(coords: vec2<i32>, offset: vec4<f32>) -> vec3<f32> {
|
||||
if(is_outside) {
|
||||
return vec3<f32>(0.0);
|
||||
} else {
|
||||
let f = max(vec4<f32>(0.01) / offset - vec4<f32>(0.003), vec4<f32>(0.0));
|
||||
let f = max(vec4<f32>(0.008) / offset - vec4<f32>(0.0024), vec4<f32>(0.0));
|
||||
return textureLoad(screen_texture, coords, 0).rgb * (f.x + f.y + f.z + f.w);
|
||||
}
|
||||
}
|
||||
@@ -39,10 +40,6 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let o = vec2<f32>(0.5) - (in.tex_coords - pixel);
|
||||
let pixel = vec2<i32>(pixel);
|
||||
|
||||
if(pixel.x < -1 || pixel.y < -1 || pixel.x > 320 || pixel.y > 240) {
|
||||
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
let offset_x = o.xxxx + vec4<f32>(-0.125, 0.375, 0.125, -0.375) * uniforms.texture_scale.z;
|
||||
let offset_y = o.yyyy + vec4<f32>(-0.375, -0.125, 0.375, 0.125) * uniforms.texture_scale.z;
|
||||
|
||||
157
uw8-window/src/gpu/fast_crt.rs
Normal file
157
uw8-window/src/gpu/fast_crt.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use super::Filter;
|
||||
|
||||
pub struct FastCrtFilter {
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
bind_group: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl FastCrtFilter {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
screen: &wgpu::TextureView,
|
||||
resolution: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
) -> FastCrtFilter {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(resolution),
|
||||
};
|
||||
|
||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
contents: bytemuck::cast_slice(&[uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&screen),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("fast_crt.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: Default::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
FastCrtFilter {
|
||||
uniform_buffer,
|
||||
bind_group,
|
||||
pipeline: render_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter for FastCrtFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||
}
|
||||
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..6, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||
[
|
||||
scale / res.width as f32,
|
||||
scale / res.height as f32,
|
||||
2.0 / scale,
|
||||
0.0,
|
||||
]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Uniforms {
|
||||
texture_scale: [f32; 4],
|
||||
}
|
||||
54
uw8-window/src/gpu/fast_crt.wgsl
Normal file
54
uw8-window/src/gpu/fast_crt.wgsl
Normal file
@@ -0,0 +1,54 @@
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) tex_coords: vec2<f32>,
|
||||
}
|
||||
|
||||
struct Uniforms {
|
||||
texture_scale: vec4<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) in_vertex_index: u32,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||
let x = 0.0 + f32(i % 2u) * 320.0;
|
||||
let y = 0.0 + f32(i / 2u) * 240.0;
|
||||
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) * uniforms.texture_scale.xy, 0.0, 1.0);
|
||||
out.tex_coords = vec2<f32>(x, y);
|
||||
return out;
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||
@group(0) @binding(1) var linear_sampler: sampler;
|
||||
|
||||
fn row_factor(offset: f32) -> f32 {
|
||||
return 1.0 / (1.0 + offset * offset * 16.0);
|
||||
}
|
||||
|
||||
fn col_factor(offset: f32) -> f32 {
|
||||
let offset = max(0.0, abs(offset) - 0.4);
|
||||
return 1.0 / (1.0 + offset * offset * 16.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let base = round(in.tex_coords) - vec2<f32>(0.5);
|
||||
let frac = in.tex_coords - base;
|
||||
|
||||
let top_factor = row_factor(frac.y);
|
||||
let bottom_factor = row_factor(frac.y - 1.0);
|
||||
|
||||
let v = base.y + bottom_factor / (bottom_factor + top_factor);
|
||||
|
||||
let left_factor = col_factor(frac.x);
|
||||
let right_factor = col_factor(frac.x - 1.0);
|
||||
|
||||
let u = base.x + right_factor / (right_factor + left_factor);
|
||||
|
||||
return textureSample(screen_texture, linear_sampler, vec2<f32>(u, v) / vec2<f32>(320.0, 240.0)) * (top_factor + bottom_factor) * (left_factor + right_factor) * 1.1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::Framebuffer;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::{num::NonZeroU32, time::Instant};
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, VirtualKeyCode, WindowEvent},
|
||||
@@ -10,8 +9,20 @@ use winit::{
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::EventLoopExtMacOS;
|
||||
#[cfg(target_os = "linux")]
|
||||
use winit::platform::unix::EventLoopExtUnix;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winit::platform::windows::EventLoopExtWindows;
|
||||
|
||||
mod crt;
|
||||
mod fast_crt;
|
||||
mod square;
|
||||
|
||||
use crt::CrtFilter;
|
||||
use fast_crt::FastCrtFilter;
|
||||
use square::SquareFilter;
|
||||
|
||||
pub struct Window {
|
||||
event_loop: EventLoop<()>,
|
||||
@@ -77,6 +88,187 @@ impl Window {
|
||||
device,
|
||||
queue,
|
||||
} = self;
|
||||
|
||||
let palette_screen_mode = PaletteScreenMode::new(&device);
|
||||
|
||||
let mut surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface.get_supported_formats(&adapter)[0],
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
};
|
||||
|
||||
let mut filter: Box<dyn Filter> = Box::new(CrtFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
));
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
let mut reset = false;
|
||||
let mut gamepad = 0;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
let _ = (&window, &instance, &surface, &adapter, &device);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
surface_config.width = new_size.width;
|
||||
surface_config.height = new_size.height;
|
||||
surface.configure(&device, &surface_config);
|
||||
filter.resize(&queue, new_size);
|
||||
}
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
fn gamepad_button(input: &winit::event::KeyboardInput) -> u32 {
|
||||
match input.scancode {
|
||||
44 => 16,
|
||||
45 => 32,
|
||||
30 => 64,
|
||||
31 => 128,
|
||||
_ => match input.virtual_keycode {
|
||||
Some(VirtualKeyCode::Up) => 1,
|
||||
Some(VirtualKeyCode::Down) => 2,
|
||||
Some(VirtualKeyCode::Left) => 4,
|
||||
Some(VirtualKeyCode::Right) => 8,
|
||||
_ => 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
if input.state == winit::event::ElementState::Pressed {
|
||||
match input.virtual_keycode {
|
||||
Some(VirtualKeyCode::Escape) => *control_flow = ControlFlow::Exit,
|
||||
Some(VirtualKeyCode::F) => {
|
||||
window.set_fullscreen(if window.fullscreen().is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
});
|
||||
}
|
||||
Some(VirtualKeyCode::R) => reset = true,
|
||||
Some(VirtualKeyCode::Key1) => {
|
||||
filter = Box::new(SquareFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
Some(VirtualKeyCode::Key2) => {
|
||||
filter = Box::new(FastCrtFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
Some(VirtualKeyCode::Key3) => {
|
||||
filter = Box::new(CrtFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
gamepad |= gamepad_button(&input);
|
||||
} else {
|
||||
gamepad &= !gamepad_button(&input);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
if let ControlFlow::WaitUntil(t) = *control_flow {
|
||||
if Instant::now() < t {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let next_frame = update(
|
||||
&mut GpuFramebuffer {
|
||||
queue: &queue,
|
||||
framebuffer: &palette_screen_mode,
|
||||
},
|
||||
gamepad,
|
||||
reset,
|
||||
);
|
||||
reset = false;
|
||||
*control_flow = ControlFlow::WaitUntil(next_frame);
|
||||
|
||||
let output = surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
|
||||
palette_screen_mode.resolve_screen(&mut encoder);
|
||||
|
||||
{
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
}),
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
filter.render(&mut render_pass);
|
||||
}
|
||||
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct GpuFramebuffer<'a> {
|
||||
framebuffer: &'a PaletteScreenMode,
|
||||
queue: &'a wgpu::Queue,
|
||||
}
|
||||
|
||||
impl<'a> Framebuffer for GpuFramebuffer<'a> {
|
||||
fn update(&mut self, pixels: &[u8], palette: &[u8]) {
|
||||
self.framebuffer.write_framebuffer(self.queue, pixels);
|
||||
self.framebuffer.write_palette(self.queue, palette);
|
||||
}
|
||||
}
|
||||
|
||||
trait Filter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>);
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>);
|
||||
}
|
||||
|
||||
struct PaletteScreenMode {
|
||||
framebuffer: wgpu::Texture,
|
||||
palette: wgpu::Texture,
|
||||
screen_view: wgpu::TextureView,
|
||||
bind_group: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl PaletteScreenMode {
|
||||
fn new(device: &wgpu::Device) -> PaletteScreenMode {
|
||||
let framebuffer_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
size: wgpu::Extent3d {
|
||||
width: 320,
|
||||
@@ -126,16 +318,6 @@ impl Window {
|
||||
let screen_texture_view =
|
||||
screen_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let mut uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(window.inner_size()),
|
||||
};
|
||||
|
||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
contents: bytemuck::cast_slice(&[uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let palette_bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
@@ -178,48 +360,6 @@ impl Window {
|
||||
label: None,
|
||||
});
|
||||
|
||||
let crt_bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let crt_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &crt_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&screen_texture_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let palette_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("palette.wgsl").into()),
|
||||
@@ -255,165 +395,19 @@ impl Window {
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let crt_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("crt.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&crt_bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let mut surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface.get_supported_formats(&adapter)[0],
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
};
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &crt_shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &crt_shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: Default::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
let _ = (&window, &instance, &surface, &adapter, &device);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
surface_config.width = new_size.width;
|
||||
surface_config.height = new_size.height;
|
||||
surface.configure(&device, &surface_config);
|
||||
uniforms.texture_scale = texture_scale_from_resolution(new_size);
|
||||
queue.write_buffer(&uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||
}
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
if input.state == winit::event::ElementState::Pressed {
|
||||
match input.virtual_keycode {
|
||||
Some(VirtualKeyCode::Escape) => *control_flow = ControlFlow::Exit,
|
||||
Some(VirtualKeyCode::F) => {
|
||||
window.set_fullscreen(if window.fullscreen().is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
PaletteScreenMode {
|
||||
framebuffer: framebuffer_texture,
|
||||
palette: palette_texture,
|
||||
screen_view: screen_texture_view,
|
||||
bind_group: palette_bind_group,
|
||||
pipeline: palette_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
if let ControlFlow::WaitUntil(t) = *control_flow {
|
||||
if Instant::now() < t {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let next_frame = update(
|
||||
&mut GpuFramebuffer {
|
||||
queue: &queue,
|
||||
framebuffer: &framebuffer_texture,
|
||||
palette: &palette_texture,
|
||||
},
|
||||
0,
|
||||
false,
|
||||
);
|
||||
*control_flow = ControlFlow::WaitUntil(next_frame);
|
||||
|
||||
let output = surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
|
||||
{
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &screen_texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&palette_pipeline);
|
||||
render_pass.set_bind_group(0, &palette_bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
}
|
||||
|
||||
{
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&render_pipeline);
|
||||
render_pass.set_bind_group(0, &crt_bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
}
|
||||
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct GpuFramebuffer<'a> {
|
||||
framebuffer: &'a wgpu::Texture,
|
||||
palette: &'a wgpu::Texture,
|
||||
queue: &'a wgpu::Queue,
|
||||
}
|
||||
|
||||
impl<'a> Framebuffer for GpuFramebuffer<'a> {
|
||||
fn update(&mut self, pixels: &[u8], palette: &[u8]) {
|
||||
self.queue.write_texture(
|
||||
fn write_framebuffer(&self, queue: &wgpu::Queue, pixels: &[u8]) {
|
||||
queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: self.framebuffer,
|
||||
texture: &self.framebuffer,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
@@ -430,9 +424,12 @@ impl<'a> Framebuffer for GpuFramebuffer<'a> {
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
self.queue.write_texture(
|
||||
}
|
||||
|
||||
fn write_palette(&self, queue: &wgpu::Queue, palette: &[u8]) {
|
||||
queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: self.palette,
|
||||
texture: &self.palette,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
@@ -450,20 +447,23 @@ impl<'a> Framebuffer for GpuFramebuffer<'a> {
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||
[
|
||||
res.width as f32 / scale,
|
||||
res.height as f32 / scale,
|
||||
2.0 / scale,
|
||||
0.0,
|
||||
]
|
||||
}
|
||||
fn resolve_screen(&self, encoder: &mut wgpu::CommandEncoder) {
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &self.screen_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Uniforms {
|
||||
texture_scale: [f32; 4],
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
157
uw8-window/src/gpu/square.rs
Normal file
157
uw8-window/src/gpu/square.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use super::Filter;
|
||||
|
||||
pub struct SquareFilter {
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
bind_group: wgpu::BindGroup,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl SquareFilter {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
screen: &wgpu::TextureView,
|
||||
resolution: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
) -> SquareFilter {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(resolution),
|
||||
};
|
||||
|
||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
contents: bytemuck::cast_slice(&[uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&screen),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("square.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: Default::default(),
|
||||
depth_stencil: None,
|
||||
multisample: Default::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
SquareFilter {
|
||||
uniform_buffer,
|
||||
bind_group,
|
||||
pipeline: render_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter for SquareFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||
}
|
||||
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..6, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||
[
|
||||
scale / res.width as f32,
|
||||
scale / res.height as f32,
|
||||
2.0 / scale,
|
||||
0.0,
|
||||
]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Uniforms {
|
||||
texture_scale: [f32; 4],
|
||||
}
|
||||
44
uw8-window/src/gpu/square.wgsl
Normal file
44
uw8-window/src/gpu/square.wgsl
Normal file
@@ -0,0 +1,44 @@
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) tex_coords: vec2<f32>,
|
||||
}
|
||||
|
||||
struct Uniforms {
|
||||
texture_scale: vec4<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) in_vertex_index: u32,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||
let x = 0.0 + f32(i % 2u) * 320.0;
|
||||
let y = 0.0 + f32(i / 2u) * 240.0;
|
||||
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) * uniforms.texture_scale.xy, 0.0, 1.0);
|
||||
out.tex_coords = vec2<f32>(x, y);
|
||||
return out;
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||
@group(0) @binding(1) var linear_sampler: sampler;
|
||||
|
||||
fn aa_tex_coord(c: f32) -> f32 {
|
||||
let low = c - uniforms.texture_scale.z * 0.5;
|
||||
let high = c + uniforms.texture_scale.z * 0.5;
|
||||
let base = floor(low);
|
||||
let center = base + 0.5;
|
||||
let next = base + 1.0;
|
||||
if high > next {
|
||||
return center + (high - next) / (high - low);
|
||||
} else {
|
||||
return center;
|
||||
}
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSample(screen_texture, linear_sampler, vec2<f32>(aa_tex_coord(in.tex_coords.x), aa_tex_coord(in.tex_coords.y)) / vec2<f32>(320.0, 240.0));
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
@@ -15,13 +15,22 @@ fn main() {
|
||||
palette[i as usize] = r + (g << 8) + (b << 16);
|
||||
}
|
||||
|
||||
let mut prev_frame = Instant::now();
|
||||
let mut fps_start = Instant::now();
|
||||
let mut fps_counter = 0;
|
||||
|
||||
uw8_window::run(move |gpu_framebuffer, _gamepads, _reset| {
|
||||
uw8_window::run(true, move |gpu_framebuffer, _gamepads, _reset| {
|
||||
for _ in 0..1 {
|
||||
draw_frame(&mut framebuffer, start_time.elapsed().as_secs_f32());
|
||||
}
|
||||
gpu_framebuffer.update(&framebuffer, bytemuck::cast_slice(&palette));
|
||||
prev_frame += Duration::from_secs_f32(1.0 / 60.0);
|
||||
prev_frame
|
||||
fps_counter += 1;
|
||||
let elapsed = fps_start.elapsed().as_secs_f32();
|
||||
if elapsed >= 1.0 {
|
||||
println!("{:.1} fps", fps_counter as f32 / elapsed);
|
||||
fps_start = Instant::now();
|
||||
fps_counter = 0;
|
||||
}
|
||||
Instant::now()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="uw8">
|
||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.0
|
||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.1
|
||||
</div>
|
||||
<div id="centered">
|
||||
<canvas class="screen" id="screen" width="320" height="240">
|
||||
|
||||
Reference in New Issue
Block a user