Bug 1591527 - Integrate partial repainting with OS compositor surfaces. r=kvark,mstange

This passes the existing dirty rect for a picture cache update
through the native compositor interface, allowing compositors
to only update a sub-rect of a tile.

Also update the example to pass dirty rect to DirectComposition,
and add debug drawing for compositor redraw region.

Differential Revision: https://phabricator.services.mozilla.com/D50767

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Glenn Watson 2019-10-28 20:56:42 +00:00
Родитель d8ae659ab1
Коммит 074c0fe3ca
10 изменённых файлов: 99 добавлений и 15 удалений

Просмотреть файл

@ -36,9 +36,10 @@ void wr_compositor_begin_frame(void* aCompositor) {
}
void wr_compositor_bind(void* aCompositor, wr::NativeSurfaceId aId,
wr::DeviceIntPoint* aOffset, uint32_t* aFboId) {
wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
wr::DeviceIntRect aDirtyRect) {
RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
compositor->Bind(aId, aOffset, aFboId);
compositor->Bind(aId, aOffset, aFboId, aDirtyRect);
}
void wr_compositor_create_surface(void* aCompositor, wr::NativeSurfaceId aId,

Просмотреть файл

@ -69,7 +69,7 @@ class RenderCompositor {
virtual void CompositorBeginFrame() {}
virtual void CompositorEndFrame() {}
virtual void Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset,
uint32_t* aFboId) {}
uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) {}
virtual void Unbind() {}
virtual void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize) {
}

Просмотреть файл

