Bug 1679215 - limit SWGL compositing to dirty region. r=mattwoodrow

Differential Revision: https://phabricator.services.mozilla.com/D101256
This commit is contained in:
Lee Salzman 2021-01-10 23:27:01 +00:00
parent 8bee9f98ac
commit 50180b37c8
9 changed files with 169 additions and 61 deletions

View File

@ -571,7 +571,8 @@ bool RenderCompositorNativeSWGL::InitDefaultFramebuffer(
if (!MapNativeLayer(mNativeLayerForEntireWindow, aBounds, aBounds)) {
return false;
}
wr_swgl_init_default_framebuffer(mContext, aBounds.width, aBounds.height,
wr_swgl_init_default_framebuffer(mContext, aBounds.x, aBounds.y,
aBounds.width, aBounds.height,
mLayerStride, mLayerValidRectData);
}
return true;

View File

@ -45,10 +45,17 @@ bool RenderCompositorSWGL::MakeCurrent() {
}
bool RenderCompositorSWGL::BeginFrame() {
// Request a new draw target to use from the widget...
// Set up a temporary region representing the entire window surface in case a
// dirty region is not supplied.
ClearMappedBuffer();
LayoutDeviceIntRect bounds(LayoutDeviceIntPoint(), GetBufferSize());
mRegion = LayoutDeviceIntRegion(bounds);
mRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
wr_swgl_make_current(mContext);
return true;
}
bool RenderCompositorSWGL::AllocateMappedBuffer() {
// Request a new draw target to use from the widget...
MOZ_ASSERT(!mDT);
layers::BufferMode bufferMode = layers::BufferMode::BUFFERED;
mDT = mWidget->StartRemoteDrawingInRegion(mRegion, &bufferMode);
if (!mDT) {
@ -62,18 +69,24 @@ bool RenderCompositorSWGL::BeginFrame() {
int32_t stride = 0;
gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
if (!mSurface && mDT->LockBits(&data, &size, &stride, &format) &&
(size != bounds.Size().ToUnknownSize() ||
(format != gfx::SurfaceFormat::B8G8R8A8 &&
format != gfx::SurfaceFormat::B8G8R8X8))) {
(format != gfx::SurfaceFormat::B8G8R8A8 &&
format != gfx::SurfaceFormat::B8G8R8X8)) {
// We tried to lock the DT and it succeeded, but the size or format
// of the data is not compatible, so just release it and fall back below...
mDT->ReleaseBits(data);
data = nullptr;
}
LayoutDeviceIntRect bounds = mRegion.GetBounds();
// If locking succeeded above, just use that.
if (data) {
mMappedData = data;
mMappedStride = stride;
// Disambiguate whether the widget's draw target has its origin at zero or
// if it is offset to the dirty region origin.
if (size == gfx::IntSize(bounds.XMost(), bounds.YMost())) {
// Update the bounds to include zero if the origin is at zero.
bounds.ExpandToEnclose(LayoutDeviceIntPoint(0, 0));
}
} else {
// If we couldn't lock the DT above, then allocate a data surface and map
// that for usage with SWGL.
@ -93,13 +106,44 @@ bool RenderCompositorSWGL::BeginFrame() {
mMappedStride = map.mStride;
}
MOZ_ASSERT(mMappedData != nullptr && mMappedStride > 0);
wr_swgl_make_current(mContext);
wr_swgl_init_default_framebuffer(mContext, bounds.width, bounds.height,
mMappedStride, mMappedData);
wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
bounds.height, mMappedStride, mMappedData);
return true;
}
void RenderCompositorSWGL::CommitMappedBuffer() {
void RenderCompositorSWGL::StartCompositing(
const wr::DeviceIntRect* aDirtyRects, size_t aNumDirtyRects) {
if (mDT) {
// Cancel any existing buffers that might accidentally be left from updates
CommitMappedBuffer(false);
// Reset the region to the widget bounds
mRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
}
if (aNumDirtyRects) {
// Install the dirty rects into the bounds of the existing region
auto bounds = mRegion.GetBounds();
mRegion.SetEmpty();
for (size_t i = 0; i < aNumDirtyRects; i++) {
const auto& rect = aDirtyRects[i];
mRegion.OrWith(LayoutDeviceIntRect(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height));
}
// Ensure the region lies within the widget bounds
mRegion.AndWith(bounds);
}
// Now that the dirty rects have been supplied and the composition region
// is known, allocate and install a framebuffer encompassing the composition
// region.
if (!AllocateMappedBuffer()) {
gfxCriticalNote
<< "RenderCompositorSWGL failed mapping default framebuffer";
// If allocation of the mapped default framebuffer failed, then just install
// a small temporary framebuffer so compositing can still proceed.
wr_swgl_init_default_framebuffer(mContext, 0, 0, 2, 2, 0, nullptr);
}
}
void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
if (!mDT) {
return;
}
@ -109,13 +153,15 @@ void RenderCompositorSWGL::CommitMappedBuffer() {
// If we're using a data surface, unmap it and draw it to the DT if there
// are any supplied dirty rects.
mSurface->Unmap();
for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
const LayoutDeviceIntRect& dirtyRect = iter.Get();
gfx::Rect bounds(dirtyRect.x, dirtyRect.y, dirtyRect.width,
dirtyRect.height);
mDT->DrawSurface(mSurface, bounds, bounds,
gfx::DrawSurfaceOptions(gfx::SamplingFilter::POINT),
gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
if (aDirty) {
for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
const LayoutDeviceIntRect& dirtyRect = iter.Get();
gfx::Rect bounds(dirtyRect.x, dirtyRect.y, dirtyRect.width,
dirtyRect.height);
mDT->DrawSurface(mSurface, bounds, bounds,
gfx::DrawSurfaceOptions(gfx::SamplingFilter::POINT),
gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
}
}
} else {
// Otherwise, we had locked the DT directly. Just release the data.
@ -126,18 +172,13 @@ void RenderCompositorSWGL::CommitMappedBuffer() {
ClearMappedBuffer();
}
void RenderCompositorSWGL::CancelFrame() { CommitMappedBuffer(); }
void RenderCompositorSWGL::CancelFrame() { CommitMappedBuffer(false); }
RenderedFrameId RenderCompositorSWGL::EndFrame(
const nsTArray<DeviceIntRect>& aDirtyRects) {
if (!aDirtyRects.IsEmpty()) {
mRegion.SetEmpty();
for (auto& rect : aDirtyRects) {
mRegion.OrWith(LayoutDeviceIntRect(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height));
}
}
// Dirty rects have already been set inside StartCompositing. We need to keep
// those dirty rects exactly the same here so we supply the same exact region
// to EndRemoteDrawingInRegion as for StartRemoteDrawingInRegion.
RenderedFrameId frameId = GetNextRenderFrameId();
CommitMappedBuffer();
return frameId;

View File

@ -31,6 +31,9 @@ class RenderCompositorSWGL : public RenderCompositor {
void CancelFrame() override;
RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
void StartCompositing(const wr::DeviceIntRect* aDirtyRects,
size_t aNumDirtyRects) override;
bool UsePartialPresent() override { return true; }
void Pause() override;
@ -62,7 +65,9 @@ class RenderCompositorSWGL : public RenderCompositor {
void ClearMappedBuffer();
void CommitMappedBuffer();
bool AllocateMappedBuffer();
void CommitMappedBuffer(bool aDirty = true);
};
} // namespace wr

View File

@ -42,12 +42,14 @@ pub extern "C" fn wr_swgl_make_current(ctx: *mut c_void) {
#[no_mangle]
pub extern "C" fn wr_swgl_init_default_framebuffer(
ctx: *mut c_void,
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
buf: *mut c_void,
) {
swgl::Context::from(ctx).init_default_framebuffer(width, height, stride, buf);
swgl::Context::from(ctx).init_default_framebuffer(x, y, width, height, stride, buf);
}
#[no_mangle]
@ -1583,9 +1585,6 @@ impl Compositor for SwCompositor {
self.late_surfaces.clear();
self.reset_overlaps();
if self.composite_thread.is_some() {
self.locked_framebuffer = self.gl.lock_framebuffer(0);
}
}
fn add_surface(
@ -1644,6 +1643,8 @@ impl Compositor for SwCompositor {
}
}
self.locked_framebuffer = self.gl.lock_framebuffer(0);
composite_thread.start_compositing();
// Issue any initial composite jobs for the SwComposite thread.
let mut lock = composite_thread.lock();

View File

@ -32,8 +32,7 @@ static NO_INLINE void scale_blit(Texture& srctex, const IntRect& srcReq,
// Limit dest sampling bounds to overlap source bounds
dstBounds.intersect(srcBounds);
// Compute the clipped bounds, relative to dstBounds.
IntRect clippedDest = dstBounds.intersection(clipRect);
clippedDest.offset(-dstBounds.x0, -dstBounds.y0);
IntRect clippedDest = dstBounds.intersection(clipRect) - dstBounds.origin();
// Check if clipped sampling bounds are empty
if (clippedDest.is_empty()) {
return;
@ -289,12 +288,12 @@ void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
if (invertY) {
swap(dstY0, dstY1);
}
IntRect srcReq = {srcX0, srcY0, srcX1, srcY1};
IntRect dstReq = {dstX0, dstY0, dstX1, dstY1};
IntRect clipRect = {0, 0, dstReq.width(), dstReq.height()};
IntRect srcReq = IntRect{srcX0, srcY0, srcX1, srcY1} - srctex.offset;
IntRect dstReq = IntRect{dstX0, dstY0, dstX1, dstY1} - dsttex.offset;
if (srcReq.is_empty() || dstReq.is_empty()) {
return;
}
IntRect clipRect = {0, 0, dstReq.width(), dstReq.height()};
prepare_texture(srctex);
prepare_texture(dsttex, &dstReq);
if (!srcReq.same_size(dstReq) && srctex.width >= 2 && filter == GL_LINEAR &&
@ -424,8 +423,10 @@ void Composite(LockedTexture* lockedDst, LockedTexture* lockedSrc, GLint srcX,
assert(srctex.bpp() == 4);
assert(dsttex.bpp() == 4);
IntRect srcReq = {srcX, srcY, srcX + srcWidth, srcY + srcHeight};
IntRect dstReq = {dstX, dstY, dstX + dstWidth, dstY + dstHeight};
IntRect srcReq =
IntRect{srcX, srcY, srcX + srcWidth, srcY + srcHeight} - srctex.offset;
IntRect dstReq =
IntRect{dstX, dstY, dstX + dstWidth, dstY + dstHeight} - dsttex.offset;
// Compute clip rect as relative to the dstReq, as that's the same coords
// as used for the sampling bounds.
IntRect clipRect = {clipX - dstX,
@ -899,10 +900,13 @@ void CompositeYUV(LockedTexture* lockedDst, LockedTexture* lockedY,
(ytex.bpp() == 2 && colorDepth > 8));
// assert(ytex.width == utex.width && ytex.height == utex.height);
assert(utex.width == vtex.width && utex.height == vtex.height);
assert(ytex.offset == utex.offset && ytex.offset == vtex.offset);
assert(dsttex.bpp() == 4);
IntRect srcReq = {srcX, srcY, srcX + srcWidth, srcY + srcHeight};
IntRect dstReq = {dstX, dstY, dstX + dstWidth, dstY + dstHeight};
IntRect srcReq =
IntRect{srcX, srcY, srcX + srcWidth, srcY + srcHeight} - ytex.offset;
IntRect dstReq =
IntRect{dstX, dstY, dstX + dstWidth, dstY + dstHeight} - dsttex.offset;
// Compute clip rect as relative to the dstReq, as that's the same coords
// as used for the sampling bounds.
IntRect clipRect = {clipX - dstX,

View File

@ -80,6 +80,8 @@
using namespace glsl;
typedef ivec2_scalar IntPoint;
struct IntRect {
int x0;
int y0;
@ -90,6 +92,8 @@ struct IntRect {
int height() const { return y1 - y0; }
bool is_empty() const { return width() <= 0 || height() <= 0; }
IntPoint origin() const { return IntPoint(x0, y0); }
bool same_size(const IntRect& o) const {
return width() == o.width() && height() == o.height();
}
@ -129,13 +133,20 @@ struct IntRect {
swap(y0, y1);
}
IntRect& offset(int dx, int dy) {
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
IntRect& offset(const IntPoint& o) {
x0 += o.x;
y0 += o.y;
x1 += o.x;
y1 += o.y;
return *this;
}
IntRect operator+(const IntPoint& o) const {
return IntRect(*this).offset(o);
}
IntRect operator-(const IntPoint& o) const {
return IntRect(*this).offset(-o);
}
};
struct VertexAttrib {
@ -535,6 +546,10 @@ struct Texture {
// locks, we need to disallow modifying or destroying the texture as it may
// be accessed by other threads where modifications could lead to races.
int32_t locked = 0;
// When used as an attachment of a framebuffer, rendering to the texture
// behaves as if it is located at the given offset such that the offset is
// subtracted from all transformed vertexes after the viewport is applied.
IntPoint offset;
enum FLAGS {
// If the buffer is internally-allocated by SWGL
@ -684,10 +699,11 @@ struct Texture {
~Texture() { cleanup(); }
IntRect bounds() const { return IntRect{0, 0, width, height}; }
IntRect offset_bounds() const { return bounds() + offset; }
// Find the valid sampling bounds relative to the requested region
IntRect sample_bounds(const IntRect& req, bool invertY = false) const {
IntRect bb = bounds().intersect(req).offset(-req.x0, -req.y0);
IntRect bb = bounds().intersect(req) - req.origin();
if (invertY) bb.invert_y(req.height());
return bb;
}
@ -965,8 +981,13 @@ struct Context {
return textures[texture_units[unit].texture_rectangle_binding];
}
IntRect apply_scissor(IntRect bb) const {
return scissortest ? bb.intersect(scissor) : bb;
IntRect apply_scissor(IntRect bb,
const IntPoint& origin = IntPoint(0, 0)) const {
return scissortest ? bb.intersect(scissor - origin) : bb;
}
IntRect apply_scissor(const Texture& t) const {
return apply_scissor(t.bounds(), t.offset);
}
};
static Context* ctx = nullptr;
@ -2345,7 +2366,7 @@ static void clear_buffer(Texture& t, T value, int layer, IntRect bb,
template <typename T>
static inline void clear_buffer(Texture& t, T value, int layer = 0) {
IntRect bb = ctx->apply_scissor(t.bounds());
IntRect bb = ctx->apply_scissor(t);
if (bb.width() > 0) {
clear_buffer<T>(t, value, layer, bb);
}
@ -2439,7 +2460,7 @@ static void prepare_texture(Texture& t, const IntRect* skip) {
}
static inline bool clear_requires_scissor(Texture& t) {
return ctx->scissortest && !ctx->scissor.contains(t.bounds());
return ctx->scissortest && !ctx->scissor.contains(t.offset_bounds());
}
// Setup a clear on a texture. This may either force an immediate clear or
@ -2449,7 +2470,8 @@ static void request_clear(Texture& t, int layer, T value) {
// If the clear would require a scissor, force clear anything outside
// the scissor, and then immediately clear anything inside the scissor.
if (clear_requires_scissor(t)) {
force_clear<T>(t, &ctx->scissor);
IntRect skip = ctx->scissor - t.offset;
force_clear<T>(t, &skip);
clear_buffer<T>(t, value, layer);
} else if (t.depth > 1) {
// Delayed clear is not supported on texture arrays.
@ -2483,7 +2505,7 @@ static ALWAYS_INLINE void fill_depth_run(DepthRun* dst, size_t n,
void Texture::fill_depth_runs(uint16_t depth) {
if (!buf) return;
assert(cleared());
IntRect bb = ctx->apply_scissor(bounds());
IntRect bb = ctx->apply_scissor(*this);
DepthRun* runs = (DepthRun*)sample_ptr(0, bb.y0);
for (int rows = bb.height(); rows > 0; rows--) {
if (bb.width() >= width) {
@ -2504,7 +2526,8 @@ void Texture::fill_depth_runs(uint16_t depth) {
extern "C" {
void InitDefaultFramebuffer(int width, int height, int stride, void* buf) {
void InitDefaultFramebuffer(int x, int y, int width, int height, int stride,
void* buf) {
Framebuffer& fb = ctx->framebuffers[0];
if (!fb.color_attachment) {
GenTextures(1, &fb.color_attachment);
@ -2514,12 +2537,14 @@ void InitDefaultFramebuffer(int width, int height, int stride, void* buf) {
// the underlying storage for the color buffer texture.
Texture& colortex = ctx->textures[fb.color_attachment];
set_tex_storage(colortex, GL_RGBA8, width, height, buf, stride);
colortex.offset = IntPoint(x, y);
if (!fb.depth_attachment) {
GenTextures(1, &fb.depth_attachment);
}
// Ensure dimensions of the depth buffer match the color buffer.
Texture& depthtex = ctx->textures[fb.depth_attachment];
set_tex_storage(depthtex, GL_DEPTH_COMPONENT16, width, height);
depthtex.offset = IntPoint(x, y);
}
void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width,
@ -2532,6 +2557,7 @@ void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width,
if (flush) {
prepare_texture(colortex);
}
assert(colortex.offset == IntPoint(0, 0));
*width = colortex.width;
*height = colortex.height;
*stride = colortex.stride();
@ -2625,6 +2651,9 @@ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
prepare_texture(t);
// debugf("read pixels %d, %d, %d, %d from fb %d with format %x\n", x, y,
// width, height, ctx->read_framebuffer_binding, t.internal_format);
x -= t.offset.x;
y -= t.offset.y;
assert(x >= 0 && y >= 0);
assert(x + width <= t.width);
assert(y + height <= t.height);
if (internal_format_for_data(format, type) != t.internal_format) {
@ -3109,7 +3138,7 @@ struct ClipRect {
float y1;
ClipRect(const IntRect& i) : x0(i.x0), y0(i.y0), x1(i.x1), y1(i.y1) {}
ClipRect(Texture& t) : ClipRect(ctx->apply_scissor(t.bounds())) {}
ClipRect(const Texture& t) : ClipRect(ctx->apply_scissor(t)) {}
template <typename P>
bool overlaps(int nump, const P* p) const {
@ -3920,7 +3949,8 @@ static void draw_perspective(int nump, Interpolants interp_outs[4],
vec3_scalar scale =
vec3_scalar(ctx->viewport.width(), ctx->viewport.height(), 1) * 0.5f;
vec3_scalar offset =
vec3_scalar(ctx->viewport.x0, ctx->viewport.y0, 0.0f) + scale;
make_vec3(make_vec2(ctx->viewport.origin() - colortex.offset), 0.0f) +
scale;
if (test_none(pos.z <= -pos.w || pos.z >= pos.w)) {
// No points cross the near or far planes, so no clipping required.
// Just divide coords by W and convert to viewport. We assume the W
@ -4017,7 +4047,7 @@ static void draw_quad(int nump, Texture& colortex, int layer,
if(!isfinite(w)) w = 0.0f;
vec2 screen = (pos.sel(X, Y) * w + 1) * 0.5f *
vec2_scalar(ctx->viewport.width(), ctx->viewport.height()) +
vec2_scalar(ctx->viewport.x0, ctx->viewport.y0);
make_vec2(ctx->viewport.origin() - colortex.offset);
Point2D p[4] = {{screen.x.x, screen.y.x},
{screen.x.y, screen.y.y},
{screen.x.z, screen.y.z},
@ -4166,6 +4196,7 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
assert(depthtex.internal_format == GL_DEPTH_COMPONENT16);
assert(colortex.width == depthtex.width &&
colortex.height == depthtex.height);
assert(colortex.offset == depthtex.offset);
}
// debugf("current_vertex_array %d\n", ctx->current_vertex_array);

View File

@ -747,6 +747,8 @@ struct ivec2_scalar {
return ivec2_scalar{select(c1), select(c2)};
}
ivec2_scalar operator-() const { return ivec2_scalar{-x, -y}; }
ivec2_scalar& operator+=(ivec2_scalar a) {
x += a.x;
y += a.y;
@ -771,6 +773,14 @@ struct ivec2_scalar {
friend ivec2_scalar operator+(ivec2_scalar a, ivec2_scalar b) {
return ivec2_scalar{a.x + b.x, a.y + b.y};
}
friend ivec2_scalar operator-(ivec2_scalar a, ivec2_scalar b) {
return ivec2_scalar{a.x - b.x, a.y - b.y};
}
friend bool operator==(const ivec2_scalar& l, const ivec2_scalar& r) {
return l.x == r.x && l.y == r.y;
}
};
struct ivec2 {

View File

@ -254,7 +254,14 @@ extern "C" {
fn GetString(name: GLenum) -> *const c_char;
fn GetStringi(name: GLenum, index: GLuint) -> *const c_char;
fn GetError() -> GLenum;
fn InitDefaultFramebuffer(width: i32, height: i32, stride: i32, buf: *mut c_void);
fn InitDefaultFramebuffer(
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
buf: *mut c_void,
);
fn GetColorBuffer(
fbo: GLuint,
flush: GLboolean,
@ -363,9 +370,17 @@ impl Context {
}
}
pub fn init_default_framebuffer(&self, width: i32, height: i32, stride: i32, buf: *mut c_void) {
pub fn init_default_framebuffer(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
buf: *mut c_void,
) {
unsafe {
InitDefaultFramebuffer(width, height, stride, buf);
InitDefaultFramebuffer(x, y, width, height, stride, buf);
}
}

View File

@ -289,7 +289,7 @@ impl WindowWrapper {
#[cfg(feature = "software")]
fn update_software(&self, dim: DeviceIntSize) {
if let Some(swgl) = self.software_gl() {
swgl.init_default_framebuffer(dim.width, dim.height, 0, std::ptr::null_mut());
swgl.init_default_framebuffer(0, 0, dim.width, dim.height, 0, std::ptr::null_mut());
}
}