1use astrelis_core::logging;
11use astrelis_render::{
12 Color, GraphicsContext, RenderTarget, RenderableWindow, WindowContextDescriptor,
13};
14use astrelis_winit::{
15 WindowId,
16 app::run_app,
17 window::{WindowBackend, WindowDescriptor},
18};
19
20struct App {
21 window: RenderableWindow,
22 window_id: WindowId,
23 pipeline: wgpu::RenderPipeline,
24 bind_group: wgpu::BindGroup,
25 vertex_buffer: wgpu::Buffer,
26}
27
28fn main() {
29 logging::init();
30
31 run_app(|ctx| {
32 let graphics_ctx = GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
33
34 let window = ctx
35 .create_window(WindowDescriptor {
36 title: "Textured Window".to_string(),
37 ..Default::default()
38 })
39 .expect("Failed to create window");
40
41 let window = RenderableWindow::new_with_descriptor(
42 window,
43 graphics_ctx.clone(),
44 WindowContextDescriptor {
45 format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
46 ..Default::default()
47 },
48 ).expect("Failed to create renderable window");
49
50 let shader = graphics_ctx
52 .device()
53 .create_shader_module(wgpu::ShaderModuleDescriptor {
54 label: Some("Texture Shader"),
55 source: wgpu::ShaderSource::Wgsl(include_str!("textured_window.wgsl").into()),
56 });
57
58 let texture_size = wgpu::Extent3d {
62 width: 256,
63 height: 256,
64 depth_or_array_layers: 1,
65 };
66
67 let texture = graphics_ctx
68 .device()
69 .create_texture(&wgpu::TextureDescriptor {
70 label: Some("Example Texture"),
71 size: texture_size,
72 mip_level_count: 1,
73 sample_count: 1,
74 dimension: wgpu::TextureDimension::D2,
75 format: wgpu::TextureFormat::Rgba8UnormSrgb,
76 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
77 view_formats: &[],
78 });
79
80 let mut texture_data = vec![0u8; (256 * 256 * 4) as usize];
81 for y in 0..256 {
82 for x in 0..256 {
83 let idx = ((y * 256 + x) * 4) as usize;
84 texture_data[idx] = x as u8;
85 texture_data[idx + 1] = y as u8;
86 texture_data[idx + 2] = ((x + y) / 2) as u8;
87 texture_data[idx + 3] = 255;
88 }
89 }
90
91 graphics_ctx.queue().write_texture(
92 wgpu::TexelCopyTextureInfo {
93 texture: &texture,
94 mip_level: 0,
95 origin: wgpu::Origin3d::ZERO,
96 aspect: wgpu::TextureAspect::All,
97 },
98 &texture_data,
99 wgpu::TexelCopyBufferLayout {
100 offset: 0,
101 bytes_per_row: Some(256 * 4),
102 rows_per_image: Some(256),
103 },
104 texture_size,
105 );
106
107 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
109 let sampler = graphics_ctx
112 .device()
113 .create_sampler(&wgpu::SamplerDescriptor {
114 address_mode_u: wgpu::AddressMode::ClampToEdge,
115 address_mode_v: wgpu::AddressMode::ClampToEdge,
116 address_mode_w: wgpu::AddressMode::ClampToEdge,
117 mag_filter: wgpu::FilterMode::Linear,
118 min_filter: wgpu::FilterMode::Nearest,
119 mipmap_filter: wgpu::FilterMode::Nearest,
120 ..Default::default()
121 });
122
123 let bind_group_layout =
127 graphics_ctx
128 .device()
129 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
130 label: Some("Texture Bind Group Layout"),
131 entries: &[
132 wgpu::BindGroupLayoutEntry {
133 binding: 0,
134 visibility: wgpu::ShaderStages::FRAGMENT,
135 ty: wgpu::BindingType::Texture {
136 multisampled: false,
137 view_dimension: wgpu::TextureViewDimension::D2,
138 sample_type: wgpu::TextureSampleType::Float { filterable: true },
139 },
140 count: None,
141 },
142 wgpu::BindGroupLayoutEntry {
143 binding: 1,
144 visibility: wgpu::ShaderStages::FRAGMENT,
145 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
146 count: None,
147 },
148 ],
149 });
150
151 let bind_group = graphics_ctx
152 .device()
153 .create_bind_group(&wgpu::BindGroupDescriptor {
154 label: Some("Texture Bind Group"),
155 layout: &bind_group_layout,
156 entries: &[
157 wgpu::BindGroupEntry {
158 binding: 0,
159 resource: wgpu::BindingResource::TextureView(&texture_view),
160 },
161 wgpu::BindGroupEntry {
162 binding: 1,
163 resource: wgpu::BindingResource::Sampler(&sampler),
164 },
165 ],
166 });
167
168 let pipeline_layout =
170 graphics_ctx
171 .device()
172 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
173 label: Some("Render Pipeline Layout"),
174 bind_group_layouts: &[&bind_group_layout],
175 push_constant_ranges: &[],
176 });
177
178 let pipeline =
179 graphics_ctx
180 .device()
181 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
182 label: Some("Render Pipeline"),
183 layout: Some(&pipeline_layout),
184 vertex: wgpu::VertexState {
185 module: &shader,
186 entry_point: Some("vs_main"),
187 buffers: &[wgpu::VertexBufferLayout {
188 array_stride: 4 * 4,
190 step_mode: wgpu::VertexStepMode::Vertex,
191 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
192 }],
193 compilation_options: wgpu::PipelineCompilationOptions::default(),
194 },
195 fragment: Some(wgpu::FragmentState {
196 module: &shader,
197 entry_point: Some("fs_main"),
198 targets: &[Some(wgpu::ColorTargetState {
199 format: wgpu::TextureFormat::Bgra8UnormSrgb,
200 blend: Some(wgpu::BlendState::REPLACE),
201 write_mask: wgpu::ColorWrites::ALL,
202 })],
203 compilation_options: wgpu::PipelineCompilationOptions::default(),
204 }),
205 primitive: wgpu::PrimitiveState {
206 topology: wgpu::PrimitiveTopology::TriangleList,
207 strip_index_format: None,
208 front_face: wgpu::FrontFace::Ccw,
209 cull_mode: Some(wgpu::Face::Back),
210 polygon_mode: wgpu::PolygonMode::Fill,
211 unclipped_depth: false,
212 conservative: false,
213 },
214 depth_stencil: None,
215 multisample: wgpu::MultisampleState {
216 count: 1,
217 mask: !0,
218 alpha_to_coverage_enabled: false,
219 },
220 multiview: None,
221 cache: None,
222 });
223
224 #[rustfmt::skip]
225 let vertices: &[f32] = &[
226 -0.8, -0.8, 0.0, 1.0,
227 0.8, -0.8, 1.0, 1.0,
228 0.8, 0.8, 1.0, 0.0,
229 -0.8, -0.8, 0.0, 1.0,
230 0.8, 0.8, 1.0, 0.0,
231 -0.8, 0.8, 0.0, 0.0,
232 ];
233
234 let vertex_buffer = graphics_ctx.device().create_buffer(&wgpu::BufferDescriptor {
235 label: Some("Vertex Buffer"),
236 size: (vertices.len() * std::mem::size_of::<f32>()) as u64,
237 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
238 mapped_at_creation: false,
239 });
240
241 graphics_ctx
242 .queue()
243 .write_buffer(&vertex_buffer, 0, bytemuck::cast_slice(vertices));
244
245 let window_id = window.id();
246
247 Box::new(App {
248 window,
249 window_id,
250 pipeline,
251 bind_group,
252 vertex_buffer,
253 })
254 });
255}
256
257impl astrelis_winit::app::App for App {
258 fn update(&mut self, _ctx: &mut astrelis_winit::app::AppCtx, _time: &astrelis_winit::FrameTime) {
259 }
261
262 fn render(
263 &mut self,
264 _ctx: &mut astrelis_winit::app::AppCtx,
265 window_id: WindowId,
266 events: &mut astrelis_winit::event::EventBatch,
267 ) {
268 if window_id != self.window_id {
269 return;
270 }
271
272 events.dispatch(|event| {
274 if let astrelis_winit::event::Event::WindowResized(size) = event {
275 self.window.resized(*size);
276 astrelis_winit::event::HandleStatus::consumed()
277 } else {
278 astrelis_winit::event::HandleStatus::ignored()
279 }
280 });
281
282 let mut frame = self.window.begin_drawing();
284
285 frame.clear_and_render(
288 RenderTarget::Surface,
289 Color::rgb(0.1, 0.2, 0.3),
290 |pass| {
291 let pass = pass.wgpu_pass();
292 pass.set_pipeline(&self.pipeline);
293 pass.set_bind_group(0, &self.bind_group, &[]);
294 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
295 pass.draw(0..6, 0..1);
296 },
297 );
298
299 frame.finish();
300 }
301}