mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Window with textured polygon
This commit is contained in:
commit
b30ee294a6
17 changed files with 2804 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(Windows) Launch",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/target/debug/graphite.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false
|
||||
},
|
||||
{
|
||||
"name": "(LLDB) Launch",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/target/debug/graphite.exe",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
}
|
||||
]
|
||||
}
|
2036
Cargo.lock
generated
Normal file
2036
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "graphite"
|
||||
version = "0.1.0"
|
||||
authors = ["Keavon Chambers <graphite@keavon.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
image = "0.22"
|
||||
winit = "0.20"
|
||||
wgpu = "0.4"
|
||||
glsl-to-spirv = "0.1"
|
||||
failure = "0.1.7"
|
||||
cgmath = "0.17"
|
||||
palette = "0.5"
|
11
shaders/shader.frag
Normal file
11
shaders/shader.frag
Normal file
|
@ -0,0 +1,11 @@
|
|||
#version 450
|
||||
|
||||
layout(location=0) in vec2 v_uv;
|
||||
|
||||
layout(location=0) out vec4 f_color;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D t_texture;
|
||||
|
||||
void main() {
|
||||
f_color = texture(t_texture, v_uv / textureSize(t_texture, 0) * 100);
|
||||
}
|
10
shaders/shader.vert
Normal file
10
shaders/shader.vert
Normal file
|
@ -0,0 +1,10 @@
|
|||
#version 450
|
||||
|
||||
layout(location=0) in vec2 a_position;
|
||||
|
||||
layout(location=0) out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
v_uv = a_position;
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}
|
240
src/application.rs
Normal file
240
src/application.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
// use super::render_state::RenderState;
|
||||
// use super::program_state::ProgramState;
|
||||
use super::color_palette::ColorPalette;
|
||||
use super::gui_rect::GUIRect;
|
||||
use super::pipeline::Pipeline;
|
||||
use super::pipeline::PipelineDetails;
|
||||
use super::shader_cache::ShaderCache;
|
||||
use super::texture::Texture;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use winit::event::*;
|
||||
use winit::event_loop::ControlFlow;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
|
||||
pub struct Application {
|
||||
pub surface: wgpu::Surface,
|
||||
pub adapter: wgpu::Adapter,
|
||||
pub device: wgpu::Device,
|
||||
pub queue: wgpu::Queue,
|
||||
pub swap_chain_descriptor: wgpu::SwapChainDescriptor,
|
||||
pub swap_chain: wgpu::SwapChain,
|
||||
pub shader_cache: ShaderCache,
|
||||
// pub texture_cache: TextureCache,
|
||||
pub gui_rect_queue: VecDeque<GUIRect>,
|
||||
pub pipeline_queue: VecDeque<Pipeline>,
|
||||
pub temp_color_toggle: bool,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
pub fn new(window: &Window) -> Self {
|
||||
// Window as understood by WGPU for rendering onto
|
||||
let surface = wgpu::Surface::create(window);
|
||||
|
||||
// Represents a GPU, exposes the real GPU device and queue
|
||||
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { ..Default::default() }).unwrap();
|
||||
|
||||
// Requests the device and queue from the adapter
|
||||
let requested_device = adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
extensions: wgpu::Extensions { anisotropic_filtering: false },
|
||||
limits: Default::default(),
|
||||
});
|
||||
|
||||
// Connection to the physical GPU
|
||||
let device = requested_device.0;
|
||||
|
||||
// Represents the GPU command queue, to submit CommandBuffers
|
||||
let queue = requested_device.1;
|
||||
|
||||
// Properties for the swap chain frame buffers
|
||||
let swap_chain_descriptor = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
present_mode: wgpu::PresentMode::Vsync,
|
||||
};
|
||||
|
||||
// Series of frame buffers with images presented to the surface
|
||||
let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor);
|
||||
|
||||
// Cache of all loaded shaders
|
||||
let shader_cache = ShaderCache::new();
|
||||
|
||||
let gui_rect_queue = VecDeque::new();
|
||||
|
||||
let pipeline_queue = VecDeque::new();
|
||||
|
||||
Self {
|
||||
surface,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
swap_chain_descriptor,
|
||||
swap_chain,
|
||||
shader_cache,
|
||||
gui_rect_queue,
|
||||
pipeline_queue,
|
||||
temp_color_toggle: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn example(&mut self) {
|
||||
self.shader_cache.load(&self.device, "shaders/shader.vert", glsl_to_spirv::ShaderType::Vertex).unwrap();
|
||||
self.shader_cache.load(&self.device, "shaders/shader.frag", glsl_to_spirv::ShaderType::Fragment).unwrap();
|
||||
|
||||
let vertex_shader = self.shader_cache.get_by_path("shaders/shader.vert").unwrap();
|
||||
let fragment_shader = self.shader_cache.get_by_path("shaders/shader.frag").unwrap();
|
||||
|
||||
let texture_view = Texture::from_filepath(&self.device, &mut self.queue, "textures/grid.png").unwrap().view;
|
||||
|
||||
let example_pipeline = Pipeline::new(&self.device, PipelineDetails {
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
texture_view: Some(&texture_view),
|
||||
});
|
||||
|
||||
self.pipeline_queue.push_back(example_pipeline);
|
||||
}
|
||||
|
||||
pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) {
|
||||
event_loop.run(move |event, _, control_flow| self.main_event_loop(event, control_flow, &window));
|
||||
}
|
||||
|
||||
pub fn main_event_loop<T>(&mut self, event: Event<'_, T>, control_flow: &mut ControlFlow, window: &Window) {
|
||||
match event {
|
||||
// Handle all window events in sequence
|
||||
Event::WindowEvent { ref event, window_id } if window_id == window.id() => {
|
||||
self.window_event(event, control_flow);
|
||||
},
|
||||
// After handling every event and updating the GUI, request a new sequence of draw commands
|
||||
Event::MainEventsCleared => {
|
||||
// Turn the GUI changes into draw commands added to the render pipeline queue
|
||||
self.redraw();
|
||||
|
||||
// If any draw commands were actually added, ask the window to issue a redraw event
|
||||
if !self.pipeline_queue.is_empty() {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
// Resizing or calling `window.request_redraw()` now redraws the GUI with the pipeline queue
|
||||
Event::RedrawRequested(_) => {
|
||||
self.render();
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
// Catch extraneous events
|
||||
_ => {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_event(&mut self, event: &WindowEvent, control_flow: &mut ControlFlow) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
self.quit(control_flow);
|
||||
},
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
self.keyboard_event(input, control_flow);
|
||||
},
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.resize(*physical_size);
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
self.resize(**new_inner_size);
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
_ => {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyboard_event(&mut self, input: &KeyboardInput, control_flow: &mut ControlFlow) {
|
||||
match input {
|
||||
KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => {
|
||||
self.quit(control_flow);
|
||||
},
|
||||
KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => {
|
||||
self.example();
|
||||
},
|
||||
_ => {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn quit(&self, control_flow: &mut ControlFlow) {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||
self.swap_chain_descriptor.width = new_size.width;
|
||||
self.swap_chain_descriptor.height = new_size.height;
|
||||
|
||||
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.swap_chain_descriptor);
|
||||
|
||||
// TODO: Mark root of GUI as dirty to force redraw of everything
|
||||
}
|
||||
|
||||
// Traverse the dirty GUI elements and queue up pipelines to render each GUI rectangle (box/sprite)
|
||||
pub fn redraw(&mut self) {
|
||||
|
||||
}
|
||||
|
||||
// Render the queue of pipeline draw commands over the current window
|
||||
pub fn render(&mut self) {
|
||||
// Turn the queue of pipelines each into a command buffer and submit it to the render queue
|
||||
while !self.pipeline_queue.is_empty() {
|
||||
// Get a frame buffer to render on
|
||||
let frame = self.swap_chain.get_next_texture();
|
||||
|
||||
// Get the pipeline to render in this iteration
|
||||
let pipeline_struct = self.pipeline_queue.pop_back().unwrap();
|
||||
|
||||
// Generates a render pass that commands are applied to, then generates a command buffer when finished
|
||||
let mut command_encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
|
||||
|
||||
// Temporary way to swap clear color every render
|
||||
let color = match self.temp_color_toggle {
|
||||
true => ColorPalette::get_color_linear(ColorPalette::MildBlack),
|
||||
false => ColorPalette::get_color_linear(ColorPalette::NearBlack),
|
||||
};
|
||||
self.temp_color_toggle = !self.temp_color_toggle;
|
||||
|
||||
// Recording of commands while in "rendering mode" that go into a command buffer
|
||||
let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[
|
||||
wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: &frame.view,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: color,
|
||||
}
|
||||
],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
// Commands sent to the GPU for drawing during this render pass
|
||||
render_pass.set_pipeline(&pipeline_struct.render_pipeline);
|
||||
render_pass.set_vertex_buffers(0, &[(&pipeline_struct.vertex_buffer, 0)]);
|
||||
render_pass.set_index_buffer(&pipeline_struct.index_buffer, 0);
|
||||
render_pass.set_bind_group(0, &pipeline_struct.texture_bind_group, &[]);
|
||||
render_pass.draw_indexed(0..pipeline_struct.index_count, 0, 0..1);
|
||||
|
||||
// Done sending render pass commands so we can give up mutation rights to command_encoder
|
||||
drop(render_pass);
|
||||
|
||||
// Turn the recording of commands into a complete command buffer
|
||||
let command_buffer = command_encoder.finish();
|
||||
|
||||
// Submit the command buffer to the GPU command queue
|
||||
self.queue.submit(&[command_buffer]);
|
||||
}
|
||||
}
|
||||
}
|
69
src/color_palette.rs
Normal file
69
src/color_palette.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
#[allow(dead_code)]
|
||||
pub enum ColorPalette {
|
||||
Black,
|
||||
NearBlack,
|
||||
MildBlack,
|
||||
DarkGray,
|
||||
DimGray,
|
||||
DullGray,
|
||||
LowerGray,
|
||||
MiddleGray,
|
||||
UpperGray,
|
||||
PaleGray,
|
||||
SoftGray,
|
||||
LightGray,
|
||||
BrightGray,
|
||||
MildWhite,
|
||||
NearWhite,
|
||||
White,
|
||||
Accent,
|
||||
}
|
||||
|
||||
impl ColorPalette {
|
||||
pub fn get_color(self) -> wgpu::Color {
|
||||
let grayscale = match self {
|
||||
ColorPalette::Black => 0 * 17, // #000000
|
||||
ColorPalette::NearBlack => 1 * 17, // #111111
|
||||
ColorPalette::MildBlack => 2 * 17, // #222222
|
||||
ColorPalette::DarkGray => 3 * 17, // #333333
|
||||
ColorPalette::DimGray => 4 * 17, // #444444
|
||||
ColorPalette::DullGray => 5 * 17, // #555555
|
||||
ColorPalette::LowerGray => 6 * 17, // #666666
|
||||
ColorPalette::MiddleGray => 7 * 17, // #777777
|
||||
ColorPalette::UpperGray => 8 * 17, // #888888
|
||||
ColorPalette::PaleGray => 9 * 17, // #999999
|
||||
ColorPalette::SoftGray => 10 * 17, // #aaaaaa
|
||||
ColorPalette::LightGray => 11 * 17, // #bbbbbb
|
||||
ColorPalette::BrightGray => 12 * 17, // #cccccc
|
||||
ColorPalette::MildWhite => 13 * 17, // #dddddd
|
||||
ColorPalette::NearWhite => 14 * 17, // #eeeeee
|
||||
ColorPalette::White => 15 * 17, // #ffffff
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
if grayscale > -1 {
|
||||
let value = grayscale as f64 / 255.0;
|
||||
return wgpu::Color { r: value, g: value, b: value, a: 1.0 };
|
||||
}
|
||||
|
||||
let rgba = match self {
|
||||
ColorPalette::Accent => (75, 121, 167, 255), // #4b79a7
|
||||
_ => (0, 0, 0, 255), // Unimplemented returns black
|
||||
};
|
||||
|
||||
wgpu::Color {
|
||||
r: rgba.0 as f64 / 255.0,
|
||||
g: rgba.1 as f64 / 255.0,
|
||||
b: rgba.2 as f64 / 255.0,
|
||||
a: rgba.3 as f64 / 255.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_color_linear(self) -> wgpu::Color {
|
||||
let standard_rgb = ColorPalette::get_color(self);
|
||||
|
||||
let linear = palette::Srgb::new(standard_rgb.r, standard_rgb.g, standard_rgb.b).into_linear();
|
||||
|
||||
wgpu::Color { r: linear.red, g: linear.green, b: linear.blue, a: standard_rgb.a }
|
||||
}
|
||||
}
|
23
src/gui_rect.rs
Normal file
23
src/gui_rect.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
pub struct GUIRect {
|
||||
pub corners: Corners<(f32, f32)>,
|
||||
pub corners_radius: Corners<f32>,
|
||||
pub sides_inset: Sides<f32>,
|
||||
pub border: f32,
|
||||
pub border_color: wgpu::Color,
|
||||
pub fill_color: wgpu::Color,
|
||||
pub fill_texture: Option<wgpu::Texture>,
|
||||
}
|
||||
|
||||
pub struct Corners<T> {
|
||||
pub top_left: T,
|
||||
pub top_right: T,
|
||||
pub bottom_right: T,
|
||||
pub bottom_left: T,
|
||||
}
|
||||
|
||||
pub struct Sides<T> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
30
src/main.rs
Normal file
30
src/main.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
mod application;
|
||||
mod gui_rect;
|
||||
mod pipeline;
|
||||
mod program_state;
|
||||
mod texture;
|
||||
mod color_palette;
|
||||
mod shader_cache;
|
||||
|
||||
use application::Application;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
fn main() {
|
||||
// Handles all window events, user input, and redraws
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
// Application window in the operating system
|
||||
let window = WindowBuilder::new().with_title("Graphite").build(&event_loop).unwrap();
|
||||
|
||||
// Initialize the render pipeline
|
||||
let mut app = Application::new(&window);
|
||||
app.example();
|
||||
|
||||
// State managers for render pipeline and program logic
|
||||
// let app_render_state = RenderState::new(&mut app);
|
||||
// let app_program_state = ProgramState::new(&mut app);
|
||||
|
||||
// Begin the application lifecycle
|
||||
app.begin_lifecycle(event_loop, window);
|
||||
}
|
125
src/pipeline.rs
Normal file
125
src/pipeline.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
pub struct PipelineDetails<'a> {
|
||||
pub vertex_shader: &'a wgpu::ShaderModule,
|
||||
pub fragment_shader: &'a wgpu::ShaderModule,
|
||||
pub texture_view: Option<&'a wgpu::TextureView>,
|
||||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
pub render_pipeline: wgpu::RenderPipeline,
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub index_buffer: wgpu::Buffer,
|
||||
pub index_count: u32,
|
||||
pub texture_bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device, pipeline_details: PipelineDetails) -> Self {
|
||||
let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
bindings: &[
|
||||
wgpu::BindGroupLayoutBinding {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::SampledTexture {
|
||||
multisampled: false,
|
||||
dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
},
|
||||
// wgpu::BindGroupLayoutBinding {
|
||||
// binding: 1,
|
||||
// visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
// ty: wgpu::BindingType::Sampler,
|
||||
// },
|
||||
],
|
||||
});
|
||||
|
||||
let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &texture_bind_group_layout,
|
||||
bindings: &[
|
||||
wgpu::Binding {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(pipeline_details.texture_view.unwrap()),
|
||||
},
|
||||
// wgpu::Binding {
|
||||
// binding: 1,
|
||||
// resource: wgpu::BindingResource::Sampler(&texture.sampler),
|
||||
// }
|
||||
],
|
||||
});
|
||||
|
||||
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&texture_bind_group_layout],
|
||||
});
|
||||
|
||||
let vertex_buffer_descriptors = wgpu::VertexBufferDescriptor {
|
||||
stride: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttributeDescriptor {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float2,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
layout: &render_pipeline_layout,
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: pipeline_details.vertex_shader,
|
||||
entry_point: "main",
|
||||
},
|
||||
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
|
||||
module: pipeline_details.fragment_shader,
|
||||
entry_point: "main",
|
||||
}),
|
||||
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: wgpu::CullMode::Back,
|
||||
depth_bias: 0,
|
||||
depth_bias_slope_scale: 0.0,
|
||||
depth_bias_clamp: 0.0,
|
||||
}),
|
||||
color_states: &[
|
||||
wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
},
|
||||
],
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
depth_stencil_state: None,
|
||||
index_format: wgpu::IndexFormat::Uint16,
|
||||
vertex_buffers: &[vertex_buffer_descriptors],
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
});
|
||||
|
||||
let vertex_buffer = device.create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX).fill_from_slice(VERTICES);
|
||||
let index_buffer = device.create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX).fill_from_slice(INDICES);
|
||||
let index_count = INDICES.len() as u32;
|
||||
|
||||
Self {
|
||||
render_pipeline,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
index_count,
|
||||
texture_bind_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const VERTICES: &[[f32; 2]] = &[
|
||||
[-0.0868241, -0.49240386],
|
||||
[-0.49513406, -0.06958647],
|
||||
[-0.21918549, 0.44939706],
|
||||
[0.35966998, 0.3473291],
|
||||
[0.44147372, -0.2347359],
|
||||
];
|
||||
|
||||
const INDICES: &[u16] = &[
|
||||
0, 1, 4,
|
||||
1, 2, 4,
|
||||
2, 3, 4,
|
||||
];
|
13
src/program_state.rs
Normal file
13
src/program_state.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use super::application::Application;
|
||||
|
||||
pub struct ProgramState {
|
||||
|
||||
}
|
||||
|
||||
impl ProgramState {
|
||||
pub fn new(application: &mut Application) -> ProgramState {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
26
src/render_state.rs
Normal file
26
src/render_state.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// use super::texture::Texture;
|
||||
// use super::application::Application;
|
||||
|
||||
// use std::collections::HashMap;
|
||||
|
||||
// pub struct RenderState {
|
||||
// pub render_pipeline: wgpu::RenderPipeline,
|
||||
// pub vertex_buffer: wgpu::Buffer,
|
||||
// pub index_buffer: wgpu::Buffer,
|
||||
// pub num_indices: u32,
|
||||
// pub texture: Texture,
|
||||
// pub texture_bind_group: wgpu::BindGroup,
|
||||
// }
|
||||
|
||||
// impl RenderState {
|
||||
// pub fn new(application: &mut Application) -> Self {
|
||||
|
||||
|
||||
// Self {
|
||||
// vertex_buffer,
|
||||
// index_buffer,
|
||||
// num_indices,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
49
src/shader_cache.rs
Normal file
49
src/shader_cache.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ShaderID {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
pub struct ShaderCache {
|
||||
pub shaders: Vec<wgpu::ShaderModule>,
|
||||
pub path_to_id: HashMap<String, ShaderID>,
|
||||
}
|
||||
|
||||
impl ShaderCache {
|
||||
pub fn new() -> Self {
|
||||
let shaders = Vec::new();
|
||||
let path_to_id = HashMap::new();
|
||||
|
||||
Self {
|
||||
shaders,
|
||||
path_to_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_by_path(&self, path: &str) -> Option<&wgpu::ShaderModule> {
|
||||
match self.path_to_id.get(path) {
|
||||
Some(id) => self.shaders.get(id.index),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_by_id(&self, id: ShaderID) -> Option<&wgpu::ShaderModule> {
|
||||
// self.shaders.get(id.index)
|
||||
// }
|
||||
|
||||
pub fn load(&mut self, device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> Result<(), std::io::Error> {
|
||||
if self.path_to_id.get(path).is_none() {
|
||||
let source = std::fs::read_to_string(path)?;
|
||||
let spirv = glsl_to_spirv::compile(&source[..], shader_type).unwrap();
|
||||
let compiled = wgpu::read_spirv(spirv).unwrap();
|
||||
let shader = device.create_shader_module(&compiled);
|
||||
|
||||
let length = self.path_to_id.len();
|
||||
self.path_to_id.insert(String::from(path), ShaderID { index: length });
|
||||
self.shaders.push(shader);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
78
src/texture.rs
Normal file
78
src/texture.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use image::GenericImageView;
|
||||
|
||||
pub struct Texture {
|
||||
pub texture: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
pub fn from_filepath(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str) -> Result<Self, failure::Error> {
|
||||
let bytes = std::fs::read(path)?;
|
||||
Texture::from_bytes(device, queue, &bytes[..])
|
||||
}
|
||||
|
||||
pub fn from_bytes(device: &wgpu::Device, queue: &mut wgpu::Queue, bytes: &[u8]) -> Result<Self, failure::Error> {
|
||||
let img = image::load_from_memory(bytes)?;
|
||||
Self::from_image(device, queue, &img)
|
||||
}
|
||||
|
||||
pub fn from_image(device: &wgpu::Device, queue: &mut wgpu::Queue, img: &image::DynamicImage) -> Result<Self, failure::Error> {
|
||||
let rgba = img.as_rgba8().unwrap();
|
||||
let dimensions = img.dimensions();
|
||||
let size = wgpu::Extent3d {
|
||||
width: dimensions.0,
|
||||
height: dimensions.1,
|
||||
depth: 1,
|
||||
};
|
||||
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
size,
|
||||
array_layer_count: 1,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
|
||||
});
|
||||
|
||||
let buffer = device.create_buffer_mapped(rgba.len(), wgpu::BufferUsage::COPY_SRC).fill_from_slice(&rgba);
|
||||
|
||||
let mut encoder = device.create_command_encoder(&Default::default());
|
||||
|
||||
encoder.copy_buffer_to_texture(
|
||||
wgpu::BufferCopyView {
|
||||
buffer: &buffer,
|
||||
offset: 0,
|
||||
row_pitch: 4 * dimensions.0,
|
||||
image_height: dimensions.1,
|
||||
},
|
||||
wgpu::TextureCopyView {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
array_layer: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
},
|
||||
size,
|
||||
);
|
||||
|
||||
let command_buffer = encoder.finish();
|
||||
|
||||
let view = texture.create_default_view();
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
lod_min_clamp: -100.0,
|
||||
lod_max_clamp: 100.0,
|
||||
compare_function: wgpu::CompareFunction::Always,
|
||||
});
|
||||
|
||||
queue.submit(&[command_buffer]);
|
||||
|
||||
Ok(Self { texture, view, sampler })
|
||||
}
|
||||
}
|
50
src/texture_cache.rs
Normal file
50
src/texture_cache.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use std::collections::HashMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TextureID {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
pub struct TextureCache {
|
||||
pub textures: Vec<wgpu::Texture>,
|
||||
pub name_to_id: HashMap<String, TextureID>,
|
||||
}
|
||||
|
||||
impl ShaderCache {
|
||||
pub fn new() -> Self {
|
||||
let shaders = Vec::new();
|
||||
let name_to_id = HashMap::new();
|
||||
|
||||
Self {
|
||||
shaders,
|
||||
name_to_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_by_path(&self, path: &str) -> Option<&wgpu::ShaderModule> {
|
||||
match self.name_to_id.get(path) {
|
||||
Some(id) => self.shaders.get(id.index),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_by_id(&self, id: ShaderID) -> Option<&wgpu::ShaderModule> {
|
||||
self.shaders.get(id.index)
|
||||
}
|
||||
|
||||
pub fn load(&mut self, device: &wgpu::Device, path: &str, shader_type: glsl_to_spirv::ShaderType) -> Result<(), std::io::Error> {
|
||||
if self.name_to_id.get(path).is_none() {
|
||||
let source = std::fs::read_to_string(path)?;
|
||||
let spirv = glsl_to_spirv::compile(&source[..], shader_type).unwrap();
|
||||
let compiled = wgpu::read_spirv(spirv).unwrap();
|
||||
let shader = device.create_shader_module(&compiled);
|
||||
|
||||
let length = self.name_to_id.len();
|
||||
self.name_to_id.insert(String::from(path), ShaderID { index: length });
|
||||
self.shaders.push(shader);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
BIN
textures/grid.png
Normal file
BIN
textures/grid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Loading…
Add table
Add a link
Reference in a new issue