1use crate::{
24 asset::{manager::ResourceManager, untyped::ResourceKind},
25 core::{
26 algebra::{Matrix4, Vector2, Vector4},
27 arrayvec::ArrayVec,
28 color::Color,
29 math::Rect,
30 some_or_continue,
31 sstorage::ImmutableString,
32 },
33 graphics::{
34 buffer::BufferUsage,
35 error::FrameworkError,
36 framebuffer::{GpuFrameBuffer, ResourceBindGroup, ResourceBinding},
37 geometry_buffer::{
38 AttributeDefinition, AttributeKind, ElementsDescriptor, GpuGeometryBuffer,
39 GpuGeometryBufferDescriptor, VertexBufferData, VertexBufferDescriptor,
40 },
41 gpu_program::ShaderResourceKind,
42 server::GraphicsServer,
43 uniform::StaticUniformBuffer,
44 BlendFactor, BlendFunc, BlendParameters, ColorMask, CompareFunc, DrawParameters,
45 ElementRange, ScissorBox, StencilFunc,
46 },
47 gui::{
48 brush::Brush,
49 draw::Command,
50 draw::{CommandTexture, DrawingContext},
51 },
52 renderer::{
53 bundle::{self, make_texture_binding},
54 cache::{
55 shader::{binding, property, PropertyGroup, RenderMaterial, ShaderCache},
56 uniform::{UniformBlockLocation, UniformBufferCache, UniformMemoryAllocator},
57 },
58 resources::RendererResources,
59 RenderPassStatistics, TextureCache,
60 },
61 resource::texture::{Texture, TextureKind, TexturePixelKind, TextureResource},
62};
63use fyrox_ui::UserInterface;
64use uuid::Uuid;
65
66pub struct UiRenderer {
68 geometry_buffer: GpuGeometryBuffer,
69 clipping_geometry_buffer: GpuGeometryBuffer,
70}
71
72pub struct UiRenderContext<'a, 'b, 'c> {
74 pub server: &'a dyn GraphicsServer,
76 pub viewport: Rect<i32>,
78 pub frame_buffer: &'b GpuFrameBuffer,
80 pub frame_width: f32,
82 pub frame_height: f32,
84 pub drawing_context: &'c DrawingContext,
86 pub renderer_resources: &'a RendererResources,
88 pub texture_cache: &'a mut TextureCache,
90 pub uniform_buffer_cache: &'a mut UniformBufferCache,
92 pub render_pass_cache: &'a mut ShaderCache,
94 pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
96 pub resource_manager: &'a ResourceManager,
98}
99
100pub struct UiRenderInfo<'a> {
102 pub ui: &'a UserInterface,
104 pub render_target: Option<TextureResource>,
107 pub clear_color: Color,
110 pub resource_manager: &'a ResourceManager,
112}
113
114fn write_uniform_blocks(
115 ortho: &Matrix4<f32>,
116 resolution: Vector2<f32>,
117 commands: &[Command],
118 uniform_memory_allocator: &mut UniformMemoryAllocator,
119) -> Vec<ArrayVec<(usize, UniformBlockLocation), 8>> {
120 let mut block_locations = Vec::with_capacity(commands.len());
121
122 for cmd in commands {
123 let mut command_block_locations = ArrayVec::<(usize, UniformBlockLocation), 8>::new();
124
125 let material_data_guard = cmd.material.data_ref();
126 let material = some_or_continue!(material_data_guard.as_loaded_ref());
127 let shader_data_guard = material.shader().data_ref();
128 let shader = some_or_continue!(shader_data_guard.as_loaded_ref());
129
130 for resource in shader.definition.resources.iter() {
131 if resource.name.as_str() == "fyrox_widgetData" {
132 let mut raw_stops = [0.0; 16];
133 let mut raw_colors = [Vector4::default(); 16];
134 let bounds_max = cmd.bounds.right_bottom_corner();
135
136 let (gradient_origin, gradient_end) = match cmd.brush {
137 Brush::Solid(_) => (Vector2::default(), Vector2::default()),
138 Brush::LinearGradient { from, to, .. } => (from, to),
139 Brush::RadialGradient { center, .. } => (center, Vector2::default()),
140 };
141
142 let solid_color = match cmd.brush {
143 Brush::Solid(color) => color,
144 _ => Color::WHITE,
145 };
146 let gradient_colors = match cmd.brush {
147 Brush::Solid(_) => &raw_colors,
148 Brush::LinearGradient { ref stops, .. }
149 | Brush::RadialGradient { ref stops, .. } => {
150 for (i, point) in stops.iter().enumerate() {
151 raw_colors[i] = point.color.as_frgba();
152 }
153 &raw_colors
154 }
155 };
156 let gradient_stops = match cmd.brush {
157 Brush::Solid(_) => &raw_stops,
158 Brush::LinearGradient { ref stops, .. }
159 | Brush::RadialGradient { ref stops, .. } => {
160 for (i, point) in stops.iter().enumerate() {
161 raw_stops[i] = point.stop;
162 }
163 &raw_stops
164 }
165 };
166 let brush_type = match cmd.brush {
167 Brush::Solid(_) => 0,
168 Brush::LinearGradient { .. } => 1,
169 Brush::RadialGradient { .. } => 2,
170 };
171 let gradient_point_count = match cmd.brush {
172 Brush::Solid(_) => 0,
173 Brush::LinearGradient { ref stops, .. }
174 | Brush::RadialGradient { ref stops, .. } => stops.len() as i32,
175 };
176
177 let is_font_texture = matches!(cmd.texture, CommandTexture::Font { .. });
178
179 let buffer = StaticUniformBuffer::<2048>::new()
180 .with(ortho)
181 .with(&cmd.transform)
182 .with(&solid_color)
183 .with(gradient_colors.as_slice())
184 .with(gradient_stops.as_slice())
185 .with(&gradient_origin)
186 .with(&gradient_end)
187 .with(&resolution)
188 .with(&cmd.bounds.position)
189 .with(&bounds_max)
190 .with(&is_font_texture)
191 .with(&cmd.opacity)
192 .with(&brush_type)
193 .with(&gradient_point_count);
194
195 command_block_locations
196 .push((resource.binding, uniform_memory_allocator.allocate(buffer)));
197 } else if let ShaderResourceKind::PropertyGroup(ref shader_property_group) =
198 resource.kind
199 {
200 let mut buf = StaticUniformBuffer::<16384>::new();
201
202 if let Some(material_property_group) =
203 material.property_group_ref(resource.name.clone())
204 {
205 bundle::write_with_material(
206 shader_property_group,
207 material_property_group,
208 |c, n| c.property_ref(n.clone()).map(|p| p.as_ref()),
209 &mut buf,
210 );
211 } else {
212 bundle::write_shader_values(shader_property_group, &mut buf)
213 }
214
215 command_block_locations
216 .push((resource.binding, uniform_memory_allocator.allocate(buf)));
217 }
218 }
219
220 block_locations.push(command_block_locations);
221 }
222
223 block_locations
224}
225
226impl UiRenderer {
227 pub(in crate::renderer) fn new(server: &dyn GraphicsServer) -> Result<Self, FrameworkError> {
228 let geometry_buffer_desc = GpuGeometryBufferDescriptor {
229 name: "UiGeometryBuffer",
230 elements: ElementsDescriptor::Triangles(&[]),
231 buffers: &[VertexBufferDescriptor {
232 usage: BufferUsage::DynamicDraw,
233 attributes: &[
234 AttributeDefinition {
235 location: 0,
236 kind: AttributeKind::Float,
237 component_count: 2,
238 normalized: false,
239 divisor: 0,
240 },
241 AttributeDefinition {
242 location: 1,
243 kind: AttributeKind::Float,
244 component_count: 2,
245 normalized: false,
246 divisor: 0,
247 },
248 AttributeDefinition {
249 location: 2,
250 kind: AttributeKind::UnsignedByte,
251 component_count: 4,
252 normalized: true, divisor: 0,
254 },
255 ],
256 data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
257 }],
258 usage: BufferUsage::DynamicDraw,
259 };
260
261 let clipping_geometry_buffer_desc = GpuGeometryBufferDescriptor {
262 name: "UiClippingGeometryBuffer",
263 elements: ElementsDescriptor::Triangles(&[]),
264 buffers: &[VertexBufferDescriptor {
265 usage: BufferUsage::DynamicDraw,
266 attributes: &[
267 AttributeDefinition {
269 location: 0,
270 kind: AttributeKind::Float,
271 component_count: 2,
272 normalized: false,
273 divisor: 0,
274 },
275 ],
276 data: VertexBufferData::new::<crate::gui::draw::Vertex>(None),
277 }],
278 usage: BufferUsage::DynamicDraw,
279 };
280
281 Ok(Self {
282 geometry_buffer: server.create_geometry_buffer(geometry_buffer_desc)?,
283 clipping_geometry_buffer: server
284 .create_geometry_buffer(clipping_geometry_buffer_desc)?,
285 })
286 }
287
288 pub fn render(
290 &mut self,
291 args: UiRenderContext,
292 ) -> Result<RenderPassStatistics, FrameworkError> {
293 let UiRenderContext {
294 server,
295 viewport,
296 frame_buffer,
297 frame_width,
298 frame_height,
299 drawing_context,
300 renderer_resources,
301 texture_cache,
302 uniform_buffer_cache,
303 render_pass_cache,
304 uniform_memory_allocator,
305 resource_manager,
306 } = args;
307
308 let mut statistics = RenderPassStatistics::default();
309
310 self.geometry_buffer
311 .set_buffer_data_of_type(0, drawing_context.get_vertices());
312 self.geometry_buffer
313 .set_triangles(drawing_context.get_triangles());
314
315 let ortho = Matrix4::new_orthographic(0.0, frame_width, frame_height, 0.0, -1.0, 1.0);
316 let resolution = Vector2::new(frame_width, frame_height);
317
318 let uniform_blocks = write_uniform_blocks(
319 &ortho,
320 resolution,
321 drawing_context.get_commands(),
322 uniform_memory_allocator,
323 );
324
325 uniform_memory_allocator.upload(server)?;
326
327 for (cmd, command_uniform_blocks) in
328 drawing_context.get_commands().iter().zip(uniform_blocks)
329 {
330 let mut clip_bounds = cmd.clip_bounds;
331 clip_bounds.position.x = clip_bounds.position.x.floor();
332 clip_bounds.position.y = clip_bounds.position.y.floor();
333 clip_bounds.size.x = clip_bounds.size.x.ceil();
334 clip_bounds.size.y = clip_bounds.size.y.ceil();
335
336 let scissor_box = Some(ScissorBox {
337 x: clip_bounds.position.x as i32,
338 y: viewport.size.y - (clip_bounds.position.y + clip_bounds.size.y) as i32,
340 width: clip_bounds.size.x as i32,
341 height: clip_bounds.size.y as i32,
342 });
343
344 let mut stencil_test = None;
345
346 if let Some(clipping_geometry) = cmd.clipping_geometry.as_ref() {
349 frame_buffer.clear(viewport, None, None, Some(0));
350
351 self.clipping_geometry_buffer
352 .set_buffer_data_of_type(0, &clipping_geometry.vertex_buffer);
353 self.clipping_geometry_buffer
354 .set_triangles(&clipping_geometry.triangle_buffer);
355
356 let properties = PropertyGroup::from([
358 property("projectionMatrix", &ortho),
359 property("worldMatrix", &cmd.transform),
360 ]);
361 let material = RenderMaterial::from([binding("properties", &properties)]);
362 statistics += renderer_resources.shaders.ui.run_pass(
363 1,
364 &ImmutableString::new("Clip"),
365 frame_buffer,
366 &self.geometry_buffer,
367 viewport,
368 &material,
369 uniform_buffer_cache,
370 Default::default(),
371 None,
372 )?;
373
374 stencil_test = Some(StencilFunc {
376 func: CompareFunc::Equal,
377 ref_value: 1,
378 ..Default::default()
379 });
380 }
381
382 let params = DrawParameters {
383 cull_face: None,
384 color_write: ColorMask::all(true),
385 depth_write: false,
386 stencil_test,
387 depth_test: None,
388 blend: Some(BlendParameters {
389 func: BlendFunc::new(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha),
390 ..Default::default()
391 }),
392 stencil_op: Default::default(),
393 scissor_box,
394 };
395
396 let element_range = ElementRange::Specific {
397 offset: cmd.triangles.start,
398 count: cmd.triangles.end - cmd.triangles.start,
399 };
400
401 let material_data_guard = cmd.material.data_ref();
402 let material = some_or_continue!(material_data_guard.as_loaded_ref());
403
404 if let Some(render_pass_container) = render_pass_cache.get(server, material.shader()) {
405 let shader_data_guard = material.shader().data_ref();
406 let shader = some_or_continue!(shader_data_guard.as_loaded_ref());
407
408 let render_pass = render_pass_container.get(&ImmutableString::new("Forward"))?;
409
410 let mut resource_bindings = ArrayVec::<ResourceBinding, 32>::new();
411
412 for resource in shader.definition.resources.iter() {
413 match resource.kind {
414 ShaderResourceKind::Texture { fallback, .. } => {
415 if resource.name.as_str() == "fyrox_widgetTexture" {
416 let mut diffuse_texture = (
417 &renderer_resources.white_dummy,
418 &renderer_resources.linear_wrap_sampler,
419 );
420
421 match &cmd.texture {
422 CommandTexture::Font {
423 font,
424 page_index,
425 height,
426 } => {
427 if let Some(font) = font.state().data() {
428 let page_size = font.page_size() as u32;
429 if let Some(page) = font
430 .atlases
431 .get_mut(height)
432 .and_then(|atlas| atlas.pages.get_mut(*page_index))
433 {
434 if page.texture.is_none() || page.modified {
435 if let Some(details) = Texture::from_bytes(
436 TextureKind::Rectangle {
437 width: page_size,
438 height: page_size,
439 },
440 TexturePixelKind::R8,
441 page.pixels.clone(),
442 ) {
443 page.texture = Some(
444 TextureResource::new_ok(
445 Uuid::new_v4(),
446 ResourceKind::Embedded,
447 details,
448 )
449 .into(),
450 );
451 page.modified = false;
452 }
453 }
454 if let Some(texture) = texture_cache.get(
455 server,
456 resource_manager,
457 &page
458 .texture
459 .as_ref()
460 .unwrap()
461 .try_cast::<Texture>()
462 .unwrap(),
463 ) {
464 diffuse_texture = (
465 &texture.gpu_texture,
466 &texture.gpu_sampler,
467 );
468 }
469 }
470 }
471 }
472 CommandTexture::Texture(texture) => {
473 if let Some(texture) =
474 texture_cache.get(server, resource_manager, texture)
475 {
476 diffuse_texture =
477 (&texture.gpu_texture, &texture.gpu_sampler);
478 }
479 }
480 _ => (),
481 }
482
483 resource_bindings.push(ResourceBinding::texture(
484 diffuse_texture.0,
485 diffuse_texture.1,
486 resource.binding,
487 ))
488 } else {
489 resource_bindings.push(make_texture_binding(
490 server,
491 material,
492 resource,
493 renderer_resources,
494 fallback,
495 resource_manager,
496 texture_cache,
497 ))
498 }
499 }
500 ShaderResourceKind::PropertyGroup(_) => {
501 if let Some((_, block_location)) = command_uniform_blocks
502 .iter()
503 .find(|(binding, _)| *binding == resource.binding)
504 {
505 resource_bindings.push(
506 uniform_memory_allocator
507 .block_to_binding(*block_location, resource.binding),
508 );
509 }
510 }
511 }
512 }
513
514 statistics += frame_buffer.draw(
515 &self.geometry_buffer,
516 viewport,
517 &render_pass.program,
518 ¶ms,
519 &[ResourceBindGroup {
520 bindings: &resource_bindings,
521 }],
522 element_range,
523 )?;
524 }
525 }
526
527 Ok(statistics)
528 }
529}