2016-06-30 03:56:12 +00:00
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2016-06-06 21:31:12 +00:00
|
|
|
use std::collections::HashMap;
|
2016-06-04 17:54:33 +00:00
|
|
|
use std::fs::File;
|
2017-10-15 01:58:19 +00:00
|
|
|
use std::hash::BuildHasherDefault;
|
2016-06-04 17:54:33 +00:00
|
|
|
use std::io::{self, Read};
|
2016-02-26 04:59:21 +00:00
|
|
|
use std::mem::size_of;
|
2016-06-09 15:30:55 +00:00
|
|
|
use std::path::{PathBuf};
|
2016-02-26 04:59:21 +00:00
|
|
|
use std::ptr;
|
2016-10-23 22:37:06 +00:00
|
|
|
use std::sync::mpsc;
|
2017-08-30 19:34:23 +00:00
|
|
|
use std::time::Duration;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-09 15:30:55 +00:00
|
|
|
use cgmath;
|
2017-01-13 18:03:51 +00:00
|
|
|
use fnv::FnvHasher;
|
2016-12-31 19:17:02 +00:00
|
|
|
use font::{self, Rasterizer, Rasterize, RasterizedGlyph, FontDesc, GlyphKey, FontKey};
|
2016-02-26 04:59:21 +00:00
|
|
|
use gl::types::*;
|
2016-06-03 03:27:07 +00:00
|
|
|
use gl;
|
2017-01-07 00:26:31 +00:00
|
|
|
use index::{Line, Column, RangeInclusive};
|
2017-08-30 19:34:23 +00:00
|
|
|
use notify::{Watcher, watcher, RecursiveMode, DebouncedEvent};
|
2016-12-10 05:10:49 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
use config::{self, Config, Delta};
|
2017-02-11 20:49:40 +00:00
|
|
|
use term::{self, cell, RenderableCell};
|
2017-01-13 18:03:51 +00:00
|
|
|
use window::{Size, Pixels};
|
2016-06-03 03:27:07 +00:00
|
|
|
|
2017-01-13 18:03:51 +00:00
|
|
|
use Rgb;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
// Shader paths for live reload
|
2016-06-04 17:54:33 +00:00
|
|
|
static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl");
|
|
|
|
static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
// Shader source which is used when live-shader-reload feature is disable
|
|
|
|
static TEXT_SHADER_F: &'static str = include_str!(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl")
|
|
|
|
);
|
|
|
|
static TEXT_SHADER_V: &'static str = include_str!(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl")
|
|
|
|
);
|
|
|
|
|
2016-12-17 06:13:51 +00:00
|
|
|
/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory
|
2016-06-06 21:31:12 +00:00
|
|
|
pub trait LoadGlyph {
|
|
|
|
/// Load the rasterized glyph into GPU memory
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph;
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
/// Clear any state accumulated from previous loaded glyphs
|
|
|
|
///
|
|
|
|
/// This can, for instance, be used to reset the texture Atlas.
|
|
|
|
fn clear(&mut self);
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 22:37:06 +00:00
|
|
|
enum Msg {
|
|
|
|
ShaderReload,
|
|
|
|
}
|
|
|
|
|
2017-01-02 03:19:15 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
ShaderCreation(ShaderCreationError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::error::Error for Error {
|
|
|
|
fn cause(&self) -> Option<&::std::error::Error> {
|
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => Some(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => err.description(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => {
|
|
|
|
write!(f, "There was an error initializing the shaders: {}", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ShaderCreationError> for Error {
|
|
|
|
fn from(val: ShaderCreationError) -> Error {
|
|
|
|
Error::ShaderCreation(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
/// Text drawing program
|
|
|
|
///
|
|
|
|
/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
|
2016-06-05 04:26:28 +00:00
|
|
|
#[derive(Debug)]
|
2016-06-06 20:20:35 +00:00
|
|
|
pub struct ShaderProgram {
|
|
|
|
// Program id
|
|
|
|
id: GLuint,
|
|
|
|
|
|
|
|
/// projection matrix uniform
|
|
|
|
u_projection: GLint,
|
|
|
|
|
|
|
|
/// Terminal dimensions (pixels)
|
|
|
|
u_term_dim: GLint,
|
|
|
|
|
|
|
|
/// Cell dimensions (pixels)
|
|
|
|
u_cell_dim: GLint,
|
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
/// Visual bell
|
|
|
|
u_visual_bell: GLint,
|
|
|
|
|
2016-06-06 23:54:15 +00:00
|
|
|
/// Background pass flag
|
|
|
|
///
|
|
|
|
/// Rendering is split into two passes; 1 for backgrounds, and one for text
|
2016-10-24 17:59:25 +00:00
|
|
|
u_background: GLint,
|
2017-05-06 15:45:23 +00:00
|
|
|
|
|
|
|
padding_x: f32,
|
|
|
|
padding_y: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
2016-06-05 04:26:28 +00:00
|
|
|
pub struct Glyph {
|
|
|
|
tex_id: GLuint,
|
2017-07-04 02:43:44 +00:00
|
|
|
top: f32,
|
|
|
|
left: f32,
|
|
|
|
width: f32,
|
|
|
|
height: f32,
|
|
|
|
uv_bot: f32,
|
|
|
|
uv_left: f32,
|
|
|
|
uv_width: f32,
|
|
|
|
uv_height: f32,
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
/// Naïve glyph cache
|
|
|
|
///
|
2016-07-04 00:00:00 +00:00
|
|
|
/// Currently only keyed by `char`, and thus not possible to hold different
|
|
|
|
/// representations of the same code point.
|
2016-06-06 21:31:12 +00:00
|
|
|
pub struct GlyphCache {
|
|
|
|
/// Cache of buffered glyphs
|
2017-01-13 18:03:51 +00:00
|
|
|
cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>,
|
2016-06-06 21:31:12 +00:00
|
|
|
|
|
|
|
/// Rasterizer for loading new glyphs
|
|
|
|
rasterizer: Rasterizer,
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
/// regular font
|
|
|
|
font_key: FontKey,
|
2016-06-06 21:31:12 +00:00
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
/// italic font
|
|
|
|
italic_key: FontKey,
|
|
|
|
|
|
|
|
/// bold font
|
|
|
|
bold_key: FontKey,
|
|
|
|
|
|
|
|
/// font size
|
|
|
|
font_size: font::Size,
|
2017-01-15 17:38:04 +00:00
|
|
|
|
|
|
|
/// glyph offset
|
2017-04-27 02:28:18 +00:00
|
|
|
glyph_offset: Delta,
|
2017-05-03 22:12:23 +00:00
|
|
|
|
|
|
|
metrics: ::font::Metrics,
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GlyphCache {
|
2017-01-01 03:49:51 +00:00
|
|
|
pub fn new<L>(
|
|
|
|
mut rasterizer: Rasterizer,
|
2017-10-15 01:58:19 +00:00
|
|
|
font: &config::Font,
|
2017-01-01 03:49:51 +00:00
|
|
|
loader: &mut L
|
|
|
|
) -> Result<GlyphCache, font::Error>
|
2016-08-12 18:38:41 +00:00
|
|
|
where L: LoadGlyph
|
|
|
|
{
|
2017-10-15 01:58:19 +00:00
|
|
|
let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?;
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
// Need to load at least one glyph for the face before calling metrics.
|
|
|
|
// The glyph requested here ('m' at the time of writing) has no special
|
|
|
|
// meaning.
|
|
|
|
rasterizer.get_glyph(&GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
|
|
|
|
let metrics = rasterizer.metrics(regular)?;
|
|
|
|
|
|
|
|
let mut cache = GlyphCache {
|
|
|
|
cache: HashMap::default(),
|
|
|
|
rasterizer: rasterizer,
|
|
|
|
font_size: font.size(),
|
|
|
|
font_key: regular,
|
|
|
|
bold_key: bold,
|
|
|
|
italic_key: italic,
|
|
|
|
glyph_offset: *font.glyph_offset(),
|
|
|
|
metrics: metrics
|
|
|
|
};
|
|
|
|
|
|
|
|
cache.load_glyphs_for_font(regular, loader);
|
|
|
|
cache.load_glyphs_for_font(bold, loader);
|
|
|
|
cache.load_glyphs_for_font(italic, loader);
|
|
|
|
|
|
|
|
Ok(cache)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_glyphs_for_font<L: LoadGlyph>(
|
|
|
|
&mut self,
|
|
|
|
font: FontKey,
|
|
|
|
loader: &mut L,
|
|
|
|
) {
|
|
|
|
let size = self.font_size;
|
|
|
|
for i in RangeInclusive::new(32u8, 128u8) {
|
|
|
|
self.get(&GlyphKey {
|
|
|
|
font_key: font,
|
|
|
|
c: i as char,
|
|
|
|
size: size
|
|
|
|
}, loader);
|
2017-05-13 09:46:31 +00:00
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes font keys for (Regular, Bold, Italic)
|
|
|
|
fn compute_font_keys(
|
|
|
|
font: &config::Font,
|
|
|
|
rasterizer: &mut Rasterizer
|
|
|
|
) -> Result<(FontKey, FontKey, FontKey), font::Error> {
|
|
|
|
let size = font.size();
|
2017-05-13 09:46:31 +00:00
|
|
|
|
|
|
|
// Load regular font
|
2017-10-15 01:58:19 +00:00
|
|
|
let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal);
|
2017-01-03 02:52:41 +00:00
|
|
|
|
2016-12-17 06:48:04 +00:00
|
|
|
let regular = rasterizer
|
2017-01-01 03:49:51 +00:00
|
|
|
.load_font(®ular_desc, size)?;
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-05-13 09:46:31 +00:00
|
|
|
// helper to load a description if it is not the regular_desc
|
2017-10-15 01:58:19 +00:00
|
|
|
let mut load_or_regular = |desc:FontDesc| {
|
2017-05-13 09:46:31 +00:00
|
|
|
if desc == regular_desc {
|
|
|
|
regular
|
|
|
|
} else {
|
|
|
|
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
|
|
|
|
}
|
2017-01-03 02:52:41 +00:00
|
|
|
};
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-05-13 09:46:31 +00:00
|
|
|
// Load bold font
|
2017-10-15 01:58:19 +00:00
|
|
|
let bold_desc = Self::make_desc(&font.bold, font::Slant::Normal, font::Weight::Bold);
|
2017-05-13 09:46:31 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
let bold = load_or_regular(bold_desc);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
|
|
|
// Load italic font
|
2017-10-15 01:58:19 +00:00
|
|
|
let italic_desc = Self::make_desc(&font.italic, font::Slant::Italic, font::Weight::Normal);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
let italic = load_or_regular(italic_desc);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
Ok((regular, bold, italic))
|
|
|
|
}
|
2017-05-03 22:12:23 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
fn make_desc(
|
|
|
|
desc: &config::FontDescription,
|
|
|
|
slant: font::Slant,
|
|
|
|
weight: font::Weight,
|
|
|
|
) -> FontDesc {
|
|
|
|
let style = if let Some(ref spec) = desc.style {
|
|
|
|
font::Style::Specific(spec.to_owned())
|
|
|
|
} else {
|
|
|
|
font::Style::Description {slant:slant, weight:weight}
|
2016-08-12 18:38:41 +00:00
|
|
|
};
|
2017-10-15 01:58:19 +00:00
|
|
|
FontDesc::new(&desc.family[..], style)
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
pub fn font_metrics(&self) -> font::Metrics {
|
2017-01-01 03:49:51 +00:00
|
|
|
self.rasterizer
|
2017-04-22 20:01:25 +00:00
|
|
|
.metrics(self.font_key)
|
2017-01-01 03:49:51 +00:00
|
|
|
.expect("metrics load since font is loaded at glyph cache creation")
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2017-01-13 18:03:51 +00:00
|
|
|
pub fn get<'a, L>(&'a mut self, glyph_key: &GlyphKey, loader: &mut L) -> &'a Glyph
|
2016-06-06 21:31:12 +00:00
|
|
|
where L: LoadGlyph
|
|
|
|
{
|
2017-01-15 17:38:04 +00:00
|
|
|
let glyph_offset = self.glyph_offset;
|
2017-01-13 18:03:51 +00:00
|
|
|
let rasterizer = &mut self.rasterizer;
|
2017-05-03 22:12:23 +00:00
|
|
|
let metrics = &self.metrics;
|
2017-01-13 18:03:51 +00:00
|
|
|
self.cache
|
|
|
|
.entry(*glyph_key)
|
|
|
|
.or_insert_with(|| {
|
2017-11-29 23:38:29 +00:00
|
|
|
let mut rasterized = rasterizer.get_glyph(glyph_key)
|
2017-01-13 18:03:51 +00:00
|
|
|
.unwrap_or_else(|_| Default::default());
|
2017-01-15 17:38:04 +00:00
|
|
|
|
|
|
|
rasterized.left += glyph_offset.x as i32;
|
|
|
|
rasterized.top += glyph_offset.y as i32;
|
2017-05-03 22:12:23 +00:00
|
|
|
rasterized.top -= metrics.descent as i32;
|
2017-01-15 17:38:04 +00:00
|
|
|
|
2017-01-13 18:03:51 +00:00
|
|
|
loader.load_glyph(&rasterized)
|
|
|
|
})
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
pub fn update_font_size<L: LoadGlyph>(
|
|
|
|
&mut self,
|
|
|
|
font: &config::Font,
|
|
|
|
delta: i8,
|
|
|
|
loader: &mut L
|
|
|
|
) -> Result<(), font::Error> {
|
|
|
|
// Clear currently cached data in both GL and the registry
|
|
|
|
loader.clear();
|
|
|
|
self.cache = HashMap::default();
|
|
|
|
|
|
|
|
// Recompute font keys
|
|
|
|
let font = font.to_owned().with_size_delta(delta as _);
|
|
|
|
println!("{:?}", font.size);
|
|
|
|
let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?;
|
|
|
|
self.rasterizer.get_glyph(&GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
|
|
|
|
let metrics = self.rasterizer.metrics(regular)?;
|
|
|
|
|
|
|
|
self.font_size = font.size;
|
|
|
|
self.font_key = regular;
|
|
|
|
self.bold_key = bold;
|
|
|
|
self.italic_key = italic;
|
|
|
|
self.metrics = metrics;
|
|
|
|
|
|
|
|
self.load_glyphs_for_font(regular, loader);
|
|
|
|
self.load_glyphs_for_font(bold, loader);
|
|
|
|
self.load_glyphs_for_font(italic, loader);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[derive(Debug)]
|
2016-10-24 17:59:25 +00:00
|
|
|
#[repr(C)]
|
2016-06-06 20:20:35 +00:00
|
|
|
struct InstanceData {
|
2017-07-04 02:43:44 +00:00
|
|
|
// coords
|
|
|
|
col: f32,
|
|
|
|
row: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// glyph offset
|
2017-07-04 02:43:44 +00:00
|
|
|
left: f32,
|
|
|
|
top: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// glyph scale
|
2017-07-04 02:43:44 +00:00
|
|
|
width: f32,
|
|
|
|
height: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// uv offset
|
2017-07-04 02:43:44 +00:00
|
|
|
uv_left: f32,
|
|
|
|
uv_bot: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// uv scale
|
2017-07-04 02:43:44 +00:00
|
|
|
uv_width: f32,
|
|
|
|
uv_height: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// color
|
2017-07-04 02:43:44 +00:00
|
|
|
r: f32,
|
|
|
|
g: f32,
|
|
|
|
b: f32,
|
2016-06-06 23:54:15 +00:00
|
|
|
// background color
|
2017-07-04 02:43:44 +00:00
|
|
|
bg_r: f32,
|
|
|
|
bg_g: f32,
|
|
|
|
bg_b: f32,
|
2017-10-21 22:26:42 +00:00
|
|
|
bg_a: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2016-02-26 04:59:21 +00:00
|
|
|
pub struct QuadRenderer {
|
|
|
|
program: ShaderProgram,
|
|
|
|
vao: GLuint,
|
|
|
|
vbo: GLuint,
|
|
|
|
ebo: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
vbo_instance: GLuint,
|
2016-06-03 05:14:55 +00:00
|
|
|
atlas: Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: usize,
|
2016-06-03 05:14:55 +00:00
|
|
|
active_tex: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
batch: Batch,
|
2016-10-23 22:37:06 +00:00
|
|
|
rx: mpsc::Receiver<Msg>,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RenderApi<'a> {
|
|
|
|
active_tex: &'a mut GLuint,
|
|
|
|
batch: &'a mut Batch,
|
2016-06-06 21:31:12 +00:00
|
|
|
atlas: &'a mut Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &'a mut usize,
|
2016-06-06 23:54:15 +00:00
|
|
|
program: &'a mut ShaderProgram,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'a Config,
|
|
|
|
visual_bell_intensity: f32
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct LoaderApi<'a> {
|
|
|
|
active_tex: &'a mut GLuint,
|
|
|
|
atlas: &'a mut Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &'a mut usize,
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
|
|
|
|
2016-02-27 21:08:39 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PackedVertex {
|
|
|
|
x: f32,
|
|
|
|
y: f32,
|
2016-02-27 06:30:42 +00:00
|
|
|
}
|
|
|
|
|
2016-06-05 04:26:28 +00:00
|
|
|
#[derive(Debug)]
|
2016-06-06 20:20:35 +00:00
|
|
|
pub struct Batch {
|
2016-06-05 04:26:28 +00:00
|
|
|
tex: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
instances: Vec<InstanceData>,
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Batch {
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
2017-02-11 20:49:40 +00:00
|
|
|
pub fn new() -> Batch {
|
2016-06-05 04:26:28 +00:00
|
|
|
Batch {
|
|
|
|
tex: 0,
|
2016-06-06 20:20:35 +00:00
|
|
|
instances: Vec::with_capacity(BATCH_MAX),
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 21:13:44 +00:00
|
|
|
pub fn add_item(
|
|
|
|
&mut self,
|
2017-02-11 20:49:40 +00:00
|
|
|
cell: &RenderableCell,
|
2016-12-04 21:13:44 +00:00
|
|
|
glyph: &Glyph,
|
|
|
|
) {
|
2016-06-05 04:26:28 +00:00
|
|
|
if self.is_empty() {
|
|
|
|
self.tex = glyph.tex_id;
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
self.instances.push(InstanceData {
|
2017-07-04 02:43:44 +00:00
|
|
|
col: cell.column.0 as f32,
|
|
|
|
row: cell.line.0 as f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
|
|
|
|
top: glyph.top,
|
|
|
|
left: glyph.left,
|
|
|
|
width: glyph.width,
|
|
|
|
height: glyph.height,
|
|
|
|
|
|
|
|
uv_bot: glyph.uv_bot,
|
|
|
|
uv_left: glyph.uv_left,
|
|
|
|
uv_width: glyph.uv_width,
|
|
|
|
uv_height: glyph.uv_height,
|
|
|
|
|
2017-07-04 02:43:44 +00:00
|
|
|
r: cell.fg.r as f32,
|
|
|
|
g: cell.fg.g as f32,
|
|
|
|
b: cell.fg.b as f32,
|
2016-06-06 23:54:15 +00:00
|
|
|
|
2017-07-04 02:43:44 +00:00
|
|
|
bg_r: cell.bg.r as f32,
|
|
|
|
bg_g: cell.bg.g as f32,
|
|
|
|
bg_b: cell.bg.b as f32,
|
2017-10-21 22:26:42 +00:00
|
|
|
bg_a: cell.bg_alpha,
|
2016-11-28 22:13:11 +00:00
|
|
|
});
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn full(&self) -> bool {
|
|
|
|
self.capacity() == self.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
2016-06-06 20:20:35 +00:00
|
|
|
self.instances.len()
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn capacity(&self) -> usize {
|
|
|
|
BATCH_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.len() == 0
|
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn size(&self) -> usize {
|
|
|
|
self.len() * size_of::<InstanceData>()
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.tex = 0;
|
|
|
|
self.instances.clear();
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Maximum items to be drawn in a batch.
|
2016-11-12 00:51:01 +00:00
|
|
|
const BATCH_MAX: usize = 65_536;
|
2016-06-06 20:29:05 +00:00
|
|
|
const ATLAS_SIZE: i32 = 1024;
|
2016-05-21 18:08:50 +00:00
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
impl QuadRenderer {
|
|
|
|
// TODO should probably hand this a transform instead of width/height
|
2017-05-06 15:45:23 +00:00
|
|
|
pub fn new(config: &Config, size: Size<Pixels<u32>>) -> Result<QuadRenderer, Error> {
|
|
|
|
let program = ShaderProgram::new(config, size)?;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
let mut vao: GLuint = 0;
|
|
|
|
let mut vbo: GLuint = 0;
|
|
|
|
let mut ebo: GLuint = 0;
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
let mut vbo_instance: GLuint = 0;
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2016-12-10 05:10:49 +00:00
|
|
|
gl::Enable(gl::BLEND);
|
|
|
|
gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
|
|
|
|
gl::Enable(gl::MULTISAMPLE);
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
gl::GenVertexArrays(1, &mut vao);
|
|
|
|
gl::GenBuffers(1, &mut vbo);
|
|
|
|
gl::GenBuffers(1, &mut ebo);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::GenBuffers(1, &mut vbo_instance);
|
2016-02-26 04:59:21 +00:00
|
|
|
gl::BindVertexArray(vao);
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// ----------------------------
|
|
|
|
// setup vertex position buffer
|
|
|
|
// ----------------------------
|
2016-06-04 22:30:17 +00:00
|
|
|
// Top right, Bottom right, Bottom left, Top left
|
|
|
|
let vertices = [
|
|
|
|
PackedVertex { x: 1.0, y: 1.0 },
|
|
|
|
PackedVertex { x: 1.0, y: 0.0 },
|
|
|
|
PackedVertex { x: 0.0, y: 0.0 },
|
|
|
|
PackedVertex { x: 0.0, y: 1.0 },
|
|
|
|
];
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
|
|
|
|
|
|
|
|
gl::VertexAttribPointer(0, 2,
|
|
|
|
gl::FLOAT, gl::FALSE,
|
|
|
|
size_of::<PackedVertex>() as i32,
|
|
|
|
ptr::null());
|
|
|
|
gl::EnableVertexAttribArray(0);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::BufferData(gl::ARRAY_BUFFER,
|
|
|
|
(size_of::<PackedVertex>() * vertices.len()) as GLsizeiptr,
|
|
|
|
vertices.as_ptr() as *const _,
|
|
|
|
gl::STATIC_DRAW);
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Set up element buffer
|
|
|
|
// ---------------------
|
2016-02-26 04:59:21 +00:00
|
|
|
let indices: [u32; 6] = [0, 1, 3,
|
|
|
|
1, 2, 3];
|
|
|
|
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
|
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
|
2016-06-06 20:20:35 +00:00
|
|
|
(6 * size_of::<u32>()) as isize,
|
2016-02-26 04:59:21 +00:00
|
|
|
indices.as_ptr() as *const _,
|
|
|
|
gl::STATIC_DRAW);
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// ----------------------------
|
|
|
|
// Setup vertex instance buffer
|
|
|
|
// ----------------------------
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, vbo_instance);
|
|
|
|
gl::BufferData(gl::ARRAY_BUFFER,
|
|
|
|
(BATCH_MAX * size_of::<InstanceData>()) as isize,
|
|
|
|
ptr::null(), gl::STREAM_DRAW);
|
|
|
|
// coords
|
|
|
|
gl::VertexAttribPointer(1, 2,
|
2017-07-04 02:43:44 +00:00
|
|
|
gl::FLOAT, gl::FALSE,
|
2016-06-06 20:20:35 +00:00
|
|
|
size_of::<InstanceData>() as i32,
|
2016-02-26 04:59:21 +00:00
|
|
|
ptr::null());
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::EnableVertexAttribArray(1);
|
|
|
|
gl::VertexAttribDivisor(1, 1);
|
|
|
|
// glyphoffset
|
|
|
|
gl::VertexAttribPointer(2, 4,
|
2017-07-04 02:43:44 +00:00
|
|
|
gl::FLOAT, gl::FALSE,
|
2016-06-06 20:20:35 +00:00
|
|
|
size_of::<InstanceData>() as i32,
|
2017-07-04 02:43:44 +00:00
|
|
|
(2 * size_of::<f32>()) as *const _);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::EnableVertexAttribArray(2);
|
|
|
|
gl::VertexAttribDivisor(2, 1);
|
|
|
|
// uv
|
|
|
|
gl::VertexAttribPointer(3, 4,
|
2017-07-04 02:43:44 +00:00
|
|
|
gl::FLOAT, gl::FALSE,
|
2016-06-06 20:20:35 +00:00
|
|
|
size_of::<InstanceData>() as i32,
|
2017-07-04 02:43:44 +00:00
|
|
|
(6 * size_of::<f32>()) as *const _);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::EnableVertexAttribArray(3);
|
|
|
|
gl::VertexAttribDivisor(3, 1);
|
|
|
|
// color
|
|
|
|
gl::VertexAttribPointer(4, 3,
|
2017-07-04 02:43:44 +00:00
|
|
|
gl::FLOAT, gl::FALSE,
|
2016-06-06 20:20:35 +00:00
|
|
|
size_of::<InstanceData>() as i32,
|
2017-07-04 02:43:44 +00:00
|
|
|
(10 * size_of::<f32>()) as *const _);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::EnableVertexAttribArray(4);
|
|
|
|
gl::VertexAttribDivisor(4, 1);
|
2016-06-06 23:54:15 +00:00
|
|
|
// color
|
2017-10-21 22:26:42 +00:00
|
|
|
gl::VertexAttribPointer(5, 4,
|
2017-07-04 02:43:44 +00:00
|
|
|
gl::FLOAT, gl::FALSE,
|
2016-06-06 23:54:15 +00:00
|
|
|
size_of::<InstanceData>() as i32,
|
2017-07-04 02:43:44 +00:00
|
|
|
(13 * size_of::<f32>()) as *const _);
|
2016-06-06 23:54:15 +00:00
|
|
|
gl::EnableVertexAttribArray(5);
|
|
|
|
gl::VertexAttribDivisor(5, 1);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
gl::BindVertexArray(0);
|
2016-02-27 21:08:39 +00:00
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 22:37:06 +00:00
|
|
|
let (msg_tx, msg_rx) = mpsc::channel();
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
if cfg!(feature = "live-shader-reload") {
|
|
|
|
::std::thread::spawn(move || {
|
|
|
|
let (tx, rx) = ::std::sync::mpsc::channel();
|
2017-09-05 16:03:30 +00:00
|
|
|
// The Duration argument is a debouncing period.
|
2017-09-05 16:01:07 +00:00
|
|
|
let mut watcher = watcher(tx, Duration::from_millis(10)).expect("create file watcher");
|
2017-08-30 19:34:23 +00:00
|
|
|
watcher.watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
|
|
|
|
.expect("watch fragment shader");
|
|
|
|
watcher.watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
|
|
|
|
.expect("watch vertex shader");
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
loop {
|
|
|
|
let event = rx.recv().expect("watcher event");
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2017-08-30 19:34:23 +00:00
|
|
|
match event {
|
|
|
|
DebouncedEvent::Rename(_, _) => continue,
|
|
|
|
DebouncedEvent::Create(_) | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => {
|
|
|
|
msg_tx.send(Msg::ShaderReload).expect("msg send ok");
|
2016-10-27 15:43:23 +00:00
|
|
|
}
|
2017-08-30 19:34:23 +00:00
|
|
|
_ => {}
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-10-27 15:43:23 +00:00
|
|
|
});
|
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-06-03 16:26:58 +00:00
|
|
|
let mut renderer = QuadRenderer {
|
2016-02-26 04:59:21 +00:00
|
|
|
program: program,
|
|
|
|
vao: vao,
|
|
|
|
vbo: vbo,
|
|
|
|
ebo: ebo,
|
2016-06-06 20:20:35 +00:00
|
|
|
vbo_instance: vbo_instance,
|
2016-06-03 16:26:58 +00:00
|
|
|
atlas: Vec::new(),
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: 0,
|
2016-06-03 05:14:55 +00:00
|
|
|
active_tex: 0,
|
2017-02-11 20:49:40 +00:00
|
|
|
batch: Batch::new(),
|
2016-10-23 22:37:06 +00:00
|
|
|
rx: msg_rx,
|
2016-06-03 16:26:58 +00:00
|
|
|
};
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
let atlas = Atlas::new(ATLAS_SIZE);
|
2016-06-03 16:26:58 +00:00
|
|
|
renderer.atlas.push(atlas);
|
|
|
|
|
2017-01-02 03:19:15 +00:00
|
|
|
Ok(renderer)
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
|
|
|
|
2016-12-04 21:13:44 +00:00
|
|
|
pub fn with_api<F, T>(
|
|
|
|
&mut self,
|
|
|
|
config: &Config,
|
|
|
|
props: &term::SizeInfo,
|
2017-02-11 20:49:40 +00:00
|
|
|
visual_bell_intensity: f64,
|
2016-12-04 21:13:44 +00:00
|
|
|
func: F
|
|
|
|
) -> T
|
2016-08-12 18:38:41 +00:00
|
|
|
where F: FnOnce(RenderApi) -> T
|
2016-06-03 03:27:07 +00:00
|
|
|
{
|
2016-10-23 22:37:06 +00:00
|
|
|
while let Ok(msg) = self.rx.try_recv() {
|
|
|
|
match msg {
|
|
|
|
Msg::ShaderReload => {
|
2017-09-28 00:29:44 +00:00
|
|
|
self.reload_shaders(config, Size {
|
2016-12-10 05:10:49 +00:00
|
|
|
width: Pixels(props.width as u32),
|
|
|
|
height: Pixels(props.height as u32)
|
|
|
|
});
|
2016-10-23 22:37:06 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2016-06-03 03:27:07 +00:00
|
|
|
self.program.activate();
|
2016-06-04 22:30:17 +00:00
|
|
|
self.program.set_term_uniforms(props);
|
2017-02-11 20:49:40 +00:00
|
|
|
self.program.set_visual_bell(visual_bell_intensity as _);
|
2016-06-03 03:27:07 +00:00
|
|
|
|
|
|
|
gl::BindVertexArray(self.vao);
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_instance);
|
2016-06-03 05:14:55 +00:00
|
|
|
gl::ActiveTexture(gl::TEXTURE0);
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
let res = func(RenderApi {
|
2016-06-06 20:20:35 +00:00
|
|
|
active_tex: &mut self.active_tex,
|
|
|
|
batch: &mut self.batch,
|
2016-06-06 21:31:12 +00:00
|
|
|
atlas: &mut self.atlas,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &mut self.current_atlas,
|
2016-06-06 23:54:15 +00:00
|
|
|
program: &mut self.program,
|
2017-02-11 20:49:40 +00:00
|
|
|
visual_bell_intensity: visual_bell_intensity as _,
|
|
|
|
config: config,
|
2016-06-06 20:20:35 +00:00
|
|
|
});
|
2016-06-03 03:27:07 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
|
|
|
gl::BindVertexArray(0);
|
|
|
|
|
|
|
|
self.program.deactivate();
|
|
|
|
}
|
2016-08-12 18:38:41 +00:00
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_loader<F, T>(&mut self, func: F) -> T
|
|
|
|
where F: FnOnce(LoaderApi) -> T
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
gl::ActiveTexture(gl::TEXTURE0);
|
|
|
|
}
|
|
|
|
|
|
|
|
func(LoaderApi {
|
|
|
|
active_tex: &mut self.active_tex,
|
|
|
|
atlas: &mut self.atlas,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &mut self.current_atlas,
|
2016-08-12 18:38:41 +00:00
|
|
|
})
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
|
|
|
|
2017-05-06 15:45:23 +00:00
|
|
|
pub fn reload_shaders(&mut self, config: &Config, size: Size<Pixels<u32>>) {
|
2017-10-21 22:26:42 +00:00
|
|
|
warn!("Reloading shaders ...");
|
2017-05-06 15:45:23 +00:00
|
|
|
let program = match ShaderProgram::new(config, size) {
|
2017-10-21 22:26:42 +00:00
|
|
|
Ok(program) => {
|
|
|
|
warn!(" ... OK");
|
|
|
|
program
|
|
|
|
},
|
2016-06-04 17:54:33 +00:00
|
|
|
Err(err) => {
|
|
|
|
match err {
|
|
|
|
ShaderCreationError::Io(err) => {
|
2017-01-13 07:15:06 +00:00
|
|
|
error!("Error reading shader file: {}", err);
|
2016-06-04 17:54:33 +00:00
|
|
|
},
|
|
|
|
ShaderCreationError::Compile(path, log) => {
|
2017-10-21 22:26:42 +00:00
|
|
|
error!("Error compiling shader at {:?}\n{}", path, log);
|
|
|
|
}
|
|
|
|
ShaderCreationError::Link(log) => {
|
|
|
|
error!("Error reloading shaders: {}", log);
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
self.active_tex = 0;
|
|
|
|
self.program = program;
|
|
|
|
}
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
pub fn resize(&mut self, width: i32, height: i32) {
|
2017-05-06 15:45:23 +00:00
|
|
|
let padding_x = self.program.padding_x as i32;
|
|
|
|
let padding_y = self.program.padding_y as i32;
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
// viewport
|
|
|
|
unsafe {
|
2017-05-06 15:45:23 +00:00
|
|
|
gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// update projection
|
|
|
|
self.program.activate();
|
|
|
|
self.program.update_projection(width as f32, height as f32);
|
|
|
|
self.program.deactivate();
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
impl<'a> RenderApi<'a> {
|
2017-07-28 22:14:18 +00:00
|
|
|
pub fn clear(&self, color: Rgb) {
|
2017-08-20 16:55:45 +00:00
|
|
|
let alpha = self.config.background_opacity().get();
|
2016-11-28 22:13:11 +00:00
|
|
|
unsafe {
|
|
|
|
gl::ClearColor(
|
2017-08-20 16:55:45 +00:00
|
|
|
(self.visual_bell_intensity + color.r as f32 / 255.0).min(1.0) * alpha,
|
|
|
|
(self.visual_bell_intensity + color.g as f32 / 255.0).min(1.0) * alpha,
|
|
|
|
(self.visual_bell_intensity + color.b as f32 / 255.0).min(1.0) * alpha,
|
|
|
|
alpha
|
2016-11-28 22:13:11 +00:00
|
|
|
);
|
|
|
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
fn render_batch(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
gl::BufferSubData(gl::ARRAY_BUFFER, 0, self.batch.size() as isize,
|
|
|
|
self.batch.instances.as_ptr() as *const _);
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// Bind texture if necessary
|
|
|
|
if *self.active_tex != self.batch.tex {
|
|
|
|
unsafe {
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, self.batch.tex);
|
|
|
|
}
|
|
|
|
*self.active_tex = self.batch.tex;
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
unsafe {
|
2016-06-06 23:54:15 +00:00
|
|
|
self.program.set_background_pass(true);
|
|
|
|
gl::DrawElementsInstanced(gl::TRIANGLES,
|
|
|
|
6, gl::UNSIGNED_INT, ptr::null(),
|
|
|
|
self.batch.len() as GLsizei);
|
|
|
|
self.program.set_background_pass(false);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::DrawElementsInstanced(gl::TRIANGLES,
|
|
|
|
6, gl::UNSIGNED_INT, ptr::null(),
|
|
|
|
self.batch.len() as GLsizei);
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
self.batch.clear();
|
|
|
|
}
|
|
|
|
/// Render a string in a predefined location. Used for printing render time for profiling and
|
|
|
|
/// optimization.
|
2016-10-15 22:56:27 +00:00
|
|
|
pub fn render_string(
|
|
|
|
&mut self,
|
2016-11-28 22:13:11 +00:00
|
|
|
string: &str,
|
2016-10-15 22:56:27 +00:00
|
|
|
glyph_cache: &mut GlyphCache,
|
2017-02-11 20:49:40 +00:00
|
|
|
color: Rgb,
|
2016-10-15 22:56:27 +00:00
|
|
|
) {
|
2016-11-28 22:13:11 +00:00
|
|
|
let line = Line(23);
|
|
|
|
let col = Column(0);
|
|
|
|
|
|
|
|
let cells = string.chars()
|
|
|
|
.enumerate()
|
2017-02-11 20:49:40 +00:00
|
|
|
.map(|(i, c)| RenderableCell {
|
2016-11-28 22:13:11 +00:00
|
|
|
line: line,
|
|
|
|
column: col + i,
|
2017-02-11 20:49:40 +00:00
|
|
|
c: c,
|
|
|
|
bg: color,
|
|
|
|
fg: Rgb { r: 0, g: 0, b: 0 },
|
|
|
|
flags: cell::Flags::empty(),
|
2017-10-21 22:26:42 +00:00
|
|
|
bg_alpha: 1.0
|
2016-11-28 22:13:11 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-12-04 19:13:24 +00:00
|
|
|
self.render_cells(cells.into_iter(), glyph_cache);
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
2017-02-11 20:49:40 +00:00
|
|
|
fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
|
2016-06-06 20:29:05 +00:00
|
|
|
// Flush batch if tex changing
|
2016-12-17 06:13:51 +00:00
|
|
|
if !self.batch.is_empty() && self.batch.tex != glyph.tex_id {
|
|
|
|
self.render_batch();
|
2016-06-06 20:29:05 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 20:49:40 +00:00
|
|
|
self.batch.add_item(cell, glyph);
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// Render batch and clear if it's full
|
|
|
|
if self.batch.full() {
|
|
|
|
self.render_batch();
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-12-04 19:13:24 +00:00
|
|
|
pub fn render_cells<I>(
|
2016-10-23 22:37:06 +00:00
|
|
|
&mut self,
|
2016-12-04 19:13:24 +00:00
|
|
|
cells: I,
|
2016-10-23 22:37:06 +00:00
|
|
|
glyph_cache: &mut GlyphCache
|
2016-11-28 22:13:11 +00:00
|
|
|
)
|
2017-02-11 20:49:40 +00:00
|
|
|
where I: Iterator<Item=RenderableCell>
|
2016-11-28 22:13:11 +00:00
|
|
|
{
|
2016-12-04 19:13:24 +00:00
|
|
|
for cell in cells {
|
2016-11-28 22:13:11 +00:00
|
|
|
// Get font key for cell
|
|
|
|
// FIXME this is super inefficient.
|
|
|
|
let mut font_key = glyph_cache.font_key;
|
|
|
|
if cell.flags.contains(cell::BOLD) {
|
|
|
|
font_key = glyph_cache.bold_key;
|
|
|
|
} else if cell.flags.contains(cell::ITALIC) {
|
|
|
|
font_key = glyph_cache.italic_key;
|
|
|
|
}
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
let glyph_key = GlyphKey {
|
|
|
|
font_key: font_key,
|
|
|
|
size: glyph_cache.font_size,
|
|
|
|
c: cell.c
|
|
|
|
};
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-01-13 18:03:51 +00:00
|
|
|
// Add cell to batch
|
|
|
|
{
|
|
|
|
let glyph = glyph_cache.get(&glyph_key, self);
|
|
|
|
self.add_render_item(&cell, glyph);
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME This is a super hacky way to do underlined text. During
|
|
|
|
// a time crunch to release 0.1, this seemed like a really
|
|
|
|
// easy, clean hack.
|
|
|
|
if cell.flags.contains(cell::UNDERLINE) {
|
|
|
|
let glyph_key = GlyphKey {
|
|
|
|
font_key: font_key,
|
|
|
|
size: glyph_cache.font_size,
|
|
|
|
c: '_'
|
|
|
|
};
|
|
|
|
|
|
|
|
let underscore = glyph_cache.get(&glyph_key, self);
|
|
|
|
self.add_render_item(&cell, underscore);
|
|
|
|
}
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2017-12-20 02:59:45 +00:00
|
|
|
/// Load a glyph into a texture atlas
|
|
|
|
///
|
|
|
|
/// If the current atlas is full, a new one will be created.
|
|
|
|
#[inline]
|
|
|
|
fn load_glyph(
|
|
|
|
active_tex: &mut GLuint,
|
|
|
|
atlas: &mut Vec<Atlas>,
|
|
|
|
current_atlas: &mut usize,
|
|
|
|
rasterized: &RasterizedGlyph
|
|
|
|
) -> Glyph {
|
|
|
|
// At least one atlas is guaranteed to be in the `self.atlas` list; thus
|
|
|
|
// the unwrap.
|
|
|
|
match atlas[*current_atlas].insert(rasterized, active_tex) {
|
|
|
|
Ok(glyph) => glyph,
|
|
|
|
Err(_) => {
|
|
|
|
*current_atlas += 1;
|
|
|
|
if *current_atlas == atlas.len() {
|
|
|
|
let new = Atlas::new(ATLAS_SIZE);
|
|
|
|
*active_tex = 0; // Atlas::new binds a texture. Ugh this is sloppy.
|
|
|
|
atlas.push(new);
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
2017-12-20 02:59:45 +00:00
|
|
|
load_glyph(active_tex, atlas, current_atlas, rasterized)
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-20 02:59:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn clear_atlas(atlas: &mut Vec<Atlas>, current_atlas: &mut usize) {
|
|
|
|
for atlas in atlas.iter_mut() {
|
|
|
|
atlas.clear();
|
|
|
|
}
|
|
|
|
*current_atlas = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> LoadGlyph for LoaderApi<'a> {
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
|
|
|
|
load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
|
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
fn clear(&mut self) {
|
2017-12-20 02:59:45 +00:00
|
|
|
clear_atlas(self.atlas, self.current_atlas)
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
impl<'a> LoadGlyph for RenderApi<'a> {
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
|
2017-12-20 02:59:45 +00:00
|
|
|
load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
fn clear(&mut self) {
|
2017-12-20 02:59:45 +00:00
|
|
|
clear_atlas(self.atlas, self.current_atlas)
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
impl<'a> Drop for RenderApi<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if !self.batch.is_empty() {
|
|
|
|
self.render_batch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
impl ShaderProgram {
|
|
|
|
pub fn activate(&self) {
|
|
|
|
unsafe {
|
|
|
|
gl::UseProgram(self.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn deactivate(&self) {
|
|
|
|
unsafe {
|
|
|
|
gl::UseProgram(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-06 15:45:23 +00:00
|
|
|
pub fn new(
|
|
|
|
config: &Config,
|
|
|
|
size: Size<Pixels<u32>>
|
|
|
|
) -> Result<ShaderProgram, ShaderCreationError> {
|
2016-10-27 15:43:23 +00:00
|
|
|
let vertex_source = if cfg!(feature = "live-shader-reload") {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(TEXT_SHADER_V)
|
|
|
|
};
|
|
|
|
let vertex_shader = ShaderProgram::create_shader(
|
|
|
|
TEXT_SHADER_V_PATH,
|
|
|
|
gl::VERTEX_SHADER,
|
|
|
|
vertex_source
|
|
|
|
)?;
|
|
|
|
let frag_source = if cfg!(feature = "live-shader-reload") {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(TEXT_SHADER_F)
|
|
|
|
};
|
|
|
|
let fragment_shader = ShaderProgram::create_shader(
|
|
|
|
TEXT_SHADER_F_PATH,
|
|
|
|
gl::FRAGMENT_SHADER,
|
|
|
|
frag_source
|
|
|
|
)?;
|
2017-10-21 22:26:42 +00:00
|
|
|
let program = ShaderProgram::create_program(vertex_shader, fragment_shader)?;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteShader(vertex_shader);
|
|
|
|
gl::DeleteShader(fragment_shader);
|
2016-06-04 22:30:17 +00:00
|
|
|
gl::UseProgram(program);
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! cptr {
|
|
|
|
($thing:expr) => { $thing.as_ptr() as *const _ }
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! assert_uniform_valid {
|
|
|
|
($uniform:expr) => {
|
|
|
|
assert!($uniform != gl::INVALID_VALUE as i32);
|
|
|
|
assert!($uniform != gl::INVALID_OPERATION as i32);
|
|
|
|
};
|
|
|
|
( $( $uniform:expr ),* ) => {
|
2016-08-12 18:38:41 +00:00
|
|
|
$( assert_uniform_valid!($uniform); )*
|
2016-06-04 22:30:17 +00:00
|
|
|
};
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get uniform locations
|
2017-10-21 22:26:42 +00:00
|
|
|
let (projection, term_dim, cell_dim, visual_bell, background) = unsafe {
|
2016-06-04 22:30:17 +00:00
|
|
|
(
|
|
|
|
gl::GetUniformLocation(program, cptr!(b"projection\0")),
|
|
|
|
gl::GetUniformLocation(program, cptr!(b"termDim\0")),
|
|
|
|
gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
|
2017-02-03 23:34:52 +00:00
|
|
|
gl::GetUniformLocation(program, cptr!(b"visualBell\0")),
|
2016-06-06 23:54:15 +00:00
|
|
|
gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
|
2016-06-04 22:30:17 +00:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2016-06-10 03:39:40 +00:00
|
|
|
assert_uniform_valid!(projection, term_dim, cell_dim);
|
2016-06-03 03:27:07 +00:00
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
let shader = ShaderProgram {
|
|
|
|
id: program,
|
|
|
|
u_projection: projection,
|
2016-06-04 22:30:17 +00:00
|
|
|
u_term_dim: term_dim,
|
|
|
|
u_cell_dim: cell_dim,
|
2017-02-03 23:34:52 +00:00
|
|
|
u_visual_bell: visual_bell,
|
2016-06-06 23:54:15 +00:00
|
|
|
u_background: background,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: config.padding().x.floor(),
|
|
|
|
padding_y: config.padding().y.floor(),
|
2016-02-26 04:59:21 +00:00
|
|
|
};
|
|
|
|
|
2016-12-10 05:10:49 +00:00
|
|
|
shader.update_projection(*size.width as f32, *size.height as f32);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
shader.deactivate();
|
|
|
|
|
|
|
|
Ok(shader)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_projection(&self, width: f32, height: f32) {
|
2017-05-06 15:45:23 +00:00
|
|
|
// Bounds check
|
|
|
|
if (width as u32) < (2 * self.padding_x as u32) ||
|
|
|
|
(height as u32) < (2 * self.padding_y as u32)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
// set projection uniform
|
2017-05-06 15:45:23 +00:00
|
|
|
//
|
|
|
|
// NB Not sure why padding change only requires changing the vertical
|
|
|
|
// translation in the projection, but this makes everything work
|
|
|
|
// correctly.
|
|
|
|
let ortho = cgmath::ortho(0., width - 2. * self.padding_x, 2. * self.padding_y, height,
|
|
|
|
-1., 1.);
|
2016-02-26 04:59:21 +00:00
|
|
|
let projection: [[f32; 4]; 4] = ortho.into();
|
|
|
|
|
2017-01-13 07:15:06 +00:00
|
|
|
info!("width: {}, height: {}", width, height);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
unsafe {
|
2016-06-29 17:21:02 +00:00
|
|
|
gl::UniformMatrix4fv(self.u_projection,
|
2016-02-26 04:59:21 +00:00
|
|
|
1, gl::FALSE, projection.as_ptr() as *const _);
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 16:18:54 +00:00
|
|
|
fn set_term_uniforms(&self, props: &term::SizeInfo) {
|
2016-06-04 22:30:17 +00:00
|
|
|
unsafe {
|
|
|
|
gl::Uniform2f(self.u_term_dim, props.width, props.height);
|
|
|
|
gl::Uniform2f(self.u_cell_dim, props.cell_width, props.cell_height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
fn set_visual_bell(&self, visual_bell: f32) {
|
|
|
|
unsafe {
|
|
|
|
gl::Uniform1f(self.u_visual_bell, visual_bell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 23:54:15 +00:00
|
|
|
fn set_background_pass(&self, background_pass: bool) {
|
|
|
|
let value = if background_pass {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::Uniform1i(self.u_background, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-21 22:26:42 +00:00
|
|
|
fn create_program(vertex: GLuint, fragment: GLuint) -> Result<GLuint, ShaderCreationError> {
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
|
|
|
let program = gl::CreateProgram();
|
|
|
|
gl::AttachShader(program, vertex);
|
|
|
|
gl::AttachShader(program, fragment);
|
|
|
|
gl::LinkProgram(program);
|
|
|
|
|
|
|
|
let mut success: GLint = 0;
|
|
|
|
gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
|
|
|
|
|
2017-11-29 13:42:16 +00:00
|
|
|
if success == (gl::TRUE as GLint) {
|
2017-10-21 22:26:42 +00:00
|
|
|
Ok(program)
|
2017-11-29 13:42:16 +00:00
|
|
|
} else {
|
|
|
|
Err(ShaderCreationError::Link(get_program_info_log(program)))
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
fn create_shader(
|
|
|
|
path: &str,
|
|
|
|
kind: GLenum,
|
|
|
|
source: Option<&'static str>
|
|
|
|
) -> Result<GLuint, ShaderCreationError> {
|
|
|
|
let from_disk;
|
|
|
|
let source = if let Some(src) = source {
|
|
|
|
src
|
|
|
|
} else {
|
|
|
|
from_disk = read_file(path)?;
|
|
|
|
&from_disk[..]
|
|
|
|
};
|
|
|
|
|
|
|
|
let len: [GLint; 1] = [source.len() as GLint];
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
let shader = unsafe {
|
|
|
|
let shader = gl::CreateShader(kind);
|
2017-01-05 14:22:51 +00:00
|
|
|
gl::ShaderSource(shader, 1, &(source.as_ptr() as *const _), len.as_ptr());
|
2016-06-04 17:54:33 +00:00
|
|
|
gl::CompileShader(shader);
|
|
|
|
shader
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut success: GLint = 0;
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2016-06-04 17:54:33 +00:00
|
|
|
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
if success == (gl::TRUE as GLint) {
|
|
|
|
Ok(shader)
|
|
|
|
} else {
|
|
|
|
// Read log
|
|
|
|
let log = get_shader_info_log(shader);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
// Cleanup
|
|
|
|
unsafe { gl::DeleteShader(shader); }
|
|
|
|
|
|
|
|
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
impl Drop for ShaderProgram {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteProgram(self.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-05 04:26:28 +00:00
|
|
|
fn get_program_info_log(program: GLuint) -> String {
|
|
|
|
// Get expected log length
|
|
|
|
let mut max_length: GLint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut max_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the info log
|
|
|
|
let mut actual_length: GLint = 0;
|
|
|
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
|
|
|
unsafe {
|
|
|
|
gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build a string
|
|
|
|
unsafe {
|
|
|
|
buf.set_len(actual_length as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX should we expect opengl to return garbage?
|
|
|
|
String::from_utf8(buf).unwrap()
|
|
|
|
}
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
fn get_shader_info_log(shader: GLuint) -> String {
|
|
|
|
// Get expected log length
|
|
|
|
let mut max_length: GLint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut max_length);
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
// Read the info log
|
|
|
|
let mut actual_length: GLint = 0;
|
|
|
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
|
|
|
unsafe {
|
|
|
|
gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
// Build a string
|
|
|
|
unsafe {
|
|
|
|
buf.set_len(actual_length as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX should we expect opengl to return garbage?
|
|
|
|
String::from_utf8(buf).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_file(path: &str) -> Result<String, io::Error> {
|
|
|
|
let mut f = File::open(path)?;
|
|
|
|
let mut buf = String::new();
|
|
|
|
f.read_to_string(&mut buf)?;
|
|
|
|
|
|
|
|
Ok(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ShaderCreationError {
|
|
|
|
/// Error reading file
|
|
|
|
Io(io::Error),
|
|
|
|
|
|
|
|
/// Error compiling shader
|
|
|
|
Compile(PathBuf, String),
|
2017-10-21 22:26:42 +00:00
|
|
|
|
|
|
|
/// Problem linking
|
|
|
|
Link(String),
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::error::Error for ShaderCreationError {
|
|
|
|
fn cause(&self) -> Option<&::std::error::Error> {
|
|
|
|
match *self {
|
|
|
|
ShaderCreationError::Io(ref err) => Some(err),
|
2017-10-21 22:26:42 +00:00
|
|
|
_ => None,
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
match *self {
|
|
|
|
ShaderCreationError::Io(ref err) => err.description(),
|
|
|
|
ShaderCreationError::Compile(ref _path, ref s) => s.as_str(),
|
2017-10-21 22:26:42 +00:00
|
|
|
ShaderCreationError::Link(ref s) => s.as_str(),
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
impl ::std::fmt::Display for ShaderCreationError {
|
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
|
|
match *self {
|
2017-01-02 03:19:15 +00:00
|
|
|
ShaderCreationError::Io(ref err) => write!(f, "couldn't read shader: {}", err),
|
2016-06-04 17:54:33 +00:00
|
|
|
ShaderCreationError::Compile(ref _path, ref s) => {
|
2017-01-02 03:19:15 +00:00
|
|
|
write!(f, "failed compiling shader: {}", s)
|
2016-06-04 17:54:33 +00:00
|
|
|
},
|
2017-10-21 22:26:42 +00:00
|
|
|
ShaderCreationError::Link(ref s) => {
|
|
|
|
write!(f, "failed linking shader: {}", s)
|
|
|
|
},
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for ShaderCreationError {
|
|
|
|
fn from(val: io::Error) -> ShaderCreationError {
|
|
|
|
ShaderCreationError::Io(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
/// Manages a single texture atlas
|
|
|
|
///
|
|
|
|
/// The strategy for filling an atlas looks roughly like this:
|
|
|
|
///
|
2016-11-20 00:16:20 +00:00
|
|
|
/// ```ignore
|
2016-06-03 05:14:55 +00:00
|
|
|
/// (width, height)
|
|
|
|
/// ┌─────┬─────┬─────┬─────┬─────┐
|
|
|
|
/// │ 10 │ │ │ │ │ <- Empty spaces; can be filled while
|
|
|
|
/// │ │ │ │ │ │ glyph_height < height - row_baseline
|
|
|
|
/// ├⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┤
|
|
|
|
/// │ 5 │ 6 │ 7 │ 8 │ 9 │
|
|
|
|
/// │ │ │ │ │ │
|
|
|
|
/// ├⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┴⎼⎼⎼⎼⎼┤ <- Row height is tallest glyph in row; this is
|
|
|
|
/// │ 1 │ 2 │ 3 │ 4 │ used as the baseline for the following row.
|
|
|
|
/// │ │ │ │ │ <- Row considered full when next glyph doesn't
|
|
|
|
/// └─────┴─────┴─────┴───────────┘ fit in the row.
|
|
|
|
/// (0, 0) x->
|
2016-11-20 00:16:20 +00:00
|
|
|
/// ```
|
2016-06-06 20:20:35 +00:00
|
|
|
#[derive(Debug)]
|
2016-06-03 05:14:55 +00:00
|
|
|
struct Atlas {
|
|
|
|
/// Texture id for this atlas
|
|
|
|
id: GLuint,
|
|
|
|
|
|
|
|
/// Width of atlas
|
2016-02-26 04:59:21 +00:00
|
|
|
width: i32,
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Height of atlas
|
2016-02-26 04:59:21 +00:00
|
|
|
height: i32,
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Left-most free pixel in a row.
|
|
|
|
///
|
|
|
|
/// This is called the extent because it is the upper bound of used pixels
|
|
|
|
/// in a row.
|
|
|
|
row_extent: i32,
|
|
|
|
|
|
|
|
/// Baseline for glyphs in the current row
|
|
|
|
row_baseline: i32,
|
|
|
|
|
|
|
|
/// Tallest glyph in current row
|
|
|
|
///
|
|
|
|
/// This is used as the advance when end of row is reached
|
|
|
|
row_tallest: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Error that can happen when inserting a texture to the Atlas
|
|
|
|
enum AtlasInsertError {
|
|
|
|
/// Texture atlas is full
|
|
|
|
Full,
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
impl Atlas {
|
2016-06-06 21:31:12 +00:00
|
|
|
fn new(size: i32) -> Atlas {
|
|
|
|
let mut id: GLuint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
|
|
|
|
gl::GenTextures(1, &mut id);
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, id);
|
|
|
|
gl::TexImage2D(
|
|
|
|
gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
gl::RGB as i32,
|
|
|
|
size,
|
|
|
|
size,
|
|
|
|
0,
|
|
|
|
gl::RGB,
|
|
|
|
gl::UNSIGNED_BYTE,
|
|
|
|
ptr::null()
|
|
|
|
);
|
|
|
|
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
|
|
|
|
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Atlas {
|
|
|
|
id: id,
|
|
|
|
width: size,
|
|
|
|
height: size,
|
|
|
|
row_extent: 0,
|
|
|
|
row_baseline: 0,
|
|
|
|
row_tallest: 0,
|
|
|
|
}
|
|
|
|
}
|
2016-06-03 05:14:55 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.row_extent = 0;
|
|
|
|
self.row_baseline = 0;
|
|
|
|
self.row_tallest = 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
/// Insert a RasterizedGlyph into the texture atlas
|
2016-06-03 16:26:58 +00:00
|
|
|
pub fn insert(&mut self,
|
|
|
|
glyph: &RasterizedGlyph,
|
|
|
|
active_tex: &mut u32)
|
|
|
|
-> Result<Glyph, AtlasInsertError>
|
|
|
|
{
|
2016-06-03 05:14:55 +00:00
|
|
|
// If there's not enough room in current row, go onto next one
|
|
|
|
if !self.room_in_row(glyph) {
|
|
|
|
self.advance_row()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's still not room, there's nothing that can be done here.
|
|
|
|
if !self.room_in_row(glyph) {
|
|
|
|
return Err(AtlasInsertError::Full);
|
|
|
|
}
|
|
|
|
|
|
|
|
// There appears to be room; load the glyph.
|
2016-06-03 16:26:58 +00:00
|
|
|
Ok(self.insert_inner(glyph, active_tex))
|
2016-06-03 05:14:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert the glyph without checking for room
|
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// Internal function for use once atlas has been checked for space. GL
|
|
|
|
/// errors could still occur at this point if we were checking for them;
|
|
|
|
/// hence, the Result.
|
2016-06-03 16:26:58 +00:00
|
|
|
fn insert_inner(&mut self,
|
|
|
|
glyph: &RasterizedGlyph,
|
|
|
|
active_tex: &mut u32)
|
|
|
|
-> Glyph
|
|
|
|
{
|
2016-06-03 05:14:55 +00:00
|
|
|
let offset_y = self.row_baseline;
|
|
|
|
let offset_x = self.row_extent;
|
|
|
|
let height = glyph.height as i32;
|
|
|
|
let width = glyph.width as i32;
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, self.id);
|
|
|
|
|
|
|
|
// Load data into OpenGL
|
|
|
|
gl::TexSubImage2D(
|
|
|
|
gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
offset_x,
|
|
|
|
offset_y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
gl::RGB,
|
|
|
|
gl::UNSIGNED_BYTE,
|
|
|
|
glyph.buf.as_ptr() as *const _
|
|
|
|
);
|
|
|
|
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
2016-06-03 16:26:58 +00:00
|
|
|
*active_tex = 0;
|
2016-06-03 05:14:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update Atlas state
|
|
|
|
self.row_extent = offset_x + width;
|
|
|
|
if height > self.row_tallest {
|
|
|
|
self.row_tallest = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate UV coordinates
|
|
|
|
let uv_bot = offset_y as f32 / self.height as f32;
|
|
|
|
let uv_left = offset_x as f32 / self.width as f32;
|
|
|
|
let uv_height = height as f32 / self.height as f32;
|
|
|
|
let uv_width = width as f32 / self.width as f32;
|
|
|
|
|
2016-12-17 06:13:51 +00:00
|
|
|
Glyph {
|
2016-06-04 22:30:17 +00:00
|
|
|
tex_id: self.id,
|
2017-07-04 02:43:44 +00:00
|
|
|
top: glyph.top as f32,
|
|
|
|
width: width as f32,
|
|
|
|
height: height as f32,
|
|
|
|
left: glyph.left as f32,
|
|
|
|
uv_bot: uv_bot,
|
|
|
|
uv_left: uv_left,
|
|
|
|
uv_width: uv_width,
|
|
|
|
uv_height: uv_height,
|
2016-12-17 06:13:51 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Check if there's room in the current row for given glyph
|
|
|
|
fn room_in_row(&self, raw: &RasterizedGlyph) -> bool {
|
|
|
|
let next_extent = self.row_extent + raw.width as i32;
|
|
|
|
let enough_width = next_extent <= self.width;
|
|
|
|
let enough_height = (raw.height as i32) < (self.height - self.row_baseline);
|
|
|
|
|
|
|
|
enough_width && enough_height
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mark current row as finished and prepare to insert into the next row
|
|
|
|
fn advance_row(&mut self) -> Result<(), AtlasInsertError> {
|
|
|
|
let advance_to = self.row_baseline + self.row_tallest;
|
|
|
|
if self.height - advance_to <= 0 {
|
|
|
|
return Err(AtlasInsertError::Full);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.row_baseline = advance_to;
|
|
|
|
self.row_extent = 0;
|
|
|
|
self.row_tallest = 0;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|