fyrox_graphics/server.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#![warn(missing_docs)]
22
23//! Graphics server is an abstraction layer over various graphics APIs used on different platforms
24//! supported by the engine.
25
26use crate::{
27 buffer::{GpuBuffer, GpuBufferDescriptor},
28 error::FrameworkError,
29 framebuffer::{Attachment, GpuFrameBuffer},
30 geometry_buffer::{GpuGeometryBuffer, GpuGeometryBufferDescriptor},
31 gpu_program::{GpuProgram, GpuShader, ShaderKind, ShaderResourceDefinition},
32 gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
33 query::GpuQuery,
34 read_buffer::GpuAsyncReadBuffer,
35 sampler::{GpuSampler, GpuSamplerDescriptor},
36 stats::PipelineStatistics,
37 PolygonFace, PolygonFillMode,
38};
39use fyrox_core::define_as_any_trait;
40use std::fmt::{Display, Formatter};
41use std::rc::{Rc, Weak};
42
43/// Graphics server capabilities.
44#[derive(Debug)]
45pub struct ServerCapabilities {
46 /// The maximum size in basic machine units of a uniform block, which must be at least 16384.
47 pub max_uniform_block_size: usize,
48 /// The minimum required alignment for uniform buffer sizes and offset. The initial value is 1.
49 pub uniform_buffer_offset_alignment: usize,
50 /// The maximum, absolute value of the texture level-of-detail bias. The value must be at least
51 /// 2.0.
52 pub max_lod_bias: f32,
53}
54
55/// Contains information about used memory per each category of GPU resource. This is not precise
56/// data; it only calculates total requested memory by the user of a graphics server and does not
57/// include additional memory overhead in the video driver. Yet this information could still be
58/// useful.
59#[derive(Default, Debug, Clone)]
60pub struct ServerMemoryUsage {
61 /// Total number of bytes used by all textures (including render targets).
62 pub textures: usize,
63 /// Total number of bytes used by all buffers (vertex, index, uniform, etc.)
64 pub buffers: usize,
65}
66
67impl Display for ServerMemoryUsage {
68 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
69 const MEGABYTE: f32 = 1024.0 * 1024.0;
70 write!(
71 f,
72 "Textures: {:.3} Mb\nBuffers: {:.3} Mb",
73 self.textures as f32 / MEGABYTE,
74 self.buffers as f32 / MEGABYTE
75 )
76 }
77}
78
79/// A shared reference to a graphics server.
80pub type SharedGraphicsServer = Rc<dyn GraphicsServer>;
81
82define_as_any_trait!(GraphicsServerAsAny => GraphicsServer);
83
84/// A named debug scope RAII object that automatically exits the scope on drop.
85pub struct RenderingScope {
86 server: Weak<dyn GraphicsServer>,
87}
88
89impl Drop for RenderingScope {
90 fn drop(&mut self) {
91 if let Some(server) = self.server.upgrade() {
92 server.pop_debug_group();
93 }
94 }
95}
96
97/// Graphics server is an abstraction layer over various graphics APIs used on different platforms
98/// supported by the engine. Such abstraction layer tries to provide more or less high-level and
99/// unified interface, that can be used to build graphics pipelines quickly and more or less efficiently.
100///
101/// Low-level GAPI-specific optimizations could be performed using direct access to the underlying API,
102/// by downcasting to a specific type.
103pub trait GraphicsServer: GraphicsServerAsAny {
104 /// Creates a GPU buffer with the given size and kind. Usage is a hint to the video driver
105 /// that allows to perform some potential performance optimizations.
106 fn create_buffer(&self, desc: GpuBufferDescriptor) -> Result<GpuBuffer, FrameworkError>;
107
108 /// Creates a new GPU texture using the given descriptor.
109 fn create_texture(&self, desc: GpuTextureDescriptor) -> Result<GpuTexture, FrameworkError>;
110
111 /// Creates a new GPU sampler that can be used to sample texels from a texture.
112 fn create_sampler(&self, desc: GpuSamplerDescriptor) -> Result<GpuSampler, FrameworkError>;
113
114 /// Creates a new frame buffer using the given depth and color attachments. Depth attachment
115 /// not exist, but there must be at least one color attachment of a format that supports rendering.
116 fn create_frame_buffer(
117 &self,
118 depth_attachment: Option<Attachment>,
119 color_attachments: Vec<Attachment>,
120 ) -> Result<GpuFrameBuffer, FrameworkError>;
121
122 /// Creates a frame buffer that "connected" to the final image that will be displayed to the
123 /// screen.
124 fn back_buffer(&self) -> GpuFrameBuffer;
125
126 /// Creates a new GPU query, that can perform asynchronous data fetching from GPU. Usually it
127 /// is used to create occlusion queries.
128 fn create_query(&self) -> Result<GpuQuery, FrameworkError>;
129
130 /// Creates a new named GPU shader. The name could be used for debugging purposes. The
131 /// implementation of graphics server will generate proper resource bindings in the shader code
132 /// for you.
133 fn create_shader(
134 &self,
135 name: String,
136 kind: ShaderKind,
137 source: String,
138 resources: &[ShaderResourceDefinition],
139 line_offset: isize,
140 ) -> Result<GpuShader, FrameworkError>;
141
142 /// Creates a new named GPU program using source code of both vertex and fragment shaders. The
143 /// name could be used for debugging purposes. The implementation of graphics server will generate
144 /// proper resource bindings in the shader code for you.
145 fn create_program(
146 &self,
147 name: &str,
148 vertex_source: String,
149 vertex_source_line_offset: isize,
150 fragment_source: String,
151 fragment_source_line_offset: isize,
152 resources: &[ShaderResourceDefinition],
153 ) -> Result<GpuProgram, FrameworkError>;
154
155 /// Creates a new named GPU program using a pair of vertex and fragment shaders. The name could
156 /// be used for debugging purposes. The implementation of graphics server will generate proper
157 /// resource bindings in the shader code for you.
158 fn create_program_from_shaders(
159 &self,
160 name: &str,
161 vertex_shader: &GpuShader,
162 fragment_shader: &GpuShader,
163 resources: &[ShaderResourceDefinition],
164 ) -> Result<GpuProgram, FrameworkError>;
165
166 /// Creates a new read-back buffer, that can be used to obtain texture data from GPU. It can be
167 /// used to read rendering result from GPU to CPU memory and save the result to disk.
168 fn create_async_read_buffer(
169 &self,
170 name: &str,
171 pixel_size: usize,
172 pixel_count: usize,
173 ) -> Result<GpuAsyncReadBuffer, FrameworkError>;
174
175 /// Creates a new geometry buffer, which consists of one or more vertex buffers and only one
176 /// element buffer. Geometry buffer could be considered as a complex mesh storage allocated on
177 /// GPU.
178 fn create_geometry_buffer(
179 &self,
180 desc: GpuGeometryBufferDescriptor,
181 ) -> Result<GpuGeometryBuffer, FrameworkError>;
182
183 /// Creates a weak reference to the shared graphics server.
184 fn weak(&self) -> Weak<dyn GraphicsServer>;
185
186 /// Sends all scheduled GPU command buffers for execution on GPU without waiting for a certain
187 /// threshold.
188 fn flush(&self);
189
190 /// Waits until all the scheduled GPU commands are fully executed. This is blocking operation, and
191 /// it blocks the current thread until all the commands are fully executed.
192 fn finish(&self);
193
194 /// Unbinds the all bound resources from the graphics pipeline.
195 fn invalidate_resource_bindings_cache(&self);
196
197 /// Returns GPU pipeline statistics. See [`PipelineStatistics`] for more info.
198 fn pipeline_statistics(&self) -> PipelineStatistics;
199
200 /// Swaps the front and back buffers and thus presenting the final image on screen. There could
201 /// be more than two buffers, and it is up to the graphics server implementation to choose the
202 /// right amount, but it can't be less than two.
203 fn swap_buffers(&self) -> Result<(), FrameworkError>;
204
205 /// Notifies the graphics server that the size of the back buffer has changed. It has very limited
206 /// use and there are very few platforms (Linux with Wayland mostly) that needs this function to
207 /// be called.
208 fn set_frame_size(&self, new_size: (u32, u32));
209
210 /// Returns current capabilities of the graphics server. See [`ServerCapabilities`] for more info.
211 fn capabilities(&self) -> ServerCapabilities;
212
213 /// Sets current polygon fill mode for front faces, back faces, or both.
214 /// The mode of front faces is controlled separately from the mode of back faces,
215 /// and `polygon_face` determines which mode is set by this method.
216 /// See [`PolygonFace`] and [`PolygonFillMode`] docs for more info.
217 fn set_polygon_fill_mode(&self, polygon_face: PolygonFace, polygon_fill_mode: PolygonFillMode);
218
219 /// Generates mipmaps for the given texture. Graphics server implementation can pick any desired
220 /// way of mipmaps generation, depending on the underlying GAPI capabilities.
221 fn generate_mipmap(&self, texture: &GpuTexture);
222
223 /// Fetches the total amount of memory used by the graphics server.
224 fn memory_usage(&self) -> ServerMemoryUsage;
225
226 /// Begins a new named debug group. It is recommended to use [`Self::begin_scope`] instead,
227 /// so that the compiler will manage the scope lifetime for your correctly. Otherwise, a forgotten
228 /// call to [`Self::pop_debug_group`] may cause stack overflow or underflow errors.
229 fn push_debug_group(&self, name: &str);
230
231 /// Ends the current debug group.
232 fn pop_debug_group(&self);
233
234 /// Begins a new debug scope by creating a temporary object that automatically exits the
235 /// scope on drop. The scope could be created like so: `let _debug_scope = server.begin_scope("ScopeName");`
236 /// Note the `let _debug_scope = ...` part - it is important to keep the produced object alive
237 /// until the end of the current semantic scope. Do not call this method like so:
238 /// `server.begin_scope("VisibilityTest");` because it will enter and leave the scope instantly.
239 fn begin_scope(&self, name: &str) -> RenderingScope {
240 self.push_debug_group(name);
241
242 RenderingScope {
243 server: self.weak(),
244 }
245 }
246
247 /// A shortcut for [`Self::create_texture`], that creates a rectangular texture with the given
248 /// size and pixel kind.
249 fn create_2d_render_target(
250 &self,
251 name: &str,
252 pixel_kind: PixelKind,
253 width: usize,
254 height: usize,
255 ) -> Result<GpuTexture, FrameworkError> {
256 self.create_texture(GpuTextureDescriptor {
257 name,
258 kind: GpuTextureKind::Rectangle { width, height },
259 pixel_kind,
260 ..Default::default()
261 })
262 }
263
264 /// A shortcut for [`Self::create_texture`], that creates a cube texture with the given
265 /// size and pixel kind.
266 fn create_cube_render_target(
267 &self,
268 name: &str,
269 pixel_kind: PixelKind,
270 size: usize,
271 ) -> Result<GpuTexture, FrameworkError> {
272 self.create_texture(GpuTextureDescriptor {
273 name,
274 kind: GpuTextureKind::Cube { size },
275 pixel_kind,
276 ..Default::default()
277 })
278 }
279}