aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/renderer/graphics/mod.rs
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2021-10-05 14:36:31 -0600
committerJosh Rahm <rahm@google.com>2021-10-05 14:36:31 -0600
commit7a209fa45f1f4d07cb4a885e8ea3d03e47cf48ae (patch)
tree026d75fdc19e19952cfba3020c118f24df4ac412 /alacritty/src/renderer/graphics/mod.rs
parent1725e30e144b04e2e2e30efc76eb968c97a0eabf (diff)
parent98fbb3f9285d8c00836e3bcfa6e1e13bf809e2a2 (diff)
downloadr-alacritty-7a209fa45f1f4d07cb4a885e8ea3d03e47cf48ae.tar.gz
r-alacritty-7a209fa45f1f4d07cb4a885e8ea3d03e47cf48ae.tar.bz2
r-alacritty-7a209fa45f1f4d07cb4a885e8ea3d03e47cf48ae.zip
Merge remote-tracking branch 'betaboon/graphics' into experimental
Diffstat (limited to 'alacritty/src/renderer/graphics/mod.rs')
-rw-r--r--alacritty/src/renderer/graphics/mod.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/alacritty/src/renderer/graphics/mod.rs b/alacritty/src/renderer/graphics/mod.rs
new file mode 100644
index 00000000..3fecd549
--- /dev/null
+++ b/alacritty/src/renderer/graphics/mod.rs
@@ -0,0 +1,149 @@
+//! This module implements the functionality to support graphics in the grid.
+
+use std::mem;
+
+use alacritty_terminal::graphics::{ColorType, GraphicData, GraphicId, UpdateQueues};
+use alacritty_terminal::term::SizeInfo;
+
+use log::trace;
+use serde::{Deserialize, Serialize};
+
+use crate::gl;
+use crate::gl::types::*;
+use crate::renderer;
+
+use std::collections::HashMap;
+
+mod draw;
+mod shader;
+
+pub use draw::RenderList;
+
+/// Type for texture names generated in the GPU.
+#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
+pub struct TextureName(GLuint);
+
+// In debug mode, check if the inner value was set to zero, so we can detect if
+// the associated texture was deleted from the GPU.
+#[cfg(debug_assertions)]
+impl Drop for TextureName {
+ fn drop(&mut self) {
+ if self.0 != 0 {
+ log::error!("Texture {} was not deleted.", self.0);
+ }
+ }
+}
+
+/// Texture for a graphic in the grid.
+#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
+pub struct GraphicTexture {
+ /// Texture in the GPU where the graphic pixels are stored.
+ texture: TextureName,
+
+ /// Cell height at the moment graphic was created.
+ ///
+ /// Used to scale it if the user increases or decreases the font size.
+ cell_height: f32,
+
+ /// Width in pixels of the graphic.
+ width: u16,
+
+ /// Height in pixels of the graphic.
+ height: u16,
+}
+
+#[derive(Debug)]
+pub struct GraphicsRenderer {
+ /// Program in the GPU to render graphics.
+ program: shader::GraphicsShaderProgram,
+
+ /// Collection to associate graphic identifiers with their textures.
+ graphic_textures: HashMap<GraphicId, GraphicTexture>,
+}
+
+impl GraphicsRenderer {
+ pub fn new() -> Result<GraphicsRenderer, renderer::Error> {
+ let program = shader::GraphicsShaderProgram::new()?;
+ Ok(GraphicsRenderer { program, graphic_textures: HashMap::default() })
+ }
+
+ /// Run the required actions to apply changes for the graphics in the grid.
+ #[inline]
+ pub fn run_updates(&mut self, update_queues: UpdateQueues, size_info: &SizeInfo) {
+ self.remove_graphics(update_queues.remove_queue);
+ self.upload_pending_graphics(update_queues.pending, size_info);
+ }
+
+ /// Release resources used by removed graphics.
+ fn remove_graphics(&mut self, removed_ids: Vec<GraphicId>) {
+ let mut textures = Vec::with_capacity(removed_ids.len());
+ for id in removed_ids {
+ if let Some(mut graphic_texture) = self.graphic_textures.remove(&id) {
+ // Reset the inner value of TextureName, so the Drop implementation
+ // (in debug mode) can verify that the texture was deleted.
+ textures.push(mem::take(&mut graphic_texture.texture.0));
+ }
+ }
+
+ trace!("Call glDeleteTextures with {} items", textures.len());
+
+ unsafe {
+ gl::DeleteTextures(textures.len() as GLint, textures.as_ptr());
+ }
+ }
+
+ /// Create new textures in the GPU, and upload the pixels to them.
+ fn upload_pending_graphics(&mut self, graphics: Vec<GraphicData>, size_info: &SizeInfo) {
+ for graphic in graphics {
+ let mut texture = 0;
+
+ unsafe {
+ gl::GenTextures(1, &mut texture);
+ trace!("Texture generated: {}", texture);
+
+ gl::BindTexture(gl::TEXTURE_2D, texture);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAX_LEVEL, 0);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
+ gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
+
+ let pixel_format = match graphic.color_type {
+ ColorType::Rgb => gl::RGB,
+ ColorType::Rgba => gl::RGBA,
+ };
+
+ gl::TexImage2D(
+ gl::TEXTURE_2D,
+ 0,
+ gl::RGBA as GLint,
+ graphic.width as GLint,
+ graphic.height as GLint,
+ 0,
+ pixel_format,
+ gl::UNSIGNED_BYTE,
+ graphic.pixels.as_ptr().cast(),
+ );
+
+ gl::BindTexture(gl::TEXTURE_2D, 0);
+ }
+
+ let graphic_texture = GraphicTexture {
+ texture: TextureName(texture),
+ cell_height: size_info.cell_height(),
+ width: graphic.width as u16,
+ height: graphic.height as u16,
+ };
+
+ self.graphic_textures.insert(graphic.id, graphic_texture);
+ }
+ }
+
+ /// Draw graphics in the display.
+ #[inline]
+ pub fn draw(&mut self, render_list: RenderList, size_info: &SizeInfo) {
+ if !render_list.is_empty() {
+ render_list.draw(self, size_info);
+ }
+ }
+}