зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1446358 - Update webrender to commit 2083e83d958dd4a230ccae5c518e4bc8fbf88009. r=jrmuizel
MozReview-Commit-ID: 36QLv2CRPh0 --HG-- extra : rebase_source : 5b9e8f2e6ca099761e9f3e492c9d62b00c44c63d
This commit is contained in:
Родитель
8a9179124b
Коммит
814ae3282d
|
@ -35,8 +35,8 @@ smallvec = "0.6"
|
|||
ws = { optional = true, version = "0.7.3" }
|
||||
serde_json = { optional = true, version = "1.0" }
|
||||
serde = { optional = true, version = "1.0", features = ["serde_derive"] }
|
||||
image = { optional = true, version = "0.17" }
|
||||
base64 = { optional = true, version = "0.3.0" }
|
||||
image = { optional = true, version = "0.18" }
|
||||
base64 = { optional = true, version = "0.6" }
|
||||
ron = { optional = true, version = "0.1.7" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
flat varying int vGradientAddress;
|
||||
flat varying float vGradientRepeat;
|
||||
|
||||
flat varying vec2 vStartCenter;
|
||||
flat varying vec2 vEndCenter;
|
||||
flat varying vec2 vCenter;
|
||||
flat varying float vStartRadius;
|
||||
flat varying float vEndRadius;
|
||||
|
||||
|
@ -23,8 +22,8 @@ varying vec2 vLocalPos;
|
|||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
struct RadialGradient {
|
||||
vec4 start_end_center;
|
||||
vec4 start_end_radius_ratio_xy_extend_mode;
|
||||
vec4 center_start_end_radius;
|
||||
vec4 ratio_xy_extend_mode;
|
||||
};
|
||||
|
||||
RadialGradient fetch_radial_gradient(int address) {
|
||||
|
@ -43,23 +42,20 @@ void brush_vs(
|
|||
|
||||
vPos = vi.local_pos - local_rect.p0;
|
||||
|
||||
vStartCenter = gradient.start_end_center.xy;
|
||||
vEndCenter = gradient.start_end_center.zw;
|
||||
|
||||
vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
|
||||
vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
|
||||
vCenter = gradient.center_start_end_radius.xy;
|
||||
vStartRadius = gradient.center_start_end_radius.z;
|
||||
vEndRadius = gradient.center_start_end_radius.w;
|
||||
|
||||
// Transform all coordinates by the y scale so the
|
||||
// fragment shader can work with circles
|
||||
float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
|
||||
float ratio_xy = gradient.ratio_xy_extend_mode.x;
|
||||
vPos.y *= ratio_xy;
|
||||
vStartCenter.y *= ratio_xy;
|
||||
vEndCenter.y *= ratio_xy;
|
||||
vCenter.y *= ratio_xy;
|
||||
|
||||
vGradientAddress = user_data.x;
|
||||
|
||||
// Whether to repeat the gradient instead of clamping.
|
||||
vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) != EXTEND_MODE_CLAMP);
|
||||
vGradientRepeat = float(int(gradient.ratio_xy_extend_mode.y) != EXTEND_MODE_CLAMP);
|
||||
|
||||
#ifdef WR_FEATURE_ALPHA_PASS
|
||||
vLocalPos = vi.local_pos;
|
||||
|
@ -69,14 +65,13 @@ void brush_vs(
|
|||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
vec4 brush_fs() {
|
||||
vec2 cd = vEndCenter - vStartCenter;
|
||||
vec2 pd = vPos - vStartCenter;
|
||||
vec2 pd = vPos - vCenter;
|
||||
float rd = vEndRadius - vStartRadius;
|
||||
|
||||
// Solve for t in length(t * cd - pd) = vStartRadius + t * rd
|
||||
// Solve for t in length(t - pd) = vStartRadius + t * rd
|
||||
// using a quadratic equation in form of At^2 - 2Bt + C = 0
|
||||
float A = dot(cd, cd) - rd * rd;
|
||||
float B = dot(pd, cd) + vStartRadius * rd;
|
||||
float A = -(rd * rd);
|
||||
float B = vStartRadius * rd;
|
||||
float C = dot(pd, pd) - vStartRadius * vStartRadius;
|
||||
|
||||
float offset;
|
||||
|
|
|
@ -931,22 +931,6 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: remove once the Angle workaround is no longer needed
|
||||
pub fn reset_angle_sampler_metadata(&mut self, texture: &Texture) {
|
||||
self.bind_texture(DEFAULT_TEXTURE, texture);
|
||||
self.gl.tex_parameter_f(
|
||||
texture.target,
|
||||
gl::TEXTURE_BASE_LEVEL,
|
||||
1.0 as _,
|
||||
);
|
||||
self.gl.draw_arrays(gl::TRIANGLES, 0, 1); // dummy draw
|
||||
self.gl.tex_parameter_f(
|
||||
texture.target,
|
||||
gl::TEXTURE_BASE_LEVEL,
|
||||
0.0 as _, // assumes 0.0 is the normal value for this texture
|
||||
);
|
||||
}
|
||||
|
||||
pub fn create_texture(
|
||||
&mut self,
|
||||
target: TextureTarget,
|
||||
|
|
|
@ -738,11 +738,10 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
self.add_radial_gradient(
|
||||
clip_and_scroll,
|
||||
&prim_info,
|
||||
info.gradient.start_center,
|
||||
info.gradient.center,
|
||||
info.gradient.start_radius,
|
||||
info.gradient.end_center,
|
||||
info.gradient.end_radius,
|
||||
info.gradient.ratio_xy,
|
||||
info.gradient.radius.width / info.gradient.radius.height,
|
||||
item.gradient_stops(),
|
||||
info.gradient.extend_mode,
|
||||
info.tile_size,
|
||||
|
@ -1824,11 +1823,10 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
self.add_radial_gradient(
|
||||
clip_and_scroll,
|
||||
&info,
|
||||
border.gradient.start_center - segment_rel,
|
||||
border.gradient.center - segment_rel,
|
||||
border.gradient.start_radius,
|
||||
border.gradient.end_center - segment_rel,
|
||||
border.gradient.end_radius,
|
||||
border.gradient.ratio_xy,
|
||||
border.gradient.radius.width / border.gradient.radius.height,
|
||||
gradient_stops,
|
||||
border.gradient.extend_mode,
|
||||
segment.size,
|
||||
|
@ -1938,9 +1936,8 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
&mut self,
|
||||
clip_and_scroll: ScrollNodeAndClipChain,
|
||||
info: &LayerPrimitiveInfo,
|
||||
start_center: LayerPoint,
|
||||
center: LayerPoint,
|
||||
start_radius: f32,
|
||||
end_center: LayerPoint,
|
||||
end_radius: f32,
|
||||
ratio_xy: f32,
|
||||
stops: ItemRange<GradientStop>,
|
||||
|
@ -1951,8 +1948,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
BrushKind::RadialGradient {
|
||||
stops_range: stops,
|
||||
extend_mode,
|
||||
start_center,
|
||||
end_center,
|
||||
center,
|
||||
start_radius,
|
||||
end_radius,
|
||||
ratio_xy,
|
||||
|
@ -1973,9 +1969,8 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
&mut self,
|
||||
clip_and_scroll: ScrollNodeAndClipChain,
|
||||
info: &LayerPrimitiveInfo,
|
||||
start_center: LayerPoint,
|
||||
center: LayerPoint,
|
||||
start_radius: f32,
|
||||
end_center: LayerPoint,
|
||||
end_radius: f32,
|
||||
ratio_xy: f32,
|
||||
stops: ItemRange<GradientStop>,
|
||||
|
@ -1996,9 +1991,8 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
self.add_radial_gradient_impl(
|
||||
clip_and_scroll,
|
||||
info,
|
||||
start_center,
|
||||
center,
|
||||
start_radius,
|
||||
end_center,
|
||||
end_radius,
|
||||
ratio_xy,
|
||||
stops,
|
||||
|
@ -2010,9 +2004,8 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
self.add_radial_gradient_impl(
|
||||
clip_and_scroll,
|
||||
&prim_info,
|
||||
start_center,
|
||||
center,
|
||||
start_radius,
|
||||
end_center,
|
||||
end_radius,
|
||||
ratio_xy,
|
||||
stops,
|
||||
|
|
|
@ -2,38 +2,43 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{DevicePoint, DeviceUintSize, GlyphKey};
|
||||
use api::GlyphKey;
|
||||
use glyph_rasterizer::{FontInstance, GlyphFormat};
|
||||
use internal_types::FastHashMap;
|
||||
use resource_cache::ResourceClassCache;
|
||||
use std::sync::Arc;
|
||||
use texture_cache::TextureCacheHandle;
|
||||
use texture_cache::{TextureCache, TextureCacheHandle, EvictionNotice};
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GenericCachedGlyphInfo<D> {
|
||||
pub struct CachedGlyphInfo {
|
||||
pub texture_cache_handle: TextureCacheHandle,
|
||||
pub glyph_bytes: D,
|
||||
pub size: DeviceUintSize,
|
||||
pub offset: DevicePoint,
|
||||
pub scale: f32,
|
||||
pub format: GlyphFormat,
|
||||
}
|
||||
|
||||
pub type CachedGlyphInfo = GenericCachedGlyphInfo<Arc<Vec<u8>>>;
|
||||
pub type GlyphKeyCache = ResourceClassCache<GlyphKey, Option<CachedGlyphInfo>>;
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum GlyphCacheEntry {
|
||||
// A glyph that has been successfully rasterized.
|
||||
Cached(CachedGlyphInfo),
|
||||
// A glyph that should not be rasterized (i.e. a space).
|
||||
Blank,
|
||||
// A glyph that has been submitted to the font backend for rasterization,
|
||||
// but is still pending a result.
|
||||
Pending,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "capture", feature = "replay"))]
|
||||
pub type PlainCachedGlyphInfo = GenericCachedGlyphInfo<String>;
|
||||
#[cfg(any(feature = "capture", feature = "replay"))]
|
||||
pub type PlainGlyphKeyCache = ResourceClassCache<GlyphKey, Option<PlainCachedGlyphInfo>>;
|
||||
#[cfg(feature = "capture")]
|
||||
pub type PlainGlyphCacheRef<'a> = FastHashMap<&'a FontInstance, PlainGlyphKeyCache>;
|
||||
#[cfg(feature = "replay")]
|
||||
pub type PlainGlyphCacheOwn = FastHashMap<FontInstance, PlainGlyphKeyCache>;
|
||||
pub type GlyphKeyCache = ResourceClassCache<GlyphKey, GlyphCacheEntry, EvictionNotice>;
|
||||
|
||||
impl GlyphKeyCache {
|
||||
pub fn eviction_notice(&self) -> &EvictionNotice {
|
||||
&self.user_data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct GlyphCache {
|
||||
pub glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>,
|
||||
glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>,
|
||||
}
|
||||
|
||||
impl GlyphCache {
|
||||
|
@ -46,7 +51,7 @@ impl GlyphCache {
|
|||
pub fn get_glyph_key_cache_for_font_mut(&mut self, font: FontInstance) -> &mut GlyphKeyCache {
|
||||
self.glyph_key_caches
|
||||
.entry(font)
|
||||
.or_insert(ResourceClassCache::new())
|
||||
.or_insert_with(|| GlyphKeyCache::new())
|
||||
}
|
||||
|
||||
pub fn get_glyph_key_cache_for_font(&self, font: &FontInstance) -> &GlyphKeyCache {
|
||||
|
@ -78,4 +83,36 @@ impl GlyphCache {
|
|||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out evicted entries from glyph key caches and, if possible,
|
||||
// also remove entirely any subsequently empty glyph key caches.
|
||||
fn clear_evicted(&mut self, texture_cache: &TextureCache) {
|
||||
self.glyph_key_caches.retain(|_, cache| {
|
||||
// Scan for any glyph key caches that have evictions.
|
||||
if cache.eviction_notice().check() {
|
||||
// If there are evictions, filter out any glyphs evicted from the
|
||||
// texture cache from the glyph key cache.
|
||||
let mut keep_cache = false;
|
||||
cache.retain(|_, entry| {
|
||||
let keep_glyph = match *entry {
|
||||
GlyphCacheEntry::Cached(ref glyph) =>
|
||||
texture_cache.is_allocated(&glyph.texture_cache_handle),
|
||||
GlyphCacheEntry::Pending => true,
|
||||
// If the cache only has blank glyphs left, just get rid of it.
|
||||
GlyphCacheEntry::Blank => false,
|
||||
};
|
||||
keep_cache |= keep_glyph;
|
||||
keep_glyph
|
||||
});
|
||||
// Only keep the glyph key cache if it still has valid glyphs.
|
||||
keep_cache
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, texture_cache: &TextureCache) {
|
||||
self.clear_evicted(texture_cache);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
|
||||
#[cfg(test)]
|
||||
use api::{IdNamespace, LayoutPoint};
|
||||
use api::{ColorF, ColorU, DevicePoint, DeviceUintSize};
|
||||
use api::{ColorF, ColorU};
|
||||
use api::{FontInstanceFlags, FontInstancePlatformOptions};
|
||||
use api::{FontKey, FontRenderMode, FontTemplate, FontVariation};
|
||||
use api::{GlyphDimensions, GlyphKey, SubpixelDirection};
|
||||
use api::{ImageData, ImageDescriptor, ImageFormat, LayerToWorldTransform};
|
||||
use app_units::Au;
|
||||
use device::TextureFilter;
|
||||
use glyph_cache::{CachedGlyphInfo, GlyphCache};
|
||||
use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::{FastHashSet, ResourceCacheError};
|
||||
use internal_types::ResourceCacheError;
|
||||
use platform::font::FontContext;
|
||||
use profiler::TextureCacheProfileCounters;
|
||||
use rayon::ThreadPool;
|
||||
|
@ -309,11 +309,11 @@ pub struct GlyphRasterizer {
|
|||
// because the glyph cache hash table is not updated
|
||||
// until the end of the frame when we wait for glyph requests
|
||||
// to be resolved.
|
||||
pending_glyphs: FastHashSet<GlyphRequest>,
|
||||
pending_glyphs: usize,
|
||||
|
||||
// Receives the rendered glyphs.
|
||||
glyph_rx: Receiver<Vec<GlyphRasterJob>>,
|
||||
glyph_tx: Sender<Vec<GlyphRasterJob>>,
|
||||
glyph_rx: Receiver<GlyphRasterJobs>,
|
||||
glyph_tx: Sender<GlyphRasterJobs>,
|
||||
|
||||
// We defer removing fonts to the end of the frame so that:
|
||||
// - this work is done outside of the critical path,
|
||||
|
@ -341,7 +341,7 @@ impl GlyphRasterizer {
|
|||
shared_context: Mutex::new(shared_context),
|
||||
workers: Arc::clone(&workers),
|
||||
}),
|
||||
pending_glyphs: FastHashSet::default(),
|
||||
pending_glyphs: 0,
|
||||
glyph_rx,
|
||||
glyph_tx,
|
||||
workers,
|
||||
|
@ -391,7 +391,7 @@ impl GlyphRasterizer {
|
|||
.lock_shared_context()
|
||||
.has_font(&font.font_key)
|
||||
);
|
||||
let mut glyphs = Vec::new();
|
||||
let mut new_glyphs = Vec::new();
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
|
||||
|
||||
|
@ -399,58 +399,51 @@ impl GlyphRasterizer {
|
|||
for key in glyph_keys {
|
||||
match glyph_key_cache.entry(key.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
if let Ok(Some(ref mut glyph_info)) = *entry.get_mut() {
|
||||
if texture_cache.request(&mut glyph_info.texture_cache_handle, gpu_cache) {
|
||||
// This case gets hit when we have already rasterized
|
||||
// the glyph and stored it in CPU memory, but the glyph
|
||||
// has been evicted from the texture cache. In which case
|
||||
// we need to re-upload it to the GPU.
|
||||
texture_cache.update(
|
||||
&mut glyph_info.texture_cache_handle,
|
||||
ImageDescriptor {
|
||||
width: glyph_info.size.width,
|
||||
height: glyph_info.size.height,
|
||||
stride: None,
|
||||
format: ImageFormat::BGRA8,
|
||||
is_opaque: false,
|
||||
offset: 0,
|
||||
},
|
||||
TextureFilter::Linear,
|
||||
Some(ImageData::Raw(glyph_info.glyph_bytes.clone())),
|
||||
[glyph_info.offset.x, glyph_info.offset.y, glyph_info.scale],
|
||||
None,
|
||||
gpu_cache,
|
||||
);
|
||||
let value = entry.into_mut();
|
||||
match *value {
|
||||
GlyphCacheEntry::Cached(ref glyph) => {
|
||||
// Skip the glyph if it is already has a valid texture cache handle.
|
||||
if !texture_cache.request(&glyph.texture_cache_handle, gpu_cache) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Otherwise, skip the entry if it is blank or pending.
|
||||
GlyphCacheEntry::Blank |
|
||||
GlyphCacheEntry::Pending => continue,
|
||||
}
|
||||
// This case gets hit when we already rasterized the glyph, but the
|
||||
// glyph has been evicted from the texture cache. Just force it to
|
||||
// pending so it gets rematerialized.
|
||||
*value = GlyphCacheEntry::Pending;
|
||||
}
|
||||
Entry::Vacant(..) => {
|
||||
let request = GlyphRequest::new(&font, key);
|
||||
if self.pending_glyphs.insert(request.clone()) {
|
||||
glyphs.push(request);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
// This is the first time we've seen the glyph, so mark it as pending.
|
||||
entry.insert(GlyphCacheEntry::Pending);
|
||||
}
|
||||
}
|
||||
|
||||
new_glyphs.push(key.clone());
|
||||
}
|
||||
|
||||
if glyphs.is_empty() {
|
||||
if new_glyphs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.pending_glyphs += 1;
|
||||
let font_contexts = Arc::clone(&self.font_contexts);
|
||||
let glyph_tx = self.glyph_tx.clone();
|
||||
// spawn an async task to get off of the render backend thread as early as
|
||||
// possible and in that task use rayon's fork join dispatch to rasterize the
|
||||
// glyphs in the thread pool.
|
||||
self.workers.spawn(move || {
|
||||
let jobs = glyphs
|
||||
let jobs = new_glyphs
|
||||
.par_iter()
|
||||
.map(|request: &GlyphRequest| {
|
||||
.map(|key: &GlyphKey| {
|
||||
profile_scope!("glyph-raster");
|
||||
let mut context = font_contexts.lock_current_context();
|
||||
let job = GlyphRasterJob {
|
||||
request: request.clone(),
|
||||
result: context.rasterize_glyph(&request.font, &request.key),
|
||||
key: key.clone(),
|
||||
result: context.rasterize_glyph(&font, key),
|
||||
};
|
||||
|
||||
// Sanity check.
|
||||
|
@ -466,7 +459,7 @@ impl GlyphRasterizer {
|
|||
})
|
||||
.collect();
|
||||
|
||||
glyph_tx.send(jobs).unwrap();
|
||||
glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -493,72 +486,60 @@ impl GlyphRasterizer {
|
|||
gpu_cache: &mut GpuCache,
|
||||
_texture_cache_profile: &mut TextureCacheProfileCounters,
|
||||
) {
|
||||
let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len());
|
||||
// Pull rasterized glyphs from the queue and Update the caches.
|
||||
while self.pending_glyphs > 0 {
|
||||
self.pending_glyphs -= 1;
|
||||
|
||||
// Pull rasterized glyphs from the queue.
|
||||
|
||||
while !self.pending_glyphs.is_empty() {
|
||||
// TODO: rather than blocking until all pending glyphs are available
|
||||
// we could try_recv and steal work from the thread pool to take advantage
|
||||
// of the fact that this thread is alive and we avoid the added latency
|
||||
// of blocking it.
|
||||
let raster_jobs = self.glyph_rx
|
||||
let GlyphRasterJobs { font, mut jobs } = self.glyph_rx
|
||||
.recv()
|
||||
.expect("BUG: Should be glyphs pending!");
|
||||
for job in raster_jobs {
|
||||
debug_assert!(self.pending_glyphs.contains(&job.request));
|
||||
self.pending_glyphs.remove(&job.request);
|
||||
|
||||
rasterized_glyphs.push(job);
|
||||
}
|
||||
}
|
||||
// Ensure that the glyphs are always processed in the same
|
||||
// order for a given text run (since iterating a hash set doesn't
|
||||
// guarantee order). This can show up as very small float inaccuacry
|
||||
// differences in rasterizers due to the different coordinates
|
||||
// that text runs get associated with by the texture cache allocator.
|
||||
jobs.sort_by(|a, b| a.key.cmp(&b.key));
|
||||
|
||||
// Ensure that the glyphs are always processed in the same
|
||||
// order for a given text run (since iterating a hash set doesn't
|
||||
// guarantee order). This can show up as very small float inaccuacry
|
||||
// differences in rasterizers due to the different coordinates
|
||||
// that text runs get associated with by the texture cache allocator.
|
||||
rasterized_glyphs.sort_by(|a, b| a.request.cmp(&b.request));
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font);
|
||||
|
||||
// Update the caches.
|
||||
for job in rasterized_glyphs {
|
||||
let glyph_info = job.result
|
||||
.and_then(|glyph| if glyph.width > 0 && glyph.height > 0 {
|
||||
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
||||
let glyph_bytes = Arc::new(glyph.bytes);
|
||||
let mut texture_cache_handle = TextureCacheHandle::new();
|
||||
texture_cache.request(&mut texture_cache_handle, gpu_cache);
|
||||
texture_cache.update(
|
||||
&mut texture_cache_handle,
|
||||
ImageDescriptor {
|
||||
width: glyph.width,
|
||||
height: glyph.height,
|
||||
stride: None,
|
||||
format: ImageFormat::BGRA8,
|
||||
is_opaque: false,
|
||||
offset: 0,
|
||||
},
|
||||
TextureFilter::Linear,
|
||||
Some(ImageData::Raw(glyph_bytes.clone())),
|
||||
[glyph.left, -glyph.top, glyph.scale],
|
||||
None,
|
||||
gpu_cache,
|
||||
);
|
||||
Some(CachedGlyphInfo {
|
||||
texture_cache_handle,
|
||||
glyph_bytes,
|
||||
size: DeviceUintSize::new(glyph.width, glyph.height),
|
||||
offset: DevicePoint::new(glyph.left, -glyph.top),
|
||||
scale: glyph.scale,
|
||||
format: glyph.format,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
for GlyphRasterJob { key, result } in jobs {
|
||||
let glyph_info = result.map_or(GlyphCacheEntry::Blank, |glyph| {
|
||||
if glyph.width > 0 && glyph.height > 0 {
|
||||
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
|
||||
let mut texture_cache_handle = TextureCacheHandle::new();
|
||||
texture_cache.request(&mut texture_cache_handle, gpu_cache);
|
||||
texture_cache.update(
|
||||
&mut texture_cache_handle,
|
||||
ImageDescriptor {
|
||||
width: glyph.width,
|
||||
height: glyph.height,
|
||||
stride: None,
|
||||
format: ImageFormat::BGRA8,
|
||||
is_opaque: false,
|
||||
offset: 0,
|
||||
},
|
||||
TextureFilter::Linear,
|
||||
Some(ImageData::Raw(Arc::new(glyph.bytes))),
|
||||
[glyph.left, -glyph.top, glyph.scale],
|
||||
None,
|
||||
gpu_cache,
|
||||
Some(glyph_key_cache.eviction_notice()),
|
||||
);
|
||||
GlyphCacheEntry::Cached(CachedGlyphInfo {
|
||||
texture_cache_handle,
|
||||
format: glyph.format,
|
||||
})
|
||||
} else {
|
||||
GlyphCacheEntry::Blank
|
||||
}
|
||||
});
|
||||
|
||||
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font);
|
||||
|
||||
glyph_key_cache.insert(job.request.key, Ok(glyph_info));
|
||||
glyph_key_cache.insert(key, glyph_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we are done with the critical path (rendering the glyphs),
|
||||
|
@ -583,7 +564,7 @@ impl GlyphRasterizer {
|
|||
#[cfg(feature = "replay")]
|
||||
pub fn reset(&mut self) {
|
||||
//TODO: any signals need to be sent to the workers?
|
||||
self.pending_glyphs.clear();
|
||||
self.pending_glyphs = 0;
|
||||
self.fonts_to_remove.clear();
|
||||
}
|
||||
}
|
||||
|
@ -619,10 +600,15 @@ impl GlyphRequest {
|
|||
}
|
||||
|
||||
struct GlyphRasterJob {
|
||||
request: GlyphRequest,
|
||||
key: GlyphKey,
|
||||
result: Option<RasterizedGlyph>,
|
||||
}
|
||||
|
||||
struct GlyphRasterJobs {
|
||||
font: FontInstance,
|
||||
jobs: Vec<GlyphRasterJob>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rasterize_200_glyphs() {
|
||||
// This test loads a font from disc, the renders 4 requests containing
|
||||
|
|
|
@ -222,8 +222,7 @@ pub enum BrushKind {
|
|||
gradient_index: CachedGradientIndex,
|
||||
stops_range: ItemRange<GradientStop>,
|
||||
extend_mode: ExtendMode,
|
||||
start_center: LayerPoint,
|
||||
end_center: LayerPoint,
|
||||
center: LayerPoint,
|
||||
start_radius: f32,
|
||||
end_radius: f32,
|
||||
ratio_xy: f32,
|
||||
|
@ -379,18 +378,18 @@ impl BrushPrimitive {
|
|||
0.0,
|
||||
]);
|
||||
}
|
||||
BrushKind::RadialGradient { start_center, end_center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
|
||||
request.push([
|
||||
start_center.x,
|
||||
start_center.y,
|
||||
end_center.x,
|
||||
end_center.y,
|
||||
]);
|
||||
BrushKind::RadialGradient { center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
|
||||
request.push([
|
||||
center.x,
|
||||
center.y,
|
||||
start_radius,
|
||||
end_radius,
|
||||
]);
|
||||
request.push([
|
||||
ratio_xy,
|
||||
pack_as_float(extend_mode as u32),
|
||||
0.,
|
||||
0.,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{ApiMsg, FrameMsg};
|
||||
use api::{ApiMsg, FrameMsg, SceneMsg};
|
||||
use bincode::{serialize, Infinite};
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::any::TypeId;
|
||||
|
@ -67,11 +67,23 @@ pub fn should_record_msg(msg: &ApiMsg) -> bool {
|
|||
ApiMsg::AddDocument { .. } |
|
||||
ApiMsg::DeleteDocument(..) => true,
|
||||
ApiMsg::UpdateDocument(_, ref msgs) => {
|
||||
if msgs.generate_frame {
|
||||
return true;
|
||||
}
|
||||
|
||||
for msg in &msgs.scene_ops {
|
||||
match *msg {
|
||||
SceneMsg::SetDisplayList { .. } |
|
||||
SceneMsg::SetRootPipeline { .. } => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for msg in &msgs.frame_ops {
|
||||
match *msg {
|
||||
FrameMsg::GetScrollNodeState(..) |
|
||||
FrameMsg::HitTest(..) => {}
|
||||
_ => { return true; }
|
||||
_ => return true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -695,8 +695,9 @@ impl RenderBackend {
|
|||
|
||||
pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
|
||||
let mut frame_counter: u32 = 0;
|
||||
let mut keep_going = true;
|
||||
|
||||
loop {
|
||||
while keep_going {
|
||||
profile_scope!("handle_msg");
|
||||
|
||||
while let Ok(msg) = self.scene_rx.try_recv() {
|
||||
|
@ -740,7 +741,7 @@ impl RenderBackend {
|
|||
}
|
||||
}
|
||||
|
||||
let keep_going = match self.api_rx.recv() {
|
||||
keep_going = match self.api_rx.recv() {
|
||||
Ok(msg) => {
|
||||
if let Some(ref mut r) = self.recorder {
|
||||
r.write_msg(frame_counter, &msg);
|
||||
|
@ -749,13 +750,10 @@ impl RenderBackend {
|
|||
}
|
||||
Err(..) => { false }
|
||||
};
|
||||
|
||||
if !keep_going {
|
||||
let _ = self.scene_tx.send(SceneBuilderRequest::Stop);
|
||||
self.notifier.shut_down();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self.scene_tx.send(SceneBuilderRequest::Stop);
|
||||
self.notifier.shut_down();
|
||||
}
|
||||
|
||||
fn process_api_msg(
|
||||
|
@ -965,14 +963,6 @@ impl RenderBackend {
|
|||
|
||||
let doc = self.documents.get_mut(&document_id).unwrap();
|
||||
|
||||
if !doc.can_render() {
|
||||
// TODO: this happens if we are building the first scene asynchronously and
|
||||
// scroll at the same time. we should keep track of the fact that we skipped
|
||||
// composition here and do it as soon as we receive the scene.
|
||||
op.render = false;
|
||||
op.composite = false;
|
||||
}
|
||||
|
||||
if transaction_msg.generate_frame {
|
||||
if let Some(ref mut ros) = doc.render_on_scroll {
|
||||
*ros = true;
|
||||
|
@ -984,6 +974,14 @@ impl RenderBackend {
|
|||
}
|
||||
}
|
||||
|
||||
if !doc.can_render() {
|
||||
// TODO: this happens if we are building the first scene asynchronously and
|
||||
// scroll at the same time. we should keep track of the fact that we skipped
|
||||
// composition here and do it as soon as we receive the scene.
|
||||
op.render = false;
|
||||
op.composite = false;
|
||||
}
|
||||
|
||||
debug_assert!(op.render || !op.composite);
|
||||
|
||||
if op.render {
|
||||
|
|
|
@ -933,6 +933,7 @@ impl RenderTaskCache {
|
|||
[0.0; 3],
|
||||
None,
|
||||
gpu_cache,
|
||||
None,
|
||||
);
|
||||
|
||||
// Get the allocation details in the texture cache, and store
|
||||
|
|
|
@ -35,7 +35,7 @@ use device::{ProgramCache, ReadPixelsFormat};
|
|||
use euclid::{rect, Transform3D};
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use gleam::gl;
|
||||
use glyph_rasterizer::GlyphFormat;
|
||||
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
|
||||
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
|
||||
use gpu_types::PrimitiveInstance;
|
||||
use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
|
||||
|
@ -1309,9 +1309,6 @@ impl Renderer {
|
|||
|
||||
let shaders = Shaders::new(&mut device, gl_type, &options)?;
|
||||
|
||||
let texture_cache = TextureCache::new(max_device_size);
|
||||
let max_texture_size = texture_cache.max_texture_size();
|
||||
|
||||
let backend_profile_counters = BackendProfileCounters::new();
|
||||
|
||||
let dither_matrix_texture = if options.enable_dithering {
|
||||
|
@ -1489,11 +1486,7 @@ impl Renderer {
|
|||
let thread_listener_for_scene_builder = thread_listener.clone();
|
||||
let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
|
||||
let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
|
||||
let resource_cache = ResourceCache::new(
|
||||
texture_cache,
|
||||
workers,
|
||||
blob_image_renderer,
|
||||
)?;
|
||||
let glyph_rasterizer = GlyphRasterizer::new(workers)?;
|
||||
|
||||
let (scene_builder, scene_tx, scene_rx) = SceneBuilder::new(config, api_tx.clone());
|
||||
thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
|
||||
|
@ -1515,6 +1508,14 @@ impl Renderer {
|
|||
if let Some(ref thread_listener) = *thread_listener_for_render_backend {
|
||||
thread_listener.thread_started(&rb_thread_name);
|
||||
}
|
||||
|
||||
let texture_cache = TextureCache::new(max_device_size);
|
||||
let resource_cache = ResourceCache::new(
|
||||
texture_cache,
|
||||
glyph_rasterizer,
|
||||
blob_image_renderer,
|
||||
);
|
||||
|
||||
let mut backend = RenderBackend::new(
|
||||
api_rx,
|
||||
payload_rx_for_backend,
|
||||
|
@ -1552,7 +1553,7 @@ impl Renderer {
|
|||
backend_profile_counters: BackendProfileCounters::new(),
|
||||
profile_counters: RendererProfileCounters::new(),
|
||||
profiler: Profiler::new(),
|
||||
max_texture_size: max_texture_size,
|
||||
max_texture_size: max_device_size,
|
||||
max_recorded_profiles: options.max_recorded_profiles,
|
||||
clear_color: options.clear_color,
|
||||
enable_clear_scissor: options.enable_clear_scissor,
|
||||
|
@ -2358,14 +2359,6 @@ impl Renderer {
|
|||
textures: &BatchTextures,
|
||||
stats: &mut RendererStats,
|
||||
) {
|
||||
// Work around Angle bug that forgets to update sampler metadata,
|
||||
// by making the use of those samplers uniform across programs.
|
||||
// https://github.com/servo/webrender/wiki/Driver-issues#texturesize-in-vertex-shaders
|
||||
let work_around_angle_bug = cfg!(windows);
|
||||
if work_around_angle_bug {
|
||||
self.device.reset_angle_sampler_metadata(&self.texture_resolver.dummy_cache_texture);
|
||||
}
|
||||
|
||||
for i in 0 .. textures.colors.len() {
|
||||
self.texture_resolver.bind(
|
||||
&textures.colors[i],
|
||||
|
|
|
@ -19,16 +19,11 @@ use capture::PlainExternalImage;
|
|||
#[cfg(any(feature = "replay", feature = "png"))]
|
||||
use capture::CaptureConfig;
|
||||
use device::TextureFilter;
|
||||
use glyph_cache::GlyphCache;
|
||||
#[cfg(feature = "capture")]
|
||||
use glyph_cache::{PlainGlyphCacheRef, PlainCachedGlyphInfo};
|
||||
#[cfg(feature = "replay")]
|
||||
use glyph_cache::{CachedGlyphInfo, PlainGlyphCacheOwn};
|
||||
use glyph_cache::{GlyphCache, GlyphCacheEntry};
|
||||
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
|
||||
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
|
||||
use internal_types::{FastHashMap, FastHashSet, ResourceCacheError, SourceTexture, TextureUpdateList};
|
||||
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
|
||||
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
|
||||
use rayon::ThreadPool;
|
||||
use render_backend::FrameId;
|
||||
use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId, RenderTaskTree};
|
||||
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
|
||||
|
@ -143,46 +138,40 @@ struct CachedImageInfo {
|
|||
epoch: Epoch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Clone, Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum ResourceClassCacheError {
|
||||
OverLimitSize,
|
||||
}
|
||||
|
||||
pub type ResourceCacheResult<V> = Result<V, ResourceClassCacheError>;
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct ResourceClassCache<K: Hash + Eq, V> {
|
||||
resources: FastHashMap<K, ResourceCacheResult<V>>,
|
||||
pub struct ResourceClassCache<K: Hash + Eq, V, U: Default> {
|
||||
resources: FastHashMap<K, V>,
|
||||
pub user_data: U,
|
||||
}
|
||||
|
||||
impl<K, V> ResourceClassCache<K, V>
|
||||
impl<K, V, U> ResourceClassCache<K, V, U>
|
||||
where
|
||||
K: Clone + Hash + Eq + Debug,
|
||||
U: Default,
|
||||
{
|
||||
pub fn new() -> ResourceClassCache<K, V> {
|
||||
pub fn new() -> ResourceClassCache<K, V, U> {
|
||||
ResourceClassCache {
|
||||
resources: FastHashMap::default(),
|
||||
user_data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, key: &K) -> &ResourceCacheResult<V> {
|
||||
pub fn get(&self, key: &K) -> &V {
|
||||
self.resources.get(key)
|
||||
.expect("Didn't find a cached resource with that ID!")
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: K, value: ResourceCacheResult<V>) {
|
||||
pub fn insert(&mut self, key: K, value: V) {
|
||||
self.resources.insert(key, value);
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, key: &K) -> &mut ResourceCacheResult<V> {
|
||||
pub fn get_mut(&mut self, key: &K) -> &mut V {
|
||||
self.resources.get_mut(key)
|
||||
.expect("Didn't find a cached resource with that ID!")
|
||||
}
|
||||
|
||||
pub fn entry(&mut self, key: K) -> Entry<K, ResourceCacheResult<V>> {
|
||||
pub fn entry(&mut self, key: K) -> Entry<K, V> {
|
||||
self.resources.entry(key)
|
||||
}
|
||||
|
||||
|
@ -203,6 +192,13 @@ where
|
|||
let _ = self.resources.remove(&key).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
{
|
||||
self.resources.retain(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,7 +220,14 @@ impl Into<BlobImageRequest> for ImageRequest {
|
|||
}
|
||||
}
|
||||
|
||||
type ImageCache = ResourceClassCache<ImageRequest, CachedImageInfo>;
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Clone, Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum ImageCacheError {
|
||||
OverLimitSize,
|
||||
}
|
||||
|
||||
type ImageCache = ResourceClassCache<ImageRequest, Result<CachedImageInfo, ImageCacheError>, ()>;
|
||||
pub type FontInstanceMap = Arc<RwLock<FastHashMap<FontInstanceKey, FontInstance>>>;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -273,12 +276,10 @@ pub struct ResourceCache {
|
|||
impl ResourceCache {
|
||||
pub fn new(
|
||||
texture_cache: TextureCache,
|
||||
workers: Arc<ThreadPool>,
|
||||
glyph_rasterizer: GlyphRasterizer,
|
||||
blob_image_renderer: Option<Box<BlobImageRenderer>>,
|
||||
) -> Result<Self, ResourceCacheError> {
|
||||
let glyph_rasterizer = GlyphRasterizer::new(workers)?;
|
||||
|
||||
Ok(ResourceCache {
|
||||
) -> Self {
|
||||
ResourceCache {
|
||||
cached_glyphs: GlyphCache::new(),
|
||||
cached_images: ResourceClassCache::new(),
|
||||
cached_render_tasks: RenderTaskCache::new(),
|
||||
|
@ -290,7 +291,7 @@ impl ResourceCache {
|
|||
pending_image_requests: FastHashSet::default(),
|
||||
glyph_rasterizer,
|
||||
blob_image_renderer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_texture_size(&self) -> u32 {
|
||||
|
@ -566,7 +567,7 @@ impl ResourceCache {
|
|||
// The image or tiling size is too big for hardware texture size.
|
||||
warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
|
||||
template.descriptor.width, template.descriptor.height, template.tiling.unwrap_or(0));
|
||||
self.cached_images.insert(request, Err(ResourceClassCacheError::OverLimitSize));
|
||||
self.cached_images.insert(request, Err(ImageCacheError::OverLimitSize));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -682,7 +683,7 @@ impl ResourceCache {
|
|||
debug_assert!(fetch_buffer.is_empty());
|
||||
|
||||
for (loop_index, key) in glyph_keys.iter().enumerate() {
|
||||
if let Ok(Some(ref glyph)) = *glyph_key_cache.get(key) {
|
||||
if let GlyphCacheEntry::Cached(ref glyph) = *glyph_key_cache.get(key) {
|
||||
let cache_item = self.texture_cache.get(&glyph.texture_cache_handle);
|
||||
if current_texture_id != cache_item.texture_id ||
|
||||
current_glyph_format != glyph.format {
|
||||
|
@ -794,6 +795,7 @@ impl ResourceCache {
|
|||
debug_assert_eq!(self.state, State::Idle);
|
||||
self.state = State::AddResources;
|
||||
self.texture_cache.begin_frame(frame_id);
|
||||
self.cached_glyphs.begin_frame(&mut self.texture_cache);
|
||||
self.cached_render_tasks.begin_frame(&mut self.texture_cache);
|
||||
self.current_frame_id = frame_id;
|
||||
}
|
||||
|
@ -928,6 +930,7 @@ impl ResourceCache {
|
|||
[0.0; 3],
|
||||
image_template.dirty_rect,
|
||||
gpu_cache,
|
||||
None,
|
||||
);
|
||||
image_template.dirty_rect = None;
|
||||
}
|
||||
|
@ -1035,7 +1038,7 @@ pub struct PlainResources {
|
|||
#[derive(Serialize)]
|
||||
pub struct PlainCacheRef<'a> {
|
||||
current_frame_id: FrameId,
|
||||
glyphs: PlainGlyphCacheRef<'a>,
|
||||
glyphs: &'a GlyphCache,
|
||||
glyph_dimensions: &'a GlyphDimensionsCache,
|
||||
images: &'a ImageCache,
|
||||
render_tasks: &'a RenderTaskCache,
|
||||
|
@ -1046,7 +1049,7 @@ pub struct PlainCacheRef<'a> {
|
|||
#[derive(Deserialize)]
|
||||
pub struct PlainCacheOwn {
|
||||
current_frame_id: FrameId,
|
||||
glyphs: PlainGlyphCacheOwn,
|
||||
glyphs: GlyphCache,
|
||||
glyph_dimensions: GlyphDimensionsCache,
|
||||
images: ImageCache,
|
||||
render_tasks: RenderTaskCache,
|
||||
|
@ -1228,64 +1231,10 @@ impl ResourceCache {
|
|||
}
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
pub fn save_caches(&self, root: &PathBuf) -> PlainCacheRef {
|
||||
use std::io::Write;
|
||||
use std::fs;
|
||||
|
||||
let path_glyphs = root.join("glyphs");
|
||||
if !path_glyphs.is_dir() {
|
||||
fs::create_dir(&path_glyphs).unwrap();
|
||||
}
|
||||
|
||||
info!("\tcached glyphs");
|
||||
let mut glyph_paths = FastHashMap::default();
|
||||
for cache in self.cached_glyphs.glyph_key_caches.values() {
|
||||
for result in cache.resources.values() {
|
||||
let arc = match *result {
|
||||
Ok(Some(ref info)) => &info.glyph_bytes,
|
||||
Ok(None) | Err(_) => continue,
|
||||
};
|
||||
let glyph_id = glyph_paths.len() + 1;
|
||||
let entry = match glyph_paths.entry(arc.as_ptr()) {
|
||||
Entry::Occupied(_) => continue,
|
||||
Entry::Vacant(e) => e,
|
||||
};
|
||||
|
||||
let file_name = format!("{}.raw", glyph_id);
|
||||
let short_path = format!("glyphs/{}", file_name);
|
||||
fs::File::create(path_glyphs.join(&file_name))
|
||||
.expect(&format!("Unable to create {}", short_path))
|
||||
.write_all(&*arc)
|
||||
.unwrap();
|
||||
entry.insert(short_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_caches(&self, _root: &PathBuf) -> PlainCacheRef {
|
||||
PlainCacheRef {
|
||||
current_frame_id: self.current_frame_id,
|
||||
glyphs: self.cached_glyphs.glyph_key_caches
|
||||
.iter()
|
||||
.map(|(font_instance, cache)| {
|
||||
let resources = cache.resources
|
||||
.iter()
|
||||
.map(|(key, result)| {
|
||||
(key.clone(), match *result {
|
||||
Ok(Some(ref info)) => Ok(Some(PlainCachedGlyphInfo {
|
||||
texture_cache_handle: info.texture_cache_handle.clone(),
|
||||
glyph_bytes: glyph_paths[&info.glyph_bytes.as_ptr()].clone(),
|
||||
size: info.size,
|
||||
offset: info.offset,
|
||||
scale: info.scale,
|
||||
format: info.format,
|
||||
})),
|
||||
Ok(None) => Ok(None),
|
||||
Err(ref e) => Err(e.clone()),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
(font_instance, ResourceClassCache { resources })
|
||||
})
|
||||
.collect(),
|
||||
glyphs: &self.cached_glyphs,
|
||||
glyph_dimensions: &self.cached_glyph_dimensions,
|
||||
images: &self.cached_images,
|
||||
render_tasks: &self.cached_render_tasks,
|
||||
|
@ -1311,47 +1260,8 @@ impl ResourceCache {
|
|||
|
||||
match caches {
|
||||
Some(cached) => {
|
||||
let glyph_key_caches = cached.glyphs
|
||||
.into_iter()
|
||||
.map(|(font_instance, rcc)| {
|
||||
let resources = rcc.resources
|
||||
.into_iter()
|
||||
.map(|(key, result)| {
|
||||
(key, match result {
|
||||
Ok(Some(info)) => {
|
||||
let glyph_bytes = match raw_map.entry(info.glyph_bytes) {
|
||||
Entry::Occupied(e) => {
|
||||
e.get().clone()
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
let mut buffer = Vec::new();
|
||||
File::open(root.join(e.key()))
|
||||
.expect(&format!("Unable to open {}", e.key()))
|
||||
.read_to_end(&mut buffer)
|
||||
.unwrap();
|
||||
e.insert(Arc::new(buffer))
|
||||
.clone()
|
||||
}
|
||||
};
|
||||
Ok(Some(CachedGlyphInfo {
|
||||
texture_cache_handle: info.texture_cache_handle,
|
||||
glyph_bytes,
|
||||
size: info.size,
|
||||
offset: info.offset,
|
||||
scale: info.scale,
|
||||
format: info.format,
|
||||
}))
|
||||
},
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
(font_instance, ResourceClassCache { resources })
|
||||
})
|
||||
.collect();
|
||||
self.current_frame_id = cached.current_frame_id;
|
||||
self.cached_glyphs = GlyphCache { glyph_key_caches };
|
||||
self.cached_glyphs = cached.glyphs;
|
||||
self.cached_glyph_dimensions = cached.glyph_dimensions;
|
||||
self.cached_images = cached.images;
|
||||
self.cached_render_tasks = cached.render_tasks;
|
||||
|
|
|
@ -14,8 +14,10 @@ use internal_types::{RenderTargetInfo, SourceTexture, TextureUpdate, TextureUpda
|
|||
use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
|
||||
use render_backend::FrameId;
|
||||
use resource_cache::CacheItem;
|
||||
use std::cell::Cell;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
// The fixed number of layers for the shared texture cache.
|
||||
// There is one array texture per image format, allocated lazily.
|
||||
|
@ -103,6 +105,8 @@ struct CacheEntry {
|
|||
filter: TextureFilter,
|
||||
// The actual device texture ID this is part of.
|
||||
texture_id: CacheTextureId,
|
||||
// Optional notice when the entry is evicted from the cache.
|
||||
eviction_notice: Option<EvictionNotice>,
|
||||
}
|
||||
|
||||
impl CacheEntry {
|
||||
|
@ -124,6 +128,7 @@ impl CacheEntry {
|
|||
format,
|
||||
filter,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
eviction_notice: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +155,12 @@ impl CacheEntry {
|
|||
image_source.write_gpu_blocks(&mut request);
|
||||
}
|
||||
}
|
||||
|
||||
fn evict(&self) {
|
||||
if let Some(eviction_notice) = self.eviction_notice.as_ref() {
|
||||
eviction_notice.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type WeakCacheEntryHandle = WeakFreeListHandle<CacheEntry>;
|
||||
|
@ -173,6 +184,34 @@ impl TextureCacheHandle {
|
|||
}
|
||||
}
|
||||
|
||||
// An eviction notice is a shared condition useful for detecting
|
||||
// when a TextureCacheHandle gets evicted from the TextureCache.
|
||||
// It is optionally installed to the TextureCache when an update()
|
||||
// is scheduled. A single notice may be shared among any number of
|
||||
// TextureCacheHandle updates. The notice may then be subsequently
|
||||
// checked to see if any of the updates using it have been evicted.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct EvictionNotice {
|
||||
evicted: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl EvictionNotice {
|
||||
fn notify(&self) {
|
||||
self.evicted.set(true);
|
||||
}
|
||||
|
||||
pub fn check(&self) -> bool {
|
||||
if self.evicted.get() {
|
||||
self.evicted.set(false);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct TextureCache {
|
||||
|
@ -301,6 +340,7 @@ impl TextureCache {
|
|||
user_data: [f32; 3],
|
||||
mut dirty_rect: Option<DeviceUintRect>,
|
||||
gpu_cache: &mut GpuCache,
|
||||
eviction_notice: Option<&EvictionNotice>,
|
||||
) {
|
||||
// Determine if we need to allocate texture cache memory
|
||||
// for this item. We need to reallocate if any of the following
|
||||
|
@ -339,6 +379,9 @@ impl TextureCache {
|
|||
.get_opt_mut(handle.entry.as_ref().unwrap())
|
||||
.expect("BUG: handle must be valid now");
|
||||
|
||||
// Install the new eviction notice for this update, if applicable.
|
||||
entry.eviction_notice = eviction_notice.cloned();
|
||||
|
||||
// Invalidate the contents of the resource rect in the GPU cache.
|
||||
// This ensures that the update_gpu_cache below will add
|
||||
// the new information to the GPU cache.
|
||||
|
@ -498,6 +541,7 @@ impl TextureCache {
|
|||
// Free the selected items
|
||||
for handle in eviction_candidates {
|
||||
let entry = self.entries.free(handle);
|
||||
entry.evict();
|
||||
self.free(entry);
|
||||
}
|
||||
|
||||
|
@ -552,6 +596,7 @@ impl TextureCache {
|
|||
retained_entries.push(handle);
|
||||
} else {
|
||||
let entry = self.entries.free(handle);
|
||||
entry.evict();
|
||||
if let Some(region) = self.free(entry) {
|
||||
found_matching_slab |= region.slab_size == needed_slab_size;
|
||||
freed_complete_page |= region.is_empty();
|
||||
|
@ -1069,6 +1114,7 @@ impl TextureArray {
|
|||
format: self.format,
|
||||
filter: self.filter,
|
||||
texture_id: self.texture_id.unwrap(),
|
||||
eviction_notice: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -418,11 +418,10 @@ pub struct GradientStop {
|
|||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct RadialGradient {
|
||||
pub start_center: LayoutPoint,
|
||||
pub center: LayoutPoint,
|
||||
pub radius: LayoutSize,
|
||||
pub start_radius: f32,
|
||||
pub end_center: LayoutPoint,
|
||||
pub end_radius: f32,
|
||||
pub ratio_xy: f32,
|
||||
pub extend_mode: ExtendMode,
|
||||
} // IMPLICIT stops: Vec<GradientStop>
|
||||
|
||||
|
|
|
@ -1188,11 +1188,10 @@ impl DisplayListBuilder {
|
|||
self.push_stops(&stops);
|
||||
|
||||
return RadialGradient {
|
||||
start_center: center,
|
||||
center,
|
||||
radius: LayoutSize::new(1.0, 1.0),
|
||||
start_radius: 0.0,
|
||||
end_center: center,
|
||||
end_radius: 1.0,
|
||||
ratio_xy: 1.0,
|
||||
extend_mode,
|
||||
};
|
||||
}
|
||||
|
@ -1203,11 +1202,10 @@ impl DisplayListBuilder {
|
|||
self.push_stops(&stops);
|
||||
|
||||
RadialGradient {
|
||||
start_center: center,
|
||||
center,
|
||||
radius,
|
||||
start_radius: radius.width * start_offset,
|
||||
end_center: center,
|
||||
end_radius: radius.width * end_offset,
|
||||
ratio_xy: radius.width / radius.height,
|
||||
extend_mode,
|
||||
}
|
||||
}
|
||||
|
@ -1216,22 +1214,20 @@ impl DisplayListBuilder {
|
|||
// because create_gradient stores the stops in anticipation
|
||||
pub fn create_complex_radial_gradient(
|
||||
&mut self,
|
||||
start_center: LayoutPoint,
|
||||
center: LayoutPoint,
|
||||
radius: LayoutSize,
|
||||
start_radius: f32,
|
||||
end_center: LayoutPoint,
|
||||
end_radius: f32,
|
||||
ratio_xy: f32,
|
||||
stops: Vec<GradientStop>,
|
||||
extend_mode: ExtendMode,
|
||||
) -> RadialGradient {
|
||||
self.push_stops(&stops);
|
||||
|
||||
RadialGradient {
|
||||
start_center,
|
||||
center,
|
||||
radius,
|
||||
start_radius,
|
||||
end_center,
|
||||
end_radius,
|
||||
ratio_xy,
|
||||
extend_mode,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
486ee5f3aefb0172c2c5703e19f833e63eb295b9
|
||||
2083e83d958dd4a230ccae5c518e4bc8fbf88009
|
||||
|
|
|
@ -6,7 +6,7 @@ build = "build.rs"
|
|||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.3"
|
||||
base64 = "0.6"
|
||||
bincode = "0.9"
|
||||
byteorder = "1.0"
|
||||
env_logger = { version = "0.5", optional = true }
|
||||
|
@ -14,7 +14,7 @@ euclid = "0.17"
|
|||
gleam = "0.4"
|
||||
glutin = "0.12"
|
||||
app_units = "0.6"
|
||||
image = "0.17"
|
||||
image = "0.18"
|
||||
clap = { version = "2", features = ["yaml"] }
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
|
@ -38,6 +38,7 @@ headless = [ "osmesa-sys", "osmesa-src" ]
|
|||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
dwrote = "0.4.1"
|
||||
mozangle = {version = "0.1.5", features = ["egl"]}
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
font-loader = "0.6"
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use glutin;
|
||||
use glutin::{WindowBuilder, ContextBuilder, EventsLoop, Window, CreationError};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub enum Context {}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use ::egl::Context;
|
||||
|
||||
impl Context {
|
||||
#[cfg(not(windows))]
|
||||
pub fn with_window(
|
||||
_: WindowBuilder,
|
||||
_: ContextBuilder,
|
||||
_: &EventsLoop,
|
||||
) -> Result<(Window, Self), CreationError> {
|
||||
Err(CreationError::PlatformSpecific("ANGLE rendering is only supported on Windows".into()))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn with_window(
|
||||
window_builder: WindowBuilder,
|
||||
context_builder: ContextBuilder,
|
||||
events_loop: &EventsLoop,
|
||||
) -> Result<(Window, Self), CreationError> {
|
||||
use glutin::os::windows::WindowExt;
|
||||
|
||||
// FIXME: &context_builder.pf_reqs https://github.com/tomaka/glutin/pull/1002
|
||||
let pf_reqs = &glutin::PixelFormatRequirements::default();
|
||||
let gl_attr = &context_builder.gl_attr.map_sharing(|_| unimplemented!());
|
||||
let window = window_builder.build(events_loop)?;
|
||||
Self::new(pf_reqs, gl_attr)
|
||||
.and_then(|p| p.finish(window.get_hwnd() as _))
|
||||
.map(|context| (window, context))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl glutin::GlContext for Context {
|
||||
unsafe fn make_current(&self) -> Result<(), glutin::ContextError> {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn is_current(&self) -> bool {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, _: &str) -> *const () {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) -> Result<(), glutin::ContextError> {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn get_api(&self) -> glutin::Api {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn get_pixel_format(&self) -> glutin::PixelFormat {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn resize(&self, _: u32, _: u32) {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,9 @@ args:
|
|||
short: h
|
||||
long: headless
|
||||
help: Enable headless rendering
|
||||
- angle:
|
||||
long: angle
|
||||
help: Enable ANGLE rendering (on Windows only)
|
||||
- dp_ratio:
|
||||
short: p
|
||||
long: device-pixel-ratio
|
||||
|
|
|
@ -0,0 +1,617 @@
|
|||
// Licensed under the Apache License, Version 2.0.
|
||||
// This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! Based on https://github.com/tomaka/glutin/blob/1b2d62c0e9/src/api/egl/mod.rs
|
||||
#![cfg(windows)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use glutin::ContextError;
|
||||
use glutin::CreationError;
|
||||
use glutin::GlAttributes;
|
||||
use glutin::GlContext;
|
||||
use glutin::GlRequest;
|
||||
use glutin::PixelFormat;
|
||||
use glutin::PixelFormatRequirements;
|
||||
use glutin::ReleaseBehavior;
|
||||
use glutin::Robustness;
|
||||
use glutin::Api;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_int;
|
||||
use std::{mem, ptr};
|
||||
use std::cell::Cell;
|
||||
|
||||
use mozangle::egl::ffi as egl;
|
||||
mod ffi {
|
||||
pub use mozangle::egl::ffi as egl;
|
||||
pub use mozangle::egl::ffi::*;
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
display: ffi::egl::types::EGLDisplay,
|
||||
context: ffi::egl::types::EGLContext,
|
||||
surface: Cell<ffi::egl::types::EGLSurface>,
|
||||
api: Api,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Start building an EGL context.
|
||||
///
|
||||
/// This function initializes some things and chooses the pixel format.
|
||||
///
|
||||
/// To finish the process, you must call `.finish(window)` on the `ContextPrototype`.
|
||||
pub fn new<'a>(
|
||||
pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &'a GlAttributes<&'a Context>,
|
||||
) -> Result<ContextPrototype<'a>, CreationError>
|
||||
{
|
||||
if opengl.sharing.is_some() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// calling `eglGetDisplay` or equivalent
|
||||
let display = unsafe { egl::GetDisplay(ptr::null_mut()) };
|
||||
|
||||
if display.is_null() {
|
||||
return Err(CreationError::OsError("Could not create EGL display object".to_string()));
|
||||
}
|
||||
|
||||
let egl_version = unsafe {
|
||||
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
|
||||
if egl::Initialize(display, &mut major, &mut minor) == 0 {
|
||||
return Err(CreationError::OsError(format!("eglInitialize failed")))
|
||||
}
|
||||
|
||||
(major, minor)
|
||||
};
|
||||
|
||||
// the list of extensions supported by the client once initialized is different from the
|
||||
// list of extensions obtained earlier
|
||||
let extensions = if egl_version >= (1, 2) {
|
||||
let p = unsafe { CStr::from_ptr(egl::QueryString(display, ffi::egl::EXTENSIONS as i32)) };
|
||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// binding the right API and choosing the version
|
||||
let (version, api) = unsafe {
|
||||
match opengl.version {
|
||||
GlRequest::Latest => {
|
||||
if egl_version >= (1, 4) {
|
||||
if egl::BindAPI(ffi::egl::OPENGL_API) != 0 {
|
||||
(None, Api::OpenGl)
|
||||
} else if egl::BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
|
||||
(None, Api::OpenGlEs)
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
} else {
|
||||
(None, Api::OpenGlEs)
|
||||
}
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGlEs, version) => {
|
||||
if egl_version >= (1, 2) {
|
||||
if egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
}
|
||||
(Some(version), Api::OpenGlEs)
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGl, version) => {
|
||||
if egl_version < (1, 4) {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
if egl::BindAPI(ffi::egl::OPENGL_API) == 0 {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
(Some(version), Api::OpenGl)
|
||||
},
|
||||
GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported),
|
||||
GlRequest::GlThenGles { opengles_version, opengl_version } => {
|
||||
if egl_version >= (1, 4) {
|
||||
if egl::BindAPI(ffi::egl::OPENGL_API) != 0 {
|
||||
(Some(opengl_version), Api::OpenGl)
|
||||
} else if egl::BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
|
||||
(Some(opengles_version), Api::OpenGlEs)
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
} else {
|
||||
(Some(opengles_version), Api::OpenGlEs)
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let (config_id, pixel_format) = unsafe {
|
||||
try!(choose_fbconfig(display, &egl_version, api, version, pf_reqs))
|
||||
};
|
||||
|
||||
Ok(ContextPrototype {
|
||||
opengl: opengl,
|
||||
display: display,
|
||||
egl_version: egl_version,
|
||||
extensions: extensions,
|
||||
api: api,
|
||||
version: version,
|
||||
config_id: config_id,
|
||||
pixel_format: pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GlContext for Context {
|
||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
||||
let ret = egl::MakeCurrent(self.display, self.surface.get(), self.surface.get(), self.context);
|
||||
|
||||
if ret == 0 {
|
||||
match egl::GetError() as u32 {
|
||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
||||
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_current(&self) -> bool {
|
||||
unsafe { egl::GetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
unsafe {
|
||||
egl::GetProcAddress(addr) as *const _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
||||
if self.surface.get() == ffi::egl::NO_SURFACE {
|
||||
return Err(ContextError::ContextLost);
|
||||
}
|
||||
|
||||
let ret = unsafe {
|
||||
egl::SwapBuffers(self.display, self.surface.get())
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
match unsafe { egl::GetError() } as u32 {
|
||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
||||
err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_api(&self) -> Api {
|
||||
self.api
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resize(&self, _: u32, _: u32) {}
|
||||
}
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
unsafe impl Sync for Context {}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||
// is still the current one
|
||||
egl::DestroyContext(self.display, self.context);
|
||||
egl::DestroySurface(self.display, self.surface.get());
|
||||
egl::Terminate(self.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContextPrototype<'a> {
|
||||
opengl: &'a GlAttributes<&'a Context>,
|
||||
display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
extensions: Vec<String>,
|
||||
api: Api,
|
||||
version: Option<(u8, u8)>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
impl<'a> ContextPrototype<'a> {
|
||||
pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint {
|
||||
let mut value = unsafe { mem::uninitialized() };
|
||||
let ret = unsafe { egl::GetConfigAttrib(self.display, self.config_id,
|
||||
ffi::egl::NATIVE_VISUAL_ID
|
||||
as ffi::egl::types::EGLint, &mut value) };
|
||||
if ret == 0 { panic!("eglGetConfigAttrib failed") };
|
||||
value
|
||||
}
|
||||
|
||||
pub fn finish(self, native_window: ffi::EGLNativeWindowType)
|
||||
-> Result<Context, CreationError>
|
||||
{
|
||||
let surface = unsafe {
|
||||
let surface = egl::CreateWindowSurface(self.display, self.config_id, native_window,
|
||||
ptr::null());
|
||||
if surface.is_null() {
|
||||
return Err(CreationError::OsError(format!("eglCreateWindowSurface failed")))
|
||||
}
|
||||
surface
|
||||
};
|
||||
|
||||
self.finish_impl(surface)
|
||||
}
|
||||
|
||||
pub fn finish_pbuffer(self, dimensions: (u32, u32)) -> Result<Context, CreationError> {
|
||||
let attrs = &[
|
||||
ffi::egl::WIDTH as c_int, dimensions.0 as c_int,
|
||||
ffi::egl::HEIGHT as c_int, dimensions.1 as c_int,
|
||||
ffi::egl::NONE as c_int,
|
||||
];
|
||||
|
||||
let surface = unsafe {
|
||||
let surface = egl::CreatePbufferSurface(self.display, self.config_id,
|
||||
attrs.as_ptr());
|
||||
if surface.is_null() {
|
||||
return Err(CreationError::OsError(format!("eglCreatePbufferSurface failed")))
|
||||
}
|
||||
surface
|
||||
};
|
||||
|
||||
self.finish_impl(surface)
|
||||
}
|
||||
|
||||
fn finish_impl(self, surface: ffi::egl::types::EGLSurface)
|
||||
-> Result<Context, CreationError>
|
||||
{
|
||||
let context = unsafe {
|
||||
if let Some(version) = self.version {
|
||||
try!(create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, version, self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness))
|
||||
|
||||
} else if self.api == Api::OpenGlEs {
|
||||
if let Ok(ctxt) = create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (2, 0), self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (1, 0),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
|
||||
} else {
|
||||
if let Ok(ctxt) = create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (3, 2), self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (3, 1),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (1, 0),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Context {
|
||||
display: self.display,
|
||||
context: context,
|
||||
surface: Cell::new(surface),
|
||||
api: self.api,
|
||||
pixel_format: self.pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn choose_fbconfig(display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
api: Api, version: Option<(u8, u8)>, reqs: &PixelFormatRequirements)
|
||||
-> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError>
|
||||
{
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
|
||||
if egl_version >= &(1, 2) {
|
||||
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
|
||||
out.push(ffi::egl::RGB_BUFFER as c_int);
|
||||
}
|
||||
|
||||
out.push(ffi::egl::SURFACE_TYPE as c_int);
|
||||
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
||||
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
||||
out.push((ffi::egl::WINDOW_BIT) as c_int);
|
||||
|
||||
match (api, version) {
|
||||
(Api::OpenGlEs, Some((3, _))) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||
},
|
||||
(Api::OpenGlEs, Some((2, _))) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||
},
|
||||
(Api::OpenGlEs, Some((1, _))) => {
|
||||
if egl_version >= &(1, 3) {
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
||||
}
|
||||
},
|
||||
(Api::OpenGlEs, _) => unimplemented!(),
|
||||
(Api::OpenGl, _) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
||||
},
|
||||
(_, _) => unimplemented!(),
|
||||
};
|
||||
|
||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
||||
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
|
||||
out.push(if hardware_accelerated {
|
||||
ffi::egl::NONE as c_int
|
||||
} else {
|
||||
ffi::egl::SLOW_CONFIG as c_int
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(color) = reqs.color_bits {
|
||||
out.push(ffi::egl::RED_SIZE as c_int);
|
||||
out.push((color / 3) as c_int);
|
||||
out.push(ffi::egl::GREEN_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
||||
out.push(ffi::egl::BLUE_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
||||
}
|
||||
|
||||
if let Some(alpha) = reqs.alpha_bits {
|
||||
out.push(ffi::egl::ALPHA_SIZE as c_int);
|
||||
out.push(alpha as c_int);
|
||||
}
|
||||
|
||||
if let Some(depth) = reqs.depth_bits {
|
||||
out.push(ffi::egl::DEPTH_SIZE as c_int);
|
||||
out.push(depth as c_int);
|
||||
}
|
||||
|
||||
if let Some(stencil) = reqs.stencil_bits {
|
||||
out.push(ffi::egl::STENCIL_SIZE as c_int);
|
||||
out.push(stencil as c_int);
|
||||
}
|
||||
|
||||
if let Some(true) = reqs.double_buffer {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
if let Some(multisampling) = reqs.multisampling {
|
||||
out.push(ffi::egl::SAMPLES as c_int);
|
||||
out.push(multisampling as c_int);
|
||||
}
|
||||
|
||||
if reqs.stereoscopy {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
// FIXME: srgb is not taken into account
|
||||
|
||||
match reqs.release_behavior {
|
||||
ReleaseBehavior::Flush => (),
|
||||
ReleaseBehavior::None => {
|
||||
// TODO: with EGL you need to manually set the behavior
|
||||
unimplemented!()
|
||||
},
|
||||
}
|
||||
|
||||
out.push(ffi::egl::NONE as c_int);
|
||||
out
|
||||
};
|
||||
|
||||
// calling `eglChooseConfig`
|
||||
let mut config_id = mem::uninitialized();
|
||||
let mut num_configs = mem::uninitialized();
|
||||
if egl::ChooseConfig(display, descriptor.as_ptr(), &mut config_id, 1, &mut num_configs) == 0 {
|
||||
return Err(CreationError::OsError(format!("eglChooseConfig failed")));
|
||||
}
|
||||
if num_configs == 0 {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
// analyzing each config
|
||||
macro_rules! attrib {
|
||||
($display:expr, $config:expr, $attr:expr) => (
|
||||
{
|
||||
let mut value = mem::uninitialized();
|
||||
let res = egl::GetConfigAttrib($display, $config,
|
||||
$attr as ffi::egl::types::EGLint, &mut value);
|
||||
if res == 0 {
|
||||
return Err(CreationError::OsError(format!("eglGetConfigAttrib failed")));
|
||||
}
|
||||
value
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
let desc = PixelFormat {
|
||||
hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT)
|
||||
!= ffi::egl::SLOW_CONFIG as i32,
|
||||
color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8 +
|
||||
attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8 +
|
||||
attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8,
|
||||
alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8,
|
||||
depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8,
|
||||
stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8,
|
||||
stereoscopy: false,
|
||||
double_buffer: true,
|
||||
multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) {
|
||||
0 | 1 => None,
|
||||
a => Some(a as u16),
|
||||
},
|
||||
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
|
||||
};
|
||||
|
||||
Ok((config_id, desc))
|
||||
}
|
||||
|
||||
unsafe fn create_context(display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
extensions: &[String], api: Api, version: (u8, u8),
|
||||
config_id: ffi::egl::types::EGLConfig, gl_debug: bool,
|
||||
gl_robustness: Robustness)
|
||||
-> Result<ffi::egl::types::EGLContext, CreationError>
|
||||
{
|
||||
let mut context_attributes = Vec::with_capacity(10);
|
||||
let mut flags = 0;
|
||||
|
||||
if egl_version >= &(1, 5) || extensions.iter().find(|s| s == &"EGL_KHR_create_context")
|
||||
.is_some()
|
||||
{
|
||||
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
|
||||
context_attributes.push(version.0 as i32);
|
||||
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
|
||||
context_attributes.push(version.1 as i32);
|
||||
|
||||
// handling robustness
|
||||
let supports_robustness = egl_version >= &(1, 5) ||
|
||||
extensions.iter()
|
||||
.find(|s| s == &"EGL_EXT_create_context_robustness")
|
||||
.is_some();
|
||||
|
||||
match gl_robustness {
|
||||
Robustness::NotRobust => (),
|
||||
|
||||
Robustness::NoError => {
|
||||
if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as c_int);
|
||||
context_attributes.push(1);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::RobustNoResetNotification => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
} else {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::TryRobustNoResetNotification => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::RobustLoseContextOnReset => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
} else {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::TryRobustLoseContextOnReset => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if gl_debug {
|
||||
if egl_version >= &(1, 5) {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
|
||||
context_attributes.push(ffi::egl::TRUE as i32);
|
||||
}
|
||||
|
||||
// TODO: using this flag sometimes generates an error
|
||||
// there was a change in the specs that added this flag, so it may not be
|
||||
// supported everywhere ; however it is not possible to know whether it is
|
||||
// supported or not
|
||||
//flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
||||
context_attributes.push(flags);
|
||||
|
||||
} else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
|
||||
// robustness is not supported
|
||||
match gl_robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
||||
context_attributes.push(version.0 as i32);
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::NONE as i32);
|
||||
|
||||
let context = egl::CreateContext(display, config_id, ptr::null(),
|
||||
context_attributes.as_ptr());
|
||||
|
||||
if context.is_null() {
|
||||
match egl::GetError() as u32 {
|
||||
ffi::egl::BAD_ATTRIBUTE => return Err(CreationError::OpenGlVersionNotSupported),
|
||||
e => panic!("eglCreateContext failed: 0x{:x}", e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(context)
|
||||
}
|
||||
|
|
@ -27,6 +27,8 @@ extern crate image;
|
|||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate mozangle;
|
||||
#[cfg(feature = "headless")]
|
||||
extern crate osmesa_sys;
|
||||
extern crate ron;
|
||||
|
@ -37,8 +39,10 @@ extern crate time;
|
|||
extern crate webrender;
|
||||
extern crate yaml_rust;
|
||||
|
||||
mod angle;
|
||||
mod binary_frame_reader;
|
||||
mod blob;
|
||||
mod egl;
|
||||
mod json_frame_writer;
|
||||
mod parse_function;
|
||||
mod perf;
|
||||
|
@ -159,6 +163,7 @@ impl HeadlessContext {
|
|||
|
||||
pub enum WindowWrapper {
|
||||
Window(glutin::GlWindow, Rc<gl::Gl>),
|
||||
Angle(glutin::Window, angle::Context, Rc<gl::Gl>),
|
||||
Headless(HeadlessContext, Rc<gl::Gl>),
|
||||
}
|
||||
|
||||
|
@ -168,21 +173,26 @@ impl WindowWrapper {
|
|||
fn swap_buffers(&self) {
|
||||
match *self {
|
||||
WindowWrapper::Window(ref window, _) => window.swap_buffers().unwrap(),
|
||||
WindowWrapper::Angle(_, ref context, _) => context.swap_buffers().unwrap(),
|
||||
WindowWrapper::Headless(_, _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_inner_size(&self) -> DeviceUintSize {
|
||||
//HACK: `winit` needs to figure out its hidpi story...
|
||||
#[cfg(target_os = "macos")]
|
||||
fn inner_size(window: &glutin::Window) -> (u32, u32) {
|
||||
let (w, h) = window.get_inner_size().unwrap();
|
||||
let factor = window.hidpi_factor();
|
||||
((w as f32 * factor) as _, (h as f32 * factor) as _)
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn inner_size(window: &glutin::Window) -> (u32, u32) {
|
||||
window.get_inner_size().unwrap()
|
||||
}
|
||||
let (w, h) = match *self {
|
||||
//HACK: `winit` needs to figure out its hidpi story...
|
||||
#[cfg(target_os = "macos")]
|
||||
WindowWrapper::Window(ref window, _) => {
|
||||
let (w, h) = window.get_inner_size().unwrap();
|
||||
let factor = window.hidpi_factor();
|
||||
((w as f32 * factor) as _, (h as f32 * factor) as _)
|
||||
},
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
WindowWrapper::Window(ref window, _) => window.get_inner_size().unwrap(),
|
||||
WindowWrapper::Window(ref window, _) => inner_size(window.window()),
|
||||
WindowWrapper::Angle(ref window, ..) => inner_size(window),
|
||||
WindowWrapper::Headless(ref context, _) => (context.width, context.height),
|
||||
};
|
||||
DeviceUintSize::new(w, h)
|
||||
|
@ -191,6 +201,7 @@ impl WindowWrapper {
|
|||
fn hidpi_factor(&self) -> f32 {
|
||||
match *self {
|
||||
WindowWrapper::Window(ref window, _) => window.hidpi_factor(),
|
||||
WindowWrapper::Angle(ref window, ..) => window.hidpi_factor(),
|
||||
WindowWrapper::Headless(_, _) => 1.0,
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +209,7 @@ impl WindowWrapper {
|
|||
fn resize(&mut self, size: DeviceUintSize) {
|
||||
match *self {
|
||||
WindowWrapper::Window(ref mut window, _) => window.set_inner_size(size.width, size.height),
|
||||
WindowWrapper::Angle(ref mut window, ..) => window.set_inner_size(size.width, size.height),
|
||||
WindowWrapper::Headless(_, _) => unimplemented!(), // requites Glutin update
|
||||
}
|
||||
}
|
||||
|
@ -205,19 +217,24 @@ impl WindowWrapper {
|
|||
fn set_title(&mut self, title: &str) {
|
||||
match *self {
|
||||
WindowWrapper::Window(ref window, _) => window.set_title(title),
|
||||
WindowWrapper::Angle(ref window, ..) => window.set_title(title),
|
||||
WindowWrapper::Headless(_, _) => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gl(&self) -> &gl::Gl {
|
||||
match *self {
|
||||
WindowWrapper::Window(_, ref gl) | WindowWrapper::Headless(_, ref gl) => &**gl,
|
||||
WindowWrapper::Window(_, ref gl) |
|
||||
WindowWrapper::Angle(_, _, ref gl) |
|
||||
WindowWrapper::Headless(_, ref gl) => &**gl,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_gl(&self) -> Rc<gl::Gl> {
|
||||
match *self {
|
||||
WindowWrapper::Window(_, ref gl) | WindowWrapper::Headless(_, ref gl) => gl.clone(),
|
||||
WindowWrapper::Window(_, ref gl) |
|
||||
WindowWrapper::Angle(_, _, ref gl) |
|
||||
WindowWrapper::Headless(_, ref gl) => gl.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,6 +244,7 @@ fn make_window(
|
|||
dp_ratio: Option<f32>,
|
||||
vsync: bool,
|
||||
events_loop: &Option<glutin::EventsLoop>,
|
||||
angle: bool,
|
||||
) -> WindowWrapper {
|
||||
let wrapper = match *events_loop {
|
||||
Some(ref events_loop) => {
|
||||
|
@ -240,25 +258,37 @@ fn make_window(
|
|||
.with_title("WRech")
|
||||
.with_multitouch()
|
||||
.with_dimensions(size.width, size.height);
|
||||
let window = glutin::GlWindow::new(window_builder, context_builder, events_loop)
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
window
|
||||
.make_current()
|
||||
.expect("unable to make context current!");
|
||||
}
|
||||
let init = |context: &glutin::GlContext| {
|
||||
unsafe {
|
||||
context
|
||||
.make_current()
|
||||
.expect("unable to make context current!");
|
||||
}
|
||||
|
||||
let gl = match window.get_api() {
|
||||
glutin::Api::OpenGl => unsafe {
|
||||
gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::OpenGlEs => unsafe {
|
||||
gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::WebGl => unimplemented!(),
|
||||
match context.get_api() {
|
||||
glutin::Api::OpenGl => unsafe {
|
||||
gl::GlFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::OpenGlEs => unsafe {
|
||||
gl::GlesFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::WebGl => unimplemented!(),
|
||||
}
|
||||
};
|
||||
WindowWrapper::Window(window, gl)
|
||||
|
||||
if angle {
|
||||
let (window, context) = angle::Context::with_window(
|
||||
window_builder, context_builder, events_loop
|
||||
).unwrap();
|
||||
let gl = init(&context);
|
||||
WindowWrapper::Angle(window, context, gl)
|
||||
} else {
|
||||
let window = glutin::GlWindow::new(window_builder, context_builder, events_loop)
|
||||
.unwrap();
|
||||
let gl = init(&window);
|
||||
WindowWrapper::Window(window, gl)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let gl = match gl::GlType::default() {
|
||||
|
@ -293,8 +323,14 @@ fn make_window(
|
|||
wrapper
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum NotifierEvent {
|
||||
WakeUp,
|
||||
ShutDown,
|
||||
}
|
||||
|
||||
struct Notifier {
|
||||
tx: Sender<()>,
|
||||
tx: Sender<NotifierEvent>,
|
||||
}
|
||||
|
||||
// setup a notifier so we can wait for frames to be finished
|
||||
|
@ -306,7 +342,11 @@ impl RenderNotifier for Notifier {
|
|||
}
|
||||
|
||||
fn wake_up(&self) {
|
||||
self.tx.send(()).unwrap();
|
||||
self.tx.send(NotifierEvent::WakeUp).unwrap();
|
||||
}
|
||||
|
||||
fn shut_down(&self) {
|
||||
self.tx.send(NotifierEvent::ShutDown).unwrap();
|
||||
}
|
||||
|
||||
fn new_document_ready(&self, _: DocumentId, scrolled: bool, _composite_needed: bool) {
|
||||
|
@ -316,7 +356,7 @@ impl RenderNotifier for Notifier {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_notifier() -> (Box<RenderNotifier>, Receiver<()>) {
|
||||
fn create_notifier() -> (Box<RenderNotifier>, Receiver<NotifierEvent>) {
|
||||
let (tx, rx) = channel();
|
||||
(Box::new(Notifier { tx: tx }), rx)
|
||||
}
|
||||
|
@ -364,7 +404,9 @@ fn main() {
|
|||
Some(glutin::EventsLoop::new())
|
||||
};
|
||||
|
||||
let mut window = make_window(size, dp_ratio, args.is_present("vsync"), &events_loop);
|
||||
let mut window = make_window(
|
||||
size, dp_ratio, args.is_present("vsync"), &events_loop, args.is_present("angle"),
|
||||
);
|
||||
let dp_ratio = dp_ratio.unwrap_or(window.hidpi_factor());
|
||||
let dim = window.get_inner_size();
|
||||
|
||||
|
@ -420,17 +462,19 @@ fn main() {
|
|||
reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
|
||||
reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
|
||||
}
|
||||
let num_failures = ReftestHarness::new(&mut wrench, &mut window, rx.unwrap())
|
||||
let rx = rx.unwrap();
|
||||
let num_failures = ReftestHarness::new(&mut wrench, &mut window, &rx)
|
||||
.run(base_manifest, specific_reftest, &reftest_options);
|
||||
wrench.renderer.deinit();
|
||||
wrench.shut_down(rx);
|
||||
// exit with an error code to fail on CI
|
||||
process::exit(num_failures as _);
|
||||
} else if let Some(_) = args.subcommand_matches("rawtest") {
|
||||
let rx = rx.unwrap();
|
||||
{
|
||||
let harness = RawtestHarness::new(&mut wrench, &mut window, rx.unwrap());
|
||||
let harness = RawtestHarness::new(&mut wrench, &mut window, &rx);
|
||||
harness.run();
|
||||
}
|
||||
wrench.renderer.deinit();
|
||||
wrench.shut_down(rx);
|
||||
return;
|
||||
} else if let Some(subargs) = args.subcommand_matches("perf") {
|
||||
// Perf mode wants to benchmark the total cost of drawing
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use NotifierEvent;
|
||||
use WindowWrapper;
|
||||
use serde_json;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -123,11 +124,11 @@ impl Profile {
|
|||
pub struct PerfHarness<'a> {
|
||||
wrench: &'a mut Wrench,
|
||||
window: &'a mut WindowWrapper,
|
||||
rx: Receiver<()>,
|
||||
rx: Receiver<NotifierEvent>,
|
||||
}
|
||||
|
||||
impl<'a> PerfHarness<'a> {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: Receiver<()>) -> Self {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: Receiver<NotifierEvent>) -> Self {
|
||||
PerfHarness { wrench, window, rx }
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use WindowWrapper;
|
||||
use {WindowWrapper, NotifierEvent};
|
||||
use image::png::PNGEncoder;
|
||||
use image::{self, ColorType, GenericImage};
|
||||
use std::fs::File;
|
||||
|
@ -77,7 +77,7 @@ pub fn png(
|
|||
surface: ReadSurface,
|
||||
window: &mut WindowWrapper,
|
||||
mut reader: YamlFrameReader,
|
||||
rx: Receiver<()>,
|
||||
rx: Receiver<NotifierEvent>,
|
||||
) {
|
||||
reader.do_frame(wrench);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use WindowWrapper;
|
||||
use {WindowWrapper, NotifierEvent};
|
||||
use blob;
|
||||
use euclid::{TypedRect, TypedSize2D, TypedPoint2D};
|
||||
use std::sync::Arc;
|
||||
|
@ -13,7 +13,7 @@ use wrench::Wrench;
|
|||
|
||||
pub struct RawtestHarness<'a> {
|
||||
wrench: &'a mut Wrench,
|
||||
rx: Receiver<()>,
|
||||
rx: &'a Receiver<NotifierEvent>,
|
||||
window: &'a mut WindowWrapper,
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ fn rect<T: Copy, U>(x: T, y: T, width: T, height: T) -> TypedRect<T, U> {
|
|||
}
|
||||
|
||||
impl<'a> RawtestHarness<'a> {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: Receiver<()>) -> Self {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver<NotifierEvent>) -> Self {
|
||||
RawtestHarness {
|
||||
wrench,
|
||||
rx,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use WindowWrapper;
|
||||
use {WindowWrapper, NotifierEvent};
|
||||
use base64;
|
||||
use image::load as load_piston_image;
|
||||
use image::png::PNGEncoder;
|
||||
|
@ -292,10 +292,10 @@ impl ReftestManifest {
|
|||
pub struct ReftestHarness<'a> {
|
||||
wrench: &'a mut Wrench,
|
||||
window: &'a mut WindowWrapper,
|
||||
rx: Receiver<()>,
|
||||
rx: &'a Receiver<NotifierEvent>,
|
||||
}
|
||||
impl<'a> ReftestHarness<'a> {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: Receiver<()>) -> Self {
|
||||
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver<NotifierEvent>) -> Self {
|
||||
ReftestHarness { wrench, window, rx }
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ use ron_frame_writer::RonFrameWriter;
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use time;
|
||||
use webrender;
|
||||
use webrender::api::*;
|
||||
use webrender::{DebugFlags, RendererStats};
|
||||
use yaml_frame_writer::YamlFrameWriterReceiver;
|
||||
use {WindowWrapper, BLACK_COLOR, WHITE_COLOR};
|
||||
use {WindowWrapper, NotifierEvent, BLACK_COLOR, WHITE_COLOR};
|
||||
|
||||
// TODO(gw): This descriptor matches what we currently support for fonts
|
||||
// but is quite a mess. We should at least document and
|
||||
|
@ -590,4 +591,18 @@ impl Wrench {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shut_down(self, rx: Receiver<NotifierEvent>) {
|
||||
self.api.shut_down();
|
||||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(NotifierEvent::ShutDown) => { break; }
|
||||
Ok(_) => {}
|
||||
Err(e) => { panic!("Did not shut down properly: {:?}.", e); }
|
||||
}
|
||||
}
|
||||
|
||||
self.renderer.deinit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -599,13 +599,10 @@ impl YamlFrameReader {
|
|||
}
|
||||
|
||||
fn to_radial_gradient(&mut self, dl: &mut DisplayListBuilder, item: &Yaml) -> RadialGradient {
|
||||
if item["start-center"].is_badvalue() {
|
||||
let center = item["center"]
|
||||
.as_point()
|
||||
.expect("radial gradient must have start center");
|
||||
let radius = item["radius"]
|
||||
.as_size()
|
||||
.expect("radial gradient must have start radius");
|
||||
let center = item["center"].as_point().expect("radial gradient must have center");
|
||||
let radius = item["radius"].as_size().expect("radial gradient must have a radius");
|
||||
|
||||
if item["start-radius"].is_badvalue() {
|
||||
let stops = item["stops"]
|
||||
.as_vec()
|
||||
.expect("radial gradient must have stops")
|
||||
|
@ -629,19 +626,12 @@ impl YamlFrameReader {
|
|||
|
||||
dl.create_radial_gradient(center, radius, stops, extend_mode)
|
||||
} else {
|
||||
let start_center = item["start-center"]
|
||||
.as_point()
|
||||
.expect("radial gradient must have start center");
|
||||
let start_radius = item["start-radius"]
|
||||
.as_force_f32()
|
||||
.expect("radial gradient must have start radius");
|
||||
let end_center = item["end-center"]
|
||||
.as_point()
|
||||
.expect("radial gradient must have end center");
|
||||
let end_radius = item["end-radius"]
|
||||
.as_force_f32()
|
||||
.expect("radial gradient must have end radius");
|
||||
let ratio_xy = item["ratio-xy"].as_force_f32().unwrap_or(1.0);
|
||||
let stops = item["stops"]
|
||||
.as_vec()
|
||||
.expect("radial gradient must have stops")
|
||||
|
@ -664,11 +654,10 @@ impl YamlFrameReader {
|
|||
};
|
||||
|
||||
dl.create_complex_radial_gradient(
|
||||
start_center,
|
||||
center,
|
||||
radius,
|
||||
start_radius,
|
||||
end_center,
|
||||
end_radius,
|
||||
ratio_xy,
|
||||
stops,
|
||||
extend_mode,
|
||||
)
|
||||
|
|
|
@ -905,11 +905,10 @@ impl YamlFrameWriter {
|
|||
];
|
||||
yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
|
||||
str_node(&mut v, "border-type", "radial-gradient");
|
||||
point_node(&mut v, "start-center", &details.gradient.start_center);
|
||||
point_node(&mut v, "center", &details.gradient.center);
|
||||
size_node(&mut v, "radius", &details.gradient.radius);
|
||||
f32_node(&mut v, "start-radius", details.gradient.start_radius);
|
||||
point_node(&mut v, "end-center", &details.gradient.end_center);
|
||||
f32_node(&mut v, "end-radius", details.gradient.end_radius);
|
||||
f32_node(&mut v, "ratio-xy", details.gradient.ratio_xy);
|
||||
let mut stops = vec![];
|
||||
for stop in display_list.get(base.gradient_stops()) {
|
||||
stops.push(Yaml::Real(stop.offset.to_string()));
|
||||
|
@ -961,11 +960,10 @@ impl YamlFrameWriter {
|
|||
}
|
||||
RadialGradient(item) => {
|
||||
str_node(&mut v, "type", "radial-gradient");
|
||||
point_node(&mut v, "start-center", &item.gradient.start_center);
|
||||
point_node(&mut v, "center", &item.gradient.center);
|
||||
size_node(&mut v, "center", &item.gradient.radius);
|
||||
f32_node(&mut v, "start-radius", item.gradient.start_radius);
|
||||
point_node(&mut v, "end-center", &item.gradient.end_center);
|
||||
f32_node(&mut v, "end-radius", item.gradient.end_radius);
|
||||
f32_node(&mut v, "ratio-xy", item.gradient.ratio_xy);
|
||||
size_node(&mut v, "tile-size", &item.tile_size);
|
||||
size_node(&mut v, "tile-spacing", &item.tile_spacing);
|
||||
let mut stops = vec![];
|
||||
|
|
Загрузка…
Ссылка в новой задаче