| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "config.h" |
| #include "ThrottledTextureUploader.h" |
| |
| #include "Extensions3DChromium.h" |
| #include <algorithm> |
| #include <public/WebGraphicsContext3D.h> |
| #include <vector> |
| |
| namespace { |
| |
| // Number of pending texture update queries to allow. |
| static const size_t maxPendingQueries = 2; |
| |
| // How many previous uploads to use when predicting future throughput. |
| static const size_t uploadHistorySize = 10; |
| |
| // Global estimated number of textures per second to maintain estimates across |
| // subsequent instances of ThrottledTextureUploader. |
| // More than one thread will not access this variable, so we do not need to synchronize access. |
| static double estimatedTexturesPerSecondGlobal = 48.0 * 60.0; |
| |
| } // anonymous namespace |
| |
| namespace cc { |
| |
| ThrottledTextureUploader::Query::Query(WebKit::WebGraphicsContext3D* context) |
| : m_context(context) |
| , m_queryId(0) |
| , m_value(0) |
| , m_hasValue(false) |
| , m_texturesUploaded(0) |
| { |
| m_queryId = m_context->createQueryEXT(); |
| } |
| |
| ThrottledTextureUploader::Query::~Query() |
| { |
| m_context->deleteQueryEXT(m_queryId); |
| } |
| |
| void ThrottledTextureUploader::Query::begin() |
| { |
| m_hasValue = false; |
| m_context->beginQueryEXT(Extensions3DChromium::COMMANDS_ISSUED_CHROMIUM, m_queryId); |
| } |
| |
| void ThrottledTextureUploader::Query::end(double texturesUploaded) |
| { |
| m_context->endQueryEXT(Extensions3DChromium::COMMANDS_ISSUED_CHROMIUM); |
| m_texturesUploaded = texturesUploaded; |
| } |
| |
| bool ThrottledTextureUploader::Query::isPending() |
| { |
| unsigned available = 1; |
| m_context->getQueryObjectuivEXT(m_queryId, Extensions3DChromium::QUERY_RESULT_AVAILABLE_EXT, &available); |
| return !available; |
| } |
| |
| void ThrottledTextureUploader::Query::wait() |
| { |
| value(); |
| return; |
| } |
| |
| unsigned ThrottledTextureUploader::Query::value() |
| { |
| if (!m_hasValue) { |
| m_context->getQueryObjectuivEXT(m_queryId, Extensions3DChromium::QUERY_RESULT_EXT, &m_value); |
| m_hasValue = true; |
| } |
| return m_value; |
| } |
| |
| double ThrottledTextureUploader::Query::texturesUploaded() |
| { |
| return m_texturesUploaded; |
| } |
| |
| ThrottledTextureUploader::ThrottledTextureUploader(WebKit::WebGraphicsContext3D* context) |
| : m_context(context) |
| , m_maxPendingQueries(maxPendingQueries) |
| , m_texturesPerSecondHistory(uploadHistorySize, estimatedTexturesPerSecondGlobal) |
| , m_texturesUploaded(0) |
| { |
| } |
| |
| ThrottledTextureUploader::ThrottledTextureUploader(WebKit::WebGraphicsContext3D* context, size_t pendingUploadLimit) |
| : m_context(context) |
| , m_maxPendingQueries(pendingUploadLimit) |
| , m_texturesPerSecondHistory(uploadHistorySize, estimatedTexturesPerSecondGlobal) |
| , m_texturesUploaded(0) |
| { |
| ASSERT(m_context); |
| } |
| |
| ThrottledTextureUploader::~ThrottledTextureUploader() |
| { |
| } |
| |
| bool ThrottledTextureUploader::isBusy() |
| { |
| processQueries(); |
| |
| if (!m_availableQueries.isEmpty()) |
| return false; |
| |
| if (m_pendingQueries.size() == m_maxPendingQueries) |
| return true; |
| |
| m_availableQueries.append(Query::create(m_context)); |
| return false; |
| } |
| |
| double ThrottledTextureUploader::estimatedTexturesPerSecond() |
| { |
| processQueries(); |
| |
| // The history should never be empty because we initialize all elements with an estimate. |
| ASSERT(m_texturesPerSecondHistory.size() == uploadHistorySize); |
| |
| // Sort the history and use the median as our estimate. |
| std::vector<double> sortedHistory(m_texturesPerSecondHistory.begin(), |
| m_texturesPerSecondHistory.end()); |
| std::sort(sortedHistory.begin(), sortedHistory.end()); |
| |
| estimatedTexturesPerSecondGlobal = sortedHistory[sortedHistory.size() / 2]; |
| return estimatedTexturesPerSecondGlobal; |
| } |
| |
| void ThrottledTextureUploader::beginUploads() |
| { |
| m_texturesUploaded = 0; |
| |
| // Wait for query to become available. |
| while (isBusy()) |
| m_pendingQueries.first()->wait(); |
| |
| ASSERT(!m_availableQueries.isEmpty()); |
| m_availableQueries.first()->begin(); |
| } |
| |
| void ThrottledTextureUploader::endUploads() |
| { |
| m_availableQueries.first()->end(m_texturesUploaded); |
| m_pendingQueries.append(m_availableQueries.takeFirst()); |
| } |
| |
| void ThrottledTextureUploader::uploadTexture(CCResourceProvider* resourceProvider, Parameters upload) |
| { |
| m_texturesUploaded++; |
| upload.texture->updateRect(resourceProvider, upload.sourceRect, upload.destOffset); |
| } |
| |
| void ThrottledTextureUploader::processQueries() |
| { |
| while (!m_pendingQueries.isEmpty()) { |
| if (m_pendingQueries.first()->isPending()) |
| break; |
| |
| unsigned usElapsed = m_pendingQueries.first()->value(); |
| double texturesPerSecond = m_pendingQueries.first()->texturesUploaded() / (usElapsed * 1e-6); |
| |
| // Remove the oldest values from our history and insert the new one |
| m_texturesPerSecondHistory.pop_back(); |
| m_texturesPerSecondHistory.push_front(texturesPerSecond); |
| |
| m_availableQueries.append(m_pendingQueries.takeFirst()); |
| } |
| } |
| |
| } |