Bug 1492880. Update webrender to commit a601f9c291cee83257241ef61aaf62353c613438

This commit is contained in:
Jeff Muizelaar 2018-09-20 23:27:49 -04:00
parent fde56b2dde
commit bb303b7ca0
10 changed files with 424 additions and 253 deletions

View File

@ -46,20 +46,24 @@ ws = { optional = true, version = "0.7.3" }
[dependencies.pathfinder_font_renderer]
git = "https://github.com/pcwalton/pathfinder"
branch = "webrender"
optional = true
# Uncomment to test FreeType on macOS:
# features = ["freetype"]
[dependencies.pathfinder_gfx_utils]
git = "https://github.com/pcwalton/pathfinder"
branch = "webrender"
optional = true
[dependencies.pathfinder_partitioner]
git = "https://github.com/pcwalton/pathfinder"
branch = "webrender"
optional = true
[dependencies.pathfinder_path_utils]
git = "https://github.com/pcwalton/pathfinder"
branch = "webrender"
optional = true
[dev-dependencies]
@ -73,5 +77,5 @@ dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.6"
core-graphics = "0.16"
core-text = { version = "11", default-features = false }
core-graphics = "0.17.1"
core-text = { version = "13", default-features = false }

View File

@ -59,19 +59,16 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
vec2 device_pos = area.screen_origin +
aPosition.xy * area.common_data.task_rect.size;
// TODO(gw): We only check the clip transform matrix here. We should
// probably also be checking the prim_transform matrix. I
// have left it as is for now, since that matches the
// previous behavior.
if (clip_transform.is_axis_aligned) {
if (clip_transform.is_axis_aligned && prim_transform.is_axis_aligned) {
mat4 snap_mat = clip_transform.m * prim_transform.inv_m;
vec4 snap_positions = compute_snap_positions(
clip_transform.m,
snap_mat,
local_clip_rect
);
vec2 snap_offsets = compute_snap_offset_impl(
device_pos,
clip_transform.m,
snap_mat,
local_clip_rect,
RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
snap_positions

View File

@ -10,21 +10,21 @@ use core_foundation::base::TCFType;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::string::{CFString, CFStringRef};
use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGBitmapByteOrder32Little};
#[cfg(not(feature = "pathfinder"))]
use core_graphics::base::kCGImageAlphaPremultipliedFirst;
use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
use core_graphics::base::{kCGBitmapByteOrder32Little};
use core_graphics::color_space::CGColorSpace;
use core_graphics::context::CGContext;
#[cfg(not(feature = "pathfinder"))]
use core_graphics::context::CGTextDrawingMode;
use core_graphics::context::{CGBlendMode, CGTextDrawingMode};
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::{CGFont, CGGlyph};
use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize};
#[cfg(not(feature = "pathfinder"))]
use core_graphics::geometry::CGRect;
use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect};
use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use euclid::Size2D;
use gamma_lut::{ColorLut, GammaLut};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
#[cfg(feature = "pathfinder")]
@ -35,10 +35,14 @@ use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
const INITIAL_CG_CONTEXT_SIDE_LENGTH: u32 = 32;
pub struct FontContext {
cg_fonts: FastHashMap<FontKey, CGFont>,
ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
#[allow(dead_code)]
graphics_context: GraphicsContext,
#[allow(dead_code)]
gamma_lut: GammaLut,
}
@ -116,7 +120,9 @@ fn get_glyph_metrics(
}
let mut advance = CGSize { width: 0.0, height: 0.0 };
ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1);
unsafe {
ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1);
}
if bounds.size.width > 0.0 {
bounds.size.width += extra_width;
@ -281,6 +287,7 @@ impl FontContext {
Ok(FontContext {
cg_fonts: FastHashMap::default(),
ct_fonts: FastHashMap::default(),
graphics_context: GraphicsContext::new(),
gamma_lut: GammaLut::new(contrast, gamma, gamma),
})
}
@ -344,12 +351,14 @@ impl FontContext {
self.get_ct_font(font_key, Au::from_px(16), &[])
.and_then(|ref ct_font| {
let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
unsafe {
let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
if result {
Some(glyph as u32)
} else {
None
if result {
Some(glyph as u32)
} else {
None
}
}
})
}
@ -494,11 +503,17 @@ impl FontContext {
None => return GlyphRasterResult::LoadFailed,
};
let bitmap = is_bitmap_font(&ct_font);
let (mut shape, (x_offset, y_offset)) = if bitmap {
(FontTransform::identity(), (0.0, 0.0))
let glyph_type = if is_bitmap_font(&ct_font) {
GlyphType::Bitmap
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
GlyphType::Vector
};
let (mut shape, (x_offset, y_offset)) = match glyph_type {
GlyphType::Bitmap => (FontTransform::identity(), (0.0, 0.0)),
GlyphType::Vector => {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
}
};
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
@ -526,7 +541,12 @@ impl FontContext {
};
let glyph = key.index() as CGGlyph;
let (strike_scale, pixel_step) = if bitmap { (y_scale, 1.0) } else { (x_scale, y_scale / x_scale) };
let (strike_scale, pixel_step) = if glyph_type == GlyphType::Bitmap {
(y_scale, 1.0)
} else {
(x_scale, y_scale / x_scale)
};
let extra_strikes = font.get_extra_strikes(strike_scale / scale);
let metrics = get_glyph_metrics(
&ct_font,
@ -540,43 +560,7 @@ impl FontContext {
return GlyphRasterResult::LoadFailed
}
// The result of this function, in all render modes, is going to be a
// BGRA surface with white text on transparency using premultiplied
// alpha. For subpixel text, the RGB values will be the mask value for
// the individual components. For bitmap glyphs, the RGB values will be
// the (premultiplied) color of the pixel. For Alpha and Mono, each
// pixel will have R==G==B==A at the end of this function.
// We access the color channels in little-endian order.
// The CGContext will create and own our pixel buffer.
// In the non-Bitmap cases, we will ask CoreGraphics to draw text onto
// an opaque background. In order to hit the most efficient path in CG
// for this, we will tell CG that the CGContext is opaque, by passing
// an "[...]AlphaNone[...]" context flag. This creates a slight
// contradiction to the way we use the buffer after CG is done with it,
// because we will convert it into text-on-transparency. But that's ok;
// we still get four bytes per pixel and CG won't mess with the alpha
// channel after we've stopped calling CG functions. We just need to
// make sure that we don't look at the alpha values of the pixels that
// we get from CG, and compute our own alpha value only from RGB.
// Note that CG requires kCGBitmapByteOrder32Little in order to do
// subpixel AA at all (which we need it to do in both Subpixel and
// Alpha+smoothing mode). But little-endian is what we want anyway, so
// this works out nicely.
let context_flags = if bitmap {
kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst
} else {
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst
};
let mut cg_context = CGContext::create_bitmap_context(
None,
metrics.rasterized_width as usize,
metrics.rasterized_height as usize,
8,
metrics.rasterized_width as usize * 4,
&CGColorSpace::create_device_rgb(),
context_flags,
);
let raster_size = Size2D::new(metrics.rasterized_width, metrics.rasterized_height);
// If the font render mode is Alpha, we support two different ways to
// compute the grayscale mask, depending on the value of the platform
@ -603,74 +587,92 @@ impl FontContext {
// the "Alpha without smoothing" and Mono modes.
let use_white_on_black = should_use_white_on_black(font.color);
let use_font_smoothing = font.flags.contains(FontInstanceFlags::FONT_SMOOTHING);
let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = if bitmap {
(true, false, 0.0, 0.0, 0.0, false)
} else {
match (font.render_mode, use_font_smoothing) {
(FontRenderMode::Subpixel, _) |
(FontRenderMode::Alpha, true) => if use_white_on_black {
(true, true, 1.0, 0.0, 1.0, false)
} else {
(true, true, 0.0, 1.0, 1.0, true)
},
(FontRenderMode::Alpha, false) => (true, false, 0.0, 1.0, 1.0, true),
(FontRenderMode::Mono, _) => (false, false, 0.0, 1.0, 1.0, true),
let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match glyph_type {
GlyphType::Bitmap => (true, false, 0.0, 0.0, 0.0, false),
GlyphType::Vector => {
match (font.render_mode, use_font_smoothing) {
(FontRenderMode::Subpixel, _) |
(FontRenderMode::Alpha, true) => if use_white_on_black {
(true, true, 1.0, 0.0, 1.0, false)
} else {
(true, true, 0.0, 1.0, 1.0, true)
},
(FontRenderMode::Alpha, false) => (true, false, 0.0, 1.0, 1.0, true),
(FontRenderMode::Mono, _) => (false, false, 0.0, 1.0, 1.0, true),
}
}
};
// These are always true in Gecko, even for non-AA fonts
cg_context.set_allows_font_subpixel_positioning(true);
cg_context.set_should_subpixel_position_fonts(true);
{
let cg_context = self.graphics_context.get_context(&raster_size, glyph_type);
// Don't quantize because we're doing it already.
cg_context.set_allows_font_subpixel_quantization(false);
cg_context.set_should_subpixel_quantize_fonts(false);
// These are always true in Gecko, even for non-AA fonts
cg_context.set_allows_font_subpixel_positioning(true);
cg_context.set_should_subpixel_position_fonts(true);
cg_context.set_should_smooth_fonts(smooth);
cg_context.set_should_antialias(antialias);
// Don't quantize because we're doing it already.
cg_context.set_allows_font_subpixel_quantization(false);
cg_context.set_should_subpixel_quantize_fonts(false);
// Fill the background. This could be opaque white, opaque black, or
// transparency.
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
let rect = CGRect {
origin: CGPoint { x: 0.0, y: 0.0 },
size: CGSize {
width: metrics.rasterized_width as f64,
height: metrics.rasterized_height as f64,
},
};
cg_context.fill_rect(rect);
cg_context.set_should_smooth_fonts(smooth);
cg_context.set_should_antialias(antialias);
// Set the text color and draw the glyphs.
cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
// Fill the background. This could be opaque white, opaque black, or
// transparency.
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
let rect = CGRect {
origin: CGPoint { x: 0.0, y: 0.0 },
size: CGSize {
width: metrics.rasterized_width as f64,
height: metrics.rasterized_height as f64,
},
};
// CG Origin is bottom left, WR is top left. Need -y offset
let mut draw_origin = CGPoint {
x: -metrics.rasterized_left as f64 + x_offset,
y: metrics.rasterized_descent as f64 - y_offset,
};
// Make sure we use the Copy blend mode, or else we'll get the Porter-Duff OVER
// operator, which can't clear to the transparent color!
cg_context.set_blend_mode(CGBlendMode::Copy);
cg_context.fill_rect(rect);
cg_context.set_blend_mode(CGBlendMode::Normal);
if let Some(transform) = transform {
cg_context.set_text_matrix(&transform);
// Set the text color and draw the glyphs.
cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
draw_origin = draw_origin.apply_transform(&transform.invert());
// CG Origin is bottom left, WR is top left. Need -y offset
let mut draw_origin = CGPoint {
x: -metrics.rasterized_left as f64 + x_offset,
y: metrics.rasterized_descent as f64 - y_offset,
};
if let Some(transform) = transform {
cg_context.set_text_matrix(&transform);
draw_origin = draw_origin.apply_transform(&transform.invert());
} else {
// Make sure to reset this because some previous glyph rasterization might have
// changed it.
cg_context.set_text_matrix(&CG_AFFINE_TRANSFORM_IDENTITY);
}
if extra_strikes > 0 {
let strikes = 1 + extra_strikes;
let glyphs = vec![glyph; strikes];
let origins = (0..strikes).map(|i| {
CGPoint {
x: draw_origin.x + i as f64 * pixel_step,
y: draw_origin.y,
}
}).collect::<Vec<_>>();
ct_font.draw_glyphs(&glyphs, &origins, cg_context.clone());
} else {
ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone());
}
}
if extra_strikes > 0 {
let strikes = 1 + extra_strikes;
let glyphs = vec![glyph; strikes];
let origins = (0..strikes)
.map(|i| CGPoint { x: draw_origin.x + i as f64 * pixel_step, y: draw_origin.y })
.collect::<Vec<_>>();
ct_font.draw_glyphs(&glyphs, &origins, cg_context.clone());
} else {
ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone());
}
let mut rasterized_pixels = self.graphics_context
.get_rasterized_pixels(&raster_size, glyph_type);
let mut rasterized_pixels = cg_context.data().to_vec();
if !bitmap {
if glyph_type == GlyphType::Vector {
// We rendered text into an opaque surface. The code below needs to
// ignore the current value of each pixel's alpha channel. But it's
// allowed to write to the alpha channel, because we're done calling
@ -722,8 +724,14 @@ impl FontContext {
top: metrics.rasterized_ascent as f32,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
scale: (if bitmap { scale / y_scale } else { scale }) as f32,
format: if bitmap { GlyphFormat::ColorBitmap } else { font.get_glyph_format() },
scale: match glyph_type {
GlyphType::Bitmap => (scale / y_scale) as f32,
GlyphType::Vector => scale as f32,
},
format: match glyph_type {
GlyphType::Bitmap => GlyphFormat::ColorBitmap,
GlyphType::Vector => font.get_glyph_format(),
},
bytes: rasterized_pixels,
})
}
@ -735,3 +743,109 @@ impl<'a> Into<CGFont> for NativeFontHandleWrapper<'a> {
(self.0).0.clone()
}
}
// Avoids taking locks by recycling Core Graphics contexts.
#[allow(dead_code)]
struct GraphicsContext {
vector_context: CGContext,
vector_context_size: Size2D<u32>,
bitmap_context: CGContext,
bitmap_context_size: Size2D<u32>,
}
impl GraphicsContext {
fn new() -> GraphicsContext {
let size = Size2D::new(INITIAL_CG_CONTEXT_SIDE_LENGTH, INITIAL_CG_CONTEXT_SIDE_LENGTH);
GraphicsContext {
vector_context: GraphicsContext::create_cg_context(&size, GlyphType::Vector),
vector_context_size: size,
bitmap_context: GraphicsContext::create_cg_context(&size, GlyphType::Bitmap),
bitmap_context_size: size,
}
}
#[allow(dead_code)]
fn get_context(&mut self, size: &Size2D<u32>, glyph_type: GlyphType)
-> &mut CGContext {
let (cached_context, cached_size) = match glyph_type {
GlyphType::Vector => {
(&mut self.vector_context, &mut self.vector_context_size)
}
GlyphType::Bitmap => {
(&mut self.bitmap_context, &mut self.bitmap_context_size)
}
};
let rounded_size = Size2D::new(size.width.next_power_of_two(),
size.height.next_power_of_two());
if rounded_size.width > cached_size.width || rounded_size.height > cached_size.height {
*cached_size = Size2D::new(u32::max(cached_size.width, rounded_size.width),
u32::max(cached_size.height, rounded_size.height));
*cached_context = GraphicsContext::create_cg_context(cached_size, glyph_type);
}
cached_context
}
#[allow(dead_code)]
fn get_rasterized_pixels(&mut self, size: &Size2D<u32>, glyph_type: GlyphType)
-> Vec<u8> {
let (cached_context, cached_size) = match glyph_type {
GlyphType::Vector => (&mut self.vector_context, &self.vector_context_size),
GlyphType::Bitmap => (&mut self.bitmap_context, &self.bitmap_context_size),
};
let cached_data = cached_context.data();
let cached_stride = cached_size.width as usize * 4;
let result_len = size.width as usize * size.height as usize * 4;
let mut result = Vec::with_capacity(result_len);
for y in (cached_size.height - size.height)..cached_size.height {
let cached_start = y as usize * cached_stride;
let cached_end = cached_start + size.width as usize * 4;
result.extend_from_slice(&cached_data[cached_start..cached_end]);
}
debug_assert_eq!(result.len(), result_len);
result
}
fn create_cg_context(size: &Size2D<u32>, glyph_type: GlyphType) -> CGContext {
// The result of rasterization, in all render modes, is going to be a
// BGRA surface with white text on transparency using premultiplied
// alpha. For subpixel text, the RGB values will be the mask value for
// the individual components. For bitmap glyphs, the RGB values will be
// the (premultiplied) color of the pixel. For Alpha and Mono, each
// pixel will have R==G==B==A at the end of this function.
// We access the color channels in little-endian order.
// The CGContext will create and own our pixel buffer.
// In the non-Bitmap cases, we will ask CoreGraphics to draw text onto
// an opaque background. In order to hit the most efficient path in CG
// for this, we will tell CG that the CGContext is opaque, by passing
// an "[...]AlphaNone[...]" context flag. This creates a slight
// contradiction to the way we use the buffer after CG is done with it,
// because we will convert it into text-on-transparency. But that's ok;
// we still get four bytes per pixel and CG won't mess with the alpha
// channel after we've stopped calling CG functions. We just need to
// make sure that we don't look at the alpha values of the pixels that
// we get from CG, and compute our own alpha value only from RGB.
// Note that CG requires kCGBitmapByteOrder32Little in order to do
// subpixel AA at all (which we need it to do in both Subpixel and
// Alpha+smoothing mode). But little-endian is what we want anyway, so
// this works out nicely.
let color_type = match glyph_type {
GlyphType::Vector => kCGImageAlphaNoneSkipFirst,
GlyphType::Bitmap => kCGImageAlphaPremultipliedFirst,
};
CGContext::create_bitmap_context(None,
size.width as usize,
size.height as usize,
8,
size.width as usize * 4,
&CGColorSpace::create_device_rgb(),
kCGBitmapByteOrder32Little | color_type)
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
enum GlyphType {
Vector,
Bitmap,
}

View File

@ -1307,6 +1307,11 @@ impl LazyInitializedDebugRenderer {
self.debug_renderer.as_mut()
}
/// Returns mut ref to `DebugRenderer` if one already exists, otherwise returns `None`.
pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> {
self.debug_renderer.as_mut()
}
pub fn deinit(self, device: &mut Device) {
if let Some(debug_renderer) = self.debug_renderer {
debug_renderer.deinit(device);
@ -2466,7 +2471,7 @@ impl Renderer {
self.gpu_profile.end_frame();
#[cfg(feature = "debug_renderer")]
{
if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
if let Some(debug_renderer) = self.debug.try_get_mut() {
debug_renderer.render(&mut self.device, framebuffer_size);
}
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AddFont, BlobImageResources, AsyncBlobImageRasterizer, ResourceUpdate};
use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest};
use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest, RasterizedBlobImage};
use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams};
@ -32,6 +32,7 @@ use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId};
use render_task::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle, RenderTaskTree};
use smallvec::SmallVec;
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
use std::collections::hash_map::ValuesMut;
use std::{cmp, mem};
@ -98,8 +99,9 @@ enum State {
}
/// Post scene building state.
struct RasterizedBlobImage {
data: FastHashMap<Option<TileOffset>, BlobImageResult>,
enum RasterizedBlob {
Tiled(FastHashMap<TileOffset, RasterizedBlobImage>),
NonTiled(Vec<RasterizedBlobImage>),
}
/// Pre scene building state.
@ -398,7 +400,7 @@ pub struct ResourceCache {
pending_image_requests: FastHashSet<ImageRequest>,
blob_image_handler: Option<Box<BlobImageHandler>>,
rasterized_blob_images: FastHashMap<ImageKey, RasterizedBlobImage>,
rasterized_blob_images: FastHashMap<ImageKey, RasterizedBlob>,
blob_image_templates: FastHashMap<ImageKey, BlobImageTemplate>,
// If while building a frame we encounter blobs that we didn't already
@ -597,10 +599,40 @@ impl ResourceCache {
pub fn add_rasterized_blob_images(&mut self, images: Vec<(BlobImageRequest, BlobImageResult)>) {
for (request, result) in images {
let data = match result {
Ok(data) => data,
Err(..) => {
warn!("Failed to rasterize a blob image");
continue;
}
};
// First make sure we have an entry for this key (using a placeholder
// if need be).
let image = self.rasterized_blob_images.entry(request.key).or_insert_with(
|| { RasterizedBlobImage { data: FastHashMap::default() } }
|| { RasterizedBlob::Tiled(FastHashMap::default()) }
);
image.data.insert(request.tile, result);
if let Some(tile) = request.tile {
if let RasterizedBlob::NonTiled(..) = *image {
*image = RasterizedBlob::Tiled(FastHashMap::default());
}
if let RasterizedBlob::Tiled(ref mut tiles) = *image {
tiles.insert(tile, data);
}
} else {
if let RasterizedBlob::NonTiled(ref mut queue) = *image {
// If our new rasterized rect overwrites items in the queue, discard them.
queue.retain(|img| {
!data.rasterized_rect.contains_rect(&img.rasterized_rect)
});
queue.push(data);
} else {
*image = RasterizedBlob::NonTiled(vec![data]);
}
}
}
}
@ -925,9 +957,10 @@ impl ResourceCache {
if template.data.is_blob() {
let request: BlobImageRequest = request.into();
let missing = match self.rasterized_blob_images.get(&request.key) {
Some(img) => !img.data.contains_key(&request.tile),
None => true,
let missing = match (self.rasterized_blob_images.get(&request.key), request.tile) {
(Some(RasterizedBlob::Tiled(tiles)), Some(tile)) => !tiles.contains_key(&tile),
(Some(RasterizedBlob::NonTiled(..)), None) => false,
_ => true,
};
// For some reason the blob image is missing. We'll fall back to
@ -1062,13 +1095,32 @@ impl ResourceCache {
);
});
} else {
let needs_upload = match self.cached_images.try_get(&key) {
let mut needs_upload = match self.cached_images.try_get(&key) {
Some(&ImageResult::UntiledAuto(ref entry)) => {
self.texture_cache.needs_upload(&entry.texture_cache_handle)
}
_ => true,
};
// If the queue of ratserized updates is growing it probably means that
// the texture is not getting uploaded because the display item is off-screen.
// In that case we are better off
// - Either not kicking rasterization for that image (avoid wasted cpu work
// but will jank next time the item is visible because of lazy rasterization.
// - Clobber the update queue by pushing an update with a larger dirty rect
// to prevent it from accumulating.
//
// We do the latter here but it's not ideal and might want to revisit and do
// the former instead.
match self.rasterized_blob_images.get(&key) {
Some(RasterizedBlob::NonTiled(ref queue)) => {
if queue.len() > 2 {
needs_upload = true;
}
}
_ => {},
};
let dirty_rect = if needs_upload {
// The texture cache entry has been evicted, treat it as all dirty.
None
@ -1114,28 +1166,19 @@ impl ResourceCache {
Some(size) => size,
None => { return; }
};
let image = match self.rasterized_blob_images.get_mut(&key) {
Some(image) => image,
None => {
//println!("Missing rasterized blob (key={:?})!", key);
return;
}
let tiles = match self.rasterized_blob_images.get_mut(&key) {
Some(RasterizedBlob::Tiled(tiles)) => tiles,
_ => { return; }
};
let tile_range = compute_tile_range(
&area,
&template.descriptor.size,
tile_size,
);
image.data.retain(|tile, _| {
match *tile {
Some(offset) => tile_range.contains(&offset),
// This would be a bug. If we get here the blob should be tiled.
None => {
error!("Blob image template and image data tiling don't match.");
false
}
}
});
tiles.retain(|tile, _| { tile_range.contains(tile) });
}
pub fn request_glyphs(
@ -1400,28 +1443,34 @@ impl ResourceCache {
let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
debug_assert!(image_template.data.uses_texture_cache());
let mut blob_rasterized_rect = None;
let image_data = match image_template.data {
let mut updates: SmallVec<[(ImageData, Option<DeviceUintRect>); 1]> = SmallVec::new();
match image_template.data {
ImageData::Raw(..) | ImageData::External(..) => {
// Safe to clone here since the Raw image data is an
// Arc, and the external image data is small.
image_template.data.clone()
updates.push((image_template.data.clone(), None));
}
ImageData::Blob(..) => {
let blob_image = self.rasterized_blob_images.get(&request.key).unwrap();
match blob_image.data.get(&request.tile) {
Some(result) => {
let result = result
.as_ref()
.expect("Failed to render a blob image");
// TODO: we may want to not panic and show a placeholder instead.
blob_rasterized_rect = Some(result.rasterized_rect);
ImageData::Raw(Arc::clone(&result.data))
let blob_image = self.rasterized_blob_images.get_mut(&request.key).unwrap();
match (blob_image, request.tile) {
(RasterizedBlob::Tiled(ref tiles), Some(tile)) => {
let img = &tiles[&tile];
updates.push((
ImageData::Raw(Arc::clone(&img.data)),
Some(img.rasterized_rect)
));
}
None => {
(RasterizedBlob::NonTiled(ref mut queue), None) => {
for img in queue.drain(..) {
updates.push((
ImageData::Raw(img.data),
Some(img.rasterized_rect)
));
}
}
_ => {
debug_assert!(false, "invalid blob image request during frame building");
continue;
}
@ -1429,100 +1478,102 @@ impl ResourceCache {
}
};
let entry = match *self.cached_images.get_mut(&request.key) {
ImageResult::UntiledAuto(ref mut entry) => entry,
ImageResult::Multi(ref mut entries) => entries.get_mut(&request.into()),
ImageResult::Err(_) => panic!("Update requested for invalid entry")
};
match (blob_rasterized_rect, entry.dirty_rect) {
(Some(rasterized), Some(dirty)) => {
debug_assert!(request.tile.is_some() || rasterized.contains_rect(&dirty));
}
_ => {}
}
let mut descriptor = image_template.descriptor.clone();
let local_dirty_rect;
if let Some(tile) = request.tile {
let tile_size = image_template.tiling.unwrap();
let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile);
local_dirty_rect = if let Some(rect) = entry.dirty_rect.take() {
// We should either have a dirty rect, or we are re-uploading where the dirty
// rect is ignored anyway.
let intersection = intersect_for_tile(rect, clipped_tile_size, tile_size, tile);
debug_assert!(intersection.is_some() ||
self.texture_cache.needs_upload(&entry.texture_cache_handle));
intersection
} else {
None
for (image_data, blob_rasterized_rect) in updates {
let entry = match *self.cached_images.get_mut(&request.key) {
ImageResult::UntiledAuto(ref mut entry) => entry,
ImageResult::Multi(ref mut entries) => entries.get_mut(&request.into()),
ImageResult::Err(_) => panic!("Update requested for invalid entry")
};
// The tiled image could be stored on the CPU as one large image or be
// already broken up into tiles. This affects the way we compute the stride
// and offset.
let tiled_on_cpu = image_template.data.is_blob();
if !tiled_on_cpu {
let bpp = descriptor.format.bytes_per_pixel();
let stride = descriptor.compute_stride();
descriptor.stride = Some(stride);
descriptor.offset +=
tile.y as u32 * tile_size as u32 * stride +
tile.x as u32 * tile_size as u32 * bpp;
}
let mut descriptor = image_template.descriptor.clone();
let mut local_dirty_rect;
descriptor.size = clipped_tile_size;
} else {
local_dirty_rect = entry.dirty_rect.take();
}
if let Some(tile) = request.tile {
let tile_size = image_template.tiling.unwrap();
let clipped_tile_size = compute_tile_size(&descriptor, tile_size, tile);
let filter = match request.rendering {
ImageRendering::Pixelated => {
TextureFilter::Nearest
}
ImageRendering::Auto | ImageRendering::CrispEdges => {
// If the texture uses linear filtering, enable mipmaps and
// trilinear filtering, for better image quality. We only
// support this for now on textures that are not placed
// into the shared cache. This accounts for any image
// that is > 512 in either dimension, so it should cover
// the most important use cases. We may want to support
// mip-maps on shared cache items in the future.
if descriptor.allow_mipmaps &&
descriptor.size.width > 512 &&
descriptor.size.height > 512 &&
!self.texture_cache.is_allowed_in_shared_cache(
TextureFilter::Linear,
&descriptor,
) {
TextureFilter::Trilinear
local_dirty_rect = if let Some(rect) = entry.dirty_rect.take() {
// We should either have a dirty rect, or we are re-uploading where the dirty
// rect is ignored anyway.
let intersection = intersect_for_tile(rect, clipped_tile_size, tile_size, tile);
debug_assert!(intersection.is_some() ||
self.texture_cache.needs_upload(&entry.texture_cache_handle));
intersection
} else {
TextureFilter::Linear
None
};
// The tiled image could be stored on the CPU as one large image or be
// already broken up into tiles. This affects the way we compute the stride
// and offset.
let tiled_on_cpu = image_template.data.is_blob();
if !tiled_on_cpu {
let bpp = descriptor.format.bytes_per_pixel();
let stride = descriptor.compute_stride();
descriptor.stride = Some(stride);
descriptor.offset +=
tile.y as u32 * tile_size as u32 * stride +
tile.x as u32 * tile_size as u32 * bpp;
}
descriptor.size = clipped_tile_size;
} else {
local_dirty_rect = entry.dirty_rect.take();
}
};
let eviction = if image_template.data.is_blob() {
Eviction::Manual
} else {
Eviction::Auto
};
// If we are uploading the dirty region of a blob image we might have several
// rects to upload so we use each of these rasterized rects rather than the
// overall dirty rect of the image.
if blob_rasterized_rect.is_some() {
local_dirty_rect = blob_rasterized_rect;
}
//Note: at this point, the dirty rectangle is local to the descriptor space
self.texture_cache.update(
&mut entry.texture_cache_handle,
descriptor,
filter,
Some(image_data),
[0.0; 3],
local_dirty_rect,
gpu_cache,
None,
UvRectKind::Rect,
eviction,
);
let filter = match request.rendering {
ImageRendering::Pixelated => {
TextureFilter::Nearest
}
ImageRendering::Auto | ImageRendering::CrispEdges => {
// If the texture uses linear filtering, enable mipmaps and
// trilinear filtering, for better image quality. We only
// support this for now on textures that are not placed
// into the shared cache. This accounts for any image
// that is > 512 in either dimension, so it should cover
// the most important use cases. We may want to support
// mip-maps on shared cache items in the future.
if descriptor.allow_mipmaps &&
descriptor.size.width > 512 &&
descriptor.size.height > 512 &&
!self.texture_cache.is_allowed_in_shared_cache(
TextureFilter::Linear,
&descriptor,
) {
TextureFilter::Trilinear
} else {
TextureFilter::Linear
}
}
};
let eviction = if image_template.data.is_blob() {
Eviction::Manual
} else {
Eviction::Auto
};
//Note: at this point, the dirty rectangle is local to the descriptor space
self.texture_cache.update(
&mut entry.texture_cache_handle,
descriptor,
filter,
Some(image_data),
[0.0; 3],
local_dirty_rect,
gpu_cache,
None,
UvRectKind::Rect,
eviction,
);
}
}
}

View File

@ -58,8 +58,8 @@ impl SceneProperties {
pub fn flush_pending_updates(&mut self) -> bool {
let mut properties_changed = false;
if let Some(pending_properties) = self.pending_properties.take() {
if pending_properties != self.current_properties {
if let Some(ref pending_properties) = self.pending_properties {
if *pending_properties != self.current_properties {
self.transform_properties.clear();
self.float_properties.clear();
@ -73,7 +73,7 @@ impl SceneProperties {
.insert(property.key.id, property.value);
}
self.current_properties = pending_properties;
self.current_properties = pending_properties.clone();
properties_changed = true;
}
}

View File

@ -26,7 +26,7 @@ time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.6"
core-graphics = "0.16"
core-graphics = "0.17.1"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"

View File

@ -27,6 +27,6 @@ dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.6"
core-graphics = "0.16"
core-graphics = "0.17.1"
foreign-types = "0.3.0"

View File

@ -1 +1 @@
f17f6a491d6ff3dc3e13e998dda788d8f5856338
a601f9c291cee83257241ef61aaf62353c613438

View File

@ -31,7 +31,7 @@ winit = "0.16"
serde = {version = "1.0", features = ["derive"] }
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.16"
core-graphics = "0.17.1"
core-foundation = "0.6"
[features]