Skip to main content

fyrox_graphics_gl/
buffer.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
21use crate::server::GlGraphicsServer;
22use crate::ToGlConstant;
23use fyrox_graphics::buffer::GpuBufferDescriptor;
24use fyrox_graphics::{
25    buffer::{BufferKind, BufferUsage, GpuBufferTrait},
26    error::FrameworkError,
27};
28use glow::HasContext;
29use std::{cell::Cell, rc::Weak};
30
31impl ToGlConstant for BufferKind {
32    fn into_gl(self) -> u32 {
33        match self {
34            BufferKind::Vertex => glow::ARRAY_BUFFER,
35            BufferKind::Index => glow::ELEMENT_ARRAY_BUFFER,
36            BufferKind::Uniform => glow::UNIFORM_BUFFER,
37            BufferKind::PixelRead => glow::PIXEL_PACK_BUFFER,
38            BufferKind::PixelWrite => glow::PIXEL_UNPACK_BUFFER,
39        }
40    }
41}
42
43impl ToGlConstant for BufferUsage {
44    fn into_gl(self) -> u32 {
45        match self {
46            BufferUsage::StaticDraw => glow::STATIC_DRAW,
47            BufferUsage::StaticCopy => glow::STATIC_COPY,
48            BufferUsage::DynamicDraw => glow::DYNAMIC_DRAW,
49            BufferUsage::DynamicCopy => glow::DYNAMIC_COPY,
50            BufferUsage::StreamDraw => glow::STREAM_DRAW,
51            BufferUsage::StreamRead => glow::STREAM_READ,
52            BufferUsage::StreamCopy => glow::STREAM_COPY,
53            BufferUsage::StaticRead => glow::STATIC_READ,
54            BufferUsage::DynamicRead => glow::DYNAMIC_READ,
55        }
56    }
57}
58
59pub struct GlBuffer {
60    pub state: Weak<GlGraphicsServer>,
61    pub id: glow::Buffer,
62    pub size: Cell<usize>,
63    pub kind: BufferKind,
64    pub usage: BufferUsage,
65}
66
67impl GlBuffer {
68    pub fn new(
69        server: &GlGraphicsServer,
70        desc: GpuBufferDescriptor,
71    ) -> Result<Self, FrameworkError> {
72        let GpuBufferDescriptor {
73            #[allow(unused_variables)]
74            name,
75            size,
76            kind,
77            usage,
78        } = desc;
79        server.memory_usage.borrow_mut().buffers += size;
80        unsafe {
81            let gl_kind = kind.into_gl();
82            let gl_usage = usage.into_gl();
83            let id = server.gl.create_buffer()?;
84            server.gl.bind_buffer(gl_kind, Some(id));
85            #[cfg(not(target_arch = "wasm32"))]
86            if server.gl.supports_debug() && server.named_objects.get() {
87                server.gl.object_label(glow::BUFFER, id.0.get(), Some(name));
88            }
89            if size > 0 {
90                server.gl.buffer_data_size(gl_kind, size as i32, gl_usage);
91            }
92            server.gl.bind_buffer(gl_kind, None);
93            Ok(Self {
94                state: server.weak(),
95                id,
96                size: Cell::new(size),
97                kind,
98                usage,
99            })
100        }
101    }
102}
103
104impl Drop for GlBuffer {
105    fn drop(&mut self) {
106        unsafe {
107            if let Some(state) = self.state.upgrade() {
108                state.memory_usage.borrow_mut().buffers -= self.size.get();
109                state.gl.delete_buffer(self.id);
110            }
111        }
112    }
113}
114
115impl GpuBufferTrait for GlBuffer {
116    fn usage(&self) -> BufferUsage {
117        self.usage
118    }
119
120    fn kind(&self) -> BufferKind {
121        self.kind
122    }
123
124    fn size(&self) -> usize {
125        self.size.get()
126    }
127
128    fn write_data(&self, data: &[u8]) -> Result<(), FrameworkError> {
129        if data.is_empty() {
130            return Ok(());
131        }
132
133        let Some(server) = self.state.upgrade() else {
134            return Err(FrameworkError::GraphicsServerUnavailable);
135        };
136
137        let gl_kind = self.kind.into_gl();
138        let gl_usage = self.usage.into_gl();
139
140        unsafe {
141            server.gl.bind_buffer(gl_kind, Some(self.id));
142            if data.len() <= self.size.get() {
143                // Update the data.
144                server.gl.buffer_sub_data_u8_slice(gl_kind, 0, data);
145            } else {
146                let mut memory_usage = server.memory_usage.borrow_mut();
147                memory_usage.buffers -= self.size.get();
148                memory_usage.buffers += data.len();
149
150                // Realloc the internal storage.
151                server.gl.buffer_data_u8_slice(gl_kind, data, gl_usage);
152                self.size.set(data.len());
153            }
154        }
155
156        Ok(())
157    }
158
159    fn read_data(&self, data: &mut [u8]) -> Result<(), FrameworkError> {
160        let Some(server) = self.state.upgrade() else {
161            return Err(FrameworkError::GraphicsServerUnavailable);
162        };
163
164        let gl_kind = self.kind.into_gl();
165
166        unsafe {
167            server.gl.bind_buffer(gl_kind, Some(self.id));
168
169            #[cfg(not(target_arch = "wasm32"))]
170            {
171                let gl_storage =
172                    server
173                        .gl
174                        .map_buffer_range(gl_kind, 0, data.len() as i32, glow::MAP_READ_BIT);
175                assert_ne!(gl_storage, std::ptr::null_mut());
176                std::ptr::copy_nonoverlapping(gl_storage, data.as_mut_ptr(), data.len());
177                server.gl.unmap_buffer(gl_kind);
178            }
179
180            #[cfg(target_arch = "wasm32")]
181            {
182                // The only way to get buffer data on WebGL is to use glGetBufferSubData, there's
183                // no memory mapping in Web due to security reasons.
184                server.gl.get_buffer_sub_data(gl_kind, 0, data);
185            }
186
187            server.gl.bind_buffer(gl_kind, None);
188        }
189
190        Ok(())
191    }
192}