mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 921212 - Rotate buffer in place to avoid gralloc surface allocation. r=Bas
This commit is contained in:
parent
b1dcc268e4
commit
1aa99b3bef
@ -578,6 +578,15 @@ public:
|
||||
virtual TemporaryRef<SourceSurface> Snapshot() = 0;
|
||||
virtual IntSize GetSize() = 0;
|
||||
|
||||
/**
|
||||
* If possible returns the bits to this DrawTarget for direct manipulation. While
|
||||
* the bits is locked any modifications to this DrawTarget is forbidden.
|
||||
* Release takes the original data pointer for safety.
|
||||
*/
|
||||
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat) { return false; }
|
||||
virtual void ReleaseBits(uint8_t* aData) {}
|
||||
|
||||
/* Ensure that the DrawTarget backend has flushed all drawing operations to
|
||||
* this draw target. This must be called before using the backing surface of
|
||||
* this draw target outside of GFX 2D code.
|
||||
|
@ -399,6 +399,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
|
||||
|
||||
DrawTargetCairo::DrawTargetCairo()
|
||||
: mContext(nullptr)
|
||||
, mLockedBits(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -408,6 +409,7 @@ DrawTargetCairo::~DrawTargetCairo()
|
||||
if (mSurface) {
|
||||
cairo_surface_destroy(mSurface);
|
||||
}
|
||||
MOZ_ASSERT(!mLockedBits);
|
||||
}
|
||||
|
||||
IntSize
|
||||
@ -433,6 +435,31 @@ DrawTargetCairo::Snapshot()
|
||||
return mSnapshot;
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat)
|
||||
{
|
||||
if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
|
||||
WillChange();
|
||||
|
||||
mLockedBits = cairo_image_surface_get_data(mSurface);
|
||||
*aData = mLockedBits;
|
||||
*aSize = GetSize();
|
||||
*aStride = cairo_image_surface_get_stride(mSurface);
|
||||
*aFormat = GetFormat();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCairo::ReleaseBits(uint8_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(mLockedBits = aData);
|
||||
mLockedBits = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCairo::Flush()
|
||||
{
|
||||
@ -1139,6 +1166,7 @@ void
|
||||
DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
|
||||
{
|
||||
MarkSnapshotIndependent();
|
||||
MOZ_ASSERT(!mLiveSurface);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -60,6 +60,10 @@ public:
|
||||
virtual TemporaryRef<SourceSurface> Snapshot();
|
||||
virtual IntSize GetSize();
|
||||
|
||||
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat);
|
||||
virtual void ReleaseBits(uint8_t* aData);
|
||||
|
||||
virtual void Flush();
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
@ -184,6 +188,8 @@ private: // data
|
||||
cairo_surface_t* mSurface;
|
||||
IntSize mSize;
|
||||
|
||||
uint8_t* mLockedBits;
|
||||
|
||||
// The latest snapshot of this surface. This needs to be told when this
|
||||
// target is modified. We keep it alive as a cache.
|
||||
RefPtr<SourceSurfaceCairo> mSnapshot;
|
||||
|
63
gfx/layers/BufferUnrotate.cpp
Normal file
63
gfx/layers/BufferUnrotate.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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/. */
|
||||
|
||||
#include <algorithm> // min & max
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
|
||||
int aByteStride, int aXBoundary, int aYBoundary)
|
||||
{
|
||||
if (aXBoundary != 0) {
|
||||
uint8_t* line = new uint8_t[aByteWidth];
|
||||
uint32_t smallStart = 0;
|
||||
uint32_t smallLen = aXBoundary;
|
||||
uint32_t smallDest = aByteWidth - aXBoundary;
|
||||
uint32_t largeStart = aXBoundary;
|
||||
uint32_t largeLen = aByteWidth - aXBoundary;
|
||||
uint32_t largeDest = 0;
|
||||
if (aXBoundary > aByteWidth / 2) {
|
||||
smallStart = aXBoundary;
|
||||
smallLen = aByteWidth - aXBoundary;
|
||||
smallDest = 0;
|
||||
largeStart = 0;
|
||||
largeLen = aXBoundary;
|
||||
largeDest = smallLen;
|
||||
}
|
||||
|
||||
for (int y = 0; y < aHeight; y++) {
|
||||
int yOffset = y * aByteStride;
|
||||
memcpy(line, &aBuffer[yOffset + smallStart], smallLen);
|
||||
memmove(&aBuffer[yOffset + largeDest], &aBuffer[yOffset + largeStart], largeLen);
|
||||
memcpy(&aBuffer[yOffset + smallDest], line, smallLen);
|
||||
}
|
||||
|
||||
delete[] line;
|
||||
}
|
||||
|
||||
if (aYBoundary != 0) {
|
||||
uint32_t smallestHeight = std::min(aHeight - aYBoundary, aYBoundary);
|
||||
uint32_t largestHeight = std::max(aHeight - aYBoundary, aYBoundary);
|
||||
uint32_t smallOffset = 0;
|
||||
uint32_t largeOffset = aYBoundary * aByteStride;
|
||||
uint32_t largeDestOffset = 0;
|
||||
uint32_t smallDestOffset = largestHeight * aByteStride;
|
||||
if (aYBoundary > aHeight / 2) {
|
||||
smallOffset = aYBoundary * aByteStride;
|
||||
largeOffset = 0;
|
||||
largeDestOffset = smallestHeight * aByteStride;
|
||||
smallDestOffset = 0;
|
||||
}
|
||||
|
||||
uint8_t* smallestSide = new uint8_t[aByteStride * smallestHeight];
|
||||
memcpy(smallestSide, &aBuffer[smallOffset], aByteStride * smallestHeight);
|
||||
memmove(&aBuffer[largeDestOffset], &aBuffer[largeOffset], aByteStride * largestHeight);
|
||||
memcpy(&aBuffer[smallDestOffset], smallestSide, aByteStride * smallestHeight);
|
||||
delete[] smallestSide;
|
||||
}
|
||||
}
|
||||
|
14
gfx/layers/BufferUnrotate.h
Normal file
14
gfx/layers/BufferUnrotate.h
Normal file
@ -0,0 +1,14 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* 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/. */
|
||||
|
||||
#ifndef GFX_BUFFERUNROTATE_H
|
||||
#define GFX_BUFFERUNROTATE_H
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
void BufferUnrotate(uint8_t* aBuffer, int aByteWidth, int aHeight,
|
||||
int aByteStride, int aXByteBoundary, int aYBoundary);
|
||||
|
||||
#endif // GFX_BUFFERUNROTATE_H
|
@ -8,6 +8,7 @@
|
||||
#include <algorithm> // for max
|
||||
#include "BasicImplData.h" // for BasicImplData
|
||||
#include "BasicLayersImpl.h" // for ToData
|
||||
#include "BufferUnrotate.h" // for BufferUnrotate
|
||||
#include "GeckoProfiler.h" // for PROFILER_LABEL
|
||||
#include "Layers.h" // for ThebesLayer, Layer, etc
|
||||
#include "gfxColor.h" // for gfxRGBA
|
||||
@ -675,18 +676,54 @@ ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType,
|
||||
}
|
||||
}
|
||||
result.mDidSelfCopy = true;
|
||||
mDidSelfCopy = true;
|
||||
// Don't set destBuffer; we special-case self-copies, and
|
||||
// just did the necessary work above.
|
||||
mBufferRect = destBufferRect;
|
||||
} else {
|
||||
// We can't do a real self-copy because the buffer is rotated.
|
||||
// So allocate a new buffer for the destination.
|
||||
destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
|
||||
CreateBuffer(contentType, destBufferRect, bufferFlags,
|
||||
getter_AddRefs(destBuffer), getter_AddRefs(destBufferOnWhite),
|
||||
&destDTBuffer, &destDTBufferOnWhite);
|
||||
if (!destBuffer && !destDTBuffer)
|
||||
return result;
|
||||
// With azure and a data surface perform an buffer unrotate
|
||||
// (SelfCopy).
|
||||
if (IsAzureBuffer()) {
|
||||
unsigned char* data;
|
||||
IntSize size;
|
||||
int32_t stride;
|
||||
SurfaceFormat format;
|
||||
|
||||
if (mDTBuffer->LockBits(&data, &size, &stride, &format)) {
|
||||
uint8_t bytesPerPixel = BytesPerPixel(format);
|
||||
BufferUnrotate(data,
|
||||
size.width * bytesPerPixel,
|
||||
size.height, stride,
|
||||
newRotation.x * bytesPerPixel, newRotation.y);
|
||||
mDTBuffer->ReleaseBits(data);
|
||||
|
||||
if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
|
||||
mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
|
||||
uint8_t bytesPerPixel = BytesPerPixel(format);
|
||||
BufferUnrotate(data,
|
||||
size.width * bytesPerPixel,
|
||||
size.height, stride,
|
||||
newRotation.x * bytesPerPixel, newRotation.y);
|
||||
mDTBufferOnWhite->ReleaseBits(data);
|
||||
}
|
||||
|
||||
// Buffer unrotate moves all the pixels, note that
|
||||
// we self copied for SyncBackToFrontBuffer
|
||||
result.mDidSelfCopy = true;
|
||||
mDidSelfCopy = true;
|
||||
mBufferRect = destBufferRect;
|
||||
mBufferRotation = nsIntPoint(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.mDidSelfCopy) {
|
||||
destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
|
||||
CreateBuffer(contentType, destBufferRect, bufferFlags,
|
||||
getter_AddRefs(destBuffer), getter_AddRefs(destBufferOnWhite),
|
||||
&destDTBuffer, &destDTBufferOnWhite);
|
||||
if (!destBuffer && !destDTBuffer)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mBufferRect = destBufferRect;
|
||||
|
@ -146,6 +146,9 @@ protected:
|
||||
* buffer at the other end, not 2D rotation!
|
||||
*/
|
||||
nsIntPoint mBufferRotation;
|
||||
// When this is true it means that all pixels have moved inside the buffer.
|
||||
// It's not possible to sync with another buffer without a full copy.
|
||||
bool mDidSelfCopy;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -479,21 +479,13 @@ ContentClientDoubleBuffered::SyncFrontBufferToBackBuffer()
|
||||
|
||||
nsIntRegion updateRegion = mFrontUpdatedRegion;
|
||||
|
||||
int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x;
|
||||
int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y;
|
||||
|
||||
// Figure out whether the area we want to copy wraps the edges of our buffer.
|
||||
bool needFullCopy = (xBoundary < updateRegion.GetBounds().XMost() &&
|
||||
xBoundary > updateRegion.GetBounds().x) ||
|
||||
(yBoundary < updateRegion.GetBounds().YMost() &&
|
||||
yBoundary > updateRegion.GetBounds().y);
|
||||
|
||||
// This is a tricky trade off, we're going to get stuff out of our
|
||||
// frontbuffer now, but the next PaintThebes might throw it all (or mostly)
|
||||
// away if the visible region has changed. This is why in reality we want
|
||||
// this code integrated with PaintThebes to always do the optimal thing.
|
||||
|
||||
if (needFullCopy) {
|
||||
if (mDidSelfCopy) {
|
||||
mDidSelfCopy = false;
|
||||
// We can't easily draw our front buffer into us, since we're going to be
|
||||
// copying stuff around anyway it's easiest if we just move our situation
|
||||
// to non-rotated while we're at it. If this situation occurs we'll have
|
||||
|
@ -194,6 +194,7 @@ CPP_SOURCES += [
|
||||
'basic/BasicLayerManager.cpp',
|
||||
'basic/BasicLayersImpl.cpp',
|
||||
'basic/BasicThebesLayer.cpp',
|
||||
'BufferUnrotate.cpp',
|
||||
'client/CanvasClient.cpp',
|
||||
'composite/CanvasLayerComposite.cpp',
|
||||
'opengl/CanvasLayerOGL.cpp',
|
||||
|
151
gfx/tests/gtest/TestBufferRotation.cpp
Normal file
151
gfx/tests/gtest/TestBufferRotation.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "BufferUnrotate.h"
|
||||
|
||||
static unsigned char* GenerateBuffer(int bytesPerPixel,
|
||||
int width, int height,
|
||||
int stride, int xBoundary, int yBoundary)
|
||||
{
|
||||
unsigned char* buffer = new unsigned char[stride*height];
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pos = ((yBoundary + y) % height) * stride +
|
||||
((xBoundary + x) % width) * bytesPerPixel;
|
||||
for (int i = 0; i < bytesPerPixel; i++) {
|
||||
buffer[pos+i] = (x+y+i*2)%256;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static bool CheckBuffer(unsigned char* buffer, int bytesPerPixel,
|
||||
int width, int height, int stride)
|
||||
{
|
||||
int xBoundary = 0;
|
||||
int yBoundary = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pos = ((yBoundary + y) % height) * stride +
|
||||
((xBoundary + x) % width) * bytesPerPixel;
|
||||
for (int i = 0; i < bytesPerPixel; i++) {
|
||||
if (buffer[pos+i] != (x+y+i*2)%256) {
|
||||
printf("Buffer differs at %i, %i, is %i\n", x, y, (int)buffer[pos+i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(Gfx, BufferUnrotateHorizontal) {
|
||||
const int NUM_OF_TESTS = 8;
|
||||
int bytesPerPixelList[2] = {2,4};
|
||||
int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
|
||||
int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
|
||||
int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
|
||||
int yBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
|
||||
|
||||
for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
|
||||
int bytesPerPixel = bytesPerPixelList[bytesPerId];
|
||||
int stride = 256 * bytesPerPixel;
|
||||
for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
|
||||
unsigned char* buffer = GenerateBuffer(bytesPerPixel,
|
||||
width[testId], height[testId], stride,
|
||||
xBoundary[testId], yBoundary[testId]);
|
||||
BufferUnrotate(buffer,
|
||||
width[testId] * bytesPerPixel, height[testId], stride,
|
||||
xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
|
||||
|
||||
EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
|
||||
width[testId], height[testId], stride));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Gfx, BufferUnrotateVertical) {
|
||||
const int NUM_OF_TESTS = 8;
|
||||
int bytesPerPixelList[2] = {2,4};
|
||||
int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
|
||||
int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
|
||||
int xBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
|
||||
int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
|
||||
|
||||
for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
|
||||
int bytesPerPixel = bytesPerPixelList[bytesPerId];
|
||||
int stride = 256 * bytesPerPixel;
|
||||
for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
|
||||
unsigned char* buffer = GenerateBuffer(bytesPerPixel,
|
||||
width[testId], height[testId], stride,
|
||||
xBoundary[testId], yBoundary[testId]);
|
||||
BufferUnrotate(buffer, width[testId] * bytesPerPixel,
|
||||
height[testId], stride,
|
||||
xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
|
||||
|
||||
EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
|
||||
width[testId], height[testId], stride));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Gfx, BufferUnrotateBoth) {
|
||||
const int NUM_OF_TESTS = 16;
|
||||
int bytesPerPixelList[2] = {2,4};
|
||||
int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99, 100, 100, 99, 99, 100, 100, 99, 99};
|
||||
int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99, 100, 99};
|
||||
int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31};
|
||||
int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31};
|
||||
|
||||
for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
|
||||
int bytesPerPixel = bytesPerPixelList[bytesPerId];
|
||||
int stride = 256 * bytesPerPixel;
|
||||
for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
|
||||
unsigned char* buffer = GenerateBuffer(bytesPerPixel,
|
||||
width[testId], height[testId], stride,
|
||||
xBoundary[testId], yBoundary[testId]);
|
||||
BufferUnrotate(buffer,
|
||||
width[testId] * bytesPerPixel, height[testId], stride,
|
||||
xBoundary[testId] * bytesPerPixel, yBoundary[testId]);
|
||||
|
||||
EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel,
|
||||
width[testId], height[testId], stride));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Gfx, BufferUnrotateUneven) {
|
||||
const int NUM_OF_TESTS = 16;
|
||||
int bytesPerPixelList[2] = {2,4};
|
||||
int width[NUM_OF_TESTS] = {10, 100, 99, 39, 100, 40, 99, 39, 100, 50, 39, 99, 74, 60, 99, 39};
|
||||
int height[NUM_OF_TESTS] = {100, 39, 10, 99, 10, 99, 40, 99, 73, 39, 100, 39, 67, 99, 84, 99};
|
||||
int xBoundary[NUM_OF_TESTS] = {0, 0, 30, 30, 99, 31, 0, 31, 30, 30, 30, 30, 31, 31, 31, 38};
|
||||
int yBoundary[NUM_OF_TESTS] = {30, 30, 0, 30, 0, 30, 0, 30, 31, 31, 31, 31, 31, 31, 31, 98};
|
||||
|
||||
for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
|
||||
int bytesPerPixel = bytesPerPixelList[bytesPerId];
|
||||
int stride = 256 * bytesPerPixel;
|
||||
for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
|
||||
unsigned char* buffer = GenerateBuffer(bytesPerPixel,
|
||||
width[testId], height[testId], stride,
|
||||
xBoundary[testId], yBoundary[testId]);
|
||||
BufferUnrotate(buffer,
|
||||
width[testId]*bytesPerPixel, height[testId], stride,
|
||||
xBoundary[testId]*bytesPerPixel, yBoundary[testId]);
|
||||
|
||||
EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId], height[testId], stride));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ GTEST_CPP_SOURCES += [
|
||||
'TestRegion.cpp',
|
||||
'TestColorNames.cpp',
|
||||
'TestTextures.cpp',
|
||||
'TestBufferRotation.cpp',
|
||||
]
|
||||
|
||||
# Because of gkmedia on windows we wont find these
|
||||
|
Loading…
Reference in New Issue
Block a user