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