Skip to content

[WIP] Add support for externally allocated images in the render backend #548

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 42 additions & 6 deletions webrender/src/internal_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use tiling;
use webrender_traits::{Epoch, ColorF, PipelineId};
use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem};
use webrender_traits::{ExternalImageKey, ImageFormat, MixBlendMode, NativeFontHandle, DisplayItem};
use webrender_traits::{ScrollLayerId, WebGLCommand};

pub enum GLContextHandleWrapper {
Expand Down Expand Up @@ -193,19 +193,55 @@ impl TextureSampler {
}
}

/// A reference to a texture, either an id assigned by the render backend or an
/// indirect key resolved to an id later by the renderer.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum SourceTexture {
Id(TextureId),
External(ExternalImageKey),
// TODO(nical): should this have a None variant to better separate the cases
// where the batch does not use all its texture slots and cases where a slot
// will be used but the texture hasn't been assigned yet?
}

impl SourceTexture {
pub fn invalid() -> SourceTexture { SourceTexture::Id(TextureId::invalid()) }

pub fn is_valid(&self) -> bool {
match *self {
SourceTexture::Id(id) => { id.is_valid() }
SourceTexture::External(_) => { true }
}
}

pub fn is_external(&self) -> bool {
match *self {
SourceTexture::External(_) => { true }
SourceTexture::Id(_) => { false }
}
}

pub fn to_external(&self) -> Option<ExternalImageKey> {
match *self {
SourceTexture::External(key) => Some(key),
SourceTexture::Id(_) => None,
}
}
}

/// Optional textures that can be used as a source in the shaders.
/// Textures that are not used by the batch are equal to TextureId::invalid().
/// Textures that are not used by the batch are equal to SourceTexture::invalid().
#[derive(Copy, Clone, Debug)]
pub struct BatchTextures {
pub colors: [TextureId; 3],
pub mask: TextureId,
pub colors: [SourceTexture; 3],
pub mask: SourceTexture,
}

impl BatchTextures {
pub fn no_texture() -> Self {
BatchTextures {
colors: [TextureId::invalid(); 3],
mask: TextureId::invalid(),
colors: [SourceTexture::invalid(); 3],
mask: SourceTexture::invalid(),
}
}
}
Expand Down
75 changes: 43 additions & 32 deletions webrender/src/prim_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use app_units::Au;
use device::TextureId;
use euclid::{Point2D, Matrix4D, Rect, Size2D};
use gpu_store::{GpuStore, GpuStoreAddress};
use internal_types::{device_pixel, DeviceRect, DeviceSize};
use internal_types::{SourceTexture, device_pixel, DeviceRect, DeviceSize};
use resource_cache::ResourceCache;
use std::mem;
use std::usize;
use texture_cache::TextureCacheItem;
use tiling::RenderTask;
use util::TransformedRect;
use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering};
use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ExternalImageKey, ImageRendering};
use webrender_traits::{FontRenderMode, WebGLContextId};
use webrender_traits::{ClipRegion, FontKey, ItemRange, ComplexClipRegion, GlyphKey};