@ -1150,6 +1150,7 @@ extern "C" {
id: NativeSurfaceId,
offset: &mut DeviceIntPoint,
fbo_id: &mut u32,
dirty_rect: DeviceIntRect,
);
fn wr_compositor_unbind(compositor: *mut c_void);
fn wr_compositor_begin_frame(compositor: *mut c_void);
@ -1194,6 +1195,7 @@ impl Compositor for WrCompositor {
fn bind(
&mut self,
id: NativeSurfaceId,
dirty_rect: DeviceIntRect,
) -> NativeSurfaceInfo {
let mut surface_info = NativeSurfaceInfo {
origin: DeviceIntPoint::zero(),
@ -1206,6 +1208,7 @@ impl Compositor for WrCompositor {
id,
&mut surface_info.origin,
&mut surface_info.fbo_id,
dirty_rect,
);
}

Просмотреть файл

@ -23,7 +23,7 @@ struct Tile {
// Represents the underlying DirectComposition surface texture that gets drawn into.
IDCompositionSurface *pSurface;
// Represents the node in the visual tree that defines the properties of this tile (clip, position etc).
IDCompositionVisual *pVisual;
IDCompositionVisual2 *pVisual;
};
struct Window {
@ -37,7 +37,7 @@ struct Window {
// Main interfaces to D3D11 and DirectComposition
ID3D11Device *pD3D11Device;
IDCompositionDevice *pDCompDevice;
IDCompositionDesktopDevice *pDCompDevice;
IDCompositionTarget *pDCompTarget;
IDXGIDevice *pDXGIDevice;
@ -55,7 +55,8 @@ struct Window {
// The root of the DC visual tree. Nothing is drawn on this, but
// all child tiles are parented to here.
IDCompositionVisual *pRoot;
IDCompositionVisual2 *pRoot;
IDCompositionVisualDebug *pVisualDebug;
// Maps the WR surface IDs to the DC representation of each tile.
std::map<uint64_t, Tile> tiles;
};
@ -145,9 +146,9 @@ extern "C" {
assert(SUCCEEDED(hr));
// Create a DirectComposition device
hr = DCompositionCreateDevice(
hr = DCompositionCreateDevice2(
window->pDXGIDevice,
__uuidof(IDCompositionDevice),
__uuidof(IDCompositionDesktopDevice),
(void **) &window->pDCompDevice
);
assert(SUCCEEDED(hr));
@ -237,6 +238,15 @@ extern "C" {
hr = window->pDCompTarget->SetRoot(window->pRoot);
assert(SUCCEEDED(hr));
hr = window->pRoot->QueryInterface(
__uuidof(IDCompositionVisualDebug),
(void **) &window->pVisualDebug
);
assert(SUCCEEDED(hr));
// Uncomment this to see redraw regions during composite
//window->pVisualDebug->EnableRedrawRegions();
EGLBoolean ok = eglMakeCurrent(
window->EGLDisplay,
window->fb_surface,
@ -262,6 +272,7 @@ extern "C" {
eglReleaseDeviceANGLE(window->EGLDevice);
window->pRoot->Release();
window->pVisualDebug->Release();
window->pD3D11Device->Release();
window->pDXGIDevice->Release();
window->pDCompDevice->Release();
@ -348,7 +359,11 @@ extern "C" {
Window *window,
uint64_t id,
int *x_offset,
int *y_offset
int *y_offset,
int dirty_x0,
int dirty_y0,
int dirty_width,
int dirty_height
) {
assert(window->tiles.count(id) == 1);
Tile &tile = window->tiles[id];
@ -359,15 +374,24 @@ extern "C" {
// Inform DC that we want to draw on this surface. DC uses texture
// atlases when the tiles are small. It returns an offset where the
// client code must draw into this surface when this happens.
RECT update_rect;
update_rect.left = dirty_x0;
update_rect.top = dirty_y0;
update_rect.right = dirty_x0 + dirty_width;
update_rect.bottom = dirty_y0 + dirty_height;
POINT offset;
D3D11_TEXTURE2D_DESC desc;
ID3D11Texture2D *pTexture;
HRESULT hr = tile.pSurface->BeginDraw(
NULL,
&update_rect,
__uuidof(ID3D11Texture2D),
(void **) &pTexture,
&offset
);
// DC includes the origin of the dirty / update rect in the draw offset,
// undo that here since WR expects it to be an absolute offset.
offset.x -= dirty_x0;
offset.y -= dirty_y0;
assert(SUCCEEDED(hr));
pTexture->GetDesc(&desc);

Просмотреть файл

@ -45,6 +45,10 @@ extern {
id: u64,
x_offset: &mut i32,
y_offset: &mut i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
);
fn com_dc_unbind_surface(window: *mut Window);
@ -119,6 +123,10 @@ pub fn destroy_surface(
pub fn bind_surface(
window: *mut Window,
id: u64,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> (i32, i32) {
unsafe {
let mut x_offset = 0;
@ -129,6 +137,10 @@ pub fn bind_surface(
id,
&mut x_offset,
&mut y_offset,
dirty_x0,
dirty_y0,
dirty_width,
dirty_height,
);
(x_offset, y_offset)

Просмотреть файл

@ -12,6 +12,7 @@
*/
use euclid::Angle;
use gleam::gl;
use std::ffi::CString;
use std::sync::mpsc;
@ -55,10 +56,15 @@ impl webrender::Compositor for DirectCompositeInterface {
fn bind(
&mut self,
id: webrender::NativeSurfaceId,
dirty_rect: DeviceIntRect,
) -> webrender::NativeSurfaceInfo {
let (x, y) = compositor::bind_surface(
self.window,
id.0,
dirty_rect.origin.x,
dirty_rect.origin.y,
dirty_rect.size.width,
dirty_rect.size.height,
);
webrender::NativeSurfaceInfo {
@ -148,7 +154,7 @@ fn main() {
let debug_flags = DebugFlags::empty();
let compositor_config = if enable_compositor {
webrender::CompositorConfig::Native {
max_update_rects: 0,
max_update_rects: 1,
compositor: Box::new(DirectCompositeInterface::new(window)),
}
} else {
@ -192,6 +198,7 @@ fn main() {
txn.set_root_pipeline(root_pipeline_id);
txn.generate_frame();
api.send_transaction(document_id, txn);
let mut rotation_angle = 0.0;
// Tick the compositor (in this sample, we don't block on UI events)
while compositor::tick(window) {
@ -218,6 +225,33 @@ fn main() {
),
ColorF::new(0.3, 0.3, 0.3, 1.0),
);
let rotation = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::degrees(rotation_angle));
rotation_angle += 1.0;
if rotation_angle > 360.0 {
rotation_angle = 0.0;
}
let transform_origin = LayoutVector3D::new(400.0, 400.0, 0.0);
let transform = rotation.pre_translate(-transform_origin).post_translate(transform_origin);
let spatial_id = root_builder.push_reference_frame(
LayoutPoint::zero(),
SpatialId::root_scroll_node(root_pipeline_id),
TransformStyle::Flat,
PropertyBinding::Value(transform),
ReferenceFrameKind::Transform,
);
root_builder.push_rect(
&CommonItemProperties::new(
LayoutRect::new(
LayoutPoint::new(300.0, 300.0),
LayoutSize::new(200.0, 200.0),
),
SpaceAndClipInfo {
spatial_id,
clip_id: ClipId::root(root_pipeline_id),
},
),
ColorF::new(1.0, 0.0, 0.0, 1.0),
);
txn.set_display_list(
current_epoch,
None,

Просмотреть файл

@ -242,10 +242,18 @@ pub trait Compositor {
/// Bind this surface such that WR can issue OpenGL commands
/// that will target the surface. Returns an (x, y) offset
/// where WR should draw into the surface. This can be set
/// to (0, 0) if the OS doesn't use texture atlases.
/// to (0, 0) if the OS doesn't use texture atlases. The dirty
/// rect is a local surface rect that specifies which part
/// of the surface needs to be updated. If max_update_rects
/// in CompositeConfig is 0, this will always be the size
/// of the entire surface. The returned offset is only
/// relevant to compositors that store surfaces in a texture
/// atlas (that is, WR expects that the dirty rect doesn't
/// affect the coordinates of the returned origin).
fn bind(
&mut self,
id: NativeSurfaceId,
dirty_rect: DeviceIntRect,
) -> NativeSurfaceInfo;
/// Unbind the surface. This is called by WR when it has

Просмотреть файл

@ -850,9 +850,9 @@ pub fn build_render_pass(
let scissor_rect = match render_tasks[task_id].kind {
RenderTaskKind::Picture(ref info) => info.scissor_rect,
_ => unreachable!(),
};
}.expect("bug: dirty rect must be set for picture cache tasks");
let mut batch_containers = Vec::new();
let mut alpha_batch_container = AlphaBatchContainer::new(scissor_rect);
let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect));
batcher.build(
&mut batch_containers,
&mut alpha_batch_container,
@ -865,6 +865,7 @@ pub fn build_render_pass(
surface: surface.clone(),
clear_color,
alpha_batch_container,
dirty_rect: scissor_rect,
};
picture_cache.push(target);

Просмотреть файл

@ -722,6 +722,7 @@ pub struct PictureCacheTarget {
pub surface: ResolvedSurfaceTexture,
pub alpha_batch_container: AlphaBatchContainer,
pub clear_color: Option<ColorF>,
pub dirty_rect: DeviceIntRect,
}
#[cfg_attr(feature = "capture", derive(Serialize))]

Просмотреть файл

@ -5057,7 +5057,7 @@ impl Renderer {
ResolvedSurfaceTexture::NativeSurface { id, size, .. } => {
let surface_info = match self.compositor_config {
CompositorConfig::Native { ref mut compositor, .. } => {
compositor.bind(id)
compositor.bind(id, picture_target.dirty_rect)
}
CompositorConfig::Draw { .. } => {
unreachable!();