Clamp displayports to the maximum texture size. (bug 1135907, r=kats,tn)

This commit is contained in:
David Anderson 2015-03-03 11:14:18 -08:00
parent 467a185a7a
commit abd5d9cb35

View File

@ -825,6 +825,37 @@ ApplyRectMultiplier(nsRect aRect, float aMultiplier)
return nsRect(ceil(newX), ceil(newY), floor(newWidth), floor(newHeight));
}
// Return the maximum displayport size, based on the LayerManager's maximum
// supported texture size. The result is in app units.
static nscoord
GetMaxDisplayPortSize(nsIContent* aContent)
{
MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(), "Do not clamp displayports if tiling is enabled");
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) {
return nscoord_MAX;
}
// Grab the pres context before calling GetDisplayRootFrame, since the two
// may have different contexts and therefore different resolutions.
nsPresContext* presContext = frame->PresContext();
frame = nsLayoutUtils::GetDisplayRootFrame(frame);
nsIWidget* widget = frame->GetNearestWidget();
if (!widget) {
return nscoord_MAX;
}
LayerManager* lm = widget->GetLayerManager();
if (!lm) {
return nscoord_MAX;
}
uint32_t maxSizeInDevPixels = lm->GetMaxTextureSize();
return presContext->DevPixelsToAppUnits(maxSizeInDevPixels);
}
static nsRect
GetDisplayPortFromRectData(nsIContent* aContent,
DisplayPortPropertyData* aRectData,
@ -896,27 +927,27 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
gfxSize localRes = presContext->PresShell()->GetResolution();
parentRes.scale /= localRes.width;
}
ScreenRect screenRect = LayoutDeviceRect::FromAppUnits(base, auPerDevPixel)
* parentRes;
// Expand the rect by the margins
screenRect.Inflate(aMarginsData->mMargins);
if (gfxPrefs::LayersTilesEnabled()) {
// Note on the correctness of applying the alignment in Screen space:
// The correct space to apply the alignment in would be Layer space, but
// we don't necessarily know the scale to convert to Layer space at this
// point because Layout may not yet have chosen the resolution at which to
// render (it chooses that in FrameLayerBuilder, but this can be called
// during display list building). Therefore, we perform the alignment in
// Screen space, which basically assumes that Layout chose to render at
// screen resolution; since this is what Layout does most of the time,
// this is a good approximation. A proper solution would involve moving
// the choosing of the resolution to display-list building time.
int alignmentX = gfxPlatform::GetPlatform()->GetTileWidth();
int alignmentY = gfxPlatform::GetPlatform()->GetTileHeight();
int alignmentX = gfxPlatform::GetPlatform()->GetTileWidth();
int alignmentY = gfxPlatform::GetPlatform()->GetTileHeight();
// Expand the rect by the margins
screenRect.Inflate(aMarginsData->mMargins);
// And then align it to the requested alignment.
// Note on the correctness of applying the alignment in Screen space:
// The correct space to apply the alignment in would be Layer space, but
// we don't necessarily know the scale to convert to Layer space at this
// point because Layout may not yet have chosen the resolution at which to
// render (it chooses that in FrameLayerBuilder, but this can be called
// during display list building). Therefore, we perform the alignment in
// Screen space, which basically assumes that Layout chose to render at
// screen resolution; since this is what Layout does most of the time,
// this is a good approximation. A proper solution would involve moving the
// choosing of the resolution to display-list building time.
if (gfxPrefs::LayersTilesEnabled() && (alignmentX > 0 && alignmentY > 0)) {
// Inflate the rectangle by 1 so that we always push to the next tile
// boundary. This is desirable to stop from having a rectangle with a
// moving origin occasionally being smaller when it coincidentally lines
@ -941,9 +972,37 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
float h = alignmentY * ceil(screenRect.YMost() / alignmentY) - y;
screenRect = ScreenRect(x, y, w, h);
screenRect -= scrollPosScreen;
} else {
nscoord maxSizeInAppUnits = GetMaxDisplayPortSize(aContent);
if (maxSizeInAppUnits == nscoord_MAX) {
// Pick a safe maximum displayport size for sanity purposes. This is the
// lowest maximum texture size on tileless-platforms (Windows, D3D10).
maxSizeInAppUnits = presContext->DevPixelsToAppUnits(8192);
}
// Find the maximum size in screen pixels.
int32_t maxSizeInDevPixels = presContext->AppUnitsToDevPixels(maxSizeInAppUnits);
int32_t maxSizeInScreenPixels = floor(double(maxSizeInDevPixels) * res.scale);
// For each axis, inflate the margins up to the maximum size.
const ScreenMargin& margins = aMarginsData->mMargins;
if (screenRect.height < maxSizeInScreenPixels) {
int32_t budget = maxSizeInScreenPixels - screenRect.height;
int32_t top = std::min(int32_t(margins.top), budget);
screenRect.y -= top;
screenRect.height += top + std::min(int32_t(margins.bottom), budget - top);
}
if (screenRect.width < maxSizeInScreenPixels) {
int32_t budget = maxSizeInScreenPixels - screenRect.width;
int32_t left = std::min(int32_t(margins.left), budget);
screenRect.x -= left;
screenRect.width += left + std::min(int32_t(margins.right), budget - left);
}
}
// Convert the aligned rect back into app units
// Convert the aligned rect back into app units.
nsRect result = LayoutDeviceRect::ToAppUnits(screenRect / res, auPerDevPixel);
// Expand it for the low-res buffer if needed
@ -987,11 +1046,23 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier)
NS_ASSERTION((rectData == nullptr) != (marginsData == nullptr),
"Only one of rectData or marginsData should be set!");
nsRect result;
if (rectData) {
*aResult = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
} else {
*aResult = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);
result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);
}
if (!gfxPrefs::LayersTilesEnabled()) {
// Either we should have gotten a valid rect directly from the displayport
// base, or we should have computed a valid rect from the margins.
NS_ASSERTION(result.width <= GetMaxDisplayPortSize(aContent),
"Displayport must be a valid texture size");
NS_ASSERTION(result.height <= GetMaxDisplayPortSize(aContent),
"Displayport must be a valid texture size");
}
*aResult = result;
return true;
}