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 graphics::{
26 error::FrameworkError,
27 gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
28 server::GraphicsServer,
29 },
30};
31
32/// Generic, texture-based, storage for matrices with somewhat unlimited capacity.
33///
34/// ## Motivation
35///
36/// Why it uses textures instead of SSBO? This could be done with SSBO, but it is not available on macOS because
37/// SSBO was added only in OpenGL 4.3, but macOS support up to OpenGL 4.1.
38pub struct MatrixStorage {
39 texture: GpuTexture,
40 matrices: Vec<Matrix4<f32>>,
41}
42
43impl MatrixStorage {
44 /// Creates a new matrix storage.
45 pub fn new(server: &dyn GraphicsServer) -> Result<Self, FrameworkError> {
46 let identity = [Matrix4::<f32>::identity()];
47 Ok(Self {
48 texture: server.create_texture(GpuTextureDescriptor {
49 name: "MatrixStorage",
50 kind: GpuTextureKind::Rectangle {
51 width: 4,
52 height: 1,
53 },
54 pixel_kind: PixelKind::RGBA32F,
55 data: Some(crate::core::array_as_u8_slice(&identity)),
56 ..Default::default()
57 })?,
58 matrices: Default::default(),
59 })
60 }
61
62 /// Returns matrix storage texture.
63 pub fn texture(&self) -> &GpuTexture {
64 &self.texture
65 }
66
67 /// Updates contents of the internal texture with provided matrices.
68 pub fn upload(
69 &mut self,
70 matrices: impl Iterator<Item = Matrix4<f32>>,
71 ) -> Result<(), FrameworkError> {
72 self.matrices.clear();
73 self.matrices.extend(matrices);
74
75 // Select width for the texture by restricting width at 1024 pixels.
76 let matrices_tex_size = 1024;
77 let actual_matrices_pixel_count = self.matrices.len() * 4;
78 let matrices_w = actual_matrices_pixel_count.min(matrices_tex_size);
79 let matrices_h = (actual_matrices_pixel_count as f32 / matrices_w as f32)
80 .ceil()
81 .max(1.0) as usize;
82 // Pad data to actual size.
83 for _ in 0..(((matrices_w * matrices_h) - actual_matrices_pixel_count) / 4) {
84 self.matrices.push(Default::default());
85 }
86
87 // Upload to GPU.
88 if matrices_w != 0 && matrices_h != 0 {
89 self.texture.set_data(
90 GpuTextureKind::Rectangle {
91 width: matrices_w,
92 height: matrices_h,
93 },
94 PixelKind::RGBA32F,
95 1,
96 Some(crate::core::array_as_u8_slice(&self.matrices)),
97 )?;
98 }
99
100 Ok(())
101 }
102}