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