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