// 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 #include #include 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_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 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()); } } }