fyrox_impl/renderer/
storage.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Generic, texture-based, storage for matrices with somewhat unlimited capacity.
22
23use crate::{
24    core::algebra::Matrix4,
25    renderer::framework::{
26        error::FrameworkError,
27        gpu_texture::{
28            GpuTexture, GpuTextureDescriptor, GpuTextureKind, MagnificationFilter,
29            MinificationFilter, PixelKind, WrapMode,
30        },
31        server::GraphicsServer,
32    },
33};
34use std::{cell::RefCell, rc::Rc};
35
36/// Generic, texture-based, storage for matrices with somewhat unlimited capacity.
37///
38/// ## Motivation
39///
40/// Why it uses textures instead of SSBO? This could be done with SSBO, but it is not available on macOS because
41/// SSBO was added only in OpenGL 4.3, but macOS support up to OpenGL 4.1.
42pub struct MatrixStorage {
43    texture: Rc<RefCell<dyn GpuTexture>>,
44    matrices: Vec<Matrix4<f32>>,
45}
46
47impl MatrixStorage {
48    /// Creates a new matrix storage.
49    pub fn new(server: &dyn GraphicsServer) -> Result<Self, FrameworkError> {
50        let identity = [Matrix4::<f32>::identity()];
51        Ok(Self {
52            texture: server.create_texture(GpuTextureDescriptor {
53                kind: GpuTextureKind::Rectangle {
54                    width: 4,
55                    height: 1,
56                },
57                pixel_kind: PixelKind::RGBA32F,
58                min_filter: MinificationFilter::Nearest,
59                mag_filter: MagnificationFilter::Nearest,
60                s_wrap_mode: WrapMode::ClampToEdge,
61                t_wrap_mode: WrapMode::ClampToEdge,
62                r_wrap_mode: WrapMode::ClampToEdge,
63                data: Some(crate::core::array_as_u8_slice(&identity)),
64                ..Default::default()
65            })?,
66            matrices: Default::default(),
67        })
68    }
69
70    /// Returns matrix storage texture.
71    pub fn texture(&self) -> &Rc<RefCell<dyn GpuTexture>> {
72        &self.texture
73    }
74
75    /// Updates contents of the internal texture with provided matrices.
76    pub fn upload(
77        &mut self,
78        matrices: impl Iterator<Item = Matrix4<f32>>,
79    ) -> Result<(), FrameworkError> {
80        self.matrices.clear();
81        self.matrices.extend(matrices);
82
83        // Select width for the texture by restricting width at 1024 pixels.
84        let matrices_tex_size = 1024;
85        let actual_matrices_pixel_count = self.matrices.len() * 4;
86        let matrices_w = actual_matrices_pixel_count.min(matrices_tex_size);
87        let matrices_h = (actual_matrices_pixel_count as f32 / matrices_w as f32)
88            .ceil()
89            .max(1.0) as usize;
90        // Pad data to actual size.
91        for _ in 0..(((matrices_w * matrices_h) - actual_matrices_pixel_count) / 4) {
92            self.matrices.push(Default::default());
93        }
94
95        // Upload to GPU.
96        if matrices_w != 0 && matrices_h != 0 {
97            self.texture.borrow_mut().set_data(
98                GpuTextureKind::Rectangle {
99                    width: matrices_w,
100                    height: matrices_h,
101                },
102                PixelKind::RGBA32F,
103                1,
104                Some(crate::core::array_as_u8_slice(&self.matrices)),
105            )?;
106        }
107
108        Ok(())
109    }
110}