Added: trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.cpp (0 => 107426)
--- trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.cpp (rev 0)
+++ trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.cpp 2012-02-10 20:11:47 UTC (rev 107426)
@@ -0,0 +1,910 @@
+/*
+ * 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();
+ m_parent->stopRenderTimer();
+ 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();
+ Platform::IntSize defaultMaxLayoutSize = WebPagePrivate::defaultMaxLayoutSize();
+ double maxArea = defaultMaxLayoutSize.width() * defaultMaxLayoutSize.height();
+
+ 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->startRenderTimer(); // Start the render timer since we could have some stale content here...
+}
+
+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->startRenderTimer(); // Start the render timer since we know we could have some checkerboard here...
+}
+
+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();
+
+ if (isEmpty())
+ m_parent->stopRenderTimer();
+}
+
+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();
+ if (isEmpty())
+ m_parent->stopRenderTimer();
+}
+
+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();
+
+ if (isEmpty())
+ m_parent->stopRenderTimer();
+}
+
+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;
+
+ // Update the screen only if we're not scrolling or zooming.
+ if (rendered && !m_parent->isScrollingOrZooming()) {
+ if (!m_parent->shouldDirectRenderingToWindow())
+ m_parent->blitVisibleContents();
+ else
+ m_parent->invalidateWindow();
+ m_parent->m_webPage->client()->notifyContentRendered(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;
+ // Update the screen only if we're not scrolling or zooming.
+ if (!m_parent->isScrollingOrZooming()) {
+ if (!m_parent->shouldDirectRenderingToWindow())
+ m_parent->blitVisibleContents();
+ else
+ m_parent->invalidateWindow();
+ m_parent->m_webPage->client()->notifyContentRendered(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->isScrollingOrZooming()) {
+ if (!m_parent->shouldDirectRenderingToWindow())
+ m_parent->blitVisibleContents();
+ else
+ m_parent->invalidateWindow();
+ m_parent->m_webPage->client()->notifyContentRendered(m_parent->visibleContentsRect());
+ }
+}
+
+void RenderQueue::nonVisibleScrollJobsCompleted()
+{
+ // Get rid of the completed list!
+ ASSERT(m_nonVisibleScrollJobs.empty());
+ m_nonVisibleScrollJobsCompleted.clear();
+}
+
+} // namespace WebKit
+} // namespace BlackBerry
Added: trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.h (0 => 107426)
--- trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.h (rev 0)
+++ trunk/Source/WebKit/blackberry/WebKitSupport/RenderQueue.h 2012-02-10 20:11:47 UTC (rev 107426)
@@ -0,0 +1,154 @@
+/*
+ * 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
+ */
+
+#ifndef RenderQueue_h
+#define RenderQueue_h
+
+#include <BlackBerryPlatformIntRectRegion.h>
+#include <BlackBerryPlatformPrimitives.h>
+#include <vector>
+
+namespace BlackBerry {
+namespace WebKit {
+
+class BackingStorePrivate;
+
+typedef std::vector<Platform::IntRect> IntRectList;
+
+enum SortDirection {
+ LeftToRight = 0,
+ RightToLeft,
+ TopToBottom,
+ BottomToTop,
+ NumSortDirections
+};
+
+class RenderRect : public Platform::IntRect {
+public:
+ RenderRect() { }
+ RenderRect(const Platform::IntPoint& location, const Platform::IntSize&, int splittingFactor);
+ RenderRect(int x, int y, int width, int height, int splittingFactor);
+ RenderRect(const Platform::IntRect&);
+
+ Platform::IntRect rectForRendering();
+
+ bool isCompleted() const { return !m_subRects.size(); }
+
+ const IntRectList& subRects() const { return m_subRects; }
+ void updateSortDirection(SortDirection primary, SortDirection secondary);
+
+private:
+ void initialize(int splittingFactor);
+ void split();
+ void quickSort();
+ int m_splittingFactor;
+ IntRectList m_subRects;
+ SortDirection m_primarySortDirection;
+ SortDirection m_secondarySortDirection;
+};
+
+typedef std::vector<RenderRect> RenderRectList;
+
+class RenderQueue {
+public:
+ enum JobType { VisibleZoom, VisibleScroll, RegularRender, NonVisibleScroll };
+ RenderQueue(BackingStorePrivate*);
+
+ void reset();
+ RenderRect convertToRenderRect(const Platform::IntRect&) const;
+
+ bool isEmpty(bool shouldPerformRegularRenderJobs = true) const;
+
+ bool hasCurrentRegularRenderJob() const;
+ bool hasCurrentVisibleZoomJob() const;
+ bool hasCurrentVisibleScrollJob() const;
+ bool isCurrentVisibleScrollJob(const Platform::IntRect&) const;
+ bool isCurrentVisibleScrollJobCompleted(const Platform::IntRect&) const;
+ bool isCurrentRegularRenderJob(const Platform::IntRect&) const;
+
+ bool currentRegularRenderJobBatchUnderPressure() const;
+ void setCurrentRegularRenderJobBatchUnderPressure(bool);
+
+ void eventQueueCycled();
+
+ void addToQueue(JobType, const Platform::IntRect&);
+ void addToQueue(JobType, const IntRectList&);
+
+ void updateSortDirection(int lastDeltaX, int lastDeltaY);
+ void visibleContentChanged(const Platform::IntRect&);
+ void clear(const Platform::IntRectRegion&, bool clearRegularRenderJobs);
+ void clear(const Platform::IntRect&, bool clearRegularRenderJobs);
+ void clearRegularRenderJobs(const Platform::IntRect&);
+ void clearVisibleZoom();
+ bool regularRenderJobsPreviouslyAttemptedButNotRendered(const Platform::IntRect&);
+ Platform::IntRectRegion regularRenderJobsNotRenderedRegion() const { return m_regularRenderJobsNotRenderedRegion; }
+
+ void render(bool shouldPerformRegularRenderJobs = true);
+ void renderAllCurrentRegularRenderJobs();
+
+private:
+ void startRegularRenderJobBatchIfNeeded();
+
+ // Render an item from the queue.
+ void renderVisibleZoomJob();
+ void renderVisibleScrollJob();
+ void renderRegularRenderJob();
+ void renderNonVisibleScrollJob();
+
+ // Methods to handle a completed set of scroll jobs.
+ void visibleScrollJobsCompleted(bool shouldBlit);
+ void nonVisibleScrollJobsCompleted();
+
+ // Internal method to add to the various queues.
+ void addToRegularQueue(const Platform::IntRect&);
+ void addToScrollZoomQueue(const RenderRect&, RenderRectList* queue);
+ void quickSort(RenderRectList*);
+
+ // The splitting factor for render rects.
+ int splittingFactor(const Platform::IntRect&) const;
+
+ BackingStorePrivate* m_parent;
+
+ // The highest priority queue.
+ RenderRectList m_visibleZoomJobs;
+ RenderRectList m_visibleScrollJobs;
+ RenderRectList m_visibleScrollJobsCompleted;
+ // The lowest priority queue.
+ RenderRectList m_nonVisibleScrollJobs;
+ RenderRectList m_nonVisibleScrollJobsCompleted;
+ // The regular render jobs are in the middle.
+ Platform::IntRectRegion m_regularRenderJobsRegion;
+ IntRectList m_currentRegularRenderJobsBatch;
+ Platform::IntRectRegion m_currentRegularRenderJobsBatchRegion;
+ bool m_rectsAddedToRegularRenderJobsInCurrentCycle;
+ bool m_currentRegularRenderJobsBatchUnderPressure;
+
+ // Holds the region of the page that we attempt to render, but the
+ // backingstore was not in the right place at the time. This will
+ // be checked before we try to restore a tile to it's last rendered
+ // place.
+ Platform::IntRectRegion m_regularRenderJobsNotRenderedRegion;
+
+ SortDirection m_primarySortDirection;
+ SortDirection m_secondarySortDirection;
+};
+
+} // namespace WebKit
+} // namespace BlackBerry
+
+#endif // RenderQueue_h