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