diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index af6a39ef7204..99b9aee43530 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -825,6 +825,33 @@ 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; + } + frame = nsLayoutUtils::GetDisplayRootFrame(frame); + + nsIWidget* widget = frame->GetNearestWidget(); + if (!widget) { + return nscoord_MAX; + } + LayerManager* lm = widget->GetLayerManager(); + if (!lm) { + return nscoord_MAX; + } + nsPresContext* presContext = frame->PresContext(); + + uint32_t maxSizeInDevPixels = lm->GetMaxTextureSize(); + return presContext->DevPixelsToAppUnits(maxSizeInDevPixels); +} + static nsRect GetDisplayPortFromRectData(nsIContent* aContent, DisplayPortPropertyData* aRectData, @@ -900,24 +927,23 @@ GetDisplayPortFromMarginsData(nsIContent* aContent, 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 @@ -942,9 +968,38 @@ 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 maxWidthInScreenPixels = floor(double(maxSizeInDevPixels) * res.xScale); + int32_t maxHeightInScreenPixels = floor(double(maxSizeInDevPixels) * res.yScale); + + // For each axis, inflate the margins up to the maximum size. + const ScreenMargin& margins = aMarginsData->mMargins; + if (screenRect.height < maxHeightInScreenPixels) { + int32_t budget = maxHeightInScreenPixels - 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 < maxWidthInScreenPixels) { + int32_t budget = maxWidthInScreenPixels - 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 @@ -988,11 +1043,25 @@ 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(GetMaxDisplayPortSize(aContent) <= 0 || + result.width < GetMaxDisplayPortSize(aContent), + "Displayport must be a valid texture size"); + NS_ASSERTION(GetMaxDisplayPortSize(aContent) <= 0 || + result.height < GetMaxDisplayPortSize(aContent), + "Displayport must be a valid texture size"); + } + + *aResult = result; return true; }