| /* |
| * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "config.h" |
| #include "RenderQueue.h" |
| |
| #include "BackingStore_p.h" |
| #include "WebPageClient.h" |
| #include "WebPage_p.h" |
| |
| #define DEBUG_RENDER_QUEUE 0 |
| #define DEBUG_RENDER_QUEUE_SORT 0 |
| |
| #if DEBUG_RENDER_QUEUE |
| #include <BlackBerryPlatformLog.h> |
| #include <wtf/CurrentTime.h> |
| #endif |
| |
| namespace BlackBerry { |
| namespace WebKit { |
| |
| template<SortDirection sortDirection> |
| static inline int compareRectOneDirection(const Platform::IntRect& r1, const Platform::IntRect& r2) |
| { |
| switch (sortDirection) { |
| case LeftToRight: |
| return r1.x() - r2.x(); |
| case RightToLeft: |
| return r2.x() - r1.x(); |
| case TopToBottom: |
| return r1.y() - r2.y(); |
| case BottomToTop: |
| return r2.y() - r1.y(); |
| default: |
| break; |
| } |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| template<SortDirection primarySortDirection, SortDirection secondarySortDirection> |
| static bool rectIsLessThan(const Platform::IntRect& r1, const Platform::IntRect& r2) |
| { |
| int primaryResult = compareRectOneDirection<primarySortDirection>(r1, r2); |
| if (primaryResult || secondarySortDirection == primarySortDirection) |
| return primaryResult < 0; |
| return compareRectOneDirection<secondarySortDirection>(r1, r2) < 0; |
| } |
| |
| typedef bool (*FuncRectLessThan)(const Platform::IntRect& r1, const Platform::IntRect& r2); |
| static FuncRectLessThan rectLessThanFunction(SortDirection primary, SortDirection secondary) |
| { |
| static FuncRectLessThan s_rectLessThanFunctions[NumSortDirections][NumSortDirections] = { { 0 } }; |
| static bool s_initialized = false; |
| if (!s_initialized) { |
| #define ADD_COMPARE_FUNCTION(_primary, _secondary) \ |
| s_rectLessThanFunctions[_primary][_secondary] = rectIsLessThan<_primary, _secondary> |
| |
| ADD_COMPARE_FUNCTION(LeftToRight, LeftToRight); |
| ADD_COMPARE_FUNCTION(LeftToRight, RightToLeft); |
| ADD_COMPARE_FUNCTION(LeftToRight, TopToBottom); |
| ADD_COMPARE_FUNCTION(LeftToRight, BottomToTop); |
| |
| ADD_COMPARE_FUNCTION(RightToLeft, LeftToRight); |
| ADD_COMPARE_FUNCTION(RightToLeft, RightToLeft); |
| ADD_COMPARE_FUNCTION(RightToLeft, TopToBottom); |
| ADD_COMPARE_FUNCTION(RightToLeft, BottomToTop); |
| |
| ADD_COMPARE_FUNCTION(TopToBottom, LeftToRight); |
| ADD_COMPARE_FUNCTION(TopToBottom, RightToLeft); |
| ADD_COMPARE_FUNCTION(TopToBottom, TopToBottom); |
| ADD_COMPARE_FUNCTION(TopToBottom, BottomToTop); |
| |
| ADD_COMPARE_FUNCTION(BottomToTop, LeftToRight); |
| ADD_COMPARE_FUNCTION(BottomToTop, RightToLeft); |
| ADD_COMPARE_FUNCTION(BottomToTop, TopToBottom); |
| ADD_COMPARE_FUNCTION(BottomToTop, BottomToTop); |
| #undef ADD_COMPARE_FUNCTION |
| |
| s_initialized = true; |
| } |
| |
| return s_rectLessThanFunctions[primary][secondary]; |
| } |
| |
| class RectLessThan { |
| public: |
| RectLessThan(SortDirection primarySortDirection, SortDirection secondarySortDirection) |
| : m_rectIsLessThan(rectLessThanFunction(primarySortDirection, secondarySortDirection)) |
| { |
| } |
| |
| bool operator()(const Platform::IntRect& r1, const Platform::IntRect& r2) |
| { |
| return m_rectIsLessThan(r1, r2); |
| } |
| |
| private: |
| FuncRectLessThan m_rectIsLessThan; |
| }; |
| |
| class RenderRectLessThan { |
| public: |
| RenderRectLessThan(SortDirection primarySortDirection, SortDirection secondarySortDirection) |
| : m_rectIsLessThan(rectLessThanFunction(primarySortDirection, secondarySortDirection)) |
| { |
| } |
| |
| bool operator()(const RenderRect& r1, const RenderRect& r2) |
| { |
| return m_rectIsLessThan(r1.subRects()[0], r2.subRects()[0]); |
| } |
| |
| private: |
| FuncRectLessThan m_rectIsLessThan; |
| }; |
| |
| RenderRect::RenderRect(const Platform::IntPoint& location, const Platform::IntSize& size, int splittingFactor) |
| : Platform::IntRect(location, size) |
| , m_splittingFactor(0) |
| , m_primarySortDirection(TopToBottom) |
| , m_secondarySortDirection(LeftToRight) |
| { |
| initialize(splittingFactor); |
| } |
| |
| RenderRect::RenderRect(int x, int y, int width, int height, int splittingFactor) |
| : Platform::IntRect(x, y, width, height) |
| , m_splittingFactor(0) |
| , m_primarySortDirection(TopToBottom) |
| , m_secondarySortDirection(LeftToRight) |
| { |
| initialize(splittingFactor); |
| } |
| |
| void RenderRect::initialize(int splittingFactor) |
| { |
| m_subRects.push_back(*this); |
| for (int i = 0; i < splittingFactor; ++i) |
| split(); |
| quickSort(); |
| } |
| |
| static void splitRectInHalfAndAddToList(const Platform::IntRect& rect, bool vertical, IntRectList& renderRectList) |
| { |
| if (vertical) { |
| int width1 = static_cast<int>(ceilf(rect.width() / 2.0)); |
| int width2 = static_cast<int>(floorf(rect.width() / 2.0)); |
| renderRectList.push_back(Platform::IntRect(rect.x(), rect.y(), width1, rect.height())); |
| renderRectList.push_back(Platform::IntRect(rect.x() + width1, rect.y(), width2, rect.height())); |
| } else { |
| int height1 = static_cast<int>(ceilf(rect.height() / 2.0)); |
| int height2 = static_cast<int>(floorf(rect.height() / 2.0)); |
| renderRectList.push_back(Platform::IntRect(rect.x(), rect.y(), rect.width(), height1)); |
| renderRectList.push_back(Platform::IntRect(rect.x(), rect.y() + height1, rect.width(), height2)); |
| } |
| } |
| |
| void RenderRect::split() |
| { |
| ++m_splittingFactor; |
| |
| bool vertical = !(m_splittingFactor % 2); |
| |
| IntRectList subRects; |
| for (size_t i = 0; i < m_subRects.size(); ++i) |
| splitRectInHalfAndAddToList(m_subRects.at(i), vertical, subRects); |
| m_subRects.swap(subRects); |
| } |
| |
| Platform::IntRect RenderRect::rectForRendering() |
| { |
| ASSERT(!m_subRects.empty()); |
| Platform::IntRect rect = m_subRects[0]; |
| m_subRects.erase(m_subRects.begin()); |
| return rect; |
| } |
| |
| void RenderRect::updateSortDirection(SortDirection primary, SortDirection secondary) |
| { |
| if (primary == m_primarySortDirection && secondary == m_secondarySortDirection) |
| return; |
| |
| m_primarySortDirection = primary; |
| m_secondarySortDirection = secondary; |
| |
| quickSort(); |
| } |
| |
| void RenderRect::quickSort() |
| { |
| std::sort(m_subRects.begin(), m_subRects.begin(), RectLessThan(m_primarySortDirection, m_secondarySortDirection)); |
| } |
| |
| RenderQueue::RenderQueue(BackingStorePrivate* parent) |
| : m_parent(parent) |
| , m_rectsAddedToRegularRenderJobsInCurrentCycle(false) |
| , m_currentRegularRenderJobsBatchUnderPressure(false) |
| , m_primarySortDirection(TopToBottom) |
| , m_secondarySortDirection(LeftToRight) |
| { |
| } |
| |
| void RenderQueue::reset() |
| { |
| m_rectsAddedToRegularRenderJobsInCurrentCycle = false; |
| m_currentRegularRenderJobsBatchUnderPressure = false; |
| m_primarySortDirection = TopToBottom; |
| m_secondarySortDirection = LeftToRight; |
| m_visibleZoomJobs.clear(); |
| m_visibleScrollJobs.clear(); |
| m_visibleScrollJobsCompleted.clear(); |
| m_nonVisibleScrollJobs.clear(); |
| m_nonVisibleScrollJobsCompleted.clear(); |
| m_regularRenderJobsRegion = Platform::IntRectRegion(); |
| m_currentRegularRenderJobsBatch.clear(); |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion(); |
| m_regularRenderJobsNotRenderedRegion = Platform::IntRectRegion(); |
| ASSERT(isEmpty()); |
| } |
| |
| int RenderQueue::splittingFactor(const Platform::IntRect& rect) const |
| { |
| // This method is used to split up regular render rect jobs and we want it to |
| // to be zoom invariant with respect to WebCore. In other words, if WebCore sends |
| // us a rect of viewport size to invalidate at zoom 1.0 then we split that up |
| // in the exact same way we would at zoom 2.0. The amount of content that is |
| // rendered in any one pass should stay fixed with regard to the zoom level. |
| Platform::IntRect untransformedRect = m_parent->m_webPage->d->mapFromTransformed(rect); |
| double rectArea = untransformedRect.width() * untransformedRect.height(); |
| double maxArea = m_parent->tileWidth() * m_parent->tileHeight(); |
| |
| const unsigned splitFactor = 1 << 0; |
| double renderRectArea = maxArea / splitFactor; |
| return ceil(log(rectArea / renderRectArea) / log(2.0)); |
| } |
| |
| RenderRect RenderQueue::convertToRenderRect(const Platform::IntRect& rect) const |
| { |
| return RenderRect(rect.location(), rect.size(), splittingFactor(rect)); |
| } |
| |
| bool RenderQueue::isEmpty(bool shouldPerformRegularRenderJobs) const |
| { |
| return m_visibleZoomJobs.empty() && m_visibleScrollJobs.empty() |
| && (!shouldPerformRegularRenderJobs || m_currentRegularRenderJobsBatch.empty()) |
| && (!shouldPerformRegularRenderJobs || m_regularRenderJobsRegion.isEmpty()) |
| && m_nonVisibleScrollJobs.empty(); |
| } |
| |
| bool RenderQueue::hasCurrentRegularRenderJob() const |
| { |
| return !m_currentRegularRenderJobsBatch.empty() || !m_regularRenderJobsRegion.isEmpty(); |
| } |
| |
| bool RenderQueue::hasCurrentVisibleZoomJob() const |
| { |
| return !m_visibleZoomJobs.empty(); |
| } |
| |
| bool RenderQueue::hasCurrentVisibleScrollJob() const |
| { |
| return !m_visibleScrollJobs.empty(); |
| } |
| |
| bool RenderQueue::isCurrentVisibleScrollJob(const Platform::IntRect& rect) const |
| { |
| return std::find(m_visibleScrollJobs.begin(), m_visibleScrollJobs.end(), rect) != m_visibleScrollJobs.end(); |
| } |
| |
| bool RenderQueue::isCurrentVisibleScrollJobCompleted(const Platform::IntRect& rect) const |
| { |
| return std::find(m_visibleScrollJobsCompleted.begin(), m_visibleScrollJobsCompleted.end(), rect) != m_visibleScrollJobsCompleted.end(); |
| } |
| |
| bool RenderQueue::isCurrentRegularRenderJob(const Platform::IntRect& rect) const |
| { |
| return m_regularRenderJobsRegion.isRectInRegion(rect) == Platform::IntRectRegion::ContainedInRegion |
| || m_currentRegularRenderJobsBatchRegion.isRectInRegion(rect) == Platform::IntRectRegion::ContainedInRegion; |
| } |
| |
| bool RenderQueue::currentRegularRenderJobBatchUnderPressure() const |
| { |
| return m_currentRegularRenderJobsBatchUnderPressure; |
| } |
| |
| void RenderQueue::setCurrentRegularRenderJobBatchUnderPressure(bool currentRegularRenderJobsBatchUnderPressure) |
| { |
| m_currentRegularRenderJobsBatchUnderPressure = currentRegularRenderJobsBatchUnderPressure; |
| } |
| |
| void RenderQueue::eventQueueCycled() |
| { |
| // Called by the backing store when the event queue has cycled to allow the |
| // render queue to determine if the regular render jobs are under pressure. |
| if (m_rectsAddedToRegularRenderJobsInCurrentCycle && m_currentRegularRenderJobsBatchRegion.isEmpty()) |
| m_currentRegularRenderJobsBatchUnderPressure = true; |
| m_rectsAddedToRegularRenderJobsInCurrentCycle = false; |
| } |
| |
| void RenderQueue::addToQueue(JobType type, const IntRectList& rectList) |
| { |
| for (size_t i = 0; i < rectList.size(); ++i) |
| addToQueue(type, rectList.at(i)); |
| } |
| |
| void RenderQueue::addToQueue(JobType type, const Platform::IntRect& rect) |
| { |
| if (type == NonVisibleScroll && std::find(m_visibleScrollJobs.begin(), m_visibleScrollJobs.end(), rect) != m_visibleScrollJobs.end()) |
| return; // |rect| is in a higher priority queue. |
| |
| switch (type) { |
| case VisibleZoom: |
| addToScrollZoomQueue(convertToRenderRect(rect), &m_visibleZoomJobs); |
| return; |
| case VisibleScroll: |
| addToScrollZoomQueue(convertToRenderRect(rect), &m_visibleScrollJobs); |
| return; |
| case RegularRender: |
| { |
| // Flag that we added rects in the current event queue cycle. |
| m_rectsAddedToRegularRenderJobsInCurrentCycle = true; |
| |
| // We try and detect if this newly added rect intersects or is contained in the currently running |
| // batch of render jobs. If so, then we have to start the batch over since we decompose individual |
| // rects into subrects and might have already rendered one of them. If the web page's content has |
| // changed state then this can lead to artifacts. We mark this by noting the batch is now under pressure |
| // and the backingstore will attempt to clear it at the next available opportunity. |
| Platform::IntRectRegion::IntersectionState state = m_currentRegularRenderJobsBatchRegion.isRectInRegion(rect); |
| if (state == Platform::IntRectRegion::ContainedInRegion || state == Platform::IntRectRegion::PartiallyContainedInRegion) { |
| m_regularRenderJobsRegion = Platform::IntRectRegion::unionRegions(m_regularRenderJobsRegion, m_currentRegularRenderJobsBatchRegion); |
| m_currentRegularRenderJobsBatch.clear(); |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion(); |
| m_currentRegularRenderJobsBatchUnderPressure = true; |
| } |
| addToRegularQueue(rect); |
| } |
| return; |
| case NonVisibleScroll: |
| addToScrollZoomQueue(convertToRenderRect(rect), &m_nonVisibleScrollJobs); |
| return; |
| } |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void RenderQueue::addToRegularQueue(const Platform::IntRect& rect) |
| { |
| #if DEBUG_RENDER_QUEUE |
| if (m_regularRenderJobsRegion.isRectInRegion(rect) != Platform::IntRectRegion::ContainedInRegion) { |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::addToRegularQueue %d,%d %dx%d", |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| } |
| #endif |
| |
| // Do not let the regular render queue grow past a maximum of 3 disjoint rects. |
| if (m_regularRenderJobsRegion.numRects() > 2) |
| m_regularRenderJobsRegion = Platform::unionOfRects(m_regularRenderJobsRegion.extents(), rect); |
| else |
| m_regularRenderJobsRegion = Platform::IntRectRegion::unionRegions(m_regularRenderJobsRegion, rect); |
| |
| if (!isEmpty()) |
| m_parent->dispatchRenderJob(); |
| } |
| |
| void RenderQueue::addToScrollZoomQueue(const RenderRect& rect, RenderRectList* rectList) |
| { |
| if (std::find(rectList->begin(), rectList->end(), rect) != rectList->end()) |
| return; |
| |
| #if DEBUG_RENDER_QUEUE |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::addToScrollZoomQueue %d,%d %dx%d", |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| #endif |
| rectList->push_back(rect); |
| |
| if (!isEmpty()) |
| m_parent->dispatchRenderJob(); |
| } |
| |
| void RenderQueue::quickSort(RenderRectList* queue) |
| { |
| size_t length = queue->size(); |
| if (!length) |
| return; |
| |
| for (size_t i = 0; i < length; ++i) |
| queue->at(i).updateSortDirection(m_primarySortDirection, m_secondarySortDirection); |
| return std::sort(queue->begin(), queue->end(), RenderRectLessThan(m_primarySortDirection, m_secondarySortDirection)); |
| } |
| |
| void RenderQueue::updateSortDirection(int lastDeltaX, int lastDeltaY) |
| { |
| bool primaryIsHorizontal = abs(lastDeltaX) >= abs(lastDeltaY); |
| if (primaryIsHorizontal) { |
| m_primarySortDirection = lastDeltaX <= 0 ? LeftToRight : RightToLeft; |
| m_secondarySortDirection = lastDeltaY <= 0 ? TopToBottom : BottomToTop; |
| } else { |
| m_primarySortDirection = lastDeltaY <= 0 ? TopToBottom : BottomToTop; |
| m_secondarySortDirection = lastDeltaX <= 0 ? LeftToRight : RightToLeft; |
| } |
| } |
| |
| void RenderQueue::visibleContentChanged(const Platform::IntRect& visibleContent) |
| { |
| if (m_visibleScrollJobs.empty() && m_nonVisibleScrollJobs.empty()) { |
| ASSERT(m_visibleScrollJobsCompleted.empty() && m_nonVisibleScrollJobsCompleted.empty()); |
| return; |
| } |
| |
| // Move visibleScrollJobs to nonVisibleScrollJobs if they do not intersect |
| // the visible content rect. |
| for (size_t i = 0; i < m_visibleScrollJobs.size(); ++i) { |
| RenderRect rect = m_visibleScrollJobs.at(i); |
| if (!rect.intersects(visibleContent)) { |
| m_visibleScrollJobs.erase(m_visibleScrollJobs.begin() + i); |
| addToScrollZoomQueue(rect, &m_nonVisibleScrollJobs); |
| --i; |
| } |
| } |
| |
| // Do the same for the completed list. |
| for (size_t i = 0; i < m_visibleScrollJobsCompleted.size(); ++i) { |
| RenderRect rect = m_visibleScrollJobsCompleted.at(i); |
| if (!rect.intersects(visibleContent)) { |
| m_visibleScrollJobsCompleted.erase(m_visibleScrollJobsCompleted.begin() + i); |
| addToScrollZoomQueue(rect, &m_nonVisibleScrollJobsCompleted); |
| --i; |
| } |
| } |
| |
| // Move nonVisibleScrollJobs to visibleScrollJobs if they do intersect |
| // the visible content rect. |
| for (size_t i = 0; i < m_nonVisibleScrollJobs.size(); ++i) { |
| RenderRect rect = m_nonVisibleScrollJobs.at(i); |
| if (rect.intersects(visibleContent)) { |
| m_nonVisibleScrollJobs.erase(m_nonVisibleScrollJobs.begin() + i); |
| addToScrollZoomQueue(rect, &m_visibleScrollJobs); |
| --i; |
| } |
| } |
| |
| // Do the same for the completed list. |
| for (size_t i = 0; i < m_nonVisibleScrollJobsCompleted.size(); ++i) { |
| RenderRect rect = m_nonVisibleScrollJobsCompleted.at(i); |
| if (rect.intersects(visibleContent)) { |
| m_nonVisibleScrollJobsCompleted.erase(m_nonVisibleScrollJobsCompleted.begin() + i); |
| addToScrollZoomQueue(rect, &m_visibleScrollJobsCompleted); |
| --i; |
| } |
| } |
| |
| if (m_visibleScrollJobs.empty() && !m_visibleScrollJobsCompleted.empty()) |
| visibleScrollJobsCompleted(false /*shouldBlit*/); |
| |
| if (m_nonVisibleScrollJobs.empty() && !m_nonVisibleScrollJobsCompleted.empty()) |
| nonVisibleScrollJobsCompleted(); |
| |
| // We shouldn't be empty because the early return above and the fact that this |
| // method just shuffles rects from queue to queue hence the total number of |
| // rects in the various queues should be conserved. |
| ASSERT(!isEmpty()); |
| } |
| |
| void RenderQueue::clear(const Platform::IntRectRegion& region, bool clearRegularRenderJobs) |
| { |
| IntRectList rects = region.rects(); |
| for (size_t i = 0; i < rects.size(); ++i) |
| clear(rects.at(i), clearRegularRenderJobs); |
| } |
| |
| void RenderQueue::clear(const Platform::IntRect& rect, bool clearRegularRenderJobs) |
| { |
| if (m_visibleScrollJobs.empty() && m_nonVisibleScrollJobs.empty()) |
| ASSERT(m_visibleScrollJobsCompleted.empty() && m_nonVisibleScrollJobsCompleted.empty()); |
| |
| // Remove all rects from all queues that are contained by this rect. |
| for (size_t i = 0; i < m_visibleScrollJobs.size(); ++i) { |
| if (rect.contains(m_visibleScrollJobs.at(i))) { |
| m_visibleScrollJobs.erase(m_visibleScrollJobs.begin() + i); |
| --i; |
| } |
| } |
| |
| for (size_t i = 0; i < m_visibleScrollJobsCompleted.size(); ++i) { |
| if (rect.contains(m_visibleScrollJobsCompleted.at(i))) { |
| m_visibleScrollJobsCompleted.erase(m_visibleScrollJobsCompleted.begin() + i); |
| --i; |
| } |
| } |
| |
| for (size_t i = 0; i < m_nonVisibleScrollJobs.size(); ++i) { |
| if (rect.contains(m_nonVisibleScrollJobs.at(i))) { |
| m_nonVisibleScrollJobs.erase(m_nonVisibleScrollJobs.begin() + i); |
| --i; |
| } |
| } |
| |
| for (size_t i = 0; i < m_nonVisibleScrollJobsCompleted.size(); ++i) { |
| if (rect.contains(m_nonVisibleScrollJobsCompleted.at(i))) { |
| m_nonVisibleScrollJobsCompleted.erase(m_nonVisibleScrollJobsCompleted.begin() + i); |
| --i; |
| } |
| } |
| |
| // Only clear the regular render jobs if the flag has been set. |
| if (clearRegularRenderJobs) |
| this->clearRegularRenderJobs(rect); |
| |
| if (m_visibleScrollJobs.empty() && !m_visibleScrollJobsCompleted.empty()) |
| visibleScrollJobsCompleted(false /*shouldBlit*/); |
| |
| if (m_nonVisibleScrollJobs.empty() && !m_nonVisibleScrollJobsCompleted.empty()) |
| nonVisibleScrollJobsCompleted(); |
| } |
| |
| void RenderQueue::clearRegularRenderJobs(const Platform::IntRect& rect) |
| { |
| for (size_t i = 0; i < m_currentRegularRenderJobsBatch.size(); ++i) { |
| if (rect.contains(m_currentRegularRenderJobsBatch.at(i))) { |
| m_currentRegularRenderJobsBatch.erase(m_currentRegularRenderJobsBatch.begin() + i); |
| --i; |
| } |
| } |
| m_regularRenderJobsRegion = Platform::IntRectRegion::subtractRegions(m_regularRenderJobsRegion, rect); |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion::subtractRegions(m_currentRegularRenderJobsBatchRegion, rect); |
| m_regularRenderJobsNotRenderedRegion = Platform::IntRectRegion::subtractRegions(m_regularRenderJobsNotRenderedRegion, rect); |
| } |
| |
| void RenderQueue::clearVisibleZoom() |
| { |
| m_visibleZoomJobs.clear(); |
| } |
| |
| bool RenderQueue::regularRenderJobsPreviouslyAttemptedButNotRendered(const Platform::IntRect& rect) |
| { |
| return m_regularRenderJobsNotRenderedRegion.isRectInRegion(rect) != Platform::IntRectRegion::NotInRegion; |
| } |
| |
| void RenderQueue::render(bool shouldPerformRegularRenderJobs) |
| { |
| // We request a layout here to ensure that we're executing jobs in the correct |
| // order. If we didn't request a layout here then the jobs below could result |
| // in a layout and that layout can alter this queue. So request layout if needed |
| // to ensure that the queues below are in constant state before performing the |
| // next rendering job. |
| |
| #if DEBUG_RENDER_QUEUE |
| // Start the time measurement. |
| double time = WTF::currentTime(); |
| #endif |
| |
| m_parent->requestLayoutIfNeeded(); |
| |
| #if DEBUG_RENDER_QUEUE |
| double elapsed = WTF::currentTime() - time; |
| if (elapsed) |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::render layout elapsed=%f", elapsed); |
| #endif |
| |
| // Empty the queues in a precise order of priority. |
| if (!m_visibleZoomJobs.empty()) |
| renderVisibleZoomJob(); |
| else if (!m_visibleScrollJobs.empty()) |
| renderVisibleScrollJob(); |
| else if (shouldPerformRegularRenderJobs && (!m_currentRegularRenderJobsBatch.empty() || !m_regularRenderJobsRegion.isEmpty())) { |
| if (currentRegularRenderJobBatchUnderPressure()) |
| renderAllCurrentRegularRenderJobs(); |
| else |
| renderRegularRenderJob(); |
| } else if (!m_nonVisibleScrollJobs.empty()) |
| renderNonVisibleScrollJob(); |
| } |
| |
| void RenderQueue::renderAllCurrentRegularRenderJobs() |
| { |
| #if DEBUG_RENDER_QUEUE |
| // Start the time measurement... |
| double time = WTF::currentTime(); |
| #endif |
| |
| // Request layout first |
| m_parent->requestLayoutIfNeeded(); |
| |
| #if DEBUG_RENDER_QUEUE |
| double elapsed = WTF::currentTime() - time; |
| if (elapsed) |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderAllCurrentRegularRenderJobs layout elapsed=%f", elapsed); |
| #endif |
| |
| // The state of render queue may be modified from inside requestLayoutIfNeeded. |
| // In fact, it can even be emptied entirely! Layout can trigger a call to |
| // RenderQueue::clear. See PR#101811 for instance. So we should check again here. |
| if (!hasCurrentRegularRenderJob()) |
| return; |
| |
| // If there is no current batch of jobs, then create one. |
| if (m_currentRegularRenderJobsBatchRegion.isEmpty()) { |
| |
| // Create a current region object from our regular render region. |
| m_currentRegularRenderJobsBatchRegion = m_regularRenderJobsRegion; |
| |
| // Clear this since we're about to render everything. |
| m_regularRenderJobsRegion = Platform::IntRectRegion(); |
| } |
| |
| Platform::IntRectRegion regionNotRendered; |
| if (m_parent->shouldSuppressNonVisibleRegularRenderJobs()) { |
| // Record any part of the region that doesn't intersect the current visible contents rect. |
| regionNotRendered = Platform::IntRectRegion::subtractRegions(m_currentRegularRenderJobsBatchRegion, m_parent->visibleContentsRect()); |
| m_regularRenderJobsNotRenderedRegion = Platform::IntRectRegion::unionRegions(m_regularRenderJobsNotRenderedRegion, regionNotRendered); |
| |
| #if DEBUG_RENDER_QUEUE |
| if (!regionNotRendered.isEmpty()) |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderAllCurrentRegularRenderJobs region not completely rendered!"); |
| #endif |
| |
| // Clip to the visible contents so we'll be faster. |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion::intersectRegions(m_currentRegularRenderJobsBatchRegion, m_parent->visibleContentsRect()); |
| } |
| |
| bool rendered = false; |
| if (!m_currentRegularRenderJobsBatchRegion.isEmpty()) { |
| std::vector<Platform::IntRect> rectList = m_currentRegularRenderJobsBatchRegion.rects(); |
| for (size_t i = 0; i < rectList.size(); ++i) |
| rendered = m_parent->render(rectList.at(i)) ? true : rendered; |
| } |
| |
| #if DEBUG_RENDER_QUEUE |
| // Stop the time measurement. |
| elapsed = WTF::currentTime() - time; |
| Platform::IntRect extents = m_currentRegularRenderJobsBatchRegion.extents(); |
| int numberOfRects = m_currentRegularRenderJobsBatchRegion.rects().size(); |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderAllCurrentRegularRenderJobs extents=(%d,%d %dx%d) numberOfRects=%d elapsed=%f", |
| extents.x(), extents.y(), extents.width(), extents.height(), numberOfRects, elapsed); |
| #endif |
| |
| // Clear the region and blit since this batch is now complete. |
| Platform::IntRect renderedRect = m_currentRegularRenderJobsBatchRegion.extents(); |
| m_currentRegularRenderJobsBatch.clear(); |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion(); |
| m_currentRegularRenderJobsBatchUnderPressure = false; |
| |
| if (rendered) |
| m_parent->didRenderContent(renderedRect); |
| |
| if (m_parent->shouldSuppressNonVisibleRegularRenderJobs() && !regionNotRendered.isEmpty()) |
| m_parent->updateTilesForScrollOrNotRenderedRegion(false /*checkLoading*/); |
| } |
| |
| void RenderQueue::startRegularRenderJobBatchIfNeeded() |
| { |
| if (!m_currentRegularRenderJobsBatch.empty()) |
| return; |
| |
| // Decompose the current regular render job region into render rect pieces. |
| IntRectList regularRenderJobs = m_regularRenderJobsRegion.rects(); |
| |
| // The current batch... |
| m_currentRegularRenderJobsBatch = regularRenderJobs; |
| |
| // Create a region object that will be checked when adding new rects before |
| // this batch has been completed. |
| m_currentRegularRenderJobsBatchRegion = m_regularRenderJobsRegion; |
| |
| // Clear the former region since it is now part of this batch. |
| m_regularRenderJobsRegion = Platform::IntRectRegion(); |
| |
| #if DEBUG_RENDER_QUEUE |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::startRegularRenderJobBatchIfNeeded batch size is %d!", m_currentRegularRenderJobsBatch.size()); |
| #endif |
| } |
| |
| void RenderQueue::renderVisibleZoomJob() |
| { |
| ASSERT(m_visibleZoomJobs.size() > 0); |
| |
| #if DEBUG_RENDER_QUEUE |
| // Start the time measurement. |
| double time = WTF::currentTime(); |
| #endif |
| |
| RenderRect* rect = &m_visibleZoomJobs[0]; |
| ASSERT(!rect->isCompleted()); |
| Platform::IntRect subRect = rect->rectForRendering(); |
| if (rect->isCompleted()) |
| m_visibleZoomJobs.erase(m_visibleZoomJobs.begin()); |
| |
| m_parent->render(subRect); |
| |
| // Record that it has now been rendered via a different type of job... |
| clearRegularRenderJobs(subRect); |
| |
| #if DEBUG_RENDER_QUEUE |
| // Stop the time measurement |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderVisibleZoomJob rect=(%d,%d %dx%d) elapsed=%f", |
| subRect.x(), subRect.y(), subRect.width(), subRect.height(), elapsed); |
| #endif |
| } |
| |
| void RenderQueue::renderVisibleScrollJob() |
| { |
| ASSERT(!m_visibleScrollJobs.empty()); |
| |
| #if DEBUG_RENDER_QUEUE || DEBUG_RENDER_QUEUE_SORT |
| // Start the time measurement. |
| double time = WTF::currentTime(); |
| #endif |
| |
| quickSort(&m_visibleScrollJobs); |
| |
| #if DEBUG_RENDER_QUEUE_SORT |
| // Stop the time measurement |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderVisibleScrollJob sort elapsed=%f", elapsed); |
| #endif |
| |
| RenderRect rect = m_visibleScrollJobs[0]; |
| m_visibleScrollJobs.erase(m_visibleScrollJobs.begin()); |
| |
| ASSERT(!rect.isCompleted()); |
| Platform::IntRect subRect = rect.rectForRendering(); |
| if (rect.isCompleted()) |
| m_visibleScrollJobsCompleted.push_back(rect); |
| else |
| m_visibleScrollJobs.insert(m_visibleScrollJobs.begin(), rect); |
| |
| m_parent->render(subRect); |
| |
| // Record that it has now been rendered via a different type of job... |
| clearRegularRenderJobs(subRect); |
| |
| #if DEBUG_RENDER_QUEUE |
| // Stop the time measurement |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderVisibleScrollJob rect=(%d,%d %dx%d) elapsed=%f", |
| subRect.x(), subRect.y(), subRect.width(), subRect.height(), elapsed); |
| #endif |
| |
| if (m_visibleScrollJobs.empty()) |
| visibleScrollJobsCompleted(true /*shouldBlit*/); |
| } |
| |
| void RenderQueue::renderRegularRenderJob() |
| { |
| #if DEBUG_RENDER_QUEUE |
| // Start the time measurement. |
| double time = WTF::currentTime(); |
| #endif |
| |
| ASSERT(!m_currentRegularRenderJobsBatch.empty() || !m_regularRenderJobsRegion.isEmpty()); |
| |
| startRegularRenderJobBatchIfNeeded(); |
| |
| // Take the first job from the regular render job queue. |
| Platform::IntRect rect = m_currentRegularRenderJobsBatch[0]; |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion::subtractRegions(m_currentRegularRenderJobsBatchRegion, Platform::IntRectRegion(rect)); |
| m_currentRegularRenderJobsBatch.erase(m_currentRegularRenderJobsBatch.begin()); |
| |
| Platform::IntRectRegion regionNotRendered; |
| if (m_parent->shouldSuppressNonVisibleRegularRenderJobs()) { |
| // Record any part of the region that doesn't intersect the current visible tiles rect. |
| regionNotRendered = Platform::IntRectRegion::subtractRegions(rect, m_parent->visibleContentsRect()); |
| m_regularRenderJobsNotRenderedRegion = Platform::IntRectRegion::unionRegions(m_regularRenderJobsNotRenderedRegion, regionNotRendered); |
| |
| #if DEBUG_RENDER_QUEUE |
| if (!regionNotRendered.isEmpty()) { |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderRegularRenderJob rect (%d,%d %dx%d) not completely rendered!", |
| rect.x(), rect.y(), rect.width(), rect.height()); |
| } |
| #endif |
| |
| // Clip to the visible tiles so we'll be faster. |
| rect.intersect(m_parent->visibleContentsRect()); |
| } |
| |
| if (!rect.isEmpty()) |
| m_parent->render(rect); |
| |
| #if DEBUG_RENDER_QUEUE |
| // Stop the time measurement. |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderRegularRenderJob rect=(%d,%d %dx%d) elapsed=%f", |
| rect.x(), rect.y(), rect.width(), rect.height(), elapsed); |
| #endif |
| |
| if (m_currentRegularRenderJobsBatch.empty()) { |
| Platform::IntRect renderedRect = m_currentRegularRenderJobsBatchRegion.extents(); |
| // Clear the region and the and blit since this batch is now complete. |
| m_currentRegularRenderJobsBatchRegion = Platform::IntRectRegion(); |
| m_currentRegularRenderJobsBatchUnderPressure = false; |
| m_parent->didRenderContent(renderedRect); |
| } |
| |
| // Make sure we didn't alter state of the queues that should have been empty |
| // before this method was called. |
| ASSERT(m_visibleScrollJobs.empty()); |
| |
| if (m_parent->shouldSuppressNonVisibleRegularRenderJobs() && !regionNotRendered.isEmpty()) |
| m_parent->updateTilesForScrollOrNotRenderedRegion(false /*checkLoading*/); |
| } |
| |
| void RenderQueue::renderNonVisibleScrollJob() |
| { |
| ASSERT(!m_nonVisibleScrollJobs.empty()); |
| |
| #if DEBUG_RENDER_QUEUE || DEBUG_RENDER_QUEUE_SORT |
| // Start the time measurement. |
| double time = WTF::currentTime(); |
| #endif |
| |
| quickSort(&m_nonVisibleScrollJobs); |
| |
| #if DEBUG_RENDER_QUEUE_SORT |
| // Stop the time measurement. |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderNonVisibleScrollJob sort elapsed=%f", elapsed); |
| #endif |
| |
| RenderRect rect = m_nonVisibleScrollJobs[0]; |
| m_nonVisibleScrollJobs.erase(m_nonVisibleScrollJobs.begin()); |
| |
| ASSERT(!rect.isCompleted()); |
| Platform::IntRect subRect = rect.rectForRendering(); |
| if (rect.isCompleted()) |
| m_nonVisibleScrollJobsCompleted.push_back(rect); |
| else |
| m_nonVisibleScrollJobs.insert(m_nonVisibleScrollJobs.begin(), rect); |
| |
| m_parent->render(subRect); |
| |
| // Record that it has now been rendered via a different type of job... |
| clearRegularRenderJobs(subRect); |
| |
| // Make sure we didn't alter state of the queues that should have been empty |
| // before this method was called. |
| ASSERT(m_visibleScrollJobs.empty()); |
| |
| #if DEBUG_RENDER_QUEUE |
| // Stop the time measurement. |
| double elapsed = WTF::currentTime() - time; |
| BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "RenderQueue::renderNonVisibleScrollJob rect=(%d,%d %dx%d) elapsed=%f", |
| subRect.x(), subRect.y(), subRect.width(), subRect.height(), elapsed); |
| #endif |
| |
| if (m_nonVisibleScrollJobs.empty()) |
| nonVisibleScrollJobsCompleted(); |
| } |
| |
| void RenderQueue::visibleScrollJobsCompleted(bool shouldBlit) |
| { |
| // Now blit to the screen if we are done and get rid of the completed list! |
| ASSERT(m_visibleScrollJobs.empty()); |
| m_visibleScrollJobsCompleted.clear(); |
| if (shouldBlit) |
| m_parent->didRenderContent(m_parent->visibleContentsRect()); |
| } |
| |
| void RenderQueue::nonVisibleScrollJobsCompleted() |
| { |
| // Get rid of the completed list! |
| ASSERT(m_nonVisibleScrollJobs.empty()); |
| m_nonVisibleScrollJobsCompleted.clear(); |
| } |
| |
| } // namespace WebKit |
| } // namespace BlackBerry |