mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 07:37:28 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into arrow-actions
This commit is contained in:
commit
37db1cba26
22 changed files with 1236 additions and 335 deletions
|
@ -413,6 +413,9 @@ void AvatarMixer::handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
nodeList->getOrCreateLinkedData(senderNode);
|
||||||
|
|
||||||
if (senderNode->getLinkedData()) {
|
if (senderNode->getLinkedData()) {
|
||||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
||||||
if (nodeData != nullptr) {
|
if (nodeData != nullptr) {
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
//
|
|
||||||
// MarketplaceComboBox.qml
|
|
||||||
//
|
|
||||||
// Created by Elisa Lupin-Jimenez on 3 Aug 2016
|
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import QtWebChannel 1.0
|
|
||||||
import QtWebEngine 1.1
|
|
||||||
import QtWebSockets 1.0
|
|
||||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
|
||||||
|
|
||||||
import "controls"
|
|
||||||
import "controls-uit" as Controls
|
|
||||||
import "styles"
|
|
||||||
import "styles-uit"
|
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
HifiConstants { id: hifi }
|
|
||||||
id: marketplaceComboBox
|
|
||||||
anchors.fill: parent
|
|
||||||
color: hifi.colors.baseGrayShadow
|
|
||||||
property var currentUrl: "https://metaverse.highfidelity.com/marketplace"
|
|
||||||
|
|
||||||
Controls.BaseWebView {
|
|
||||||
id: webview
|
|
||||||
url: currentUrl
|
|
||||||
anchors.top: switchMarketView.bottom
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height - 40
|
|
||||||
focus: true
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: zipTimer
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
interval: 1500
|
|
||||||
property var handler;
|
|
||||||
onTriggered: handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
property var autoCancel: 'var element = $("a.btn.cancel");
|
|
||||||
element.click();'
|
|
||||||
|
|
||||||
onNewViewRequested: {
|
|
||||||
var component = Qt.createComponent("Browser.qml");
|
|
||||||
var newWindow = component.createObject(desktop);
|
|
||||||
request.openIn(newWindow.webView);
|
|
||||||
if (File.isZippedFbx(desktop.currentUrl)) {
|
|
||||||
zipTimer.handler = function() {
|
|
||||||
newWindow.destroy();
|
|
||||||
runJavaScript(autoCancel);
|
|
||||||
}
|
|
||||||
zipTimer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property var simpleDownload: 'var element = $("a.download-file");
|
|
||||||
element.removeClass("download-file");
|
|
||||||
element.removeAttr("download");'
|
|
||||||
|
|
||||||
onLinkHovered: {
|
|
||||||
desktop.currentUrl = hoveredUrl;
|
|
||||||
// add an error message for non-fbx files
|
|
||||||
if (File.isZippedFbx(desktop.currentUrl)) {
|
|
||||||
runJavaScript(simpleDownload, function(){console.log("ran the JS");});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Controls.ComboBox {
|
|
||||||
id: switchMarketView
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
colorScheme: hifi.colorSchemes.dark
|
|
||||||
width: 200
|
|
||||||
height: 40
|
|
||||||
visible: true
|
|
||||||
model: ["Marketplace", "Clara.io"]
|
|
||||||
onCurrentIndexChanged: {
|
|
||||||
if (currentIndex === 0) { webview.url = "https://metaverse.highfidelity.com/marketplace"; }
|
|
||||||
if (currentIndex === 1) { webview.url = "https://clara.io/library"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Controls.Label {
|
|
||||||
id: switchMarketLabel
|
|
||||||
anchors.verticalCenter: switchMarketView.verticalCenter
|
|
||||||
anchors.right: switchMarketView.left
|
|
||||||
color: hifi.colors.white
|
|
||||||
text: "Explore interesting content from: "
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
167
interface/resources/qml/Marketplaces.qml
Normal file
167
interface/resources/qml/Marketplaces.qml
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
//
|
||||||
|
// Marketplaces.qml
|
||||||
|
//
|
||||||
|
// Created by Elisa Lupin-Jimenez on 3 Aug 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
import QtWebEngine 1.1
|
||||||
|
import QtWebSockets 1.0
|
||||||
|
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||||
|
|
||||||
|
import "controls"
|
||||||
|
import "controls-uit" as Controls
|
||||||
|
import "styles"
|
||||||
|
import "styles-uit"
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
id: marketplace
|
||||||
|
anchors.fill: parent
|
||||||
|
property var marketplacesUrl: "../../scripts/system/html/marketplaces.html"
|
||||||
|
property int statusBarHeight: 50
|
||||||
|
property int statusMargin: 50
|
||||||
|
property string standardMessage: "Check out other marketplaces."
|
||||||
|
property string claraMessage: "Choose a model and click Download -> Autodesk FBX."
|
||||||
|
property string claraError: "High Fidelity only supports Autodesk FBX models."
|
||||||
|
|
||||||
|
Controls.BaseWebView {
|
||||||
|
id: webview
|
||||||
|
url: marketplacesUrl
|
||||||
|
anchors.top: marketplace.top
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - statusBarHeight
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: zipTimer
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
interval: 1500
|
||||||
|
property var handler;
|
||||||
|
onTriggered: handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: alertTimer
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
interval: 9000
|
||||||
|
property var handler;
|
||||||
|
onTriggered: handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
property var autoCancel: 'var element = $("a.btn.cancel");
|
||||||
|
element.click();'
|
||||||
|
|
||||||
|
property var simpleDownload: 'var element = $("a.download-file");
|
||||||
|
element.removeClass("download-file");
|
||||||
|
element.removeAttr("download");'
|
||||||
|
|
||||||
|
function displayErrorStatus() {
|
||||||
|
alertTimer.handler = function() {
|
||||||
|
statusLabel.text = claraMessage;
|
||||||
|
statusBar.color = hifi.colors.blueHighlight;
|
||||||
|
statusIcon.text = hifi.glyphs.info;
|
||||||
|
}
|
||||||
|
alertTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
property var notFbxHandler: 'var element = $("a.btn.btn-primary.viewer-button.download-file")
|
||||||
|
element.click();'
|
||||||
|
|
||||||
|
// this code is for removing other file types from Clara.io's download options
|
||||||
|
//property var checkFileType: "$('[data-extension]:not([data-extension=\"fbx\"])').parent().remove()"
|
||||||
|
|
||||||
|
onLinkHovered: {
|
||||||
|
desktop.currentUrl = hoveredUrl;
|
||||||
|
//runJavaScript(checkFileType, function(){console.log("Remove filetypes JS injection");});
|
||||||
|
if (File.isZippedFbx(desktop.currentUrl)) {
|
||||||
|
runJavaScript(simpleDownload, function(){console.log("Download JS injection");});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.isZipped(desktop.currentUrl)) {
|
||||||
|
statusLabel.text = claraError;
|
||||||
|
statusBar.color = hifi.colors.redHighlight;
|
||||||
|
statusIcon.text = hifi.glyphs.alert;
|
||||||
|
runJavaScript(notFbxHandler, displayErrorStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadingChanged: {
|
||||||
|
if (File.isClaraLink(webview.url)) {
|
||||||
|
statusLabel.text = claraMessage;
|
||||||
|
} else {
|
||||||
|
statusLabel.text = standardMessage;
|
||||||
|
}
|
||||||
|
statusBar.color = hifi.colors.blueHighlight;
|
||||||
|
statusIcon.text = hifi.glyphs.info;
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewViewRequested: {
|
||||||
|
var component = Qt.createComponent("Browser.qml");
|
||||||
|
var newWindow = component.createObject(desktop);
|
||||||
|
request.openIn(newWindow.webView);
|
||||||
|
if (File.isZippedFbx(desktop.currentUrl)) {
|
||||||
|
runJavaScript(autoCancel);
|
||||||
|
zipTimer.handler = function() {
|
||||||
|
newWindow.destroy();
|
||||||
|
}
|
||||||
|
zipTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: statusBar
|
||||||
|
anchors.top: webview.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
color: hifi.colors.blueHighlight
|
||||||
|
|
||||||
|
Controls.Button {
|
||||||
|
id: switchMarketView
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: statusMargin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 150
|
||||||
|
text: "See all markets"
|
||||||
|
onClicked: {
|
||||||
|
webview.url = "../../scripts/system/html/marketplaces.html";
|
||||||
|
statusLabel.text = standardMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controls.Label {
|
||||||
|
id: statusLabel
|
||||||
|
anchors.verticalCenter: switchMarketView.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: statusMargin
|
||||||
|
color: hifi.colors.white
|
||||||
|
text: standardMessage
|
||||||
|
size: 18
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: statusIcon
|
||||||
|
anchors.right: statusLabel.left
|
||||||
|
anchors.verticalCenter: statusLabel.verticalCenter
|
||||||
|
text: hifi.glyphs.info
|
||||||
|
color: hifi.colors.white
|
||||||
|
size: hifi.fontSizes.tableHeadingIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -176,8 +176,6 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
|
||||||
// For processing on QThreadPool, target 2 less than the ideal number of threads, leaving
|
// For processing on QThreadPool, target 2 less than the ideal number of threads, leaving
|
||||||
// 2 logical cores available for time sensitive tasks.
|
// 2 logical cores available for time sensitive tasks.
|
||||||
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
|
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
|
||||||
static const int PROCESSING_THREAD_POOL_SIZE = std::max(MIN_PROCESSING_THREAD_POOL_SIZE,
|
|
||||||
QThread::idealThreadCount() - 2);
|
|
||||||
|
|
||||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||||
static const QString SVO_EXTENSION = ".svo";
|
static const QString SVO_EXTENSION = ".svo";
|
||||||
|
@ -537,7 +535,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
PluginContainer* pluginContainer = dynamic_cast<PluginContainer*>(this); // set the container for any plugins that care
|
PluginContainer* pluginContainer = dynamic_cast<PluginContainer*>(this); // set the container for any plugins that care
|
||||||
PluginManager::getInstance()->setContainer(pluginContainer);
|
PluginManager::getInstance()->setContainer(pluginContainer);
|
||||||
|
|
||||||
QThreadPool::globalInstance()->setMaxThreadCount(PROCESSING_THREAD_POOL_SIZE);
|
QThreadPool::globalInstance()->setMaxThreadCount(MIN_PROCESSING_THREAD_POOL_SIZE);
|
||||||
thread()->setPriority(QThread::HighPriority);
|
thread()->setPriority(QThread::HighPriority);
|
||||||
thread()->setObjectName("Main Thread");
|
thread()->setObjectName("Main Thread");
|
||||||
|
|
||||||
|
@ -707,6 +705,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
|
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
|
||||||
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
|
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
|
||||||
|
|
||||||
|
connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount);
|
||||||
|
|
||||||
// Save avatar location immediately after a teleport.
|
// Save avatar location immediately after a teleport.
|
||||||
connect(getMyAvatar(), &MyAvatar::positionGoneTo,
|
connect(getMyAvatar(), &MyAvatar::positionGoneTo,
|
||||||
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
|
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
|
||||||
|
@ -5727,3 +5727,18 @@ void Application::sendHoverLeaveEntity(QUuid id, PointerEvent event) {
|
||||||
EntityItemID entityItemID(id);
|
EntityItemID entityItemID(id);
|
||||||
emit getEntities()->hoverLeaveEntity(entityItemID, event);
|
emit getEntities()->hoverLeaveEntity(entityItemID, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME? perhaps two, one for the main thread and one for the offscreen UI rendering thread?
|
||||||
|
static const int UI_RESERVED_THREADS = 1;
|
||||||
|
// Windows won't let you have all the cores
|
||||||
|
static const int OS_RESERVED_THREADS = 1;
|
||||||
|
|
||||||
|
void Application::updateThreadPoolCount() const {
|
||||||
|
auto reservedThreads = UI_RESERVED_THREADS + OS_RESERVED_THREADS + _displayPlugin->getRequiredThreadCount();
|
||||||
|
auto availableThreads = QThread::idealThreadCount() - reservedThreads;
|
||||||
|
auto threadPoolSize = std::max(MIN_PROCESSING_THREAD_POOL_SIZE, availableThreads);
|
||||||
|
qDebug() << "Ideal Thread Count " << QThread::idealThreadCount();
|
||||||
|
qDebug() << "Reserved threads " << reservedThreads;
|
||||||
|
qDebug() << "Setting thread pool size to " << threadPoolSize;
|
||||||
|
QThreadPool::globalInstance()->setMaxThreadCount(threadPoolSize);
|
||||||
|
}
|
|
@ -286,6 +286,7 @@ public slots:
|
||||||
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
|
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
|
||||||
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||||
bool importEntities(const QString& url);
|
bool importEntities(const QString& url);
|
||||||
|
void updateThreadPoolCount() const;
|
||||||
|
|
||||||
static void setLowVelocityFilter(bool lowVelocityFilter);
|
static void setLowVelocityFilter(bool lowVelocityFilter);
|
||||||
Q_INVOKABLE void loadDialog();
|
Q_INVOKABLE void loadDialog();
|
||||||
|
|
|
@ -24,33 +24,34 @@
|
||||||
#include "AudioRingBuffer.h"
|
#include "AudioRingBuffer.h"
|
||||||
|
|
||||||
static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
||||||
|
static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." };
|
||||||
|
|
||||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, int numFramesCapacity) :
|
||||||
|
_numFrameSamples(numFrameSamples),
|
||||||
_frameCapacity(numFramesCapacity),
|
_frameCapacity(numFramesCapacity),
|
||||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
_bufferLength(numFrameSamples * (numFramesCapacity + 1))
|
||||||
_numFrameSamples(numFrameSamples),
|
|
||||||
_randomAccessMode(randomAccessMode),
|
|
||||||
_overflowCount(0)
|
|
||||||
{
|
{
|
||||||
if (numFrameSamples) {
|
if (numFrameSamples) {
|
||||||
_buffer = new int16_t[_bufferLength];
|
_buffer = new int16_t[_bufferLength];
|
||||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||||
_nextOutput = _buffer;
|
_nextOutput = _buffer;
|
||||||
_endOfLastWrite = _buffer;
|
_endOfLastWrite = _buffer;
|
||||||
} else {
|
|
||||||
_buffer = NULL;
|
|
||||||
_nextOutput = NULL;
|
|
||||||
_endOfLastWrite = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
static QString repeatedOverflowMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
|
static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioRingBuffer::~AudioRingBuffer() {
|
AudioRingBuffer::~AudioRingBuffer() {
|
||||||
delete[] _buffer;
|
delete[] _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioRingBuffer::clear() {
|
||||||
|
_endOfLastWrite = _buffer;
|
||||||
|
_nextOutput = _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioRingBuffer::reset() {
|
void AudioRingBuffer::reset() {
|
||||||
clear();
|
clear();
|
||||||
_overflowCount = 0;
|
_overflowCount = 0;
|
||||||
|
@ -58,109 +59,82 @@ void AudioRingBuffer::reset() {
|
||||||
|
|
||||||
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
|
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
|
||||||
delete[] _buffer;
|
delete[] _buffer;
|
||||||
|
_numFrameSamples = numFrameSamples;
|
||||||
_sampleCapacity = numFrameSamples * _frameCapacity;
|
_sampleCapacity = numFrameSamples * _frameCapacity;
|
||||||
_bufferLength = numFrameSamples * (_frameCapacity + 1);
|
_bufferLength = numFrameSamples * (_frameCapacity + 1);
|
||||||
_numFrameSamples = numFrameSamples;
|
|
||||||
_buffer = new int16_t[_bufferLength];
|
|
||||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
|
||||||
if (_randomAccessMode) {
|
|
||||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioRingBuffer::clear() {
|
if (numFrameSamples) {
|
||||||
_endOfLastWrite = _buffer;
|
_buffer = new int16_t[_bufferLength];
|
||||||
_nextOutput = _buffer;
|
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||||
|
} else {
|
||||||
|
_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) {
|
int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) {
|
||||||
return readData((char*)destination, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
return readData((char*)destination, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
|
||||||
|
return writeData((char*)source, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
||||||
|
}
|
||||||
|
|
||||||
int AudioRingBuffer::readData(char *data, int maxSize) {
|
int AudioRingBuffer::readData(char *data, int maxSize) {
|
||||||
|
|
||||||
// only copy up to the number of samples we have available
|
// only copy up to the number of samples we have available
|
||||||
int numReadSamples = std::min((int)(maxSize / sizeof(int16_t)), samplesAvailable());
|
int maxSamples = maxSize / sizeof(int16_t);
|
||||||
|
int numReadSamples = std::min(maxSamples, samplesAvailable());
|
||||||
// If we're in random access mode, then we consider our number of available read samples slightly
|
|
||||||
// differently. Namely, if anything has been written, we say we have as many samples as they ask for
|
|
||||||
// otherwise we say we have nothing available
|
|
||||||
if (_randomAccessMode) {
|
|
||||||
numReadSamples = _endOfLastWrite ? (maxSize / sizeof(int16_t)) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
|
if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
|
||||||
// we're going to need to do two reads to get this data, it wraps around the edge
|
// we're going to need to do two reads to get this data, it wraps around the edge
|
||||||
|
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
|
||||||
|
|
||||||
// read to the end of the buffer
|
// read to the end of the buffer
|
||||||
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
|
|
||||||
memcpy(data, _nextOutput, numSamplesToEnd * sizeof(int16_t));
|
memcpy(data, _nextOutput, numSamplesToEnd * sizeof(int16_t));
|
||||||
if (_randomAccessMode) {
|
|
||||||
memset(_nextOutput, 0, numSamplesToEnd * sizeof(int16_t)); // clear it
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the rest from the beginning of the buffer
|
// read the rest from the beginning of the buffer
|
||||||
memcpy(data + (numSamplesToEnd * sizeof(int16_t)), _buffer, (numReadSamples - numSamplesToEnd) * sizeof(int16_t));
|
memcpy(data + (numSamplesToEnd * sizeof(int16_t)), _buffer, (numReadSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||||
if (_randomAccessMode) {
|
|
||||||
memset(_buffer, 0, (numReadSamples - numSamplesToEnd) * sizeof(int16_t)); // clear it
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// read the data
|
|
||||||
memcpy(data, _nextOutput, numReadSamples * sizeof(int16_t));
|
memcpy(data, _nextOutput, numReadSamples * sizeof(int16_t));
|
||||||
if (_randomAccessMode) {
|
|
||||||
memset(_nextOutput, 0, numReadSamples * sizeof(int16_t)); // clear it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// push the position of _nextOutput by the number of samples read
|
shiftReadPosition(numReadSamples);
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numReadSamples);
|
|
||||||
|
|
||||||
return numReadSamples * sizeof(int16_t);
|
return numReadSamples * sizeof(int16_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
|
|
||||||
return writeData((const char*)source, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
||||||
// make sure we have enough bytes left for this to be the right amount of audio
|
// only copy up to the number of samples we have capacity for
|
||||||
// otherwise we should not copy that data, and leave the buffer pointers where they are
|
int maxSamples = maxSize / sizeof(int16_t);
|
||||||
int samplesToCopy = std::min((int)(maxSize / sizeof(int16_t)), _sampleCapacity);
|
int numWriteSamples = std::min(maxSamples, _sampleCapacity);
|
||||||
|
|
||||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||||
if (samplesToCopy > samplesRoomFor) {
|
|
||||||
// there's not enough room for this write. erase old data to make room for this new data
|
if (numWriteSamples > samplesRoomFor) {
|
||||||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
// there's not enough room for this write. erase old data to make room for this new data
|
||||||
|
int samplesToDelete = numWriteSamples - samplesRoomFor;
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||||
_overflowCount++;
|
_overflowCount++;
|
||||||
|
|
||||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_endOfLastWrite + samplesToCopy <= _buffer + _bufferLength) {
|
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
|
||||||
memcpy(_endOfLastWrite, data, samplesToCopy * sizeof(int16_t));
|
// we're going to need to do two writes to set this data, it wraps around the edge
|
||||||
} else {
|
|
||||||
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
||||||
|
|
||||||
|
// write to the end of the buffer
|
||||||
memcpy(_endOfLastWrite, data, numSamplesToEnd * sizeof(int16_t));
|
memcpy(_endOfLastWrite, data, numSamplesToEnd * sizeof(int16_t));
|
||||||
memcpy(_buffer, data + (numSamplesToEnd * sizeof(int16_t)), (samplesToCopy - numSamplesToEnd) * sizeof(int16_t));
|
|
||||||
|
// write the rest to the beginning of the buffer
|
||||||
|
memcpy(_buffer, data + (numSamplesToEnd * sizeof(int16_t)), (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||||
|
} else {
|
||||||
|
memcpy(_endOfLastWrite, data, numWriteSamples * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy);
|
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
|
||||||
|
|
||||||
return samplesToCopy * sizeof(int16_t);
|
return numWriteSamples * sizeof(int16_t);
|
||||||
}
|
|
||||||
|
|
||||||
int16_t& AudioRingBuffer::operator[](const int index) {
|
|
||||||
return *shiftedPositionAccomodatingWrap(_nextOutput, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int16_t& AudioRingBuffer::operator[] (const int index) const {
|
|
||||||
return *shiftedPositionAccomodatingWrap(_nextOutput, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) {
|
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioRingBuffer::samplesAvailable() const {
|
int AudioRingBuffer::samplesAvailable() const {
|
||||||
|
@ -176,35 +150,31 @@ int AudioRingBuffer::samplesAvailable() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioRingBuffer::addSilentSamples(int silentSamples) {
|
int AudioRingBuffer::addSilentSamples(int silentSamples) {
|
||||||
|
// NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
|
||||||
|
int numWriteSamples = std::min(silentSamples, _sampleCapacity);
|
||||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||||
if (silentSamples > samplesRoomFor) {
|
|
||||||
// there's not enough room for this write. write as many silent samples as we have room for
|
|
||||||
silentSamples = samplesRoomFor;
|
|
||||||
|
|
||||||
static const QString DROPPED_SILENT_DEBUG {
|
if (numWriteSamples > samplesRoomFor) {
|
||||||
"AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow."
|
numWriteSamples = samplesRoomFor;
|
||||||
};
|
|
||||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
|
|
||||||
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
|
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
// memset zeroes into the buffer, accomodate a wrap around the end
|
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
|
||||||
// push the _endOfLastWrite to the correct spot
|
|
||||||
if (_endOfLastWrite + silentSamples <= _buffer + _bufferLength) {
|
|
||||||
memset(_endOfLastWrite, 0, silentSamples * sizeof(int16_t));
|
|
||||||
} else {
|
|
||||||
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
||||||
memset(_endOfLastWrite, 0, numSamplesToEnd * sizeof(int16_t));
|
memset(_endOfLastWrite, 0, numSamplesToEnd * sizeof(int16_t));
|
||||||
memset(_buffer, 0, (silentSamples - numSamplesToEnd) * sizeof(int16_t));
|
memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||||
|
} else {
|
||||||
|
memset(_endOfLastWrite, 0, numWriteSamples * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, silentSamples);
|
|
||||||
|
|
||||||
return silentSamples;
|
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
|
||||||
|
|
||||||
|
return numWriteSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const {
|
int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const {
|
||||||
|
// NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur
|
||||||
if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
|
if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
|
||||||
// this shift will wrap the position around to the beginning of the ring
|
// this shift will wrap the position around to the beginning of the ring
|
||||||
return position + numSamplesShift - _bufferLength;
|
return position + numSamplesShift - _bufferLength;
|
||||||
|
@ -217,13 +187,15 @@ int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioRingBuffer::getFrameLoudness(const int16_t* frameStart) const {
|
float AudioRingBuffer::getFrameLoudness(const int16_t* frameStart) const {
|
||||||
|
// FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))
|
||||||
float loudness = 0.0f;
|
float loudness = 0.0f;
|
||||||
const int16_t* sampleAt = frameStart;
|
const int16_t* sampleAt = frameStart;
|
||||||
const int16_t* _bufferLastAt = _buffer + _bufferLength - 1;
|
const int16_t* bufferLastAt = _buffer + _bufferLength - 1;
|
||||||
|
|
||||||
for (int i = 0; i < _numFrameSamples; ++i) {
|
for (int i = 0; i < _numFrameSamples; ++i) {
|
||||||
loudness += (float) std::abs(*sampleAt);
|
loudness += (float) std::abs(*sampleAt);
|
||||||
sampleAt = sampleAt == _bufferLastAt ? _buffer : sampleAt + 1;
|
// wrap if necessary
|
||||||
|
sampleAt = sampleAt == bufferLastAt ? _buffer : sampleAt + 1;
|
||||||
}
|
}
|
||||||
loudness /= _numFrameSamples;
|
loudness /= _numFrameSamples;
|
||||||
loudness /= AudioConstants::MAX_SAMPLE_VALUE;
|
loudness /= AudioConstants::MAX_SAMPLE_VALUE;
|
||||||
|
@ -238,10 +210,6 @@ float AudioRingBuffer::getFrameLoudness(ConstIterator frameStart) const {
|
||||||
return getFrameLoudness(&(*frameStart));
|
return getFrameLoudness(&(*frameStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioRingBuffer::getNextOutputFrameLoudness() const {
|
|
||||||
return getFrameLoudness(_nextOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
|
int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
|
||||||
int samplesToCopy = std::min(maxSamples, _sampleCapacity);
|
int samplesToCopy = std::min(maxSamples, _sampleCapacity);
|
||||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||||
|
|
|
@ -23,73 +23,69 @@ const int DEFAULT_RING_BUFFER_FRAME_CAPACITY = 10;
|
||||||
|
|
||||||
class AudioRingBuffer {
|
class AudioRingBuffer {
|
||||||
public:
|
public:
|
||||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
AudioRingBuffer(int numFrameSamples, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
||||||
~AudioRingBuffer();
|
~AudioRingBuffer();
|
||||||
|
|
||||||
void reset();
|
// disallow copying
|
||||||
void resizeForFrameSize(int numFrameSamples);
|
AudioRingBuffer(const AudioRingBuffer&) = delete;
|
||||||
|
AudioRingBuffer(AudioRingBuffer&&) = delete;
|
||||||
|
AudioRingBuffer& operator=(const AudioRingBuffer&) = delete;
|
||||||
|
|
||||||
|
/// Invalidate any data in the buffer
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
int getSampleCapacity() const { return _sampleCapacity; }
|
/// Clear and reset the overflow count
|
||||||
int getFrameCapacity() const { return _frameCapacity; }
|
void reset();
|
||||||
|
|
||||||
|
/// Resize frame size (causes a reset())
|
||||||
|
// FIXME: discards any data in the buffer
|
||||||
|
void resizeForFrameSize(int numFrameSamples);
|
||||||
|
|
||||||
|
/// Read up to maxSamples into destination (will only read up to samplesAvailable())
|
||||||
|
/// Returns number of read samples
|
||||||
int readSamples(int16_t* destination, int maxSamples);
|
int readSamples(int16_t* destination, int maxSamples);
|
||||||
|
|
||||||
|
/// Write up to maxSamples from source (will only write up to sample capacity)
|
||||||
|
/// Returns number of written samples
|
||||||
int writeSamples(const int16_t* source, int maxSamples);
|
int writeSamples(const int16_t* source, int maxSamples);
|
||||||
|
|
||||||
int readData(char* data, int maxSize);
|
/// Write up to maxSamples silent samples (will only write until other data exists in the buffer)
|
||||||
int writeData(const char* data, int maxSize);
|
/// This method will not overwrite existing data in the buffer, instead dropping silent samples that would overflow
|
||||||
|
/// Returns number of written silent samples
|
||||||
|
int addSilentSamples(int maxSamples);
|
||||||
|
|
||||||
int16_t& operator[](const int index);
|
/// Read up to maxSize into destination
|
||||||
const int16_t& operator[] (const int index) const;
|
/// Returns number of read bytes
|
||||||
|
int readData(char* destination, int maxSize);
|
||||||
|
|
||||||
void shiftReadPosition(unsigned int numSamples);
|
/// Write up to maxSize from source
|
||||||
|
/// Returns number of written bytes
|
||||||
|
int writeData(const char* source, int maxSize);
|
||||||
|
|
||||||
float getNextOutputFrameLoudness() const;
|
/// Returns a reference to the index-th sample offset from the current read sample
|
||||||
|
int16_t& operator[](const int index) { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
|
||||||
|
const int16_t& operator[] (const int index) const { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
|
||||||
|
|
||||||
|
/// Essentially discards the next numSamples from the ring buffer
|
||||||
|
/// NOTE: This is not checked - it is possible to shift past written data
|
||||||
|
/// Use samplesAvailable() to see the distance a valid shift can go
|
||||||
|
void shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); }
|
||||||
|
|
||||||
int samplesAvailable() const;
|
int samplesAvailable() const;
|
||||||
int framesAvailable() const { return (_numFrameSamples == 0) ? 0 : samplesAvailable() / _numFrameSamples; }
|
int framesAvailable() const { return (_numFrameSamples == 0) ? 0 : samplesAvailable() / _numFrameSamples; }
|
||||||
|
float getNextOutputFrameLoudness() const { return getFrameLoudness(_nextOutput); }
|
||||||
|
|
||||||
|
|
||||||
int getNumFrameSamples() const { return _numFrameSamples; }
|
int getNumFrameSamples() const { return _numFrameSamples; }
|
||||||
|
int getFrameCapacity() const { return _frameCapacity; }
|
||||||
|
int getSampleCapacity() const { return _sampleCapacity; }
|
||||||
|
/// Return times the ring buffer has overwritten old data
|
||||||
|
int getOverflowCount() const { return _overflowCount; }
|
||||||
|
|
||||||
int getOverflowCount() const { return _overflowCount; } /// how many times has the ring buffer has overwritten old data
|
class ConstIterator {
|
||||||
|
|
||||||
int addSilentSamples(int samples);
|
|
||||||
|
|
||||||
private:
|
|
||||||
float getFrameLoudness(const int16_t* frameStart) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// disallow copying of AudioRingBuffer objects
|
|
||||||
AudioRingBuffer(const AudioRingBuffer&);
|
|
||||||
AudioRingBuffer& operator= (const AudioRingBuffer&);
|
|
||||||
|
|
||||||
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
|
|
||||||
|
|
||||||
int _frameCapacity;
|
|
||||||
int _sampleCapacity;
|
|
||||||
int _bufferLength; // actual length of _buffer: will be one frame larger than _sampleCapacity
|
|
||||||
int _numFrameSamples;
|
|
||||||
int16_t* _nextOutput;
|
|
||||||
int16_t* _endOfLastWrite;
|
|
||||||
int16_t* _buffer;
|
|
||||||
bool _randomAccessMode; /// will this ringbuffer be used for random access? if so, do some special processing
|
|
||||||
|
|
||||||
int _overflowCount; /// how many times has the ring buffer has overwritten old data
|
|
||||||
|
|
||||||
public:
|
|
||||||
class ConstIterator { //public std::iterator < std::forward_iterator_tag, int16_t > {
|
|
||||||
public:
|
public:
|
||||||
ConstIterator()
|
ConstIterator();
|
||||||
: _bufferLength(0),
|
ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at);
|
||||||
_bufferFirst(NULL),
|
|
||||||
_bufferLast(NULL),
|
|
||||||
_at(NULL) {}
|
|
||||||
ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at)
|
|
||||||
: _bufferLength(capacity),
|
|
||||||
_bufferFirst(bufferFirst),
|
|
||||||
_bufferLast(bufferFirst + capacity - 1),
|
|
||||||
_at(at) {}
|
|
||||||
ConstIterator(const ConstIterator& rhs) = default;
|
ConstIterator(const ConstIterator& rhs) = default;
|
||||||
|
|
||||||
bool isNull() const { return _at == NULL; }
|
bool isNull() const { return _at == NULL; }
|
||||||
|
@ -98,95 +94,143 @@ public:
|
||||||
bool operator!=(const ConstIterator& rhs) { return _at != rhs._at; }
|
bool operator!=(const ConstIterator& rhs) { return _at != rhs._at; }
|
||||||
const int16_t& operator*() { return *_at; }
|
const int16_t& operator*() { return *_at; }
|
||||||
|
|
||||||
ConstIterator& operator=(const ConstIterator& rhs) {
|
ConstIterator& operator=(const ConstIterator& rhs);
|
||||||
_bufferLength = rhs._bufferLength;
|
ConstIterator& operator++();
|
||||||
_bufferFirst = rhs._bufferFirst;
|
ConstIterator operator++(int);
|
||||||
_bufferLast = rhs._bufferLast;
|
ConstIterator& operator--();
|
||||||
_at = rhs._at;
|
ConstIterator operator--(int);
|
||||||
return *this;
|
const int16_t& operator[] (int i);
|
||||||
}
|
ConstIterator operator+(int i);
|
||||||
|
ConstIterator operator-(int i);
|
||||||
|
|
||||||
ConstIterator& operator++() {
|
void readSamples(int16_t* dest, int numSamples);
|
||||||
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
|
void readSamplesWithFade(int16_t* dest, int numSamples, float fade);
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstIterator operator++(int) {
|
|
||||||
ConstIterator tmp(*this);
|
|
||||||
++(*this);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstIterator& operator--() {
|
|
||||||
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstIterator operator--(int) {
|
|
||||||
ConstIterator tmp(*this);
|
|
||||||
--(*this);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int16_t& operator[] (int i) {
|
|
||||||
return *atShiftedBy(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstIterator operator+(int i) {
|
|
||||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstIterator operator-(int i) {
|
|
||||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
|
|
||||||
}
|
|
||||||
|
|
||||||
void readSamples(int16_t* dest, int numSamples) {
|
|
||||||
auto samplesToEnd = _bufferLast - _at + 1;
|
|
||||||
|
|
||||||
if (samplesToEnd >= numSamples) {
|
|
||||||
memcpy(dest, _at, numSamples * sizeof(int16_t));
|
|
||||||
_at += numSamples;
|
|
||||||
} else {
|
|
||||||
auto samplesFromStart = numSamples - samplesToEnd;
|
|
||||||
memcpy(dest, _at, samplesToEnd * sizeof(int16_t));
|
|
||||||
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * sizeof(int16_t));
|
|
||||||
|
|
||||||
_at = _bufferFirst + samplesFromStart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void readSamplesWithFade(int16_t* dest, int numSamples, float fade) {
|
|
||||||
int16_t* at = _at;
|
|
||||||
for (int i = 0; i < numSamples; i++) {
|
|
||||||
*dest = (float)*at * fade;
|
|
||||||
++dest;
|
|
||||||
at = (at == _bufferLast) ? _bufferFirst : at + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int16_t* atShiftedBy(int i) {
|
int16_t* atShiftedBy(int i);
|
||||||
i = (_at - _bufferFirst + i) % _bufferLength;
|
|
||||||
if (i < 0) {
|
|
||||||
i += _bufferLength;
|
|
||||||
}
|
|
||||||
return _bufferFirst + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _bufferLength;
|
int _bufferLength;
|
||||||
int16_t* _bufferFirst;
|
int16_t* _bufferFirst;
|
||||||
int16_t* _bufferLast;
|
int16_t* _bufferLast;
|
||||||
int16_t* _at;
|
int16_t* _at;
|
||||||
};
|
};
|
||||||
|
|
||||||
ConstIterator nextOutput() const { return ConstIterator(_buffer, _bufferLength, _nextOutput); }
|
ConstIterator nextOutput() const;
|
||||||
ConstIterator lastFrameWritten() const { return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; }
|
ConstIterator lastFrameWritten() const;
|
||||||
|
|
||||||
float getFrameLoudness(ConstIterator frameStart) const;
|
|
||||||
|
|
||||||
int writeSamples(ConstIterator source, int maxSamples);
|
int writeSamples(ConstIterator source, int maxSamples);
|
||||||
int writeSamplesWithFade(ConstIterator source, int maxSamples, float fade);
|
int writeSamplesWithFade(ConstIterator source, int maxSamples, float fade);
|
||||||
|
|
||||||
|
float getFrameLoudness(ConstIterator frameStart) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
|
||||||
|
float getFrameLoudness(const int16_t* frameStart) const;
|
||||||
|
|
||||||
|
int _numFrameSamples;
|
||||||
|
int _frameCapacity;
|
||||||
|
int _sampleCapacity;
|
||||||
|
int _bufferLength; // actual _buffer length (_sampleCapacity + 1)
|
||||||
|
int _overflowCount{ 0 }; // times the ring buffer has overwritten data
|
||||||
|
|
||||||
|
int16_t* _nextOutput{ nullptr };
|
||||||
|
int16_t* _endOfLastWrite{ nullptr };
|
||||||
|
int16_t* _buffer{ nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// inline the iterator:
|
||||||
|
inline AudioRingBuffer::ConstIterator::ConstIterator() :
|
||||||
|
_bufferLength(0),
|
||||||
|
_bufferFirst(NULL),
|
||||||
|
_bufferLast(NULL),
|
||||||
|
_at(NULL) {}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator::ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at) :
|
||||||
|
_bufferLength(capacity),
|
||||||
|
_bufferFirst(bufferFirst),
|
||||||
|
_bufferLast(bufferFirst + capacity - 1),
|
||||||
|
_at(at) {}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator=(const ConstIterator& rhs) {
|
||||||
|
_bufferLength = rhs._bufferLength;
|
||||||
|
_bufferFirst = rhs._bufferFirst;
|
||||||
|
_bufferLast = rhs._bufferLast;
|
||||||
|
_at = rhs._at;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator++() {
|
||||||
|
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator++(int) {
|
||||||
|
ConstIterator tmp(*this);
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator--() {
|
||||||
|
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator--(int) {
|
||||||
|
ConstIterator tmp(*this);
|
||||||
|
--(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const int16_t& AudioRingBuffer::ConstIterator::operator[] (int i) {
|
||||||
|
return *atShiftedBy(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator+(int i) {
|
||||||
|
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator-(int i) {
|
||||||
|
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int16_t* AudioRingBuffer::ConstIterator::atShiftedBy(int i) {
|
||||||
|
i = (_at - _bufferFirst + i) % _bufferLength;
|
||||||
|
if (i < 0) {
|
||||||
|
i += _bufferLength;
|
||||||
|
}
|
||||||
|
return _bufferFirst + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSamples) {
|
||||||
|
auto samplesToEnd = _bufferLast - _at + 1;
|
||||||
|
|
||||||
|
if (samplesToEnd >= numSamples) {
|
||||||
|
memcpy(dest, _at, numSamples * sizeof(int16_t));
|
||||||
|
_at += numSamples;
|
||||||
|
} else {
|
||||||
|
auto samplesFromStart = numSamples - samplesToEnd;
|
||||||
|
memcpy(dest, _at, samplesToEnd * sizeof(int16_t));
|
||||||
|
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * sizeof(int16_t));
|
||||||
|
|
||||||
|
_at = _bufferFirst + samplesFromStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(int16_t* dest, int numSamples, float fade) {
|
||||||
|
int16_t* at = _at;
|
||||||
|
for (int i = 0; i < numSamples; i++) {
|
||||||
|
*dest = (float)*at * fade;
|
||||||
|
++dest;
|
||||||
|
at = (at == _bufferLast) ? _bufferFirst : at + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const {
|
||||||
|
return ConstIterator(_buffer, _bufferLength, _nextOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const {
|
||||||
|
return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // hifi_AudioRingBuffer_h
|
#endif // hifi_AudioRingBuffer_h
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
const int STARVE_HISTORY_CAPACITY = 50;
|
const int STARVE_HISTORY_CAPACITY = 50;
|
||||||
|
|
||||||
InboundAudioStream::InboundAudioStream(int numFrameSamples, int numFramesCapacity, const Settings& settings) :
|
InboundAudioStream::InboundAudioStream(int numFrameSamples, int numFramesCapacity, const Settings& settings) :
|
||||||
_ringBuffer(numFrameSamples, false, numFramesCapacity),
|
_ringBuffer(numFrameSamples, numFramesCapacity),
|
||||||
_lastPopSucceeded(false),
|
_lastPopSucceeded(false),
|
||||||
_lastPopOutput(),
|
_lastPopOutput(),
|
||||||
_dynamicJitterBuffers(settings._dynamicJitterBuffers),
|
_dynamicJitterBuffers(settings._dynamicJitterBuffers),
|
||||||
|
|
|
@ -69,6 +69,8 @@ public:
|
||||||
virtual bool wantVsync() const { return true; }
|
virtual bool wantVsync() const { return true; }
|
||||||
void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
|
void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
|
||||||
bool isVsyncEnabled() const { return _vsyncEnabled; }
|
bool isVsyncEnabled() const { return _vsyncEnabled; }
|
||||||
|
// Three threads, one for rendering, one for texture transfers, one reserved for the GL driver
|
||||||
|
int getRequiredThreadCount() const override { return 3; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class PresentThread;
|
friend class PresentThread;
|
||||||
|
|
|
@ -205,8 +205,10 @@ void Context::create() {
|
||||||
formatAttribs.push_back(24);
|
formatAttribs.push_back(24);
|
||||||
formatAttribs.push_back(WGL_STENCIL_BITS_ARB);
|
formatAttribs.push_back(WGL_STENCIL_BITS_ARB);
|
||||||
formatAttribs.push_back(8);
|
formatAttribs.push_back(8);
|
||||||
formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
#ifdef NATIVE_SRGB_FRAMEBUFFER
|
||||||
formatAttribs.push_back(GL_TRUE);
|
// formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
||||||
|
// formatAttribs.push_back(GL_TRUE);
|
||||||
|
#endif
|
||||||
// terminate the list
|
// terminate the list
|
||||||
formatAttribs.push_back(0);
|
formatAttribs.push_back(0);
|
||||||
UINT numFormats;
|
UINT numFormats;
|
||||||
|
|
|
@ -128,6 +128,7 @@ public:
|
||||||
Present = QEvent::User + 1
|
Present = QEvent::User + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtual int getRequiredThreadCount() const { return 0; }
|
||||||
virtual bool isHmd() const { return false; }
|
virtual bool isHmd() const { return false; }
|
||||||
virtual int getHmdScreen() const { return -1; }
|
virtual int getHmdScreen() const { return -1; }
|
||||||
/// By default, all HMDs are stereo
|
/// By default, all HMDs are stereo
|
||||||
|
|
|
@ -65,13 +65,21 @@ bool FileScriptingInterface::isTempDir(QString tempDir) {
|
||||||
folderName = "/" + testDir.section("/", -1);
|
folderName = "/" + testDir.section("/", -1);
|
||||||
QString testContainer = testDir;
|
QString testContainer = testDir;
|
||||||
testContainer.remove(folderName);
|
testContainer.remove(folderName);
|
||||||
if (testContainer == tempContainer) return true;
|
return (testContainer == tempContainer);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
// checks whether the webview is displaying a Clara.io page for Marketplaces.qml
|
||||||
|
bool FileScriptingInterface::isClaraLink(QUrl url) {
|
||||||
|
return (url.toString().contains("clara.io") && !url.toString().contains("clara.io/signup"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileScriptingInterface::isZippedFbx(QUrl url) {
|
bool FileScriptingInterface::isZippedFbx(QUrl url) {
|
||||||
if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true;
|
return (url.toString().endsWith("fbx.zip"));
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
// checks whether a user tries to download a file that is not in .fbx format
|
||||||
|
bool FileScriptingInterface::isZipped(QUrl url) {
|
||||||
|
return (url.toString().endsWith(".zip"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function is not in use
|
// this function is not in use
|
||||||
|
|
|
@ -25,6 +25,8 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool isZippedFbx(QUrl url);
|
bool isZippedFbx(QUrl url);
|
||||||
|
bool isZipped(QUrl url);
|
||||||
|
bool isClaraLink(QUrl url);
|
||||||
QString convertUrlToPath(QUrl url);
|
QString convertUrlToPath(QUrl url);
|
||||||
void runUnzip(QString path, QUrl url);
|
void runUnzip(QString path, QUrl url);
|
||||||
QString getTempDir();
|
QString getTempDir();
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
void unsuppressKeyboard() override;
|
void unsuppressKeyboard() override;
|
||||||
bool isKeyboardVisible() override;
|
bool isKeyboardVisible() override;
|
||||||
|
|
||||||
|
// Needs an additional thread for VR submission
|
||||||
|
int getRequiredThreadCount() const override { return Parent::getRequiredThreadCount() + 1; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool internalActivate() override;
|
bool internalActivate() override;
|
||||||
void internalDeactivate() override;
|
void internalDeactivate() override;
|
||||||
|
|
98
scripts/system/html/css/marketplaces.css
Normal file
98
scripts/system/html/css/marketplaces.css
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
//
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
background: white;
|
||||||
|
padding: 0 0 0 0;
|
||||||
|
font-family:Raleway-SemiBold;
|
||||||
|
}
|
||||||
|
.marketplaces-container {
|
||||||
|
display: inline-block;
|
||||||
|
color: black;
|
||||||
|
width: 94%;
|
||||||
|
margin-left: 3%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.marketplaces-title {
|
||||||
|
margin-top: 45px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.marketplaces-intro-text {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
.marketplace-tile {
|
||||||
|
float:left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.marketplace-tile-first-column {
|
||||||
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
.marketplace-tile-second-column {
|
||||||
|
float: left;
|
||||||
|
margin-left:4%;
|
||||||
|
width: 62%;
|
||||||
|
}
|
||||||
|
.exploreButton {
|
||||||
|
font-size: 16px !important;
|
||||||
|
width: 200px !important;
|
||||||
|
height: 45px !important;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.tile-divider {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0%;
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid lightgrey;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.marketplace-tile-description {
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.marketplace-tile-image {
|
||||||
|
margin-top:15px;
|
||||||
|
max-width: 256px;
|
||||||
|
height: 128px;
|
||||||
|
margin-bottom:60px;
|
||||||
|
-webkit-box-shadow: -1px 4px 16px 0px rgba(0, 0, 0, 0.48);
|
||||||
|
-moz-box-shadow: -1px 4px 16px 0px rgba(0, 0, 0, 0.48);
|
||||||
|
box-shadow: -1px 4px 16px 0px rgba(0, 0, 0, 0.48);
|
||||||
|
}
|
||||||
|
.marketplace-clara-steps {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
.marketplace-clara-steps > li {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
@media (max-width:768px) {
|
||||||
|
.marketplace-tile-first-column {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.marketplace-tile-second-column {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.exploreButton-holder {
|
||||||
|
width:100%;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.tile-divider {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0%;
|
||||||
|
}
|
||||||
|
.marketplace-tile-image{
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
}
|
BIN
scripts/system/html/img/clara-tile.png
Normal file
BIN
scripts/system/html/img/clara-tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
BIN
scripts/system/html/img/hifi-marketplace-tile.png
Normal file
BIN
scripts/system/html/img/hifi-marketplace-tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
12
scripts/system/html/js/marketplaces.js
Normal file
12
scripts/system/html/js/marketplaces.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
function loaded() {
|
||||||
|
bindExploreButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindExploreButtons() {
|
||||||
|
$('#exploreClaraMarketplace').on('click', function() {
|
||||||
|
window.location = "https://clara.io/library?public=true"
|
||||||
|
})
|
||||||
|
$('#exploreHifiMarketplace').on('click', function() {
|
||||||
|
window.location = "http://www.highfidelity.com/marketplace"
|
||||||
|
})
|
||||||
|
}
|
65
scripts/system/html/marketplaces.html
Normal file
65
scripts/system/html/marketplaces.html
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<!--
|
||||||
|
// marketplaces.html
|
||||||
|
//
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Marketplaces</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/edit-style.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/marketplaces.css">
|
||||||
|
<script src="js/jquery-2.1.4.min.js"></script>
|
||||||
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
|
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||||
|
<script src="js/marketplaces.js"></script>
|
||||||
|
</head>
|
||||||
|
<body onload='loaded();'>
|
||||||
|
<div class="marketplaces-container">
|
||||||
|
<h2 class="marketplaces-title">
|
||||||
|
Marketplaces
|
||||||
|
</h2>
|
||||||
|
<div class="marketplaces-intro-text">
|
||||||
|
<p>
|
||||||
|
You can bring content into High Fidelity forom anywhere you want. Here are a few places that support direct import of content right now. If you'd like to suggest a Market to include here, <a href="mailto:contact@highfidelity.io">let us know.</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="marketplace-tile">
|
||||||
|
<div class="marketplace-tile-first-column">
|
||||||
|
<img class="marketplace-tile-image" src="img/hifi-marketplace-tile.png">
|
||||||
|
</div>
|
||||||
|
<div class="marketplace-tile-second-column">
|
||||||
|
<p class="marketplace-tile-description">This is the default High Fidelity marketplace. Viewing and downloading content from here is fully supported in Interface.</p>
|
||||||
|
<div class="exploreButton-holder">
|
||||||
|
<input class="blue exploreButton" type="button" value="Explore" id="exploreHifiMarketplace"></input></div>
|
||||||
|
</div>
|
||||||
|
<hr class="tile-divider">
|
||||||
|
</div>
|
||||||
|
<div class="marketplace-tile">
|
||||||
|
<div class="marketplace-tile-first-column">
|
||||||
|
<img class="marketplace-tile-image" src="img/clara-tile.png">
|
||||||
|
</div>
|
||||||
|
<div class="marketplace-tile-second-column">
|
||||||
|
<p class="marketplace-tile-description">Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:</p>
|
||||||
|
<ol class="marketplace-clara-steps">
|
||||||
|
<li><a href="http://www.clara.io/signup">Create an account here </a>or log in as an existing user.</li>
|
||||||
|
<li>Choose a model from the list and click Download -> Autodesk FBX.</li>
|
||||||
|
<li>After the file processes, click Download.</li>
|
||||||
|
<li>Add the model to your asset server, then find it from the list and choose Add To World.</li>
|
||||||
|
</ol>
|
||||||
|
<div class="exploreButton-holder">
|
||||||
|
<input class="blue exploreButton" type="button" value="Explore" id="exploreClaraMarketplace"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="tile-divider">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -14,15 +14,15 @@
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
var toolIconUrl = Script.resolvePath("../assets/images/tools/");
|
var toolIconUrl = Script.resolvePath("../assets/images/tools/");
|
||||||
var qml = Script.resolvePath("../../../resources/qml/MarketplaceComboBox.qml")
|
var qml = Script.resolvePath("../../../resources/qml/Marketplaces.qml")
|
||||||
|
|
||||||
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
||||||
|
|
||||||
var marketplaceWindow = new OverlayWindow({
|
var marketplaceWindow = new OverlayWindow({
|
||||||
title: "Marketplace",
|
title: "Marketplace",
|
||||||
source: qml,
|
source: qml,
|
||||||
width: 900,
|
width: 1000,
|
||||||
height: 700,
|
height: 900,
|
||||||
toolWindow: false,
|
toolWindow: false,
|
||||||
visible: false,
|
visible: false,
|
||||||
});
|
});
|
||||||
|
|
28
tests/render-texture-load/CMakeLists.txt
Normal file
28
tests/render-texture-load/CMakeLists.txt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
set(TARGET_NAME render-texture-load)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# This is not a testcase -- just set it up as a regular hifi project
|
||||||
|
setup_hifi_project(Quick Gui OpenGL)
|
||||||
|
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
|
|
||||||
|
# link in the shared libraries
|
||||||
|
link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics)
|
||||||
|
|
||||||
|
package_libraries_for_deployment()
|
||||||
|
|
||||||
|
target_zlib()
|
||||||
|
add_dependency_external_projects(quazip)
|
||||||
|
find_package(QuaZip REQUIRED)
|
||||||
|
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
|
target_bullet()
|
585
tests/render-texture-load/src/main.cpp
Normal file
585
tests/render-texture-load/src/main.cpp
Normal file
|
@ -0,0 +1,585 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/01
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <gl/Config.h>
|
||||||
|
#include <gl/Context.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
#include <QtCore/QLoggingCategory>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QThreadPool>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
|
#include <QtCore/QTemporaryDir>
|
||||||
|
#include <QtCore/QTemporaryFile>
|
||||||
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
#include <QtWidgets/QInputDialog>
|
||||||
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
#include <quazip5/quazip.h>
|
||||||
|
#include <quazip5/JlCompress.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <shared/RateCounter.h>
|
||||||
|
#include <AssetClient.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
|
||||||
|
#include <gpu/gl/GLBackend.h>
|
||||||
|
#include <gpu/gl/GLFramebuffer.h>
|
||||||
|
#include <gpu/gl/GLTexture.h>
|
||||||
|
#include <gpu/StandardShaderLib.h>
|
||||||
|
|
||||||
|
#include <AddressManager.h>
|
||||||
|
#include <NodeList.h>
|
||||||
|
#include <TextureCache.h>
|
||||||
|
#include <FramebufferCache.h>
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
#include <DeferredLightingEffect.h>
|
||||||
|
#include <RenderShadowTask.h>
|
||||||
|
#include <RenderDeferredTask.h>
|
||||||
|
|
||||||
|
extern QThread* RENDER_THREAD;
|
||||||
|
|
||||||
|
static const QString DATA_SET = "https://hifi-content.s3.amazonaws.com/austin/textures.zip";
|
||||||
|
static const QTemporaryDir DATA_DIR;
|
||||||
|
|
||||||
|
|
||||||
|
class FileDownloader : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using Handler = std::function<void(const QByteArray& data)>;
|
||||||
|
|
||||||
|
FileDownloader(QUrl url, const Handler& handler, QObject *parent = 0) : QObject(parent), _handler(handler) {
|
||||||
|
connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
|
||||||
|
_accessManager.get(QNetworkRequest(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitForDownload() {
|
||||||
|
while (!_complete) {
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void fileDownloaded(QNetworkReply* pReply) {
|
||||||
|
_handler(pReply->readAll());
|
||||||
|
pReply->deleteLater();
|
||||||
|
_complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager _accessManager;
|
||||||
|
Handler _handler;
|
||||||
|
bool _complete { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenderThread : public GenericThread {
|
||||||
|
using Parent = GenericThread;
|
||||||
|
public:
|
||||||
|
gl::Context _context;
|
||||||
|
gpu::PipelinePointer _presentPipeline;
|
||||||
|
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||||
|
std::atomic<size_t> _presentCount;
|
||||||
|
QElapsedTimer _elapsed;
|
||||||
|
std::atomic<uint16_t> _fps{ 1 };
|
||||||
|
RateCounter<200> _fpsCounter;
|
||||||
|
std::mutex _mutex;
|
||||||
|
std::shared_ptr<gpu::Backend> _backend;
|
||||||
|
std::vector<uint64_t> _frameTimes;
|
||||||
|
size_t _frameIndex;
|
||||||
|
std::mutex _frameLock;
|
||||||
|
std::queue<gpu::FramePointer> _pendingFrames;
|
||||||
|
gpu::FramePointer _activeFrame;
|
||||||
|
QSize _size;
|
||||||
|
static const size_t FRAME_TIME_BUFFER_SIZE{ 1024 };
|
||||||
|
|
||||||
|
void submitFrame(const gpu::FramePointer& frame) {
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
_pendingFrames.push(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void initialize(QWindow* window, gl::Context& initContext) {
|
||||||
|
setObjectName("RenderThread");
|
||||||
|
_context.setWindow(window);
|
||||||
|
_context.create();
|
||||||
|
_context.makeCurrent();
|
||||||
|
window->setSurfaceType(QSurface::OpenGLSurface);
|
||||||
|
_context.makeCurrent(_context.qglContext(), window);
|
||||||
|
// GPU library init
|
||||||
|
gpu::Context::init<gpu::gl::GLBackend>();
|
||||||
|
_gpuContext = std::make_shared<gpu::Context>();
|
||||||
|
_backend = _gpuContext->getBackend();
|
||||||
|
_context.makeCurrent();
|
||||||
|
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||||
|
_context.makeCurrent();
|
||||||
|
initContext.create();
|
||||||
|
_context.doneCurrent();
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
Parent::initialize();
|
||||||
|
_context.moveToThread(_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() override {
|
||||||
|
RENDER_THREAD = QThread::currentThread();
|
||||||
|
|
||||||
|
// Wait until the context has been moved to this thread
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.makeCurrent();
|
||||||
|
glewExperimental = true;
|
||||||
|
glewInit();
|
||||||
|
glGetError();
|
||||||
|
|
||||||
|
//wglSwapIntervalEXT(0);
|
||||||
|
_frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0);
|
||||||
|
{
|
||||||
|
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||||
|
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
_presentPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//_textOverlay = new TextOverlay(glm::uvec2(800, 600));
|
||||||
|
glViewport(0, 0, 800, 600);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
_elapsed.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() override {
|
||||||
|
_activeFrame.reset();
|
||||||
|
while (!_pendingFrames.empty()) {
|
||||||
|
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||||
|
_pendingFrames.pop();
|
||||||
|
}
|
||||||
|
_presentPipeline.reset();
|
||||||
|
_gpuContext.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderFrame(gpu::FramePointer& frame) {
|
||||||
|
++_presentCount;
|
||||||
|
_context.makeCurrent();
|
||||||
|
_backend->recycle();
|
||||||
|
_backend->syncCache();
|
||||||
|
if (frame && !frame->batches.empty()) {
|
||||||
|
_gpuContext->executeFrame(frame);
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
gpu::Batch presentBatch;
|
||||||
|
presentBatch.setViewportTransform({ 0, 0, _size.width(), _size.height() });
|
||||||
|
presentBatch.enableStereo(false);
|
||||||
|
presentBatch.resetViewTransform();
|
||||||
|
presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||||
|
presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));
|
||||||
|
presentBatch.setPipeline(_presentPipeline);
|
||||||
|
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
|
_gpuContext->executeBatch(presentBatch);
|
||||||
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
_context.makeCurrent();
|
||||||
|
_context.swapBuffers();
|
||||||
|
_fpsCounter.increment();
|
||||||
|
static size_t _frameCount{ 0 };
|
||||||
|
++_frameCount;
|
||||||
|
if (_elapsed.elapsed() >= 500) {
|
||||||
|
_fps = _fpsCounter.rate();
|
||||||
|
_frameCount = 0;
|
||||||
|
_elapsed.restart();
|
||||||
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
_context.doneCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void report() {
|
||||||
|
uint64_t total = 0;
|
||||||
|
for (const auto& t : _frameTimes) {
|
||||||
|
total += t;
|
||||||
|
}
|
||||||
|
auto averageFrameTime = total / FRAME_TIME_BUFFER_SIZE;
|
||||||
|
qDebug() << "Average frame " << averageFrameTime;
|
||||||
|
|
||||||
|
std::list<std::pair<uint64_t, size_t>> sortedHighFrames;
|
||||||
|
for (size_t i = 0; i < _frameTimes.size(); ++i) {
|
||||||
|
const auto& t = _frameTimes[i];
|
||||||
|
if (t > averageFrameTime * 6) {
|
||||||
|
sortedHighFrames.push_back({ t, i } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedHighFrames.sort();
|
||||||
|
for (const auto& p : sortedHighFrames) {
|
||||||
|
qDebug() << "Long frame " << p.first << " " << p.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool process() override {
|
||||||
|
std::queue<gpu::FramePointer> pendingFrames;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_frameLock);
|
||||||
|
pendingFrames.swap(_pendingFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!pendingFrames.empty()) {
|
||||||
|
_activeFrame = pendingFrames.front();
|
||||||
|
if (_activeFrame) {
|
||||||
|
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||||
|
}
|
||||||
|
pendingFrames.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_activeFrame) {
|
||||||
|
QThread::msleep(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto start = usecTimestampNow();
|
||||||
|
renderFrame(_activeFrame);
|
||||||
|
auto duration = usecTimestampNow() - start;
|
||||||
|
auto frameBufferIndex = _frameIndex % FRAME_TIME_BUFFER_SIZE;
|
||||||
|
_frameTimes[frameBufferIndex] = duration;
|
||||||
|
++_frameIndex;
|
||||||
|
if (0 == _frameIndex % FRAME_TIME_BUFFER_SIZE) {
|
||||||
|
report();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QString fileForPath(const QString& name) {
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
|
hash.addData(name.toLocal8Bit().data(), name.length());
|
||||||
|
QString hashStr = QString(hash.result().toHex());
|
||||||
|
auto dot = name.lastIndexOf('.');
|
||||||
|
QString extension = name.right(name.length() - dot);
|
||||||
|
QString result = DATA_DIR.path() + "/" + hashStr + extension;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a simple OpenGL window that renders text in various ways
|
||||||
|
class QTestWindow : public QWindow {
|
||||||
|
public:
|
||||||
|
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
|
||||||
|
static void setup() {
|
||||||
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
|
//DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
||||||
|
DependencyManager::set<AddressManager>();
|
||||||
|
DependencyManager::set<NodeList>(NodeType::Agent, 0);
|
||||||
|
DependencyManager::set<DeferredLightingEffect>();
|
||||||
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
|
DependencyManager::set<TextureCache>();
|
||||||
|
DependencyManager::set<FramebufferCache>();
|
||||||
|
DependencyManager::set<GeometryCache>();
|
||||||
|
DependencyManager::set<ModelCache>();
|
||||||
|
DependencyManager::set<PathUtils>();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TextureLoad {
|
||||||
|
uint32_t time;
|
||||||
|
QString file;
|
||||||
|
QString src;
|
||||||
|
};
|
||||||
|
|
||||||
|
QTestWindow() {
|
||||||
|
|
||||||
|
_currentTexture = _textures.end();
|
||||||
|
{
|
||||||
|
QStringList stringList;
|
||||||
|
QFile textFile("h:/textures/loads.txt");
|
||||||
|
textFile.open(QFile::ReadOnly);
|
||||||
|
//... (open the file for reading, etc.)
|
||||||
|
QTextStream textStream(&textFile);
|
||||||
|
while (true) {
|
||||||
|
QString line = textStream.readLine();
|
||||||
|
if (line.isNull())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
stringList.append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QString s : stringList) {
|
||||||
|
auto index = s.indexOf(" ");
|
||||||
|
QString timeStr = s.left(index);
|
||||||
|
auto time = timeStr.toUInt();
|
||||||
|
QString path = s.right(s.length() - index).trimmed();
|
||||||
|
path = fileForPath(path);
|
||||||
|
qDebug() << "Path " << path;
|
||||||
|
if (!QFileInfo(path).exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_textureLoads.push({ time, path, s });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
installEventFilter(this);
|
||||||
|
QThreadPool::globalInstance()->setMaxThreadCount(2);
|
||||||
|
QThread::currentThread()->setPriority(QThread::HighestPriority);
|
||||||
|
ResourceManager::init();
|
||||||
|
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||||
|
_size = QSize(800, 600);
|
||||||
|
_renderThread._size = _size;
|
||||||
|
setGeometry(QRect(QPoint(), _size));
|
||||||
|
create();
|
||||||
|
show();
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
// Create the initial context
|
||||||
|
_renderThread.initialize(this, _initContext);
|
||||||
|
_initContext.makeCurrent();
|
||||||
|
// FIXME use a wait condition
|
||||||
|
QThread::msleep(1000);
|
||||||
|
_renderThread.submitFrame(gpu::FramePointer());
|
||||||
|
_initContext.makeCurrent();
|
||||||
|
{
|
||||||
|
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||||
|
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
gpu::Shader::makeProgram(*program);
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
state->setDepthTest(gpu::State::DepthTest(false));
|
||||||
|
state->setScissorEnable(true);
|
||||||
|
_simplePipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
timer->setInterval(0);
|
||||||
|
connect(timer, &QTimer::timeout, this, [this] {
|
||||||
|
draw();
|
||||||
|
});
|
||||||
|
timer->start();
|
||||||
|
_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~QTestWindow() {
|
||||||
|
DependencyManager::destroy<FramebufferCache>();
|
||||||
|
DependencyManager::destroy<TextureCache>();
|
||||||
|
DependencyManager::destroy<ModelCache>();
|
||||||
|
DependencyManager::destroy<GeometryCache>();
|
||||||
|
ResourceManager::cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||||
|
if (event->type() == QEvent::Close) {
|
||||||
|
_renderThread.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWindow::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent* event) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyReleaseEvent(QKeyEvent* event) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent* event) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizeEvent(QResizeEvent* ev) override {
|
||||||
|
resizeWindow(ev->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::queue<TextureLoad> _textureLoads;
|
||||||
|
std::list<gpu::TexturePointer> _textures;
|
||||||
|
std::list<gpu::TexturePointer>::iterator _currentTexture;
|
||||||
|
|
||||||
|
uint16_t _fps;
|
||||||
|
gpu::PipelinePointer _simplePipeline;
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
if (!_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_renderCount.load() != 0 && _renderCount.load() >= _renderThread._presentCount.load()) {
|
||||||
|
QThread::usleep(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_renderCount = _renderThread._presentCount.load();
|
||||||
|
update();
|
||||||
|
|
||||||
|
QSize windowSize = _size;
|
||||||
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
framebufferCache->setFrameBufferSize(windowSize);
|
||||||
|
|
||||||
|
// Final framebuffer that will be handled to the display-plugin
|
||||||
|
render();
|
||||||
|
|
||||||
|
if (_fps != _renderThread._fps) {
|
||||||
|
_fps = _renderThread._fps;
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateText() {
|
||||||
|
setTitle(QString("FPS %1").arg(_fps));
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
auto now = usecTimestampNow();
|
||||||
|
static auto last = now;
|
||||||
|
auto delta = (now - last) / USECS_PER_MSEC;
|
||||||
|
if (!_textureLoads.empty()) {
|
||||||
|
const auto& front = _textureLoads.front();
|
||||||
|
if (delta >= front.time) {
|
||||||
|
QFileInfo fileInfo(front.file);
|
||||||
|
if (!fileInfo.exists()) {
|
||||||
|
qDebug() << "Missing file " << front.file;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Loading " << front.src;
|
||||||
|
_textures.push_back(DependencyManager::get<TextureCache>()->getImageTexture(front.file));
|
||||||
|
_currentTexture = _textures.begin();
|
||||||
|
}
|
||||||
|
_textureLoads.pop();
|
||||||
|
if (_textureLoads.empty()) {
|
||||||
|
qDebug() << "Done";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
auto& gpuContext = _renderThread._gpuContext;
|
||||||
|
gpuContext->beginFrame();
|
||||||
|
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
|
||||||
|
batch.resetStages();
|
||||||
|
});
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
auto framebuffer = DependencyManager::get<FramebufferCache>()->getFramebuffer();
|
||||||
|
|
||||||
|
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(framebuffer);
|
||||||
|
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(1, 0, 0, 1));
|
||||||
|
auto vpsize = framebuffer->getSize();
|
||||||
|
auto vppos = ivec2(0);
|
||||||
|
batch.setViewportTransform(ivec4(vppos, vpsize));
|
||||||
|
if (_currentTexture != _textures.end()) {
|
||||||
|
++_currentTexture;
|
||||||
|
}
|
||||||
|
if (_currentTexture == _textures.end()) {
|
||||||
|
_currentTexture = _textures.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentTexture != _textures.end()) {
|
||||||
|
batch.setResourceTexture(0, *_currentTexture);
|
||||||
|
}
|
||||||
|
batch.setPipeline(_simplePipeline);
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto frame = gpuContext->endFrame();
|
||||||
|
frame->framebuffer = framebuffer;
|
||||||
|
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
|
||||||
|
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||||
|
};
|
||||||
|
_renderThread.submitFrame(frame);
|
||||||
|
if (!_renderThread.isThreaded()) {
|
||||||
|
_renderThread.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizeWindow(const QSize& size) {
|
||||||
|
_size = size;
|
||||||
|
if (!_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_renderThread._size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSize _size;
|
||||||
|
std::atomic<size_t> _renderCount;
|
||||||
|
gl::OffscreenContext _initContext;
|
||||||
|
RenderThread _renderThread;
|
||||||
|
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||||
|
bool _ready { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||||
|
if (!message.isEmpty()) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
OutputDebugStringA(message.toLocal8Bit().constData());
|
||||||
|
OutputDebugStringA("\n");
|
||||||
|
#endif
|
||||||
|
std::cout << message.toLocal8Bit().constData() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * LOG_FILTER_RULES = R"V0G0N(
|
||||||
|
hifi.gpu=true
|
||||||
|
)V0G0N";
|
||||||
|
|
||||||
|
void unzipTestData(const QByteArray& zipData) {
|
||||||
|
QTemporaryFile zipFile;
|
||||||
|
if (zipFile.open()) {
|
||||||
|
zipFile.write(zipData);
|
||||||
|
zipFile.close();
|
||||||
|
}
|
||||||
|
qDebug() << zipFile.fileName();
|
||||||
|
if (!DATA_DIR.isValid()) {
|
||||||
|
qFatal("Unable to create temp dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
//auto files = JlCompress::getFileList(zipData);
|
||||||
|
auto files = JlCompress::extractDir(zipFile.fileName(), DATA_DIR.path());
|
||||||
|
qDebug() << DATA_DIR.path();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
QCoreApplication::setApplicationName("RenderPerf");
|
||||||
|
QCoreApplication::setOrganizationName("High Fidelity");
|
||||||
|
QCoreApplication::setOrganizationDomain("highfidelity.com");
|
||||||
|
qInstallMessageHandler(messageHandler);
|
||||||
|
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
|
||||||
|
|
||||||
|
|
||||||
|
FileDownloader(DATA_SET, [&](const QByteArray& data) {
|
||||||
|
qDebug() << "Fetched size " << data.size();
|
||||||
|
unzipTestData(data);
|
||||||
|
}).waitForDownload();
|
||||||
|
|
||||||
|
QTestWindow::setup();
|
||||||
|
QTestWindow window;
|
||||||
|
app.exec();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
Loading…
Reference in a new issue