Expand Down Expand Up @@ -96,7 +96,7 @@ pub enum ImagePrimitiveKind {
#[derive(Debug)]
pub struct ImagePrimitiveCpu {
pub kind: ImagePrimitiveKind,
pub color_texture_id: TextureId,
pub color_texture_id: SourceTexture,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -346,6 +346,9 @@ pub struct PrimitiveStore {
pub cpu_gradients: Vec<GradientPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
// the indices witihin gpu_data_32 of the image primitives to be resolved by
// the renderer instead of the render backend.
pub deferred_image_primitives: Vec<(ExternalImageKey, GpuStoreAddress)>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to have primitives in the name, since technically an external texture can be a part of anything, not necessarily it's own Image type primitive. Perhaps, external_images?


// Gets uploaded directly to GPU via vertex texture
pub gpu_geometry: GpuStore<PrimitiveGeometry>,
Expand Down Expand Up @@ -375,6 +378,7 @@ impl PrimitiveStore {
gpu_data128: GpuStore::new(),
device_pixel_ratio: device_pixel_ratio,
prims_to_resolve: Vec::new(),
deferred_image_primitives: Vec::new(),
}
}

Expand Down Expand Up @@ -610,22 +614,27 @@ impl PrimitiveStore {
}
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
let image_gpu: &mut ImagePrimitiveGpu = unsafe {
mem::transmute(self.gpu_data32.get_mut(metadata.gpu_prim_index))
};

let cache_item = match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, _) => {
resource_cache.get_image(image_key, image_rendering)
}
ImagePrimitiveKind::WebGL(context_id) => {
resource_cache.get_webgl_texture(&context_id)
}
};
if let Some(key) = image_cpu.color_texture_id.to_external() {
// If the image primitive uses externally allocated textures,
// we resolve it on the renderer.
self.deferred_image_primitives.push((key, metadata.gpu_prim_index));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever force the primitive to appear in the prims_to_resolve list? Perhaps, it would be easier to straight push to deferred_image_primitives from prepare_prim_for_render().

} else {
let image_gpu: &mut ImagePrimitiveGpu = unsafe {
mem::transmute(self.gpu_data32.get_mut(metadata.gpu_prim_index))
};
let cache_item = match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, _) => {
resource_cache.get_image(image_key, image_rendering)
}
ImagePrimitiveKind::WebGL(context_id) => {
resource_cache.get_webgl_texture(&context_id)
}
};

image_cpu.color_texture_id = cache_item.texture_id;
image_gpu.uv0 = cache_item.uv0;
image_gpu.uv1 = cache_item.uv1;
image_cpu.color_texture_id = SourceTexture::Id(cache_item.texture_id);
image_gpu.uv0 = cache_item.uv0;
image_gpu.uv1 = cache_item.uv1;
}
}
}
}
Expand Down Expand Up @@ -825,21 +834,23 @@ impl PrimitiveStore {
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];

prim_needs_resolve = true;
match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => {
resource_cache.request_image(image_key, image_rendering);

// TODO(gw): This doesn't actually need to be calculated each frame.
// It's cheap enough that it's not worth introducing a cache for images
// right now, but if we introduce a cache for images for some other
// reason then we might as well cache this with it.
let image_properties = resource_cache.get_image_properties(image_key);
metadata.is_opaque = image_properties.is_opaque &&
tile_spacing.width == 0.0 &&
tile_spacing.height == 0.0;
if !image_cpu.color_texture_id.is_external() {
prim_needs_resolve = true;
match image_cpu.kind {
ImagePrimitiveKind::Image(image_key, image_rendering, tile_spacing) => {
resource_cache.request_image(image_key, image_rendering);

// TODO(gw): This doesn't actually need to be calculated each frame.
// It's cheap enough that it's not worth introducing a cache for images
// right now, but if we introduce a cache for images for some other
// reason then we might as well cache this with it.
let image_properties = resource_cache.get_image_properties(image_key);
metadata.is_opaque = image_properties.is_opaque &&
tile_spacing.width == 0.0 &&
tile_spacing.height == 0.0;
}
ImagePrimitiveKind::WebGL(..) => {}
}
ImagePrimitiveKind::WebGL(..) => {}
}
}
PrimitiveKind::Gradient => {
Expand Down
35 changes: 32 additions & 3 deletions webrender/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use fnv::FnvHasher;
use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp};
use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode};
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DevicePoint};
use internal_types::{BatchTextures, TextureSampler, GLContextHandleWrapper};
use internal_types::{SourceTexture, BatchTextures, TextureSampler, GLContextHandleWrapper};
use ipc_channel::ipc;
use prim_store::{ImagePrimitiveGpu};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a red flag for me. Renderer shouldn't need to know anything about the primitive store, at least directly.

