1use crate::{
24 asset::untyped::ResourceKind,
25 core::{
26 algebra::{Matrix4, Vector2, Vector4},
27 color::Color,
28 math::Rect,
29 sstorage::ImmutableString,
30 },
31 gui::{
32 brush::Brush,
33 draw::{CommandTexture, DrawingContext},
34 },
35 renderer::{
36 cache::uniform::UniformBufferCache,
37 flat_shader::FlatShader,
38 framework::{
39 buffer::BufferUsage,
40 error::FrameworkError,
41 framebuffer::{FrameBuffer, ResourceBindGroup, ResourceBinding},
42 geometry_buffer::{
43 AttributeDefinition, AttributeKind, GeometryBuffer, GeometryBufferDescriptor,
44 VertexBufferData, VertexBufferDescriptor,
45 },
46 gpu_program::{GpuProgram, UniformLocation},
47 server::GraphicsServer,
48 uniform::StaticUniformBuffer,
49 BlendFactor, BlendFunc, BlendParameters, ColorMask, CompareFunc, DrawParameters,
50 ElementRange, ScissorBox, StencilAction, StencilFunc, StencilOp,
51 },
52 FallbackResources, RenderPassStatistics, TextureCache,
53 },
54 resource::texture::{Texture, TextureKind, TexturePixelKind, TextureResource},
55};
56use fyrox_graphics::{framebuffer::BufferLocation, geometry_buffer::ElementsDescriptor};
57
58struct UiShader {
59 program: Box<dyn GpuProgram>,
60 diffuse_texture: UniformLocation,
61 uniform_block_index: usize,
62}
63
64impl UiShader {
65 pub fn new(server: &dyn GraphicsServer) -> Result<Self, FrameworkError> {
66 let fragment_source = include_str!("shaders/ui_fs.glsl");
67 let vertex_source = include_str!("shaders/ui_vs.glsl");
68 let program = server.create_program("UIShader", vertex_source, fragment_source)?;
69 Ok(Self {
70 diffuse_texture: program.uniform_location(&ImmutableString::new("diffuseTexture"))?,
71 uniform_block_index: program.uniform_block_index(&ImmutableString::new("Uniforms"))?,
72 program,
73 })
74 }
75}
76
77pub struct UiRenderer {
79 shader: UiShader,
80 geometry_buffer: Box<dyn GeometryBuffer>,
81 clipping_geometry_buffer: Box<dyn GeometryBuffer>,
82}
83
84pub struct UiRenderContext<'a, 'b, 'c> {
86 pub server: &'a dyn GraphicsServer,
88 pub viewport: Rect<i32>,
90 pub frame_buffer: &'b mut dyn FrameBuffer,
92 pub frame_width: f32,
94 pub frame_height: f32,
96 pub drawing_context: &'c DrawingContext,
98 pub fallback_resources: &'a FallbackResources,
100 pub texture_cache: &'a mut TextureCache,
102 pub uniform_buffer_cache: &'a mut UniformBufferCache,
104 pub flat_shader: &'a FlatShader,
106}
107
108impl UiRenderer {
109 pub(in crate::renderer) fn new(server: &dyn GraphicsServer) -> Result<Self, FrameworkError> {
110 let geometry_buffer_desc = GeometryBufferDescriptor {
111 elements: ElementsDescriptor::Triangles(&[]),
112 buffers: &[VertexBufferDescriptor {
113 usage: BufferUsage::DynamicDraw,
114 attributes: &[
115 AttributeDefinition {
116 location: 0,
117 kind: AttributeKind::Float,
118 component_count: 2,
119 normalized: false,
120 divisor: 0,
121 },
122 AttributeDefinition {
123 location: 1,
124 kind: AttributeKind::Float,
125 component_count: 2,
126 normalized: false,
127 divisor: 0,
128 },
129 AttributeDefinition {
130 location: 2,
131 kind: AttributeKind::UnsignedByte,
132 component_count: 4,
133 normalized: true, divisor: 0,
135 },
136 ],
137 data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
138 }],
139 usage: BufferUsage::DynamicDraw,
140 };
141
142 let clipping_geometry_buffer_desc = GeometryBufferDescriptor {
143 elements: ElementsDescriptor::Triangles(&[]),
144 buffers: &[VertexBufferDescriptor {
145 usage: BufferUsage::DynamicDraw,
146 attributes: &[
147 AttributeDefinition {
149 location: 0,
150 kind: AttributeKind::Float,
151 component_count: 2,
152 normalized: false,
153 divisor: 0,
154 },
155 ],
156 data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
157 }],
158 usage: BufferUsage::DynamicDraw,
159 };
160
161 Ok(Self {
162 geometry_buffer: server.create_geometry_buffer(geometry_buffer_desc)?,
163 clipping_geometry_buffer: server
164 .create_geometry_buffer(clipping_geometry_buffer_desc)?,
165 shader: UiShader::new(server)?,
166 })
167 }
168
169 pub fn render(
171 &mut self,
172 args: UiRenderContext,
173 ) -> Result<RenderPassStatistics, FrameworkError> {
174 let UiRenderContext {
175 server,
176 viewport,
177 frame_buffer,
178 frame_width,
179 frame_height,
180 drawing_context,
181 fallback_resources,
182 texture_cache,
183 uniform_buffer_cache,
184 flat_shader,
185 } = args;
186
187 let mut statistics = RenderPassStatistics::default();
188
189 self.geometry_buffer
190 .set_buffer_data_of_type(0, drawing_context.get_vertices());
191 self.geometry_buffer
192 .set_triangles(drawing_context.get_triangles());
193
194 let ortho = Matrix4::new_orthographic(0.0, frame_width, frame_height, 0.0, -1.0, 1.0);
195 let resolution = Vector2::new(frame_width, frame_height);
196
197 for cmd in drawing_context.get_commands() {
198 let mut diffuse_texture = &fallback_resources.white_dummy;
199 let mut is_font_texture = false;
200
201 let mut clip_bounds = cmd.clip_bounds;
202 clip_bounds.position.x = clip_bounds.position.x.floor();
203 clip_bounds.position.y = clip_bounds.position.y.floor();
204 clip_bounds.size.x = clip_bounds.size.x.ceil();
205 clip_bounds.size.y = clip_bounds.size.y.ceil();
206
207 let scissor_box = Some(ScissorBox {
208 x: clip_bounds.position.x as i32,
209 y: viewport.size.y - (clip_bounds.position.y + clip_bounds.size.y) as i32,
211 width: clip_bounds.size.x as i32,
212 height: clip_bounds.size.y as i32,
213 });
214
215 let mut stencil_test = None;
216
217 if let Some(clipping_geometry) = cmd.clipping_geometry.as_ref() {
220 frame_buffer.clear(viewport, None, None, Some(0));
221
222 self.clipping_geometry_buffer
223 .set_buffer_data_of_type(0, &clipping_geometry.vertex_buffer);
224 self.clipping_geometry_buffer
225 .set_triangles(&clipping_geometry.triangle_buffer);
226
227 let uniform_buffer =
228 uniform_buffer_cache.write(StaticUniformBuffer::<256>::new().with(&ortho))?;
229
230 statistics += frame_buffer.draw(
232 &*self.clipping_geometry_buffer,
233 viewport,
234 &*flat_shader.program,
235 &DrawParameters {
236 cull_face: None,
237 color_write: ColorMask::all(false),
238 depth_write: false,
239 stencil_test: None,
240 depth_test: None,
241 blend: None,
242 stencil_op: StencilOp {
243 zpass: StencilAction::Incr,
244 ..Default::default()
245 },
246 scissor_box,
247 },
248 &[ResourceBindGroup {
249 bindings: &[ResourceBinding::Buffer {
250 buffer: uniform_buffer,
251 binding: BufferLocation::Auto {
252 shader_location: flat_shader.uniform_buffer_binding,
253 },
254 data_usage: Default::default(),
255 }],
256 }],
257 ElementRange::Full,
258 )?;
259
260 stencil_test = Some(StencilFunc {
262 func: CompareFunc::Equal,
263 ref_value: 1,
264 ..Default::default()
265 });
266 }
267
268 match &cmd.texture {
269 CommandTexture::Font {
270 font,
271 page_index,
272 height,
273 } => {
274 if let Some(font) = font.state().data() {
275 let page_size = font.page_size() as u32;
276 if let Some(page) = font
277 .atlases
278 .get_mut(height)
279 .and_then(|atlas| atlas.pages.get_mut(*page_index))
280 {
281 if page.texture.is_none() || page.modified {
282 if let Some(details) = Texture::from_bytes(
283 TextureKind::Rectangle {
284 width: page_size,
285 height: page_size,
286 },
287 TexturePixelKind::R8,
288 page.pixels.clone(),
289 ) {
290 page.texture = Some(
291 TextureResource::new_ok(ResourceKind::Embedded, details)
292 .into(),
293 );
294 page.modified = false;
295 }
296 }
297 if let Some(texture) = texture_cache.get(
298 server,
299 &page
300 .texture
301 .as_ref()
302 .unwrap()
303 .try_cast::<Texture>()
304 .unwrap(),
305 ) {
306 diffuse_texture = texture;
307 }
308 is_font_texture = true;
309 }
310 }
311 }
312 CommandTexture::Texture(texture) => {
313 if let Some(texture) = texture_cache.get(server, texture) {
314 diffuse_texture = texture;
315 }
316 }
317 _ => (),
318 }
319
320 let mut raw_stops = [0.0; 16];
321 let mut raw_colors = [Vector4::default(); 16];
322 let bounds_max = cmd.bounds.right_bottom_corner();
323
324 let (gradient_origin, gradient_end) = match cmd.brush {
325 Brush::Solid(_) => (Vector2::default(), Vector2::default()),
326 Brush::LinearGradient { from, to, .. } => (from, to),
327 Brush::RadialGradient { center, .. } => (center, Vector2::default()),
328 };
329
330 let params = DrawParameters {
331 cull_face: None,
332 color_write: ColorMask::all(true),
333 depth_write: false,
334 stencil_test,
335 depth_test: None,
336 blend: Some(BlendParameters {
337 func: BlendFunc::new(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha),
338 ..Default::default()
339 }),
340 stencil_op: Default::default(),
341 scissor_box,
342 };
343
344 let solid_color = match cmd.brush {
345 Brush::Solid(color) => color,
346 _ => Color::WHITE,
347 };
348 let gradient_colors = match cmd.brush {
349 Brush::Solid(_) => &raw_colors,
350 Brush::LinearGradient { ref stops, .. }
351 | Brush::RadialGradient { ref stops, .. } => {
352 for (i, point) in stops.iter().enumerate() {
353 raw_colors[i] = point.color.as_frgba();
354 }
355 &raw_colors
356 }
357 };
358 let gradient_stops = match cmd.brush {
359 Brush::Solid(_) => &raw_stops,
360 Brush::LinearGradient { ref stops, .. }
361 | Brush::RadialGradient { ref stops, .. } => {
362 for (i, point) in stops.iter().enumerate() {
363 raw_stops[i] = point.stop;
364 }
365 &raw_stops
366 }
367 };
368 let brush_type = match cmd.brush {
369 Brush::Solid(_) => 0,
370 Brush::LinearGradient { .. } => 1,
371 Brush::RadialGradient { .. } => 2,
372 };
373 let gradient_point_count = match cmd.brush {
374 Brush::Solid(_) => 0,
375 Brush::LinearGradient { ref stops, .. }
376 | Brush::RadialGradient { ref stops, .. } => stops.len() as i32,
377 };
378
379 let uniform_buffer = uniform_buffer_cache.write(
380 StaticUniformBuffer::<1024>::new()
381 .with(&ortho)
382 .with(&solid_color)
383 .with_slice(gradient_colors)
384 .with_slice(gradient_stops)
385 .with(&gradient_origin)
386 .with(&gradient_end)
387 .with(&resolution)
388 .with(&cmd.bounds.position)
389 .with(&bounds_max)
390 .with(&is_font_texture)
391 .with(&cmd.opacity)
392 .with(&brush_type)
393 .with(&gradient_point_count),
394 )?;
395
396 let shader = &self.shader;
397 statistics += frame_buffer.draw(
398 &*self.geometry_buffer,
399 viewport,
400 &*self.shader.program,
401 ¶ms,
402 &[ResourceBindGroup {
403 bindings: &[
404 ResourceBinding::texture(diffuse_texture, &shader.diffuse_texture),
405 ResourceBinding::Buffer {
406 buffer: uniform_buffer,
407 binding: BufferLocation::Auto {
408 shader_location: self.shader.uniform_block_index,
409 },
410 data_usage: Default::default(),
411 },
412 ],
413 }],
414 ElementRange::Specific {
415 offset: cmd.triangles.start,
416 count: cmd.triangles.end - cmd.triangles.start,
417 },
418 )?;
419 }
420
421 Ok(statistics)
422 }
423}