use profiler::{Profiler, BackendProfileCounters};
use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
use render_backend::RenderBackend;
Expand Down Expand Up @@ -951,9 +952,11 @@ impl Renderer {
self.device.bind_vao(self.quad_vao_id);

for i in 0..textures.colors.len() {
self.device.bind_texture(TextureSampler::color(i), textures.colors[i]);
let color_id = self.resolve_texture_id(textures.colors[i]);
self.device.bind_texture(TextureSampler::color(i), color_id);
}
self.device.bind_texture(TextureSampler::Mask, textures.mask);
let mask_id = self.resolve_texture_id(textures.mask);
self.device.bind_texture(TextureSampler::Mask, mask_id);

for chunk in ubo_data.chunks(max_prim_items) {
let ubo = self.device.create_ubo(&chunk, UBO_BIND_DATA);
Expand All @@ -967,6 +970,30 @@ impl Renderer {
}
}

fn resolve_texture_id(&self, texture: SourceTexture) -> TextureId {
match texture {
SourceTexture::Id(id) => { id }
SourceTexture::External(_key) => {
//TODO[nical]
unimplemented!();
}
}
}

fn resolve_external_image_data(&self, frame: &mut Frame) {
for &(key, prim_index) in &frame.deferred_image_primitives[..] {
let image_gpu: &mut ImagePrimitiveGpu = unsafe {
mem::transmute(frame.gpu_data32.get_mut(prim_index.0 as usize))
};

// TODO: fetch the external UV and and texture id using a callback API
// or some such, and patch up the image_gpu.

// image_gpu.uv0 =
// image_gpu.uv1 =
}
}

fn draw_target(&mut self,
render_target: Option<(TextureId, i32)>,
target: &RenderTarget,
Expand Down Expand Up @@ -1285,6 +1312,8 @@ impl Renderer {
None);
}

self.resolve_external_image_data(frame);

self.layer_texture.init(&mut self.device, &mut frame.layer_texture_data);
self.render_task_texture.init(&mut self.device, &mut frame.render_task_data);
self.data16_texture.init(&mut self.device, &mut frame.gpu_data16);
Expand Down
38 changes: 25 additions & 13 deletions webrender/src/tiling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use frame::FrameId;
use gpu_store::GpuStoreAddress;
use internal_types::{DeviceRect, DevicePoint, DeviceSize, DeviceLength, device_pixel, CompositionOp};
use internal_types::{ANGLE_FLOAT_TO_FIXED, LowLevelFilterOp};
use internal_types::{BatchTextures};
use internal_types::{SourceTexture, BatchTextures};
use layer::Layer;
use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer};
use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
Expand All @@ -34,7 +34,7 @@ use std::usize;
use texture_cache::TexturePage;
use util::{self, rect_from_points, MatrixHelpers, rect_from_points_f};
use util::{TransformedRect, TransformedRectKind, subtract_rect, pack_as_float};
use webrender_traits::{ColorF, FontKey, ImageKey, ImageRendering, MixBlendMode};
use webrender_traits::{ColorF, FontKey, ImageKey, ExternalImageKey, ImageRendering, MixBlendMode};
use webrender_traits::{BorderDisplayItem, BorderSide, BorderStyle};
use webrender_traits::{AuxiliaryLists, ItemRange, BoxShadowClipMode, ClipRegion};
use webrender_traits::{PipelineId, ScrollLayerId, WebGLContextId, FontRenderMode};
Expand All @@ -50,7 +50,7 @@ pub type AuxiliaryListsMap = HashMap<PipelineId,

trait AlphaBatchHelpers {
fn get_batch_kind(&self, metadata: &PrimitiveMetadata) -> AlphaBatchKind;
fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [TextureId; 3];
fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3];
fn get_blend_mode(&self, needs_blending: bool, metadata: &PrimitiveMetadata) -> BlendMode;
fn prim_affects_tile(&self,
prim_index: PrimitiveIndex,
Expand Down Expand Up @@ -100,8 +100,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
batch_kind
}

fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [TextureId; 3] {
let invalid = TextureId::invalid();
fn get_color_textures(&self, metadata: &PrimitiveMetadata) -> [SourceTexture; 3] {
let invalid = SourceTexture::invalid();
match metadata.prim_kind {
PrimitiveKind::Border |
PrimitiveKind::BoxShadow |
Expand All @@ -113,7 +113,7 @@ impl AlphaBatchHelpers for PrimitiveStore {
}
PrimitiveKind::TextRun => {
let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
[text_run_cpu.color_texture_id, invalid, invalid]
[SourceTexture::Id(text_run_cpu.color_texture_id), invalid, invalid]
}
// TODO(nical): YuvImage will return 3 textures.
}
Expand Down Expand Up @@ -494,7 +494,7 @@ impl AlphaBatcher {

let textures = BatchTextures {
colors: ctx.prim_store.get_color_textures(prim_metadata),
mask: prim_metadata.mask_texture_id,
mask: SourceTexture::Id(prim_metadata.mask_texture_id),
};

batch_key = AlphaBatchKey::primitive(batch_kind,
Expand Down Expand Up @@ -673,11 +673,11 @@ impl RenderTarget {
// we switch the texture atlas to use texture layers!
let textures = BatchTextures {
colors: ctx.prim_store.get_color_textures(prim_metadata),
mask: prim_metadata.mask_texture_id,
mask: SourceTexture::Id(prim_metadata.mask_texture_id),
};

debug_assert!(textures.colors[0] != TextureId::invalid());
debug_assert!(self.text_run_textures.colors[0] == TextureId::invalid() ||
debug_assert!(textures.colors[0] != SourceTexture::invalid());
debug_assert!(self.text_run_textures.colors[0] == SourceTexture::invalid() ||
self.text_run_textures.colors[0] == textures.colors[0]);
self.text_run_textures = textures;

Expand Down Expand Up @@ -1117,7 +1117,7 @@ pub enum BlurDirection {
}

#[inline]
fn textures_compatible(t1: TextureId, t2: TextureId) -> bool {
fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
!t1.is_valid() || !t2.is_valid() || t1 == t2
}

Expand Down Expand Up @@ -1419,6 +1419,7 @@ pub struct Frame {
pub gpu_data64: Vec<GpuBlock64>,
pub gpu_data128: Vec<GpuBlock128>,
pub gpu_geometry: Vec<PrimitiveGeometry>,
pub deferred_image_primitives: Vec<(ExternalImageKey, GpuStoreAddress)>,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
Expand Down Expand Up @@ -1995,7 +1996,7 @@ impl FrameBuilder {
context_id: WebGLContextId) {
let prim_cpu = ImagePrimitiveCpu {
kind: ImagePrimitiveKind::WebGL(context_id),
color_texture_id: TextureId::invalid(),
color_texture_id: SourceTexture::invalid(),
};

let prim_gpu = ImagePrimitiveGpu {
Expand All @@ -2017,11 +2018,21 @@ impl FrameBuilder {
tile_spacing: &Size2D<f32>,
image_key: ImageKey,
image_rendering: ImageRendering) {

// The external image primitve works like a regular image except that
// its information is resolved later on the render thread rather than
// using the resource cache.
let texture_id = if image_key.is_external() {
SourceTexture::External(image_key)
} else {
SourceTexture::invalid()
};

let prim_cpu = ImagePrimitiveCpu {
kind: ImagePrimitiveKind::Image(image_key,
image_rendering,
*tile_spacing),
color_texture_id: TextureId::invalid(),
color_texture_id: texture_id,
};

let prim_gpu = ImagePrimitiveGpu {
Expand Down Expand Up @@ -2458,6 +2469,7 @@ impl FrameBuilder {
gpu_data64: self.prim_store.gpu_data64.build(),
gpu_data128: self.prim_store.gpu_data128.build(),
gpu_geometry: self.prim_store.gpu_geometry.build(),
deferred_image_primitives: mem::replace(&mut self.prim_store.deferred_image_primitives, Vec::new()),
}
}
}
Loading