Merge branch 'master' into 20872

This commit is contained in:
David Rowe 2016-04-13 13:11:26 +12:00
commit b33d28852b
69 changed files with 1774 additions and 1234 deletions

View file

@ -144,6 +144,9 @@ void Agent::run() {
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
messagesThread->start(); messagesThread->start();
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);
nodeList->addSetOfNodeTypesToNodeInterestSet({ nodeList->addSetOfNodeTypesToNodeInterestSet({
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
}); });
@ -164,52 +167,85 @@ void Agent::requestScript() {
scriptURL = QUrl(_payload); scriptURL = QUrl(_payload);
} }
// setup a network access manager and // make sure this is not a script request for the file scheme
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); if (scriptURL.scheme() == URL_SCHEME_FILE) {
qWarning() << "Cannot load script for Agent from local filesystem.";
scriptRequestFinished();
return;
}
QNetworkDiskCache* cache = new QNetworkDiskCache(); auto request = ResourceManager::createResourceRequest(this, scriptURL);
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
networkAccessManager.setCache(cache);
QNetworkRequest networkRequest = QNetworkRequest(scriptURL); if (!request) {
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();
scriptRequestFinished();
return;
}
// setup a timeout for script request // setup a timeout for script request
static const int SCRIPT_TIMEOUT_MS = 10000; static const int SCRIPT_TIMEOUT_MS = 10000;
_scriptRequestTimeout = new QTimer(this); _scriptRequestTimeout = new QTimer;
connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished); connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished);
_scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS); _scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS);
qDebug() << "Downloading script at" << scriptURL.toString(); connect(request, &ResourceRequest::finished, this, &Agent::scriptRequestFinished);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); if (scriptURL.scheme() == URL_SCHEME_ATP) {
// we have an ATP URL for the script - if we're not currently connected to the AssetServer
// then wait for the nodeConnected signal to fire off the request
auto assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (!assetServer || !assetServer->getActiveSocket()) {
qDebug() << "Waiting to connect to Asset Server for ATP script download.";
_pendingScriptRequest = request;
return;
}
}
qInfo() << "Requesting script at URL" << qPrintable(request->getUrl().toString());
request->send();
}
void Agent::nodeActivated(SharedNodePointer activatedNode) {
if (_pendingScriptRequest) {
qInfo() << "Requesting script at URL" << qPrintable(_pendingScriptRequest->getUrl().toString());
_pendingScriptRequest->send();
_pendingScriptRequest = nullptr;
}
} }
void Agent::scriptRequestFinished() { void Agent::scriptRequestFinished() {
auto reply = qobject_cast<QNetworkReply*>(sender()); auto request = qobject_cast<ResourceRequest*>(sender());
_scriptRequestTimeout->stop(); // stop the script request timeout, if it's running
if (_scriptRequestTimeout) {
QMetaObject::invokeMethod(_scriptRequestTimeout, "stop");
_scriptRequestTimeout->deleteLater();
}
if (reply && reply->error() == QNetworkReply::NoError) { if (request && request->getResult() == ResourceRequest::Success) {
_scriptContents = reply->readAll(); _scriptContents = request->getData();
qDebug() << "Downloaded script:" << _scriptContents; qInfo() << "Downloaded script:" << _scriptContents;
// we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished // we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished
// to return before calling executeScript // to return before calling executeScript
QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection);
} else { } else {
if (reply) { if (request) {
qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; qWarning() << "Failed to download script at" << request->getUrl().toString() << " - bailing on assignment.";
qDebug() << "QNetworkReply error was" << reply->errorString(); qWarning() << "ResourceRequest error was" << request->getResult();
} else { } else {
qDebug() << "Failed to download script - request timed out. Bailing on assignment."; qWarning() << "Failed to download script - request timed out. Bailing on assignment.";
} }
setFinished(true); setFinished(true);
} }
reply->deleteLater(); request->deleteLater();
} }
void Agent::executeScript() { void Agent::executeScript() {

View file

@ -69,6 +69,8 @@ private slots:
void processAgentAvatarAndAudio(float deltaTime); void processAgentAvatarAndAudio(float deltaTime);
void nodeActivated(SharedNodePointer activatedNode);
private: private:
std::unique_ptr<ScriptEngine> _scriptEngine; std::unique_ptr<ScriptEngine> _scriptEngine;
EntityEditPacketSender _entityEditSender; EntityEditPacketSender _entityEditSender;
@ -84,6 +86,7 @@ private:
QString _scriptContents; QString _scriptContents;
QTimer* _scriptRequestTimeout { nullptr }; QTimer* _scriptRequestTimeout { nullptr };
ResourceRequest* _pendingScriptRequest { nullptr };
bool _isListeningToAudioStream = false; bool _isListeningToAudioStream = false;
SharedSoundPointer _avatarSound; SharedSoundPointer _avatarSound;
int _numAvatarSoundSentBytes = 0; int _numAvatarSoundSentBytes = 0;

View file

@ -122,67 +122,49 @@ void AssetServer::completeSetup() {
} }
// load whatever mappings we currently have from the local file // load whatever mappings we currently have from the local file
loadMappingsFromFile(); if (loadMappingsFromFile()) {
qInfo() << "Serving files from: " << _filesDirectory.path();
qInfo() << "Serving files from: " << _filesDirectory.path(); // Check the asset directory to output some information about what we have
auto files = _filesDirectory.entryList(QDir::Files);
// Check the asset directory to output some information about what we have QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING };
auto files = _filesDirectory.entryList(QDir::Files); auto hashedFiles = files.filter(hashFileRegex);
QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING }; qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory.";
auto hashedFiles = files.filter(hashFileRegex);
qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory."; if (_fileMappings.count() > 0) {
cleanupUnmappedFiles();
}
performMappingMigration(); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
} else {
qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded.";
setFinished(true);
}
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
} }
void AssetServer::performMappingMigration() { void AssetServer::cleanupUnmappedFiles() {
QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}(\\.[\\w]+)+$" }; QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}" };
auto files = _resourcesDirectory.entryInfoList(QDir::Files); auto files = _filesDirectory.entryInfoList(QDir::Files);
// grab the currently mapped hashes
auto mappedHashes = _fileMappings.values();
qInfo() << "Performing unmapped asset cleanup.";
for (const auto& fileInfo : files) { for (const auto& fileInfo : files) {
if (hashFileRegex.exactMatch(fileInfo.fileName())) { if (hashFileRegex.exactMatch(fileInfo.fileName())) {
// we have a pre-mapping file that we should migrate to the new mapping system if (!mappedHashes.contains(fileInfo.fileName())) {
qDebug() << "Migrating pre-mapping file" << fileInfo.fileName(); // remove the unmapped file
QFile removeableFile { fileInfo.absoluteFilePath() };
// rename the file to the same name with no extension if (removeableFile.remove()) {
QFile oldFile { fileInfo.absoluteFilePath() }; qDebug() << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped.";
auto oldAbsolutePath = fileInfo.absoluteFilePath();
auto oldFilename = fileInfo.fileName();
auto hash = oldFilename.left(SHA256_HASH_HEX_LENGTH);
auto fullExtension = oldFilename.mid(oldFilename.indexOf('.'));
qDebug() << "\tMoving" << oldAbsolutePath << "to" << oldAbsolutePath.replace(fullExtension, "");
bool renamed = oldFile.copy(_filesDirectory.filePath(hash));
if (!renamed) {
qWarning() << "\tCould not migrate pre-mapping file" << fileInfo.fileName();
} else {
qDebug() << "\tRenamed pre-mapping file" << fileInfo.fileName();
// add a new mapping with the old extension and a truncated version of the hash
const int TRUNCATED_HASH_NUM_CHAR = 16;
auto fakeFileName = "/" + hash.left(TRUNCATED_HASH_NUM_CHAR) + fullExtension;
qDebug() << "\tAdding a migration mapping from" << fakeFileName << "to" << hash;
auto it = _fileMappings.find(fakeFileName);
if (it == _fileMappings.end()) {
_fileMappings[fakeFileName] = hash;
if (writeMappingsToFile()) {
// mapping added and persisted, we can remove the migrated file
oldFile.remove();
qDebug() << "\tMigration completed for" << oldFilename;
}
} else { } else {
qDebug() << "\tCould not add migration mapping for" << hash << "since a mapping for" << fakeFileName qDebug() << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed";
<< "already exists.";
} }
} }
} }
@ -451,7 +433,7 @@ void AssetServer::sendStatsPacket() {
static const QString MAP_FILE_NAME = "map.json"; static const QString MAP_FILE_NAME = "map.json";
void AssetServer::loadMappingsFromFile() { bool AssetServer::loadMappingsFromFile() {
auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME); auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME);
@ -488,15 +470,17 @@ void AssetServer::loadMappingsFromFile() {
} }
qInfo() << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath; qInfo() << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath;
return; return true;
} }
} }
qCritical() << "Failed to read mapping file at" << mapFilePath << "- assignment will not continue."; qCritical() << "Failed to read mapping file at" << mapFilePath;
setFinished(true); return false;
} else { } else {
qInfo() << "No existing mappings loaded from file since no file was found at" << mapFilePath; qInfo() << "No existing mappings loaded from file since no file was found at" << mapFilePath;
} }
return true;
} }
bool AssetServer::writeMappingsToFile() { bool AssetServer::writeMappingsToFile() {
@ -566,6 +550,8 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
// take a copy of the current mappings in case persistence of these deletes fails // take a copy of the current mappings in case persistence of these deletes fails
auto oldMappings = _fileMappings; auto oldMappings = _fileMappings;
QSet<QString> hashesToCheckForDeletion;
// enumerate the paths to delete and remove them all // enumerate the paths to delete and remove them all
for (auto& path : paths) { for (auto& path : paths) {
@ -579,6 +565,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
while (it != _fileMappings.end()) { while (it != _fileMappings.end()) {
if (it.key().startsWith(path)) { if (it.key().startsWith(path)) {
// add this hash to the list we need to check for asset removal from the server
hashesToCheckForDeletion << it.value().toString();
it = _fileMappings.erase(it); it = _fileMappings.erase(it);
} else { } else {
++it; ++it;
@ -595,6 +584,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
} else { } else {
auto oldMapping = _fileMappings.take(path); auto oldMapping = _fileMappings.take(path);
if (!oldMapping.isNull()) { if (!oldMapping.isNull()) {
// add this hash to the list we need to check for asset removal from server
hashesToCheckForDeletion << oldMapping.toString();
qDebug() << "Deleted a mapping:" << path << "=>" << oldMapping.toString(); qDebug() << "Deleted a mapping:" << path << "=>" << oldMapping.toString();
} else { } else {
qDebug() << "Unable to delete a mapping that was not found:" << path; qDebug() << "Unable to delete a mapping that was not found:" << path;
@ -605,6 +597,30 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
// deleted the old mappings, attempt to persist to file // deleted the old mappings, attempt to persist to file
if (writeMappingsToFile()) { if (writeMappingsToFile()) {
// persistence succeeded we are good to go // persistence succeeded we are good to go
// grab the current mapped hashes
auto mappedHashes = _fileMappings.values();
// enumerate the mapped hashes and clear the list of hashes to check for anything that's present
for (auto& hashVariant : mappedHashes) {
auto it = hashesToCheckForDeletion.find(hashVariant.toString());
if (it != hashesToCheckForDeletion.end()) {
hashesToCheckForDeletion.erase(it);
}
}
// we now have a set of hashes that are unmapped - we will delete those asset files
for (auto& hash : hashesToCheckForDeletion) {
// remove the unmapped file
QFile removeableFile { _filesDirectory.absoluteFilePath(hash) };
if (removeableFile.remove()) {
qDebug() << "\tDeleted" << hash << "from asset files directory since it is now unmapped.";
} else {
qDebug() << "\tAttempt to delete unmapped file" << hash << "failed";
}
}
return true; return true;
} else { } else {
qWarning() << "Failed to persist deleted mappings, rolling back"; qWarning() << "Failed to persist deleted mappings, rolling back";

View file

@ -48,7 +48,7 @@ private:
void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
// Mapping file operations must be called from main assignment thread only // Mapping file operations must be called from main assignment thread only
void loadMappingsFromFile(); bool loadMappingsFromFile();
bool writeMappingsToFile(); bool writeMappingsToFile();
/// Set the mapping for path to hash /// Set the mapping for path to hash
@ -60,7 +60,8 @@ private:
/// Rename mapping from `oldPath` to `newPath`. Returns true if successful /// Rename mapping from `oldPath` to `newPath`. Returns true if successful
bool renameMapping(AssetPath oldPath, AssetPath newPath); bool renameMapping(AssetPath oldPath, AssetPath newPath);
void performMappingMigration(); // deletes any unmapped files from the local asset directory
void cleanupUnmappedFiles();
Mappings _fileMappings; Mappings _fileMappings;

View file

@ -870,7 +870,7 @@ function saveSettings() {
// check if we've set the basic http password - if so convert it to base64 // check if we've set the basic http password - if so convert it to base64
if (formJSON["security"]) { if (formJSON["security"]) {
var password = formJSON["security"]["http_password"]; var password = formJSON["security"]["http_password"];
if (password.length > 0) { if (password && password.length > 0) {
formJSON["security"]["http_password"] = sha256_digest(password); formJSON["security"]["http_password"] = sha256_digest(password);
} }
} }

View file

@ -1678,10 +1678,12 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
// we've pulled a username and password - now check if there is a match in our basic auth hash // we've pulled a username and password - now check if there is a match in our basic auth hash
QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString(); QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString();
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH); const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : ""; QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : "";
QString hexHeaderPassword = QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); QString hexHeaderPassword = QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) { if (settingsUsername == headerUsername
&& (settingsPassword.isEmpty() || hexHeaderPassword == settingsPassword)) {
return true; return true;
} }
} }

View file

@ -95,10 +95,6 @@ var SHOULD_SHOW_PROPERTY_MENU = false;
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."
var modelURLs = [
"Insert the URL to your FBX"
];
var mode = 0; var mode = 0;
var isActive = false; var isActive = false;
@ -432,7 +428,7 @@ var toolBar = (function() {
} }
if (newModelButton === toolBar.clicked(clickedOverlay)) { if (newModelButton === toolBar.clicked(clickedOverlay)) {
url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); url = Window.prompt("Model URL");
if (url !== null && url !== "") { if (url !== null && url !== "") {
addModel(url); addModel(url);
} }

View file

@ -0,0 +1,133 @@
//
// golfClub.js
//
// Created by Philip Rosedale on April 11, 2016.
// Copyright 2015 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
//
// A simple golf club. If you have equipped it, and pull trigger, it will either make
// you a new golf ball, or take you to your ball if one is not made.
(function () {
var ball = null;
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var collisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav";
var triggerState = false;
var BALL_GRAVITY = -9.8;
var BALL_START_VELOCITY = 0.1;
var BALL_MAX_RANGE = 10;
var BALL_DROP_DISTANCE = 0.6;
var BALL_DIAMETER = 0.07;
var BALL_LIFETIME = 3600;
var MAX_BRAKING_SPEED = 0.2;
var BALL_BRAKING_RATE = 0.5;
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
];
function triggerPulled(hand) {
// Return true if the trigger has just been pulled
var triggerValue = Controller.getValue(TRIGGER_CONTROLS[hand]);
var oldTriggerState = triggerState;
var TRIGGER_PULL_THRESHOLD = 0.5;
var TRIGGER_RELEASE_THRESHOLD = 0.4;
if (triggerValue > TRIGGER_PULL_THRESHOLD) {
triggerState = true;
} else if (triggerValue < TRIGGER_RELEASE_THRESHOLD) {
triggerState = false;
}
return (triggerState && (oldTriggerState != triggerState));
}
function ballPosition(ball) {
// return the position of this entity
var properties = Entities.getEntityProperties(ball, ['position']);
if (!properties) {
return null;
} else {
return properties.position;
}
}
function gotoPointOnGround(position, away) {
// Position yourself facing in the direction you were originally facing, but with a
// point on the ground *away* meters from *position* and in front of you.
var offset = Quat.getFront(MyAvatar.orientation);
offset.y = 0.0;
offset = Vec3.multiply(-away, Vec3.normalize(offset));
var newAvatarPosition = Vec3.sum(position, offset);
// Assuming position is on ground, put me distance from eyes to hips higher, plus a 50% adjust for longer legs.
// TODO: Need avatar callback for exact foot-to-hips height.
var halfHeight = avatarHalfHeight();
print("Height = " + halfHeight);
newAvatarPosition.y += (halfHeight * 1.5);
MyAvatar.position = newAvatarPosition;
}
function inFrontOfMe() {
return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getFront(MyAvatar.orientation)));
}
function avatarHalfHeight() {
return MyAvatar.getDefaultEyePosition().y - MyAvatar.position.y;
}
function brakeBall(ball) {
// Check the ball's velocity and slow it down if beyond a threshold
var properties = Entities.getEntityProperties(ball, ['velocity']);
if (properties) {
var velocity = Vec3.length(properties.velocity);
if ((velocity > 0) && (velocity < MAX_BRAKING_SPEED)) {
Entities.editEntity(ball, { velocity: Vec3.multiply(BALL_BRAKING_RATE, properties.velocity) });
}
}
}
function makeBall(position) {
// Create a new sphere entity
ball = Entities.addEntity({
type: "Sphere",
position: position,
color: { red: 255, green: 255, blue: 255 },
dimensions: { x: BALL_DIAMETER, y: BALL_DIAMETER, z: BALL_DIAMETER },
gravity: { x: 0, y: BALL_GRAVITY, z: 0 },
velocity: { x: 0, y: BALL_START_VELOCITY, z: 0 },
friction: 0.5,
restitution: 0.5,
shapeType: "sphere",
dynamic: true,
lifetime: BALL_LIFETIME,
collisionSoundURL: collisionSoundURL
});
}
function checkClub(params) {
var hand = params[0] == "left" ? 0 : 1;
var makeNewBall = false;
if (triggerPulled(hand)) {
// If trigger just pulled, either drop new ball or go to existing one
var position = ballPosition(ball);
if (position && (Vec3.distance(MyAvatar.position, position) < BALL_MAX_RANGE)) {
gotoPointOnGround(position, BALL_DROP_DISTANCE);
} else {
Entities.deleteEntity(ball);
makeBall(inFrontOfMe());
}
}
brakeBall(ball);
}
this.continueEquip = function(id, params) {
// While holding the club, continuously check for trigger pull and brake ball if moving.
checkClub(params);
}
});

View file

@ -0,0 +1,36 @@
{
"Entities": [
{
"collisionsWillMove": 1,
"compoundShapeURL": "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Tutorials/Putter_phys.obj",
"created": "2016-04-07T23:19:30Z",
"description": "Spawns ball or jumps to ball with trigger",
"dimensions": {
"x": 0.043093059211969376,
"y": 1.1488667726516724,
"z": 0.42455694079399109
},
"dynamic": 1,
"id": "{266a9bc2-cff5-4128-b347-5eab519d8209}",
"modelURL": "http://hifi-content.s3.amazonaws.com/caitlyn/dev/Tutorials/putter_VR.fbx",
"name": "Golf Putter",
"queryAACube": {
"scale": 1.2255613803863525,
"x": -0.61278069019317627,
"y": -0.61278069019317627,
"z": -0.61278069019317627
},
"rotation": {
"w": 0.41972994804382324,
"x": 0.78570234775543213,
"y": -0.41875332593917847,
"z": 0.17653167247772217
},
"script": "https://s3.amazonaws.com/hifi-public/philip/golfClub.js",
"shapeType": "compound",
"type": "Model",
"userData": "{\"wearable\":{\"joints\":{\"LeftHand\":[{\"x\":-0.1631782054901123,\"y\":0.44648152589797974,\"z\":0.10100018978118896},{\"x\":-0.9181621670722961,\"y\":-0.0772884339094162,\"z\":-0.3870723247528076,\"w\":-0.0343472845852375}],\"RightHand\":[{\"x\":0.16826771199703217,\"y\":0.4757269620895386,\"z\":0.07139724493026733},{\"x\":-0.7976328134536743,\"y\":-0.0011603273451328278,\"z\":0.6030101776123047,\"w\":-0.012610925361514091}]}}}"
}
],
"Version": 57
}

View file

@ -84,6 +84,11 @@ Item {
prop: "frameTextureCount", prop: "frameTextureCount",
label: "Frame", label: "Frame",
color: "#E2334D" color: "#E2334D"
},
{
prop: "textureGPUTransferCount",
label: "Transfer",
color: "#9495FF"
} }
] ]
} }
@ -104,7 +109,18 @@ Item {
prop: "textureGPUMemoryUsage", prop: "textureGPUMemoryUsage",
label: "GPU", label: "GPU",
color: "#1AC567" color: "#1AC567"
},
{
prop: "textureGPUVirtualMemoryUsage",
label: "GPU Virtual",
color: "#9495FF"
},
{
prop: "frameTextureMemoryUsage",
label: "Frame",
color: "#E2334D"
} }
] ]
} }
@ -179,7 +195,7 @@ Item {
object: Render.getConfig("DrawLight"), object: Render.getConfig("DrawLight"),
prop: "numDrawn", prop: "numDrawn",
label: "Lights", label: "Lights",
color: "#E2334D" color: "#FED959"
} }
] ]
} }

View file

@ -48,12 +48,22 @@ Item {
Text { Text {
color: root.fontColor; color: root.fontColor;
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
text: "Render Rate: " + root.renderrate text: "Render Rate: " + root.renderrate.toFixed(2);
} }
Text { Text {
color: root.fontColor; color: root.fontColor;
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
text: "Present Rate: " + root.presentrate text: "Present Rate: " + root.presentrate.toFixed(2);
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Present New Rate: " + root.presentnewrate.toFixed(2);
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Present Drop Rate: " + root.presentdroprate.toFixed(2);
} }
Text { Text {
color: root.fontColor; color: root.fontColor;

View file

@ -116,7 +116,7 @@ Column {
LinearGradient { LinearGradient {
id: bottomBar id: bottomBar
visible: isCollapsible visible: desktop.gradientsSupported && isCollapsible
width: frame.width width: frame.width
height: visible ? 4 : 0 height: visible ? 4 : 0
x: -hifi.dimensions.contentMargin.x x: -hifi.dimensions.contentMargin.x

View file

@ -34,6 +34,10 @@ FocusScope {
// The VR version of the primary menu // The VR version of the primary menu
property var rootMenu: Menu { objectName: "rootMenu" } property var rootMenu: Menu { objectName: "rootMenu" }
// FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX/AMD
// because shaders are 4.2, and do not include #version declarations.
property bool gradientsSupported: Qt.platform.os != "osx" && !~GL.vendor.indexOf("ATI")
readonly property alias zLevels: zLevels readonly property alias zLevels: zLevels
QtObject { QtObject {
id: zLevels; id: zLevels;

View file

@ -20,6 +20,8 @@ Item {
default property var decoration default property var decoration
property bool gradientsSupported: desktop.gradientsSupported
readonly property int iconSize: 22 readonly property int iconSize: 22
readonly property int frameMargin: 9 readonly property int frameMargin: 9
readonly property int frameMarginLeft: frameMargin readonly property int frameMarginLeft: frameMargin
@ -59,9 +61,7 @@ Item {
height: 1.66 * window.height height: 1.66 * window.height
x: (window.width - width) / 2 x: (window.width - width) / 2
y: window.height / 2 - 0.375 * height y: window.height / 2 - 0.375 * height
// FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX. visible: gradientsSupported && window && window.focus && pane.visible
// Check again when have a later version of QtQuick.
visible: window && window.focus && pane.visible && Qt.platform.os != "osx"
gradient: Gradient { gradient: Gradient {
// GradientStop position 0.5 is at full circumference of circle that fits inside the square. // GradientStop position 0.5 is at full circumference of circle that fits inside the square.
GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity GradientStop { position: 0.0; color: "#ff000000" } // black, 100% opacity

View file

@ -51,6 +51,7 @@ Fadable {
// property bool pinnable: false // property bool pinnable: false
// property bool pinned: false // property bool pinned: false
property bool resizable: false property bool resizable: false
property bool gradientsSupported: desktop.gradientsSupported
property vector2d minSize: Qt.vector2d(100, 100) property vector2d minSize: Qt.vector2d(100, 100)
property vector2d maxSize: Qt.vector2d(1280, 800) property vector2d maxSize: Qt.vector2d(1280, 800)
@ -142,9 +143,7 @@ Fadable {
} }
LinearGradient { LinearGradient {
// FIXME: Alpha gradients display as fuschia under QtQuick 2.5 on OSX. visible: gradientsSupported && modality != Qt.ApplicationModal
// Check again when have a later version of QtQuick.
visible: modality != Qt.ApplicationModal && Qt.platform.os != "osx"
anchors.top: contentBackground.bottom anchors.top: contentBackground.bottom
anchors.left: contentBackground.left anchors.left: contentBackground.left
width: contentBackground.width - 1 width: contentBackground.width - 1

View file

@ -511,8 +511,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_sessionRunTimer(startupTimer), _sessionRunTimer(startupTimer),
_previousSessionCrashed(setupEssentials(argc, argv)), _previousSessionCrashed(setupEssentials(argc, argv)),
_undoStackScriptingInterface(&_undoStack), _undoStackScriptingInterface(&_undoStack),
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)), _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboardRenderer(false, this, this), _entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()), _entityClipboard(new EntityTree()),
@ -1332,11 +1330,13 @@ void Application::initializeGL() {
_idleLoopStdev.reset(); _idleLoopStdev.reset();
// update before the first render // update before the first render
update(1.0f / _fps); update(0);
InfoView::show(INFO_HELP_PATH, true); InfoView::show(INFO_HELP_PATH, true);
} }
extern void setupPreferences(); extern void setupPreferences();
void Application::initializeUi() { void Application::initializeUi() {
AddressBarDialog::registerType(); AddressBarDialog::registerType();
ErrorDialog::registerType(); ErrorDialog::registerType();
@ -1348,6 +1348,9 @@ void Application::initializeUi() {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_offscreenContext->getContext()); offscreenUi->create(_offscreenContext->getContext());
auto rootContext = offscreenUi->getRootContext();
offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setProxyWindow(_window->windowHandle());
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
@ -1358,7 +1361,6 @@ void Application::initializeUi() {
// do better detection in the offscreen UI of what has focus // do better detection in the offscreen UI of what has focus
offscreenUi->setNavigationFocused(false); offscreenUi->setNavigationFocused(false);
auto rootContext = offscreenUi->getRootContext();
auto engine = rootContext->engine(); auto engine = rootContext->engine();
connect(engine, &QQmlEngine::quit, [] { connect(engine, &QQmlEngine::quit, [] {
qApp->quit(); qApp->quit();
@ -1463,7 +1465,6 @@ void Application::initializeUi() {
} }
void Application::paintGL() { void Application::paintGL() {
updateHeartbeat(); updateHeartbeat();
// Some plugins process message events, potentially leading to // Some plugins process message events, potentially leading to
@ -1481,24 +1482,7 @@ void Application::paintGL() {
return; return;
} }
_frameCount++; _frameCount++;
_frameCounter.increment();
// update fps moving average
uint64_t now = usecTimestampNow();
static uint64_t lastPaintBegin{ now };
uint64_t diff = now - lastPaintBegin;
float instantaneousFps = 0.0f;
if (diff != 0) {
instantaneousFps = (float)USECS_PER_SECOND / (float)diff;
_framesPerSecond.updateAverage(_lastInstantaneousFps);
}
lastPaintBegin = now;
// update fps once a second
if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) {
_fps = _framesPerSecond.getAverage();
_lastFramesPerSecondUpdate = now;
}
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
PerformanceTimer perfTimer("paintGL"); PerformanceTimer perfTimer("paintGL");
@ -1655,7 +1639,7 @@ void Application::paintGL() {
} }
// Update camera position // Update camera position
if (!isHMDMode()) { if (!isHMDMode()) {
_myCamera.update(1.0f / _fps); _myCamera.update(1.0f / _frameCounter.rate());
} }
} }
@ -1752,8 +1736,6 @@ void Application::paintGL() {
batch.resetStages(); batch.resetStages();
}); });
} }
_lastInstantaneousFps = instantaneousFps;
} }
void Application::runTests() { void Application::runTests() {
@ -2625,32 +2607,19 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME }; static uint32_t _renderedFrameIndex { INVALID_FRAME };
void Application::idle(uint64_t now) { void Application::idle(uint64_t now) {
// NOTICE NOTICE NOTICE NOTICE
// Do not insert new code between here and the PROFILE_RANGE declaration
// unless you know exactly what you're doing. This idle function can be
// called thousands per second or more, so any additional work that's done
// here will have a serious impact on CPU usage. Only add code after all
// the thottling logic, i.e. after PROFILE_RANGE
// NOTICE NOTICE NOTICE NOTICE
updateHeartbeat(); updateHeartbeat();
if (_aboutToQuit || _inPaint) { if (_aboutToQuit || _inPaint) {
return; // bail early, nothing to do here. return; // bail early, nothing to do here.
} }
// These tasks need to be done on our first idle, because we don't want the showing of
// overlay subwindows to do a showDesktop() until after the first time through
static bool firstIdle = true;
if (firstIdle) {
firstIdle = false;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
_overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
}
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
_keyboardMouseDevice->pluginFocusOutEvent();
_keyboardDeviceHasFocus = false;
} else if (offscreenUi && offscreenUi->getWindow()->activeFocusItem() == offscreenUi->getRootItem()) {
_keyboardDeviceHasFocus = true;
}
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
// depending on whether we're throttling or not. // depending on whether we're throttling or not.
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
@ -2684,8 +2653,36 @@ void Application::idle(uint64_t now) {
return; return;
} }
// NOTICE NOTICE NOTICE NOTICE
// do NOT add new code above this line unless you want it to be executed potentially
// thousands of times per second
// NOTICE NOTICE NOTICE NOTICE
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
// These tasks need to be done on our first idle, because we don't want the showing of
// overlay subwindows to do a showDesktop() until after the first time through
static bool firstIdle = true;
if (firstIdle) {
firstIdle = false;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
_overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
}
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
_keyboardMouseDevice->pluginFocusOutEvent();
_keyboardDeviceHasFocus = false;
} else if (offscreenUi && offscreenUi->getWindow()->activeFocusItem() == offscreenUi->getRootItem()) {
_keyboardDeviceHasFocus = true;
}
// We're going to execute idle processing, so restart the last idle timer // We're going to execute idle processing, so restart the last idle timer
_lastTimeUpdated.start(); _lastTimeUpdated.start();
@ -2694,14 +2691,7 @@ void Application::idle(uint64_t now) {
Stats::getInstance()->updateStats(); Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update(); AvatarInputs::getInstance()->update();
{ _simCounter.increment();
static uint64_t lastIdleStart{ now };
uint64_t idleStartToStartDuration = now - lastIdleStart;
if (idleStartToStartDuration != 0) {
_simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration);
}
lastIdleStart = now;
}
PerformanceTimer perfTimer("idle"); PerformanceTimer perfTimer("idle");
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
@ -2757,30 +2747,6 @@ void Application::idle(uint64_t now) {
emit checkBackgroundDownloads(); emit checkBackgroundDownloads();
} }
float Application::getAverageSimsPerSecond() {
uint64_t now = usecTimestampNow();
if (now - _lastSimsPerSecondUpdate > USECS_PER_SECOND) {
_simsPerSecondReport = _simsPerSecond.getAverage();
_lastSimsPerSecondUpdate = now;
}
return _simsPerSecondReport;
}
void Application::setAvatarSimrateSample(float sample) {
_avatarSimsPerSecond.updateAverage(sample);
}
float Application::getAvatarSimrate() {
uint64_t now = usecTimestampNow();
if (now - _lastAvatarSimsPerSecondUpdate > USECS_PER_SECOND) {
_avatarSimsPerSecondReport = _avatarSimsPerSecond.getAverage();
_lastAvatarSimsPerSecondUpdate = now;
}
return _avatarSimsPerSecondReport;
}
void Application::setLowVelocityFilter(bool lowVelocityFilter) { void Application::setLowVelocityFilter(bool lowVelocityFilter) {
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter); controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
} }
@ -3051,7 +3017,7 @@ void Application::updateLOD() const {
PerformanceTimer perfTimer("LOD"); PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!isThrottleRendering()) { if (!isThrottleRendering()) {
DependencyManager::get<LODManager>()->autoAdjustLOD(_fps); DependencyManager::get<LODManager>()->autoAdjustLOD(_frameCounter.rate());
} else { } else {
DependencyManager::get<LODManager>()->resetLODAdjust(); DependencyManager::get<LODManager>()->resetLODAdjust();
} }
@ -3482,17 +3448,13 @@ void Application::update(float deltaTime) {
// AvatarManager update // AvatarManager update
{ {
PerformanceTimer perfTimer("AvatarManger"); PerformanceTimer perfTimer("AvatarManger");
_avatarSimCounter.increment();
qApp->setAvatarSimrateSample(1.0f / deltaTime);
{ {
PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount()); PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
avatarManager->updateOtherAvatars(deltaTime); avatarManager->updateOtherAvatars(deltaTime);
} }
// update sensorToWorldMatrix for camera and hand controllers
getMyAvatar()->updateSensorToWorldMatrix();
qApp->updateMyAvatarLookAtPosition(); qApp->updateMyAvatarLookAtPosition();
{ {
@ -3805,7 +3767,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
bool Application::isHMDMode() const { bool Application::isHMDMode() const {
return getActiveDisplayPlugin()->isHmd(); return getActiveDisplayPlugin()->isHmd();
} }
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
float Application::getTargetFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); }
QRect Application::getDesirableApplicationGeometry() const { QRect Application::getDesirableApplicationGeometry() const {
QRect applicationGeometry = getWindow()->geometry(); QRect applicationGeometry = getWindow()->geometry();

View file

@ -39,6 +39,7 @@
#include <StDev.h> #include <StDev.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <AbstractUriHandler.h> #include <AbstractUriHandler.h>
#include <shared/RateCounter.h>
#include "avatar/MyAvatar.h" #include "avatar/MyAvatar.h"
#include "Bookmarks.h" #include "Bookmarks.h"
@ -155,10 +156,9 @@ public:
bool isForeground() const { return _isForeground; } bool isForeground() const { return _isForeground; }
uint32_t getFrameCount() const { return _frameCount; } size_t getFrameCount() const { return _frameCount; }
float getFps() const { return _fps; } float getFps() const { return _frameCounter.rate(); }
float getTargetFrameRate(); // frames/second float getTargetFrameRate() const; // frames/second
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
float getFieldOfView() { return _fieldOfView.get(); } float getFieldOfView() { return _fieldOfView.get(); }
void setFieldOfView(float fov); void setFieldOfView(float fov);
@ -216,10 +216,9 @@ public:
const QRect& getMirrorViewRect() const { return _mirrorViewRect; } const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
void updateMyAvatarLookAtPosition(); void updateMyAvatarLookAtPosition();
float getAvatarSimrate();
void setAvatarSimrateSample(float sample);
float getAverageSimsPerSecond(); float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
signals: signals:
void svoImportRequested(const QString& url); void svoImportRequested(const QString& url);
@ -396,12 +395,15 @@ private:
QUndoStack _undoStack; QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface; UndoStackScriptingInterface _undoStackScriptingInterface;
uint32_t _frameCount { 0 };
// Frame Rate Measurement // Frame Rate Measurement
int _frameCount; RateCounter<> _frameCounter;
float _fps; RateCounter<> _avatarSimCounter;
RateCounter<> _simCounter;
QElapsedTimer _timerStart; QElapsedTimer _timerStart;
QElapsedTimer _lastTimeUpdated; QElapsedTimer _lastTimeUpdated;
float _lastInstantaneousFps { 0.0f };
ShapeManager _shapeManager; ShapeManager _shapeManager;
PhysicalEntitySimulation _entitySimulation; PhysicalEntitySimulation _entitySimulation;
@ -488,12 +490,6 @@ private:
EntityItemID _keyboardFocusedItem; EntityItemID _keyboardFocusedItem;
quint64 _lastAcceptedKeyPress = 0; quint64 _lastAcceptedKeyPress = 0;
SimpleMovingAverage _framesPerSecond{10};
quint64 _lastFramesPerSecondUpdate = 0;
SimpleMovingAverage _simsPerSecond{10};
int _simsPerSecondReport = 0;
quint64 _lastSimsPerSecondUpdate = 0;
bool _isForeground = true; // starts out assumed to be in foreground bool _isForeground = true; // starts out assumed to be in foreground
bool _inPaint = false; bool _inPaint = false;
bool _isGLInitialized { false }; bool _isGLInitialized { false };

View file

@ -12,7 +12,6 @@
#include "FileLogger.h" #include "FileLogger.h"
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QtCore/QFile>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtGui/QDesktopServices> #include <QtGui/QDesktopServices>
@ -25,11 +24,14 @@
static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
static const QString LOGS_DIRECTORY = "Logs"; static const QString LOGS_DIRECTORY = "Logs";
// Max log size is 1 MB // Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively
static const qint64 MAX_LOG_SIZE = 1024 * 1024; // small so it doesn't go over the 2MB zipped limit for all of the files we send.
static const qint64 MAX_LOG_SIZE = 512 * 1024;
// Max log age is 1 hour // Max log age is 1 hour
static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600; static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600;
static FilePersistThread* _persistThreadInstance;
QString getLogRollerFilename() { QString getLogRollerFilename() {
QString result = FileUtils::standardPath(LOGS_DIRECTORY); QString result = FileUtils::standardPath(LOGS_DIRECTORY);
QHostAddress clientAddress = getGuessedLocalAddress(); QHostAddress clientAddress = getGuessedLocalAddress();
@ -43,51 +45,53 @@ const QString& getLogFilename() {
return fileName; return fileName;
} }
FilePersistThread::FilePersistThread(const FileLogger& logger) : _logger(logger) {
setObjectName("LogFileWriter");
class FilePersistThread : public GenericQueueThread < QString > { // A file may exist from a previous run - if it does, roll the file and suppress notifying listeners.
public: QFile file(_logger._fileName);
FilePersistThread(const FileLogger& logger) : _logger(logger) { if (file.exists()) {
setObjectName("LogFileWriter"); rollFileIfNecessary(file, false);
} }
_lastRollTime = usecTimestampNow();
}
protected: void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled) {
void rollFileIfNecessary(QFile& file) { uint64_t now = usecTimestampNow();
uint64_t now = usecTimestampNow(); if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) {
if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) { QString newFileName = getLogRollerFilename();
QString newFileName = getLogRollerFilename(); if (file.copy(newFileName)) {
if (file.copy(newFileName)) { file.open(QIODevice::WriteOnly | QIODevice::Truncate);
_lastRollTime = now; file.close();
file.open(QIODevice::WriteOnly | QIODevice::Truncate); qDebug() << "Rolled log file:" << newFileName;
file.close();
qDebug() << "Rolled log file:" << newFileName; if (notifyListenersIfRolled) {
emit rollingLogFile(newFileName);
} }
_lastRollTime = now;
} }
} }
}
virtual bool processQueueItems(const Queue& messages) { bool FilePersistThread::processQueueItems(const Queue& messages) {
QFile file(_logger._fileName); QFile file(_logger._fileName);
rollFileIfNecessary(file); rollFileIfNecessary(file);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file); QTextStream out(&file);
foreach(const QString& message, messages) { foreach(const QString& message, messages) {
out << message; out << message;
}
} }
return true;
} }
private: return true;
const FileLogger& _logger; }
uint64_t _lastRollTime = 0;
};
static FilePersistThread* _persistThreadInstance;
FileLogger::FileLogger(QObject* parent) : FileLogger::FileLogger(QObject* parent) :
AbstractLoggerInterface(parent), _fileName(getLogFilename()) AbstractLoggerInterface(parent), _fileName(getLogFilename())
{ {
_persistThreadInstance = new FilePersistThread(*this); _persistThreadInstance = new FilePersistThread(*this);
_persistThreadInstance->initialize(true, QThread::LowestPriority); _persistThreadInstance->initialize(true, QThread::LowestPriority);
connect(_persistThreadInstance, &FilePersistThread::rollingLogFile, this, &FileLogger::rollingLogFile);
} }
FileLogger::~FileLogger() { FileLogger::~FileLogger() {

View file

@ -15,6 +15,8 @@
#include "AbstractLoggerInterface.h" #include "AbstractLoggerInterface.h"
#include <GenericQueueThread.h> #include <GenericQueueThread.h>
#include <QtCore/QFile>
class FileLogger : public AbstractLoggerInterface { class FileLogger : public AbstractLoggerInterface {
Q_OBJECT Q_OBJECT
@ -27,11 +29,32 @@ public:
virtual QString getLogData() override; virtual QString getLogData() override;
virtual void locateLog() override; virtual void locateLog() override;
signals:
void rollingLogFile(QString newFilename);
private: private:
const QString _fileName; const QString _fileName;
friend class FilePersistThread; friend class FilePersistThread;
}; };
class FilePersistThread : public GenericQueueThread < QString > {
Q_OBJECT
public:
FilePersistThread(const FileLogger& logger);
signals:
void rollingLogFile(QString newFilename);
protected:
void rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled = true);
virtual bool processQueueItems(const Queue& messages);
private:
const FileLogger& _logger;
uint64_t _lastRollTime;
};
#endif // hifi_FileLogger_h #endif // hifi_FileLogger_h

View file

@ -294,20 +294,18 @@ void MyAvatar::update(float deltaTime) {
auto audio = DependencyManager::get<AudioClient>(); auto audio = DependencyManager::get<AudioClient>();
head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
simulate(deltaTime); simulate(deltaTime);
currentEnergy += energyChargeRate; currentEnergy += energyChargeRate;
currentEnergy -= getAccelerationEnergy(); currentEnergy -= getAccelerationEnergy();
currentEnergy -= getAudioEnergy(); currentEnergy -= getAudioEnergy();
if(didTeleport()) { if(didTeleport()) {
currentEnergy = 0.0f; currentEnergy = 0.0f;
} }
currentEnergy = max(0.0f, min(currentEnergy,1.0f)); currentEnergy = max(0.0f, min(currentEnergy,1.0f));
emit energyChanged(currentEnergy); emit energyChanged(currentEnergy);
} }
extern QByteArray avatarStateToFrame(const AvatarData* _avatar); extern QByteArray avatarStateToFrame(const AvatarData* _avatar);
@ -333,6 +331,10 @@ void MyAvatar::simulate(float deltaTime) {
updatePosition(deltaTime); updatePosition(deltaTime);
} }
// update sensorToWorldMatrix for camera and hand controllers
// before we perform rig animations and IK.
updateSensorToWorldMatrix();
{ {
PerformanceTimer perfTimer("skeleton"); PerformanceTimer perfTimer("skeleton");
_skeletonModel->simulate(deltaTime); _skeletonModel->simulate(deltaTime);
@ -1975,9 +1977,14 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
} }
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees auto cameraMode = qApp->getCamera()->getMode();
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; return false;
} else {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
} }
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {

View file

@ -44,7 +44,7 @@ int main(int argc, const char* argv[]) {
MiniDmpSender mpSender { BUG_SPLAT_DATABASE, BUG_SPLAT_APPLICATION_NAME, qPrintable(BuildInfo::VERSION), MiniDmpSender mpSender { BUG_SPLAT_DATABASE, BUG_SPLAT_APPLICATION_NAME, qPrintable(BuildInfo::VERSION),
nullptr, BUG_SPLAT_FLAGS }; nullptr, BUG_SPLAT_FLAGS };
#endif #endif
QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME"); QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
bool instanceMightBeRunning = true; bool instanceMightBeRunning = true;
@ -105,13 +105,14 @@ int main(int argc, const char* argv[]) {
// This is done separately from the main Application so that start-up and shut-down logic within the main Application is // This is done separately from the main Application so that start-up and shut-down logic within the main Application is
// not made more complicated than it already is. // not made more complicated than it already is.
bool override = false; bool override = false;
QString glVersion; QJsonObject glData;
{ {
OpenGLVersionChecker openGLVersionChecker(argc, const_cast<char**>(argv)); OpenGLVersionChecker openGLVersionChecker(argc, const_cast<char**>(argv));
bool valid = true; bool valid = true;
glVersion = openGLVersionChecker.checkVersion(valid, override); glData = openGLVersionChecker.checkVersion(valid, override);
if (!valid) { if (!valid) {
if (override) { if (override) {
auto glVersion = glData["version"].toString();
qCDebug(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str()); qCDebug(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
} else { } else {
qCDebug(interfaceapp, "Early exit due to OpenGL version."); qCDebug(interfaceapp, "Early exit due to OpenGL version.");
@ -148,12 +149,12 @@ int main(int argc, const char* argv[]) {
if (override) { if (override) {
auto& accountManager = AccountManager::getInstance(); auto& accountManager = AccountManager::getInstance();
if (accountManager.isLoggedIn()) { if (accountManager.isLoggedIn()) {
UserActivityLogger::getInstance().insufficientGLVersion(glVersion); UserActivityLogger::getInstance().insufficientGLVersion(glData);
} else { } else {
QObject::connect(&AccountManager::getInstance(), &AccountManager::loginComplete, [glVersion](){ QObject::connect(&AccountManager::getInstance(), &AccountManager::loginComplete, [glData](){
static bool loggedInsufficientGL = false; static bool loggedInsufficientGL = false;
if (!loggedInsufficientGL) { if (!loggedInsufficientGL) {
UserActivityLogger::getInstance().insufficientGLVersion(glVersion); UserActivityLogger::getInstance().insufficientGLVersion(glData);
loggedInsufficientGL = true; loggedInsufficientGL = true;
} }
}); });
@ -177,8 +178,18 @@ int main(int argc, const char* argv[]) {
}); });
// BugSplat WILL NOT work with file paths that do not use OS native separators. // BugSplat WILL NOT work with file paths that do not use OS native separators.
auto logPath = QDir::toNativeSeparators(app.getLogger()->getFilename()); auto logger = app.getLogger();
auto logPath = QDir::toNativeSeparators(logger->getFilename());
mpSender.sendAdditionalFile(qPrintable(logPath)); mpSender.sendAdditionalFile(qPrintable(logPath));
QMetaObject::Connection connection;
connection = QObject::connect(logger, &FileLogger::rollingLogFile, &app, [&mpSender, &connection](QString newFilename) {
// We only want to add the first rolled log file (the "beginning" of the log) to BugSplat to ensure we don't exceed the 2MB
// zipped limit, so we disconnect here.
QObject::disconnect(connection);
auto rolledLogPath = QDir::toNativeSeparators(newFilename);
mpSender.sendAdditionalFile(qPrintable(rolledLogPath));
});
#endif #endif
printSystemInformation(); printSystemInformation();

View file

@ -117,11 +117,15 @@ void Stats::updateStats(bool force) {
// we need to take one avatar out so we don't include ourselves // we need to take one avatar out so we don't include ourselves
STAT_UPDATE(avatarCount, avatarManager->size() - 1); STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE(renderrate, (int)qApp->getFps()); STAT_UPDATE(renderrate, qApp->getFps());
if (qApp->getActiveDisplayPlugin()) { if (qApp->getActiveDisplayPlugin()) {
STAT_UPDATE(presentrate, (int)round(qApp->getActiveDisplayPlugin()->presentRate())); STAT_UPDATE(presentrate, qApp->getActiveDisplayPlugin()->presentRate());
STAT_UPDATE(presentnewrate, qApp->getActiveDisplayPlugin()->newFramePresentRate());
STAT_UPDATE(presentdroprate, qApp->getActiveDisplayPlugin()->droppedFrameRate());
} else { } else {
STAT_UPDATE(presentrate, -1); STAT_UPDATE(presentrate, -1);
STAT_UPDATE(presentnewrate, -1);
STAT_UPDATE(presentdroprate, -1);
} }
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate()); STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());

View file

@ -32,8 +32,10 @@ class Stats : public QQuickItem {
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream) Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, serverCount, 0)
STATS_PROPERTY(int, renderrate, 0) STATS_PROPERTY(float, renderrate, 0)
STATS_PROPERTY(int, presentrate, 0) STATS_PROPERTY(float, presentrate, 0)
STATS_PROPERTY(float, presentnewrate, 0)
STATS_PROPERTY(float, presentdroprate, 0)
STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, simrate, 0)
STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarSimrate, 0)
STATS_PROPERTY(int, avatarCount, 0) STATS_PROPERTY(int, avatarCount, 0)
@ -117,6 +119,8 @@ signals:
void serverCountChanged(); void serverCountChanged();
void renderrateChanged(); void renderrateChanged();
void presentrateChanged(); void presentrateChanged();
void presentnewrateChanged();
void presentdroprateChanged();
void simrateChanged(); void simrateChanged();
void avatarSimrateChanged(); void avatarSimrateChanged();
void avatarCountChanged(); void avatarCountChanged();

View file

@ -30,8 +30,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
} }
}, true, false); }, true, false);
updateFramerate();
return Parent::internalActivate(); return Parent::internalActivate();
} }

View file

@ -20,7 +20,7 @@ class Basic2DWindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
public: public:
virtual const QString& getName() const override { return NAME; } virtual const QString& getName() const override { return NAME; }
virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; } virtual float getTargetFrameRate() const override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }
virtual bool internalActivate() override; virtual bool internalActivate() override;

View file

@ -30,6 +30,7 @@
#include <CursorManager.h> #include <CursorManager.h>
#include "CompositorHelper.h" #include "CompositorHelper.h"
#if THREADED_PRESENT #if THREADED_PRESENT
// FIXME, for display plugins that don't block on something like vsync, just // FIXME, for display plugins that don't block on something like vsync, just
@ -415,25 +416,18 @@ void OpenGLDisplayPlugin::updateTextures() {
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) { if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
#endif #endif
updateFrameData(); updateFrameData();
} _newFrameRate.increment();
}
_overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture); _overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
} }
void OpenGLDisplayPlugin::updateFrameData() { void OpenGLDisplayPlugin::updateFrameData() {
Lock lock(_mutex); Lock lock(_mutex);
auto previousFrameIndex = _currentRenderFrameIndex;
_currentRenderFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; _currentRenderFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
} auto skippedCount = (_currentRenderFrameIndex - previousFrameIndex) - 1;
_droppedFrameRate.increment(skippedCount);
void OpenGLDisplayPlugin::updateFramerate() {
uint64_t now = usecTimestampNow();
static uint64_t lastSwapEnd { now };
uint64_t diff = now - lastSwapEnd;
lastSwapEnd = now;
if (diff != 0) {
Lock lock(_mutex);
_usecsPerFrame.updateAverage(diff);
}
} }
void OpenGLDisplayPlugin::compositeOverlay() { void OpenGLDisplayPlugin::compositeOverlay() {
@ -545,19 +539,20 @@ void OpenGLDisplayPlugin::present() {
compositeLayers(); compositeLayers();
// Take the composite framebuffer and send it to the output device // Take the composite framebuffer and send it to the output device
internalPresent(); internalPresent();
updateFramerate(); _presentRate.increment();
} }
} }
float OpenGLDisplayPlugin::presentRate() { float OpenGLDisplayPlugin::newFramePresentRate() const {
float result { -1.0f }; return _newFrameRate.rate();
{ }
Lock lock(_mutex);
result = _usecsPerFrame.getAverage(); float OpenGLDisplayPlugin::droppedFrameRate() const {
} return _droppedFrameRate.rate();
result = 1.0f / result; }
result *= USECS_PER_SECOND;
return result; float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate();
} }
void OpenGLDisplayPlugin::drawUnitQuad() { void OpenGLDisplayPlugin::drawUnitQuad() {

View file

@ -10,6 +10,7 @@
#include "DisplayPlugin.h" #include "DisplayPlugin.h"
#include <condition_variable> #include <condition_variable>
#include <memory>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtGui/QImage> #include <QtGui/QImage>
@ -18,6 +19,7 @@
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h> #include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
#include <shared/RateCounter.h>
#define THREADED_PRESENT 1 #define THREADED_PRESENT 1
@ -41,7 +43,6 @@ public:
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
float presentRate() override;
glm::uvec2 getRecommendedRenderSize() const override { glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels(); return getSurfacePixels();
@ -53,6 +54,12 @@ public:
QImage getScreenshot() const override; QImage getScreenshot() const override;
float presentRate() const override;
float newFramePresentRate() const override;
float droppedFrameRate() const override;
protected: protected:
#if THREADED_PRESENT #if THREADED_PRESENT
friend class PresentThread; friend class PresentThread;
@ -88,7 +95,6 @@ protected:
void present(); void present();
void updateTextures(); void updateTextures();
void updateFramerate();
void drawUnitQuad(); void drawUnitQuad();
void swapBuffers(); void swapBuffers();
void eyeViewport(Eye eye) const; void eyeViewport(Eye eye) const;
@ -101,7 +107,9 @@ protected:
ShapeWrapperPtr _plane; ShapeWrapperPtr _plane;
mutable Mutex _mutex; mutable Mutex _mutex;
SimpleMovingAverage _usecsPerFrame { 10 }; RateCounter<> _droppedFrameRate;
RateCounter<> _newFrameRate;
RateCounter<> _presentRate;
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap; QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap;
uint32_t _currentRenderFrameIndex { 0 }; uint32_t _currentRenderFrameIndex { 0 };

View file

@ -90,6 +90,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
} }
if (!entity->isParentIDValid()) { if (!entity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(entity); _missingParent.append(entity);
} }
@ -256,10 +257,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
bool success; bool success;
AACube queryCube = childEntity->getQueryAACube(success); AACube queryCube = childEntity->getQueryAACube(success);
if (!success) { if (!success) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity); _missingParent.append(childEntity);
continue; continue;
} }
if (!childEntity->isParentIDValid()) { if (!childEntity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity); _missingParent.append(childEntity);
} }
@ -346,6 +349,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (result->getAncestorMissing()) { if (result->getAncestorMissing()) {
// we added the entity, but didn't know about all its ancestors, so it went into the wrong place. // we added the entity, but didn't know about all its ancestors, so it went into the wrong place.
// add it to a list of entities needing to be fixed once their parents are known. // add it to a list of entities needing to be fixed once their parents are known.
QWriteLocker locker(&_missingParentLock);
_missingParent.append(result); _missingParent.append(result);
} }
@ -1008,10 +1012,27 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
void EntityTree::fixupMissingParents() { void EntityTree::fixupMissingParents() {
MovingEntitiesOperator moveOperator(getThisPointer()); MovingEntitiesOperator moveOperator(getThisPointer());
QMutableVectorIterator<EntityItemWeakPointer> iter(_missingParent); QList<EntityItemPointer> missingParents;
while (iter.hasNext()) { {
EntityItemWeakPointer entityWP = iter.next(); QWriteLocker locker(&_missingParentLock);
EntityItemPointer entity = entityWP.lock(); QMutableVectorIterator<EntityItemWeakPointer> iter(_missingParent);
while (iter.hasNext()) {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
if (entity) {
if (entity->isParentIDValid()) {
iter.remove();
} else {
missingParents.append(entity);
}
} else {
// entity was deleted before we found its parent.
iter.remove();
}
}
}
for (EntityItemPointer entity : missingParents) {
if (entity) { if (entity) {
bool queryAACubeSuccess; bool queryAACubeSuccess;
AACube newCube = entity->getQueryAACube(queryAACubeSuccess); AACube newCube = entity->getQueryAACube(queryAACubeSuccess);
@ -1040,12 +1061,8 @@ void EntityTree::fixupMissingParents() {
if (queryAACubeSuccess && doMove) { if (queryAACubeSuccess && doMove) {
moveOperator.addEntityToMoveList(entity, newCube); moveOperator.addEntityToMoveList(entity, newCube);
iter.remove();
entity->markAncestorMissing(false); entity->markAncestorMissing(false);
} }
} else {
// entity was deleted before we found its parent.
iter.remove();
} }
} }

View file

@ -324,6 +324,8 @@ protected:
void fixupMissingParents(); // try to hook members of _missingParent to parent instances void fixupMissingParents(); // try to hook members of _missingParent to parent instances
QVector<EntityItemWeakPointer> _missingParent; // entites with a parentID but no (yet) known parent instance QVector<EntityItemWeakPointer> _missingParent; // entites with a parentID but no (yet) known parent instance
mutable QReadWriteLock _missingParentLock;
// we maintain a list of avatarIDs to notice when an entity is a child of one. // we maintain a list of avatarIDs to notice when an entity is a child of one.
QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server
QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars

View file

@ -4,6 +4,7 @@
#include <QtGui/QSurfaceFormat> #include <QtGui/QSurfaceFormat>
#include <QtOpenGL/QGL> #include <QtOpenGL/QGL>
#include <QOpenGLContext>
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
static QSurfaceFormat format; static QSurfaceFormat format;
@ -37,3 +38,21 @@ const QGLFormat& getDefaultGLFormat() {
}); });
return glFormat; return glFormat;
} }
QJsonObject getGLContextData() {
if (!QOpenGLContext::currentContext()) {
return QJsonObject();
}
QString glVersion = QString((const char*)glGetString(GL_VERSION));
QString glslVersion = QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
QString glVendor = QString((const char*) glGetString(GL_VENDOR));
QString glRenderer = QString((const char*)glGetString(GL_RENDERER));
return QJsonObject {
{ "version", glVersion },
{ "slVersion", glslVersion },
{ "vendor", glVendor },
{ "renderer", glRenderer },
};
}

View file

@ -10,6 +10,8 @@
#ifndef hifi_GLHelpers_h #ifndef hifi_GLHelpers_h
#define hifi_GLHelpers_h #define hifi_GLHelpers_h
#include <QJsonObject>
// 16 bits of depth precision // 16 bits of depth precision
#define DEFAULT_GL_DEPTH_BUFFER_BITS 16 #define DEFAULT_GL_DEPTH_BUFFER_BITS 16
// 8 bits of stencil buffer (typically you really only need 1 bit for functionality // 8 bits of stencil buffer (typically you really only need 1 bit for functionality
@ -24,4 +26,6 @@ void setGLFormatVersion(F& format, int major = 4, int minor = 5) { format.setVer
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat(); const QSurfaceFormat& getDefaultOpenGLSurfaceFormat();
const QGLFormat& getDefaultGLFormat(); const QGLFormat& getDefaultGLFormat();
QJsonObject getGLContextData();
#endif #endif

View file

@ -27,6 +27,7 @@
#include "OffscreenGLCanvas.h" #include "OffscreenGLCanvas.h"
#include "GLEscrow.h" #include "GLEscrow.h"
#include "GLHelpers.h"
// Time between receiving a request to render the offscreen UI actually triggering // Time between receiving a request to render the offscreen UI actually triggering
@ -222,6 +223,11 @@ void OffscreenQmlRenderThread::init() {
return; return;
} }
// Expose GL data to QML
auto glData = getGLContextData();
auto setGL = [=]{ _surface->getRootContext()->setContextProperty("GL", glData); };
_surface->executeOnUiThread(setGL);
_renderControl->initialize(_canvas.getContext()); _renderControl->initialize(_canvas.getContext());
setupFbo(); setupFbo();
_escrow.setRecycler([this](GLuint texture){ _escrow.setRecycler([this](GLuint texture){

View file

@ -13,16 +13,18 @@
#include <QMessageBox> #include <QMessageBox>
#include <QRegularExpression> #include <QRegularExpression>
#include <QJsonObject>
#include "Config.h" #include "Config.h"
#include "GLWidget.h" #include "GLWidget.h"
#include "GLHelpers.h"
OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) :
QApplication(argc, argv) QApplication(argc, argv)
{ {
} }
QString OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
valid = true; valid = true;
override = false; override = false;
@ -38,12 +40,12 @@ QString OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
messageBox.setStandardButtons(QMessageBox::Ok); messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.setDefaultButton(QMessageBox::Ok); messageBox.setDefaultButton(QMessageBox::Ok);
messageBox.exec(); messageBox.exec();
return QString(); return QJsonObject();
} }
// Retrieve OpenGL version // Retrieve OpenGL version
glWidget->initializeGL(); glWidget->initializeGL();
QString glVersion = QString((const char*)glGetString(GL_VERSION)); QJsonObject glData = getGLContextData();
delete glWidget; delete glWidget;
// Compare against minimum // Compare against minimum
@ -51,6 +53,8 @@ QString OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
// - major_number.minor_number // - major_number.minor_number
// - major_number.minor_number.release_number // - major_number.minor_number.release_number
// Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml
const QString version { "version" };
QString glVersion = glData[version].toString();
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
int majorNumber = versionParts[0].toInt(); int majorNumber = versionParts[0].toInt();
int minorNumber = versionParts[1].toInt(); int minorNumber = versionParts[1].toInt();
@ -72,5 +76,5 @@ QString OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
override = messageBox.exec() == QMessageBox::Ignore; override = messageBox.exec() == QMessageBox::Ignore;
} }
return glVersion; return glData;
} }

View file

@ -19,7 +19,7 @@ class OpenGLVersionChecker : public QApplication {
public: public:
OpenGLVersionChecker(int& argc, char** argv); OpenGLVersionChecker(int& argc, char** argv);
static QString checkVersion(bool& valid, bool& override); static QJsonObject checkVersion(bool& valid, bool& override);
}; };
#endif // hifi_OpenGLVersionChecker_h #endif // hifi_OpenGLVersionChecker_h

View file

@ -114,6 +114,8 @@ std::atomic<Buffer::Size> Context::_bufferGPUMemoryUsage{ 0 };
std::atomic<uint32_t> Context::_textureGPUCount{ 0 }; std::atomic<uint32_t> Context::_textureGPUCount{ 0 };
std::atomic<Texture::Size> Context::_textureGPUMemoryUsage{ 0 }; std::atomic<Texture::Size> Context::_textureGPUMemoryUsage{ 0 };
std::atomic<Texture::Size> Context::_textureGPUVirtualMemoryUsage{ 0 };
std::atomic<uint32_t> Context::_textureGPUTransferCount{ 0 };
void Context::incrementBufferGPUCount() { void Context::incrementBufferGPUCount() {
_bufferGPUCount++; _bufferGPUCount++;
@ -149,6 +151,24 @@ void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSiz
} }
} }
void Context::updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
}
if (newObjectSize > prevObjectSize) {
_textureGPUVirtualMemoryUsage.fetch_add(newObjectSize - prevObjectSize);
} else {
_textureGPUVirtualMemoryUsage.fetch_sub(prevObjectSize - newObjectSize);
}
}
void Context::incrementTextureGPUTransferCount() {
_textureGPUTransferCount++;
}
void Context::decrementTextureGPUTransferCount() {
_textureGPUTransferCount--;
}
uint32_t Context::getBufferGPUCount() { uint32_t Context::getBufferGPUCount() {
return _bufferGPUCount.load(); return _bufferGPUCount.load();
} }
@ -165,10 +185,20 @@ Context::Size Context::getTextureGPUMemoryUsage() {
return _textureGPUMemoryUsage.load(); return _textureGPUMemoryUsage.load();
} }
Context::Size Context::getTextureGPUVirtualMemoryUsage() {
return _textureGPUVirtualMemoryUsage.load();
}
uint32_t Context::getTextureGPUTransferCount() {
return _textureGPUTransferCount.load();
}
void Backend::incrementBufferGPUCount() { Context::incrementBufferGPUCount(); } void Backend::incrementBufferGPUCount() { Context::incrementBufferGPUCount(); }
void Backend::decrementBufferGPUCount() { Context::decrementBufferGPUCount(); } void Backend::decrementBufferGPUCount() { Context::decrementBufferGPUCount(); }
void Backend::updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateBufferGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateBufferGPUMemoryUsage(prevObjectSize, newObjectSize); }
void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); } void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); }
void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); } void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); }
void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); } void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); }
void Backend::updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUVirtualMemoryUsage(prevObjectSize, newObjectSize); }
void Backend::incrementTextureGPUTransferCount() { Context::incrementTextureGPUTransferCount(); }
void Backend::decrementTextureGPUTransferCount() { Context::decrementTextureGPUTransferCount(); }

View file

@ -34,6 +34,7 @@ public:
int _ISNumIndexBufferChanges = 0; int _ISNumIndexBufferChanges = 0;
int _RSNumTextureBounded = 0; int _RSNumTextureBounded = 0;
int _RSAmountTextureMemoryBounded = 0;
int _DSNumAPIDrawcalls = 0; int _DSNumAPIDrawcalls = 0;
int _DSNumDrawcalls = 0; int _DSNumDrawcalls = 0;
@ -128,6 +129,9 @@ public:
static void incrementTextureGPUCount(); static void incrementTextureGPUCount();
static void decrementTextureGPUCount(); static void decrementTextureGPUCount();
static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize); static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize);
static void updateTextureGPUVirtualMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize);
static void incrementTextureGPUTransferCount();
static void decrementTextureGPUTransferCount();
protected: protected:
StereoState _stereo; StereoState _stereo;
@ -177,6 +181,8 @@ public:
static uint32_t getTextureGPUCount(); static uint32_t getTextureGPUCount();
static Size getTextureGPUMemoryUsage(); static Size getTextureGPUMemoryUsage();
static Size getTextureGPUVirtualMemoryUsage();
static uint32_t getTextureGPUTransferCount();
protected: protected:
Context(const Context& context); Context(const Context& context);
@ -202,6 +208,9 @@ protected:
static void incrementTextureGPUCount(); static void incrementTextureGPUCount();
static void decrementTextureGPUCount(); static void decrementTextureGPUCount();
static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize);
static void incrementTextureGPUTransferCount();
static void decrementTextureGPUTransferCount();
// Buffer and Texture Counters // Buffer and Texture Counters
static std::atomic<uint32_t> _bufferGPUCount; static std::atomic<uint32_t> _bufferGPUCount;
@ -209,6 +218,9 @@ protected:
static std::atomic<uint32_t> _textureGPUCount; static std::atomic<uint32_t> _textureGPUCount;
static std::atomic<Size> _textureGPUMemoryUsage; static std::atomic<Size> _textureGPUMemoryUsage;
static std::atomic<Size> _textureGPUVirtualMemoryUsage;
static std::atomic<uint32_t> _textureGPUTransferCount;
friend class Backend; friend class Backend;
}; };

View file

@ -192,6 +192,25 @@ enum Semantic {
SRGBA, SRGBA,
SBGRA, SBGRA,
// These are generic compression format smeantic for images
_FIRST_COMPRESSED,
COMPRESSED_R,
COMPRESSED_RGB,
COMPRESSED_RGBA,
COMPRESSED_SRGB,
COMPRESSED_SRGBA,
// FIXME: Will have to be supported later:
/*COMPRESSED_BC3_RGBA, // RGBA_S3TC_DXT5_EXT,
COMPRESSED_BC3_SRGBA, // SRGB_ALPHA_S3TC_DXT5_EXT
COMPRESSED_BC7_RGBA,
COMPRESSED_BC7_SRGBA, */
_LAST_COMPRESSED,
R11G11B10, R11G11B10,
UNIFORM, UNIFORM,
@ -224,6 +243,7 @@ public:
Dimension getDimension() const { return (Dimension)_dimension; } Dimension getDimension() const { return (Dimension)_dimension; }
bool isCompressed() const { return uint8(getSemantic() - _FIRST_COMPRESSED) <= uint8(_LAST_COMPRESSED - _FIRST_COMPRESSED); }
Type getType() const { return (Type)_type; } Type getType() const { return (Type)_type; }
bool isNormalized() const { return (getType() >= NORMALIZED_START); } bool isNormalized() const { return (getType() >= NORMALIZED_START); }

View file

@ -76,15 +76,22 @@ public:
class GLTexture : public GPUObject { class GLTexture : public GPUObject {
public: public:
// The public gl texture object
GLuint _texture{ 0 };
const Stamp _storageStamp; const Stamp _storageStamp;
Stamp _contentStamp { 0 }; Stamp _contentStamp { 0 };
const GLuint _texture;
const GLenum _target; const GLenum _target;
GLTexture(const gpu::Texture& gpuTexture); GLTexture(const gpu::Texture& gpuTexture);
~GLTexture(); ~GLTexture();
void createTexture();
GLuint size() const { return _size; } GLuint size() const { return _size; }
GLuint virtualSize() const { return _virtualSize; }
void updateSize();
enum SyncState { enum SyncState {
// The texture is currently undergoing no processing, although it's content // The texture is currently undergoing no processing, although it's content
@ -119,16 +126,26 @@ public:
static const size_t CUBE_NUM_FACES = 6; static const size_t CUBE_NUM_FACES = 6;
static const GLenum CUBE_FACE_LAYOUT[6]; static const GLenum CUBE_FACE_LAYOUT[6];
private: private:
// at creation the true texture is created in GL
// it becomes public only when ready.
GLuint _privateTexture{ 0 };
void setSize(GLuint size);
void setVirtualSize(GLuint size);
GLuint _size; // true size as reported by the gl api
GLuint _virtualSize; // theorical size as expected
GLuint _numLevels{ 0 };
void transferMip(GLenum target, const Texture::PixelsPointer& mip) const; void transferMip(GLenum target, const Texture::PixelsPointer& mip) const;
const GLuint _size;
// The owning texture // The owning texture
const Texture& _gpuTexture; const Texture& _gpuTexture;
std::atomic<SyncState> _syncState { SyncState::Idle }; std::atomic<SyncState> _syncState { SyncState::Idle };
}; };
static GLTexture* syncGPUObject(const TexturePointer& texture); static GLTexture* syncGPUObject(const TexturePointer& texture, bool needTransfer = true);
static GLuint getTextureID(const TexturePointer& texture, bool sync = true); static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
// very specific for now // very specific for now

View file

@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
for (auto& b : framebuffer.getRenderBuffers()) { for (auto& b : framebuffer.getRenderBuffers()) {
surface = b._texture; surface = b._texture;
if (surface) { if (surface) {
gltexture = GLBackend::syncGPUObject(surface); gltexture = GLBackend::syncGPUObject(surface, false); // Grab the gltexture and don't transfer
} else { } else {
gltexture = nullptr; gltexture = nullptr;
} }
@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
if (framebuffer.getDepthStamp() != object->_depthStamp) { if (framebuffer.getDepthStamp() != object->_depthStamp) {
auto surface = framebuffer.getDepthStencilBuffer(); auto surface = framebuffer.getDepthStencilBuffer();
if (framebuffer.hasDepthStencil() && surface) { if (framebuffer.hasDepthStencil() && surface) {
gltexture = GLBackend::syncGPUObject(surface); gltexture = GLBackend::syncGPUObject(surface, false); // Grab the gltexture and don't transfer
} }
if (gltexture) { if (gltexture) {

View file

@ -266,6 +266,8 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
_resource._textures[slot] = resourceTexture; _resource._textures[slot] = resourceTexture;
_stats._RSAmountTextureMemoryBounded += object->size();
} else { } else {
releaseResourceTexture(slot); releaseResourceTexture(slot);
return; return;

View file

@ -45,6 +45,20 @@ static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = {
GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE
}; };
class GLTexelFormat {
public:
GLenum internalFormat;
GLenum format;
GLenum type;
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) {
return evalGLTexelFormat(dstFormat, dstFormat);
}
static GLTexelFormat evalGLTexelFormatInternal(const gpu::Element& dstFormat);
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat);
};
// Stupid preprocessor trick to turn the line macro into a string // Stupid preprocessor trick to turn the line macro into a string
#define CHECK_GL_ERROR_HELPER(x) #x #define CHECK_GL_ERROR_HELPER(x) #x
// FIXME doesn't build on Linux or Mac. Hmmmm // FIXME doesn't build on Linux or Mac. Hmmmm

View file

@ -0,0 +1,426 @@
//
// Created by Bradley Austin Davis on 2016/04/03
// Copyright 2013-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
//
#include "GLBackendShared.h"
using namespace gpu;
GLTexelFormat GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE };
return texel;
}
GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) {
if (dstFormat != srcFormat) {
GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE };
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
texel.format = GL_RED;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_R8;
break;
case gpu::COMPRESSED_R:
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
case gpu::DEPTH:
texel.internalFormat = GL_DEPTH_COMPONENT32;
break;
case gpu::DEPTH_STENCIL:
texel.type = GL_UNSIGNED_INT_24_8;
texel.format = GL_DEPTH_STENCIL;
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC2: {
texel.format = GL_RG;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC3: {
texel.format = GL_RGB;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RGB8;
break;
case gpu::COMPRESSED_RGB:
texel.internalFormat = GL_COMPRESSED_RGB;
break;
case gpu::COMPRESSED_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC4: {
texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (srcFormat.getSemantic()) {
case gpu::BGRA:
case gpu::SBGRA:
texel.format = GL_BGRA;
break;
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
case gpu::SRGBA:
default:
break;
};
switch (dstFormat.getSemantic()) {
case gpu::RGB:
texel.internalFormat = GL_RGB8;
break;
case gpu::RGBA:
texel.internalFormat = GL_RGBA8;
break;
case gpu::SRGB:
texel.internalFormat = GL_SRGB8;
break;
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8_ALPHA8;
break;
case gpu::COMPRESSED_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA;
break;
case gpu::COMPRESSED_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
break;
// FIXME: WE will want to support this later
/*
case gpu::COMPRESSED_BC3_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC3_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case gpu::COMPRESSED_BC7_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
*/
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
return texel;
} else {
GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE };
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
texel.format = GL_RED;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::COMPRESSED_R: {
texel.internalFormat = GL_COMPRESSED_RED_RGTC1;
break;
}
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
case gpu::SRGBA:
texel.internalFormat = GL_RED;
switch (dstFormat.getType()) {
case gpu::UINT32: {
texel.internalFormat = GL_R32UI;
break;
}
case gpu::INT32: {
texel.internalFormat = GL_R32I;
break;
}
case gpu::NUINT32: {
texel.internalFormat = GL_R8;
break;
}
case gpu::NINT32: {
texel.internalFormat = GL_R8_SNORM;
break;
}
case gpu::FLOAT: {
texel.internalFormat = GL_R32F;
break;
}
case gpu::UINT16: {
texel.internalFormat = GL_R16UI;
break;
}
case gpu::INT16: {
texel.internalFormat = GL_R16I;
break;
}
case gpu::NUINT16: {
texel.internalFormat = GL_R16;
break;
}
case gpu::NINT16: {
texel.internalFormat = GL_R16_SNORM;
break;
}
case gpu::HALF: {
texel.internalFormat = GL_R16F;
break;
}
case gpu::UINT8: {
texel.internalFormat = GL_R8UI;
break;
}
case gpu::INT8: {
texel.internalFormat = GL_R8I;
break;
}
case gpu::NUINT8: {
if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) {
texel.internalFormat = GL_SLUMINANCE8;
} else {
texel.internalFormat = GL_R8;
}
break;
}
case gpu::NINT8: {
texel.internalFormat = GL_R8_SNORM;
break;
}
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
}
break;
case gpu::R11G11B10:
texel.format = GL_RGB;
// the type should be float
texel.internalFormat = GL_R11F_G11F_B10F;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
texel.internalFormat = GL_DEPTH_COMPONENT32;
switch (dstFormat.getType()) {
case gpu::UINT32:
case gpu::INT32:
case gpu::NUINT32:
case gpu::NINT32: {
texel.internalFormat = GL_DEPTH_COMPONENT32;
break;
}
case gpu::FLOAT: {
texel.internalFormat = GL_DEPTH_COMPONENT32F;
break;
}
case gpu::UINT16:
case gpu::INT16:
case gpu::NUINT16:
case gpu::NINT16:
case gpu::HALF: {
texel.internalFormat = GL_DEPTH_COMPONENT16;
break;
}
case gpu::UINT8:
case gpu::INT8:
case gpu::NUINT8:
case gpu::NINT8: {
texel.internalFormat = GL_DEPTH_COMPONENT24;
break;
}
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
}
break;
case gpu::DEPTH_STENCIL:
texel.type = GL_UNSIGNED_INT_24_8;
texel.format = GL_DEPTH_STENCIL;
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC2: {
texel.format = GL_RG;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC3: {
texel.format = GL_RGB;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RGB8;
break;
case gpu::SRGB:
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGB:
texel.internalFormat = GL_COMPRESSED_RGB;
break;
case gpu::COMPRESSED_SRGB:
texel.internalFormat = GL_COMPRESSED_SRGB;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC4: {
texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
texel.internalFormat = GL_RGB8;
break;
case gpu::RGBA:
texel.internalFormat = GL_RGBA8;
switch (dstFormat.getType()) {
case gpu::UINT32:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA32UI;
break;
case gpu::INT32:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA32I;
break;
case gpu::FLOAT:
texel.internalFormat = GL_RGBA32F;
break;
case gpu::UINT16:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA16UI;
break;
case gpu::INT16:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA16I;
break;
case gpu::NUINT16:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16;
break;
case gpu::NINT16:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16_SNORM;
break;
case gpu::HALF:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16F;
break;
case gpu::UINT8:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA8UI;
break;
case gpu::INT8:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA8I;
break;
case gpu::NUINT8:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA8;
break;
case gpu::NINT8:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA8_SNORM;
break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}
break;
case gpu::SRGB:
texel.format = GL_RGB;
texel.internalFormat = GL_SRGB8;
break;
case gpu::SRGBA:
texel.format = GL_RGBA;
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
break;
case gpu::COMPRESSED_RGBA:
texel.internalFormat = GL_COMPRESSED_RGBA;
break;
case gpu::COMPRESSED_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
return texel;
}
}

View file

@ -13,7 +13,6 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include "GLBackendShared.h" #include "GLBackendShared.h"
#include "GLTexelFormat.h"
#include "GLBackendTextureTransfer.h" #include "GLBackendTextureTransfer.h"
using namespace gpu; using namespace gpu;
@ -49,49 +48,145 @@ const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = {
// Create the texture and allocate storage // Create the texture and allocate storage
GLBackend::GLTexture::GLTexture(const Texture& texture) : GLBackend::GLTexture::GLTexture(const Texture& texture) :
_storageStamp(texture.getStamp()), _texture(allocateSingleTexture()), _storageStamp(texture.getStamp()),
_target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture) _target(gpuToGLTextureType(texture)),
_size(0),
_virtualSize(0),
_numLevels(texture.maxMip() + 1),
_gpuTexture(texture)
{ {
Backend::incrementTextureGPUCount(); Backend::incrementTextureGPUCount();
Backend::updateTextureGPUMemoryUsage(0, _size);
Backend::setGPUObject(texture, this); Backend::setGPUObject(texture, this);
GLsizei width = texture.getWidth();
GLsizei height = texture.getHeight(); // updateSize();
GLsizei levels = 1; GLuint virtualSize = _gpuTexture.evalTotalSize();
if (texture.maxMip() > 0) { setVirtualSize(virtualSize);
if (texture.isAutogenerateMips()) { setSize(virtualSize);
while ((width | height) >> levels) { }
++levels;
void GLBackend::GLTexture::createTexture() {
_privateTexture = allocateSingleTexture();
GLsizei width = _gpuTexture.getWidth();
GLsizei height = _gpuTexture.getHeight();
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat());
GLint boundTex = -1;
switch (_target) {
case GL_TEXTURE_2D:
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
break;
case GL_TEXTURE_CUBE_MAP:
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex);
break;
default:
qFatal("Unsupported texture type");
}
(void)CHECK_GL_ERROR();
glBindTexture(_target, _privateTexture);
(void)CHECK_GL_ERROR();
// Fixme: this usage of TexStorage doesn;t work wtih compressed texture, altuogh it should.
// GO through the process of allocating the correct storage
/* if (GLEW_VERSION_4_2 && !texture.getTexelFormat().isCompressed()) {
glTexStorage2D(_target, _numLevels, texelFormat.internalFormat, width, height);
(void)CHECK_GL_ERROR();
} else*/
{
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _numLevels - 1);
// for (int l = 0; l < _numLevels; l++) {
{ int l = 0;
if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) {
for (size_t face = 0; face < CUBE_NUM_FACES; face++) {
glTexImage2D(CUBE_FACE_LAYOUT[face], l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
} }
} else {
glTexImage2D(_target, l, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, NULL);
} }
levels = std::max(1, std::min(texture.maxMip() + 1, levels)); width = std::max(1, (width / 2));
height = std::max(1, (height / 2));
}
(void)CHECK_GL_ERROR();
} }
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat()); syncSampler(_gpuTexture.getSampler(), _gpuTexture.getType(), this);
withPreservedTexture(_target, [&] { (void)CHECK_GL_ERROR();
glBindTexture(_target, _texture);
(void)CHECK_GL_ERROR();
// GO through the process of allocating the correct storage glBindTexture(_target, boundTex);
if (GLEW_VERSION_4_2) { (void)CHECK_GL_ERROR();
glTexStorage2D(_target, levels, texelFormat.internalFormat, width, height);
} else {
glTexImage2D(_target, 0, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, 0);
}
(void)CHECK_GL_ERROR();
syncSampler(texture.getSampler(), texture.getType(), this);
(void)CHECK_GL_ERROR();
});
} }
GLBackend::GLTexture::~GLTexture() { GLBackend::GLTexture::~GLTexture() {
if (_texture != 0) { if (_privateTexture != 0) {
glDeleteTextures(1, &_texture); glDeleteTextures(1, &_privateTexture);
} }
Backend::updateTextureGPUMemoryUsage(_size, 0); Backend::updateTextureGPUMemoryUsage(_size, 0);
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);
Backend::decrementTextureGPUCount(); Backend::decrementTextureGPUCount();
} }
void GLBackend::GLTexture::setSize(GLuint size) {
Backend::updateTextureGPUMemoryUsage(_size, size);
_size = size;
}
void GLBackend::GLTexture::setVirtualSize(GLuint size) {
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, size);
_virtualSize = size;
}
void GLBackend::GLTexture::updateSize() {
GLuint virtualSize = _gpuTexture.evalTotalSize();
setVirtualSize(virtualSize);
if (!_texture) {
setSize(virtualSize);
}
if (_gpuTexture.getTexelFormat().isCompressed()) {
GLenum proxyType = GL_TEXTURE_2D;
GLuint numFaces = 1;
if (_gpuTexture.getType() == gpu::Texture::TEX_CUBE) {
proxyType = CUBE_FACE_LAYOUT[0];
numFaces = CUBE_NUM_FACES;
}
GLint gpuSize{ 0 };
glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize);
(void)CHECK_GL_ERROR();
if (gpuSize) {
for (GLuint level = 0; level < _numLevels; level++) {
GLint levelSize{ 0 };
glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize);
levelSize *= numFaces;
if (levelSize <= 0) {
break;
}
gpuSize += levelSize;
}
(void)CHECK_GL_ERROR();
setSize(gpuSize);
} else {
setSize(virtualSize);
}
} else {
setSize(virtualSize);
}
}
bool GLBackend::GLTexture::isInvalid() const { bool GLBackend::GLTexture::isInvalid() const {
return _storageStamp < _gpuTexture.getStamp(); return _storageStamp < _gpuTexture.getStamp();
} }
@ -117,44 +212,25 @@ bool GLBackend::GLTexture::isReady() const {
return Idle == syncState; return Idle == syncState;
} }
//#define USE_PBO
// Move content bits from the CPU to the GPU for a given mip / face // Move content bits from the CPU to the GPU for a given mip / face
void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const { void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat()); GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat());
#ifdef USE_PBO glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData());
GLuint pixelBufferID;
glGenBuffers(1, &pixelBufferID);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBufferID);
//if (GLEW_VERSION_4_4) {
// glBufferStorage(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW);
//} else {
glBufferData(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW);
//}
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
memcpy(mappedBuffer, mip->readData(), mip->getSize());
//// use while PBO is still bound, assumes GL_TEXTURE_2D and offset 0
glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, 0);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pixelBufferID);
#else
//glTexImage2D(target, 0, internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes);
glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData());
(void)CHECK_GL_ERROR(); (void)CHECK_GL_ERROR();
#endif
} }
// Move content bits from the CPU to the GPU // Move content bits from the CPU to the GPU
void GLBackend::GLTexture::transfer() const { void GLBackend::GLTexture::transfer() const {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
qDebug() << "Transferring texture: " << _texture; //qDebug() << "Transferring texture: " << _privateTexture;
// Need to update the content of the GPU object from the source sysmem of the texture // Need to update the content of the GPU object from the source sysmem of the texture
if (_contentStamp >= _gpuTexture.getDataStamp()) { if (_contentStamp >= _gpuTexture.getDataStamp()) {
return; return;
} }
glBindTexture(_target, _texture); //_secretTexture
glBindTexture(_target, _privateTexture);
// glBindTexture(_target, _texture);
// GO through the process of allocating the correct storage and/or update the content // GO through the process of allocating the correct storage and/or update the content
switch (_gpuTexture.getType()) { switch (_gpuTexture.getType()) {
case Texture::TEX_2D: case Texture::TEX_2D:
@ -186,6 +262,10 @@ void GLBackend::GLTexture::transfer() const {
// Do any post-transfer operations that might be required on the main context / rendering thread // Do any post-transfer operations that might be required on the main context / rendering thread
void GLBackend::GLTexture::postTransfer() { void GLBackend::GLTexture::postTransfer() {
setSyncState(GLTexture::Idle); setSyncState(GLTexture::Idle);
// The public gltexture becaomes available
_texture = _privateTexture;
// At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory
switch (_gpuTexture.getType()) { switch (_gpuTexture.getType()) {
case Texture::TEX_2D: case Texture::TEX_2D:
@ -204,7 +284,7 @@ void GLBackend::GLTexture::postTransfer() {
} }
} }
GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer, bool needTransfer) {
const Texture& texture = *texturePointer; const Texture& texture = *texturePointer;
if (!texture.isDefined()) { if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking // NO texture definition yet so let's avoid thinking
@ -218,7 +298,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin
} }
// Object isn't ready, check what we need to do... // Object isn't ready, check what we need to do...
// Create the texture if need be (force re-creation if the storage stamp changes // Create the texture if need be (force re-creation if the storage stamp changes
// for easier use of immutable storage) // for easier use of immutable storage)
if (!object || object->isInvalid()) { if (!object || object->isInvalid()) {
@ -226,18 +306,23 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin
object = new GLTexture(texture); object = new GLTexture(texture);
} }
// need to have a gpu object? // Object maybe doens't neet to be tranasferred after creation
if (texture.getNumSlices() != 1) { if (!needTransfer) {
object->createTexture();
object->_contentStamp = texturePointer->getDataStamp();
object->postTransfer();
return object; return object;
} }
// Object might be outdated, if so, start the transfer // Object might be outdated, if so, start the transfer
// (outdated objects that are already in transfer will have reported 'true' for ready() // (outdated objects that are already in transfer will have reported 'true' for ready()
if (object->isOutdated()) { if (object->isOutdated()) {
Backend::incrementTextureGPUTransferCount();
_textureTransferHelper->transferTexture(texturePointer); _textureTransferHelper->transferTexture(texturePointer);
} }
if (GLTexture::Transferred == object->getSyncState()) { if (GLTexture::Transferred == object->getSyncState()) {
Backend::decrementTextureGPUTransferCount();
object->postTransfer(); object->postTransfer();
} }
@ -338,7 +423,8 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
return; return;
} }
GLTexture* object = GLBackend::syncGPUObject(resourceTexture); // DO not transfer the texture, this call is expected for rendering texture
GLTexture* object = GLBackend::syncGPUObject(resourceTexture, false);
if (!object) { if (!object) {
return; return;
} }

View file

@ -9,26 +9,12 @@
#include "GLBackendTextureTransfer.h" #include "GLBackendTextureTransfer.h"
#include "GLBackendShared.h" #include "GLBackendShared.h"
#include "GLTexelFormat.h"
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
#include <gl/OffscreenGLCanvas.h> #include <gl/OffscreenGLCanvas.h>
#include <gl/QOpenGLContextWrapper.h> #include <gl/QOpenGLContextWrapper.h>
//#define FORCE_DRAW_AFTER_TRANSFER
#ifdef FORCE_DRAW_AFTER_TRANSFER
#include <gl/OglplusHelpers.h>
static ProgramPtr _program;
static ProgramPtr _cubeProgram;
static ShapeWrapperPtr _plane;
static ShapeWrapperPtr _skybox;
static BasicFramebufferWrapperPtr _framebuffer;
#endif
#endif #endif
@ -50,30 +36,18 @@ GLTextureTransferHelper::GLTextureTransferHelper() {
void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) {
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer); GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; TextureTransferPackage package{ texturePointer, 0};
glFlush();
object->setSyncState(GLBackend::GLTexture::Pending); object->setSyncState(GLBackend::GLTexture::Pending);
queueItem(package); queueItem(package);
#else #else
object->transfer(); object->transfer();
object->postTransfer(); object->setSyncState(GLBackend::GLTexture::Transferred);
#endif #endif
} }
void GLTextureTransferHelper::setup() { void GLTextureTransferHelper::setup() {
#ifdef THREADED_TEXTURE_TRANSFER #ifdef THREADED_TEXTURE_TRANSFER
_canvas->makeCurrent(); _canvas->makeCurrent();
#ifdef FORCE_DRAW_AFTER_TRANSFER
_program = loadDefaultShader();
_plane = loadPlane(_program);
_cubeProgram = loadCubemapShader();
_skybox = loadSkybox(_cubeProgram);
_framebuffer = std::make_shared<BasicFramebufferWrapper>();
_framebuffer->Init({ 100, 100 });
_framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw);
#endif
#endif #endif
} }
@ -85,8 +59,6 @@ void GLTextureTransferHelper::shutdown() {
bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
for (auto package : messages) { for (auto package : messages) {
glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(package.fence);
TexturePointer texturePointer = package.texture.lock(); TexturePointer texturePointer = package.texture.lock();
// Texture no longer exists, move on to the next // Texture no longer exists, move on to the next
if (!texturePointer) { if (!texturePointer) {
@ -94,37 +66,17 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
} }
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer); GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
object->createTexture();
object->transfer(); object->transfer();
#ifdef FORCE_DRAW_AFTER_TRANSFER object->updateSize();
// Now force a draw using the texture
try {
switch (texturePointer->getType()) {
case Texture::TEX_2D:
_program->Use();
_plane->Use();
_plane->Draw();
break;
case Texture::TEX_CUBE:
_cubeProgram->Use();
_skybox->Use();
_skybox->Draw();
break;
default:
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << texturePointer->getType() << " not supported";
break;
}
} catch (const std::runtime_error& error) {
qWarning() << "Failed to render texture on background thread: " << error.what();
}
#endif
glBindTexture(object->_target, 0); glBindTexture(object->_target, 0);
auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(writeSync); glDeleteSync(writeSync);
object->_contentStamp = texturePointer->getDataStamp(); object->_contentStamp = texturePointer->getDataStamp();
object->setSyncState(GLBackend::GLTexture::Transferred); object->setSyncState(GLBackend::GLTexture::Transferred);
} }

View file

@ -1,375 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/04/03
// Copyright 2013-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
//
#include "GLBackendShared.h"
class GLTexelFormat {
public:
GLenum internalFormat;
GLenum format;
GLenum type;
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) {
return evalGLTexelFormat(dstFormat, dstFormat);
}
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) {
using namespace gpu;
if (dstFormat != srcFormat) {
GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE };
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
texel.format = GL_RED;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_R8;
break;
case gpu::DEPTH:
texel.internalFormat = GL_DEPTH_COMPONENT32;
break;
case gpu::DEPTH_STENCIL:
texel.type = GL_UNSIGNED_INT_24_8;
texel.format = GL_DEPTH_STENCIL;
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC2: {
texel.format = GL_RG;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC3: {
texel.format = GL_RGB;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RGB8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC4: {
texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (srcFormat.getSemantic()) {
case gpu::BGRA:
case gpu::SBGRA:
texel.format = GL_BGRA;
break;
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
case gpu::SRGBA:
default:
break;
};
switch (dstFormat.getSemantic()) {
case gpu::RGB:
texel.internalFormat = GL_RGB8;
break;
case gpu::RGBA:
texel.internalFormat = GL_RGBA8;
break;
case gpu::SRGB:
texel.internalFormat = GL_SRGB8;
break;
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8_ALPHA8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
return texel;
} else {
GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE };
switch (dstFormat.getDimension()) {
case gpu::SCALAR: {
texel.format = GL_RED;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
case gpu::SRGB:
case gpu::SRGBA:
texel.internalFormat = GL_R8;
switch (dstFormat.getType()) {
case gpu::UINT32: {
texel.internalFormat = GL_R32UI;
break;
}
case gpu::INT32: {
texel.internalFormat = GL_R32I;
break;
}
case gpu::NUINT32: {
texel.internalFormat = GL_R8;
break;
}
case gpu::NINT32: {
texel.internalFormat = GL_R8_SNORM;
break;
}
case gpu::FLOAT: {
texel.internalFormat = GL_R32F;
break;
}
case gpu::UINT16: {
texel.internalFormat = GL_R16UI;
break;
}
case gpu::INT16: {
texel.internalFormat = GL_R16I;
break;
}
case gpu::NUINT16: {
texel.internalFormat = GL_R16;
break;
}
case gpu::NINT16: {
texel.internalFormat = GL_R16_SNORM;
break;
}
case gpu::HALF: {
texel.internalFormat = GL_R16F;
break;
}
case gpu::UINT8: {
texel.internalFormat = GL_R8UI;
break;
}
case gpu::INT8: {
texel.internalFormat = GL_R8I;
break;
}
case gpu::NUINT8: {
if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) {
texel.internalFormat = GL_SLUMINANCE8;
} else {
texel.internalFormat = GL_R8;
}
break;
}
case gpu::NINT8: {
texel.internalFormat = GL_R8_SNORM;
break;
}
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
}
break;
case gpu::R11G11B10:
texel.format = GL_RGB;
// the type should be float
texel.internalFormat = GL_R11F_G11F_B10F;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
texel.internalFormat = GL_DEPTH_COMPONENT32;
switch (dstFormat.getType()) {
case gpu::UINT32:
case gpu::INT32:
case gpu::NUINT32:
case gpu::NINT32: {
texel.internalFormat = GL_DEPTH_COMPONENT32;
break;
}
case gpu::FLOAT: {
texel.internalFormat = GL_DEPTH_COMPONENT32F;
break;
}
case gpu::UINT16:
case gpu::INT16:
case gpu::NUINT16:
case gpu::NINT16:
case gpu::HALF: {
texel.internalFormat = GL_DEPTH_COMPONENT16;
break;
}
case gpu::UINT8:
case gpu::INT8:
case gpu::NUINT8:
case gpu::NINT8: {
texel.internalFormat = GL_DEPTH_COMPONENT24;
break;
}
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
}
break;
case gpu::DEPTH_STENCIL:
texel.type = GL_UNSIGNED_INT_24_8;
texel.format = GL_DEPTH_STENCIL;
texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC2: {
texel.format = GL_RG;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RG8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC3: {
texel.format = GL_RGB;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RGB8;
break;
case gpu::SRGB:
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
case gpu::VEC4: {
texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch (dstFormat.getSemantic()) {
case gpu::RGB:
texel.internalFormat = GL_RGB8;
break;
case gpu::RGBA:
texel.internalFormat = GL_RGBA8;
switch (dstFormat.getType()) {
case gpu::UINT32:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA32UI;
break;
case gpu::INT32:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA32I;
break;
case gpu::FLOAT:
texel.internalFormat = GL_RGBA32F;
break;
case gpu::UINT16:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA16UI;
break;
case gpu::INT16:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA16I;
break;
case gpu::NUINT16:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16;
break;
case gpu::NINT16:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16_SNORM;
break;
case gpu::HALF:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA16F;
break;
case gpu::UINT8:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA8UI;
break;
case gpu::INT8:
texel.format = GL_RGBA_INTEGER;
texel.internalFormat = GL_RGBA8I;
break;
case gpu::NUINT8:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA8;
break;
case gpu::NINT8:
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA8_SNORM;
break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();
}
break;
case gpu::SRGB:
texel.internalFormat = GL_SRGB8;
break;
case gpu::SRGBA:
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
break;
}
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
}
return texel;
}
}
};

View file

@ -46,10 +46,17 @@ uint32_t Texture::getTextureGPUCount() {
Texture::Size Texture::getTextureGPUMemoryUsage() { Texture::Size Texture::getTextureGPUMemoryUsage() {
return Context::getTextureGPUMemoryUsage(); return Context::getTextureGPUMemoryUsage();
} }
uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = {1, 1, 1, 6}; Texture::Size Texture::getTextureGPUVirtualMemoryUsage() {
return Context::getTextureGPUVirtualMemoryUsage();
}
uint32_t Texture::getTextureGPUTransferCount() {
return Context::getTextureGPUTransferCount();
}
uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = { 1, 1, 1, 6 };
Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) : Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) :
_format(format), _format(format),

View file

@ -146,6 +146,8 @@ public:
static Size getTextureCPUMemoryUsage(); static Size getTextureCPUMemoryUsage();
static uint32_t getTextureGPUCount(); static uint32_t getTextureGPUCount();
static Size getTextureGPUMemoryUsage(); static Size getTextureGPUMemoryUsage();
static Size getTextureGPUVirtualMemoryUsage();
static uint32_t getTextureGPUTransferCount();
class Usage { class Usage {
public: public:

View file

@ -417,7 +417,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
{ {
_textures = Textures(MapChannel::NUM_MAP_CHANNELS); _textures = Textures(MapChannel::NUM_MAP_CHANNELS);
if (!material.albedoTexture.filename.isEmpty()) { if (!material.albedoTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
_albedoTransform = material.albedoTexture.transform; _albedoTransform = material.albedoTexture.transform;
map->setTextureTransform(_albedoTransform); map->setTextureTransform(_albedoTransform);
@ -488,7 +488,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!albedoName.isEmpty()) { if (!albedoName.isEmpty()) {
auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl();
auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); auto map = fetchTextureMap(url, ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP);
map->setTextureTransform(_albedoTransform); map->setTextureTransform(_albedoTransform);
// when reassigning the albedo texture we also check for the alpha channel used as opacity // when reassigning the albedo texture we also check for the alpha channel used as opacity
map->setUseAlphaChannel(true); map->setUseAlphaChannel(true);
@ -497,7 +497,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!normalName.isEmpty()) { if (!normalName.isEmpty()) {
auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl(); auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl();
auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::NORMAL_MAP); auto map = fetchTextureMap(url, NORMAL_TEXTURE, MapChannel::NORMAL_MAP);
setTextureMap(MapChannel::NORMAL_MAP, map); setTextureMap(MapChannel::NORMAL_MAP, map);
} }

View file

@ -203,6 +203,18 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
switch (_type) { switch (_type) {
case ALBEDO_TEXTURE: {
return TextureLoaderFunc(model::TextureUsage::createAlbedoTextureFromImage);
break;
}
case EMISSIVE_TEXTURE: {
return TextureLoaderFunc(model::TextureUsage::createEmissiveTextureFromImage);
break;
}
case LIGHTMAP_TEXTURE: {
return TextureLoaderFunc(model::TextureUsage::createLightmapTextureFromImage);
break;
}
case CUBE_TEXTURE: { case CUBE_TEXTURE: {
return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage); return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage);
break; break;
@ -232,7 +244,6 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
break; break;
} }
case DEFAULT_TEXTURE: case DEFAULT_TEXTURE:
case EMISSIVE_TEXTURE:
default: { default: {
return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage); return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage);
break; break;

View file

@ -31,6 +31,7 @@ typedef QSharedPointer<NetworkTexture> NetworkTexturePointer;
enum TextureType { enum TextureType {
DEFAULT_TEXTURE, DEFAULT_TEXTURE,
ALBEDO_TEXTURE,
NORMAL_TEXTURE, NORMAL_TEXTURE,
BUMP_TEXTURE, BUMP_TEXTURE,
SPECULAR_TEXTURE, SPECULAR_TEXTURE,

View file

@ -19,6 +19,9 @@
using namespace model; using namespace model;
using namespace gpu; using namespace gpu;
// FIXME: Declare this to enable compression
//#define COMPRESS_TEXTURES
void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { void TextureMap::setTextureSource(TextureSourcePointer& textureSource) {
_textureSource = textureSource; _textureSource = textureSource;
@ -49,12 +52,10 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) {
_lightmapOffsetScale.y = scale; _lightmapOffsetScale.y = scale;
} }
const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) {
// FIXME why is this in the model library? Move to GPU or GPU_GL
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
QImage image = srcImage; QImage image = srcImage;
bool validAlpha = false; validAlpha = false;
bool alphaAsMask = true; alphaAsMask = true;
const uint8 OPAQUE_ALPHA = 255; const uint8 OPAQUE_ALPHA = 255;
const uint8 TRANSPARENT_ALPHA = 0; const uint8 TRANSPARENT_ALPHA = 0;
if (image.hasAlphaChannel()) { if (image.hasAlphaChannel()) {
@ -63,7 +64,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
if (image.format() != QImage::Format_ARGB32) { if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32); image = image.convertToFormat(QImage::Format_ARGB32);
} }
// Actual alpha channel? create the histogram // Actual alpha channel? create the histogram
for (int y = 0; y < image.height(); ++y) { for (int y = 0; y < image.height(); ++y) {
const QRgb* data = reinterpret_cast<const QRgb*>(image.constScanLine(y)); const QRgb* data = reinterpret_cast<const QRgb*>(image.constScanLine(y));
@ -83,23 +84,77 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05);
} }
} }
if (!validAlpha && image.format() != QImage::Format_RGB888) { if (!validAlpha && image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888); image = image.convertToFormat(QImage::Format_RGB888);
} }
return image;
}
void TextureUsage::defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
const QImage& image, bool isLinear, bool doCompress) {
#ifdef COMPRESS_TEXTURES
#else
doCompress = false;
#endif
if (image.hasAlphaChannel()) {
gpu::Semantic gpuSemantic;
gpu::Semantic mipSemantic;
if (isLinear) {
mipSemantic = gpu::SBGRA;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGBA;
} else {
gpuSemantic = gpu::SRGBA;
}
} else {
mipSemantic = gpu::BGRA;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_RGBA;
} else {
gpuSemantic = gpu::RGBA;
}
}
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpuSemantic);
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, mipSemantic);
} else {
gpu::Semantic gpuSemantic;
gpu::Semantic mipSemantic;
if (isLinear) {
mipSemantic = gpu::SRGB;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGB;
} else {
gpuSemantic = gpu::SRGB;
}
} else {
mipSemantic = gpu::RGB;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_RGB;
} else {
gpuSemantic = gpu::RGB;
}
}
formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpuSemantic);
formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, mipSemantic);
}
}
gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips) {
bool validAlpha = false;
bool alphaAsMask = true;
QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask);
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); gpu::Element formatGPU;
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); gpu::Element formatMip;
if (image.hasAlphaChannel()) { defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA));
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA));
}
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
@ -113,40 +168,46 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
theTexture->setUsage(usage.build()); theTexture->setUsage(usage.build());
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1);
// FIXME queue for transfer to GPU and block on completion
if (generateMips) {
theTexture->autoGenerateMips(-1);
}
} }
return theTexture; return theTexture;
} }
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, false, true);
}
gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
}
gpu::Texture* TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
}
gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
}
gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) { gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) {
QImage image = srcImage; QImage image = srcImage;
if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) {
if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888);
image = image.convertToFormat(QImage::Format_RGB888);
}
} else {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
} }
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
bool isLinearRGB = true;
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
if (image.hasAlphaChannel()) {
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA));
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA));
}
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
@ -179,7 +240,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
const double pStrength = 2.0; const double pStrength = 2.0;
int width = image.width(); int width = image.width();
int height = image.height(); int height = image.height();
QImage result(width, height, image.format()); QImage result(width, height, QImage::Format_RGB888);
for (int i = 0; i < width; i++) { for (int i = 0; i < width; i++) {
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1); const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
@ -227,23 +288,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
if (image.hasAlphaChannel()) {
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA));
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA));
}
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1); theTexture->autoGenerateMips(-1);
} }
return theTexture; return theTexture;
} }
@ -263,8 +317,11 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
#ifdef COMPRESS_TEXTURES
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
#else
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
#endif
gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
@ -297,9 +354,13 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
#ifdef COMPRESS_TEXTURES
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
#else
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
#endif
gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1); theTexture->autoGenerateMips(-1);
@ -327,7 +388,11 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
#ifdef COMPRESS_TEXTURES
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::COMPRESSED_R);
#else
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
#endif
gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
@ -372,315 +437,181 @@ public:
_faceYNeg(fYN), _faceYNeg(fYN),
_faceZPos(fZP), _faceZPos(fZP),
_faceZNeg(fZN) {} _faceZNeg(fZN) {}
static const CubeLayout CUBEMAP_LAYOUTS[];
static const int NUM_CUBEMAP_LAYOUTS;
static int findLayout(int width, int height) {
// Find the layout of the cubemap in the 2D image
int foundLayout = -1;
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
if ((height * CUBEMAP_LAYOUTS[i]._widthRatio) == (width * CUBEMAP_LAYOUTS[i]._heightRatio)) {
foundLayout = i;
break;
}
}
return foundLayout;
}
}; };
gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
QImage image = srcImage; // Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
//
int imageArea = image.width() * image.height(); // WIDTH
// <------>
// ^ +------+
qCDebug(modelLog) << "Cube map size:" << QString(srcImageName.c_str()) << image.width() << image.height(); // | | |
// | | +X |
int opaquePixels = 0; // | | |
int translucentPixels = 0; // H +------+
//bool isTransparent = false; // E | |
int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; // I | -X |
const int EIGHT_BIT_MAXIMUM = 255; // G | |
QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); // H +------+
// T | |
if (!image.hasAlphaChannel()) { // | | +Y |
if (image.format() != QImage::Format_RGB888) { // | | |
image = image.convertToFormat(QImage::Format_RGB888); // | +------+
} // | | |
// int redTotal = 0, greenTotal = 0, blueTotal = 0; // | | -Y |
for (int y = 0; y < image.height(); y++) { // | | |
for (int x = 0; x < image.width(); x++) { // H +------+
QRgb rgb = image.pixel(x, y); // E | |
redTotal += qRed(rgb); // I | +Z |
greenTotal += qGreen(rgb); // G | |
blueTotal += qBlue(rgb); // H +------+
} // T | |
} // | | -Z |
if (imageArea > 0) { // | | |
averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); // V +------+
} //
} else { // FaceWidth = width = height / 6
if (image.format() != QImage::Format_ARGB32) { { 1, 6,
image = image.convertToFormat(QImage::Format_ARGB32); { 0, 0, true, false },
} { 0, 1, true, false },
{ 0, 2, false, true },
// check for translucency/false transparency { 0, 3, false, true },
// int opaquePixels = 0; { 0, 4, true, false },
// int translucentPixels = 0; { 0, 5, true, false }
// int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0; },
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) { // Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
QRgb rgb = image.pixel(x, y); //
redTotal += qRed(rgb); // <-----------WIDTH----------->
greenTotal += qGreen(rgb); // ^ +------+------+------+------+
blueTotal += qBlue(rgb); // | | | | | |
int alpha = qAlpha(rgb); // | | | +Y | | |
alphaTotal += alpha; // | | | | | |
if (alpha == EIGHT_BIT_MAXIMUM) { // H +------+------+------+------+
opaquePixels++; // E | | | | |
} else if (alpha != 0) { // I | -X | -Z | +X | +Z |
translucentPixels++; // G | | | | |
} // H +------+------+------+------+
} // T | | | | |
} // | | | -Y | | |
if (opaquePixels == imageArea) { // | | | | | |
qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str()); // V +------+------+------+------+
image = image.convertToFormat(QImage::Format_RGB888); //
} // FaceWidth = width / 4 = height / 3
{ 4, 3,
averageColor = QColor(redTotal / imageArea, { 2, 1, true, false },
greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); { 0, 1, true, false },
{ 1, 0, false, true },
//isTransparent = (translucentPixels >= imageArea / 2); { 1, 2, false, true },
{ 3, 1, true, false },
{ 1, 1, true, false }
},
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
//
// <-------WIDTH-------->
// ^ +------+------+------+
// | | | | |
// | | | +Y | |
// | | | | |
// H +------+------+------+
// E | | | |
// I | -X | -Z | +X |
// G | | | |
// H +------+------+------+
// T | | | |
// | | | -Y | |
// | | | | |
// | +------+------+------+
// | | | | |
// | | | +Z! | | <+Z is upside down!
// | | | | |
// V +------+------+------+
//
// FaceWidth = width / 3 = height / 4
{ 3, 4,
{ 2, 1, true, false },
{ 0, 1, true, false },
{ 1, 0, false, true },
{ 1, 2, false, true },
{ 1, 3, false, true },
{ 1, 1, true, false }
} }
};
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) {
bool validAlpha = false;
bool alphaAsMask = true;
QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask);
gpu::Texture* theTexture = nullptr; gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatGPU;
gpu::Element formatMip;
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
// Find the layout of the cubemap in the 2D image
int foundLayout = CubeLayout::findLayout(image.width(), image.height());
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); std::vector<QImage> faces;
bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); // If found, go extract the faces as separate images
if (foundLayout >= 0) {
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); auto& layout = CubeLayout::CUBEMAP_LAYOUTS[foundLayout];
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); int faceWidth = image.width() / layout._widthRatio;
if (image.hasAlphaChannel()) {
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror));
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
} else {
qCDebug(modelLog) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
return nullptr;
} }
const CubeLayout CUBEMAP_LAYOUTS[] = { // If the 6 faces have been created go on and define the true Texture
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio: if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
// theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
// WIDTH int f = 0;
// <------> for (auto& face : faces) {
// ^ +------+ theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
// | | | f++;
// | | +X |
// | | |
// H +------+
// E | |
// I | -X |
// G | |
// H +------+
// T | |
// | | +Y |
// | | |
// | +------+
// | | |
// | | -Y |
// | | |
// H +------+
// E | |
// I | +Z |
// G | |
// H +------+
// T | |
// | | -Z |
// | | |
// V +------+
//
// FaceWidth = width = height / 6
{ 1, 6,
{0, 0, true, false},
{0, 1, true, false},
{0, 2, false, true},
{0, 3, false, true},
{0, 4, true, false},
{0, 5, true, false}
},
// Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
//
// <-----------WIDTH----------->
// ^ +------+------+------+------+
// | | | | | |
// | | | +Y | | |
// | | | | | |
// H +------+------+------+------+
// E | | | | |
// I | -X | -Z | +X | +Z |
// G | | | | |
// H +------+------+------+------+
// T | | | | |
// | | | -Y | | |
// | | | | | |
// V +------+------+------+------+
//
// FaceWidth = width / 4 = height / 3
{ 4, 3,
{2, 1, true, false},
{0, 1, true, false},
{1, 0, false, true},
{1, 2, false, true},
{3, 1, true, false},
{1, 1, true, false}
},
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
//
// <-------WIDTH-------->
// ^ +------+------+------+
// | | | | |
// | | | +Y | |
// | | | | |
// H +------+------+------+
// E | | | |
// I | -X | -Z | +X |
// G | | | |
// H +------+------+------+
// T | | | |
// | | | -Y | |
// | | | | |
// | +------+------+------+
// | | | | |
// | | | +Z! | | <+Z is upside down!
// | | | | |
// V +------+------+------+
//
// FaceWidth = width / 3 = height / 4
{ 3, 4,
{2, 1, true, false},
{0, 1, true, false},
{1, 0, false, true},
{1, 2, false, true},
{1, 3, false, true},
{1, 1, true, false}
}
};
const int NUM_CUBEMAP_LAYOUTS = sizeof(CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
// Find the layout of the cubemap in the 2D image
int foundLayout = -1;
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
if ((image.height() * CUBEMAP_LAYOUTS[i]._widthRatio) == (image.width() * CUBEMAP_LAYOUTS[i]._heightRatio)) {
foundLayout = i;
break;
}
} }
std::vector<QImage> faces; if (generateMips) {
// If found, go extract the faces as separate images
if (foundLayout >= 0) {
auto& layout = CUBEMAP_LAYOUTS[foundLayout];
int faceWidth = image.width() / layout._widthRatio;
faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
} else {
qCDebug(modelLog) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
return nullptr;
}
// If the 6 faces have been created go on and define the true Texture
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
theTexture->autoGenerateMips(-1); theTexture->autoGenerateMips(-1);
int f = 0; }
for (auto& face : faces) {
theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
f++;
}
// Generate irradiance while we are at it // Generate irradiance while we are at it
if (generateIrradiance) {
theTexture->generateIrradiance(); theTexture->generateIrradiance();
} }
}
return theTexture;
}
gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
QImage image = srcImage;
int imageArea = image.width() * image.height();
int opaquePixels = 0;
int translucentPixels = 0;
//bool isTransparent = false;
int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
const int EIGHT_BIT_MAXIMUM = 255;
QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM);
if (!image.hasAlphaChannel()) {
if (image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888);
} }
// int redTotal = 0, greenTotal = 0, blueTotal = 0;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb rgb = image.pixel(x, y);
redTotal += qRed(rgb);
greenTotal += qGreen(rgb);
blueTotal += qBlue(rgb);
}
}
if (imageArea > 0) {
averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea);
}
} else {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// check for translucency/false transparency
// int opaquePixels = 0;
// int translucentPixels = 0;
// int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb rgb = image.pixel(x, y);
redTotal += qRed(rgb);
greenTotal += qGreen(rgb);
blueTotal += qBlue(rgb);
int alpha = qAlpha(rgb);
alphaTotal += alpha;
if (alpha == EIGHT_BIT_MAXIMUM) {
opaquePixels++;
} else if (alpha != 0) {
translucentPixels++;
}
}
}
if (opaquePixels == imageArea) {
qCDebug(modelLog) << "Image with alpha channel is completely opaque:" << QString(srcImageName.c_str());
image = image.convertToFormat(QImage::Format_RGB888);
}
averageColor = QColor(redTotal / imageArea,
greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea);
//isTransparent = (translucentPixels >= imageArea / 2);
}
gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
if (image.hasAlphaChannel()) {
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA));
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA));
}
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1);
} }
return theTexture; return theTexture;
} }
gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true);
}

View file

@ -32,6 +32,8 @@ public:
int _environmentUsage = 0; int _environmentUsage = 0;
static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName);
@ -40,6 +42,13 @@ public:
static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);
static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask);
static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
const QImage& srcImage, bool isLinear, bool doCompress);
static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, bool isLinear, bool doCompress, bool generateMips);
static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance);
}; };

View file

@ -20,6 +20,7 @@ void ResourceRequest::send() {
QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection);
return; return;
} }
Q_ASSERT(_state == NotStarted); Q_ASSERT(_state == NotStarted);
_state = InProgress; _state = InProgress;

View file

@ -85,11 +85,11 @@ void UserActivityLogger::launch(QString applicationVersion, bool previousSession
logAction(ACTION_NAME, actionDetails); logAction(ACTION_NAME, actionDetails);
} }
void UserActivityLogger::insufficientGLVersion(QString glVersion) { void UserActivityLogger::insufficientGLVersion(const QJsonObject& glData) {
const QString ACTION_NAME = "insufficient_gl"; const QString ACTION_NAME = "insufficient_gl";
QJsonObject actionDetails; QJsonObject actionDetails;
QString GL_VERSION = "glVersion"; QString GL_DATA = "glData";
actionDetails.insert(GL_VERSION, glVersion); actionDetails.insert(GL_DATA, glData);
logAction(ACTION_NAME, actionDetails); logAction(ACTION_NAME, actionDetails);
} }

View file

@ -31,7 +31,7 @@ public slots:
void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime); void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime);
void insufficientGLVersion(QString glVersion); void insufficientGLVersion(const QJsonObject& glData);
void changedDisplayName(QString displayName); void changedDisplayName(QString displayName);
void changedModel(QString typeOfModel, QString modelURL); void changedModel(QString typeOfModel, QString modelURL);
@ -48,4 +48,4 @@ private:
bool _disabled; bool _disabled;
}; };
#endif // hifi_UserActivityLogger_h #endif // hifi_UserActivityLogger_h

View file

@ -66,7 +66,7 @@ public:
/// By default, all HMDs are stereo /// By default, all HMDs are stereo
virtual bool isStereo() const { return isHmd(); } virtual bool isStereo() const { return isHmd(); }
virtual bool isThrottled() const { return false; } virtual bool isThrottled() const { return false; }
virtual float getTargetFrameRate() { return 0.0f; } virtual float getTargetFrameRate() const { return 0.0f; }
/// Returns a boolean value indicating whether the display is currently visible /// Returns a boolean value indicating whether the display is currently visible
/// to the user. For monitor displays, false might indicate that a screensaver, /// to the user. For monitor displays, false might indicate that a screensaver,
@ -142,7 +142,12 @@ public:
virtual void abandonCalibration() {} virtual void abandonCalibration() {}
virtual void resetSensors() {} virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0f; } virtual float devicePixelRatio() { return 1.0f; }
virtual float presentRate() { return -1.0f; } // Rate at which we present to the display device
virtual float presentRate() const { return -1.0f; }
// Rate at which new frames are being presented to the display device
virtual float newFramePresentRate() const { return -1.0f; }
// Rate at which rendered frames are being skipped
virtual float droppedFrameRate() const { return -1.0f; }
uint32_t presentCount() const { return _presentedFrameIndex; } uint32_t presentCount() const { return _presentedFrameIndex; }
virtual void cycleDebugOutput() {} virtual void cycleDebugOutput() {}

View file

@ -32,6 +32,8 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte
config->textureGPUCount = gpu::Texture::getTextureGPUCount(); config->textureGPUCount = gpu::Texture::getTextureGPUCount();
config->textureCPUMemoryUsage = gpu::Texture::getTextureCPUMemoryUsage(); config->textureCPUMemoryUsage = gpu::Texture::getTextureCPUMemoryUsage();
config->textureGPUMemoryUsage = gpu::Texture::getTextureGPUMemoryUsage(); config->textureGPUMemoryUsage = gpu::Texture::getTextureGPUMemoryUsage();
config->textureGPUVirtualMemoryUsage = gpu::Texture::getTextureGPUVirtualMemoryUsage();
config->textureGPUTransferCount = gpu::Texture::getTextureGPUTransferCount();
gpu::ContextStats gpuStats(_gpuStats); gpu::ContextStats gpuStats(_gpuStats);
renderContext->args->_context->getStats(_gpuStats); renderContext->args->_context->getStats(_gpuStats);
@ -45,6 +47,7 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte
config->frameTextureCount = _gpuStats._RSNumTextureBounded - gpuStats._RSNumTextureBounded; config->frameTextureCount = _gpuStats._RSNumTextureBounded - gpuStats._RSNumTextureBounded;
config->frameTextureRate = config->frameTextureCount * frequency; config->frameTextureRate = config->frameTextureCount * frequency;
config->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded;
config->emitDirty(); config->emitDirty();
} }

View file

@ -33,6 +33,8 @@ namespace render {
Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty) Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty)
Q_PROPERTY(qint64 textureCPUMemoryUsage MEMBER textureCPUMemoryUsage NOTIFY dirty) Q_PROPERTY(qint64 textureCPUMemoryUsage MEMBER textureCPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(qint64 textureGPUMemoryUsage MEMBER textureGPUMemoryUsage NOTIFY dirty) Q_PROPERTY(qint64 textureGPUMemoryUsage MEMBER textureGPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(qint64 textureGPUVirtualMemoryUsage MEMBER textureGPUVirtualMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint32 textureGPUTransferCount MEMBER textureGPUTransferCount NOTIFY dirty)
Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY dirty) Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY dirty)
Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty) Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty)
@ -43,7 +45,7 @@ namespace render {
Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty) Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty) Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
public: public:
EngineStatsConfig() : Job::Config(true) {} EngineStatsConfig() : Job::Config(true) {}
@ -57,6 +59,8 @@ namespace render {
quint32 textureGPUCount{ 0 }; quint32 textureGPUCount{ 0 };
qint64 textureCPUMemoryUsage{ 0 }; qint64 textureCPUMemoryUsage{ 0 };
qint64 textureGPUMemoryUsage{ 0 }; qint64 textureGPUMemoryUsage{ 0 };
qint64 textureGPUVirtualMemoryUsage{ 0 };
quint32 textureGPUTransferCount{ 0 };
quint32 frameAPIDrawcallCount{ 0 }; quint32 frameAPIDrawcallCount{ 0 };
quint32 frameDrawcallCount{ 0 }; quint32 frameDrawcallCount{ 0 };
@ -67,6 +71,7 @@ namespace render {
quint32 frameTextureCount{ 0 }; quint32 frameTextureCount{ 0 };
quint32 frameTextureRate{ 0 }; quint32 frameTextureRate{ 0 };
qint64 frameTextureMemoryUsage{ 0 };
void emitDirty() { emit dirty(); } void emitDirty() { emit dirty(); }

View file

@ -40,6 +40,19 @@ ScriptEngines::ScriptEngines()
_scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.setSourceModel(&_scriptsModel);
_scriptsModelFilter.sort(0, Qt::AscendingOrder); _scriptsModelFilter.sort(0, Qt::AscendingOrder);
_scriptsModelFilter.setDynamicSortFilter(true); _scriptsModelFilter.setDynamicSortFilter(true);
static const int SCRIPT_SAVE_COUNTDOWN_INTERVAL_MS = 5000;
QTimer* scriptSaveTimer = new QTimer(this);
scriptSaveTimer->setSingleShot(true);
QMetaObject::Connection timerConnection = connect(scriptSaveTimer, &QTimer::timeout, [] {
DependencyManager::get<ScriptEngines>()->saveScripts();
});
connect(qApp, &QCoreApplication::aboutToQuit, [=] {
disconnect(timerConnection);
});
connect(this, &ScriptEngines::scriptCountChanged, this, [scriptSaveTimer] {
scriptSaveTimer->start(SCRIPT_SAVE_COUNTDOWN_INTERVAL_MS);
}, Qt::QueuedConnection);
} }
QUrl normalizeScriptURL(const QUrl& rawScriptURL) { QUrl normalizeScriptURL(const QUrl& rawScriptURL) {

View file

@ -1,75 +0,0 @@
//
// CPUID.cpp
//
// Created by Ryan Huffman on 3/25/16.
// 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
//
#include "CPUID.h"
#ifdef Q_OS_WIN
const CPUID::CPUID_Internal CPUID::CPU_Rep;
std::vector<CPUID::Feature> CPUID::getAllFeatures() {
std::vector<CPUID::Feature> features;
features.push_back({ "3DNOW", CPUID::_3DNOW() });
features.push_back({ "3DNOWEXT", CPUID::_3DNOWEXT() });
features.push_back({ "ABM", CPUID::ABM() });
features.push_back({ "ADX", CPUID::ADX() });
features.push_back({ "AES", CPUID::AES() });
features.push_back({ "AVX", CPUID::AVX() });
features.push_back({ "AVX2", CPUID::AVX2() });
features.push_back({ "AVX512CD", CPUID::AVX512CD() });
features.push_back({ "AVX512ER", CPUID::AVX512ER() });
features.push_back({ "AVX512F", CPUID::AVX512F() });
features.push_back({ "AVX512PF", CPUID::AVX512PF() });
features.push_back({ "BMI1", CPUID::BMI1() });
features.push_back({ "BMI2", CPUID::BMI2() });
features.push_back({ "CLFSH", CPUID::CLFSH() });
features.push_back({ "CMPXCHG16B", CPUID::CMPXCHG16B() });
features.push_back({ "CX8", CPUID::CX8() });
features.push_back({ "ERMS", CPUID::ERMS() });
features.push_back({ "F16C", CPUID::F16C() });
features.push_back({ "FMA", CPUID::FMA() });
features.push_back({ "FSGSBASE", CPUID::FSGSBASE() });
features.push_back({ "FXSR", CPUID::FXSR() });
features.push_back({ "HLE", CPUID::HLE() });
features.push_back({ "INVPCID", CPUID::INVPCID() });
features.push_back({ "LAHF", CPUID::LAHF() });
features.push_back({ "LZCNT", CPUID::LZCNT() });
features.push_back({ "MMX", CPUID::MMX() });
features.push_back({ "MMXEXT", CPUID::MMXEXT() });
features.push_back({ "MONITOR", CPUID::MONITOR() });
features.push_back({ "MOVBE", CPUID::MOVBE() });
features.push_back({ "MSR", CPUID::MSR() });
features.push_back({ "OSXSAVE", CPUID::OSXSAVE() });
features.push_back({ "PCLMULQDQ", CPUID::PCLMULQDQ() });
features.push_back({ "POPCNT", CPUID::POPCNT() });
features.push_back({ "PREFETCHWT1", CPUID::PREFETCHWT1() });
features.push_back({ "RDRAND", CPUID::RDRAND() });
features.push_back({ "RDSEED", CPUID::RDSEED() });
features.push_back({ "RDTSCP", CPUID::RDTSCP() });
features.push_back({ "RTM", CPUID::RTM() });
features.push_back({ "SEP", CPUID::SEP() });
features.push_back({ "SHA", CPUID::SHA() });
features.push_back({ "SSE", CPUID::SSE() });
features.push_back({ "SSE2", CPUID::SSE2() });
features.push_back({ "SSE3", CPUID::SSE3() });
features.push_back({ "SSE4.1", CPUID::SSE41() });
features.push_back({ "SSE4.2", CPUID::SSE42() });
features.push_back({ "SSE4a", CPUID::SSE4a() });
features.push_back({ "SSSE3", CPUID::SSSE3() });
features.push_back({ "SYSCALL", CPUID::SYSCALL() });
features.push_back({ "TBM", CPUID::TBM() });
features.push_back({ "XOP", CPUID::XOP() });
features.push_back({ "XSAVE", CPUID::XSAVE() });
return features;
};
#endif

View file

@ -0,0 +1,75 @@
//
// CPUIdent.cpp
//
// Created by Ryan Huffman on 3/25/16.
// 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
//
#include "CPUIdent.h"
#ifdef Q_OS_WIN
const CPUIdent::CPUIdent_Internal CPUIdent::CPU_Rep;
std::vector<CPUIdent::Feature> CPUIdent::getAllFeatures() {
std::vector<CPUIdent::Feature> features;
features.push_back({ "3DNOW", CPUIdent::_3DNOW() });
features.push_back({ "3DNOWEXT", CPUIdent::_3DNOWEXT() });
features.push_back({ "ABM", CPUIdent::ABM() });
features.push_back({ "ADX", CPUIdent::ADX() });
features.push_back({ "AES", CPUIdent::AES() });
features.push_back({ "AVX", CPUIdent::AVX() });
features.push_back({ "AVX2", CPUIdent::AVX2() });
features.push_back({ "AVX512CD", CPUIdent::AVX512CD() });
features.push_back({ "AVX512ER", CPUIdent::AVX512ER() });
features.push_back({ "AVX512F", CPUIdent::AVX512F() });
features.push_back({ "AVX512PF", CPUIdent::AVX512PF() });
features.push_back({ "BMI1", CPUIdent::BMI1() });
features.push_back({ "BMI2", CPUIdent::BMI2() });
features.push_back({ "CLFSH", CPUIdent::CLFSH() });
features.push_back({ "CMPXCHG16B", CPUIdent::CMPXCHG16B() });
features.push_back({ "CX8", CPUIdent::CX8() });
features.push_back({ "ERMS", CPUIdent::ERMS() });
features.push_back({ "F16C", CPUIdent::F16C() });
features.push_back({ "FMA", CPUIdent::FMA() });
features.push_back({ "FSGSBASE", CPUIdent::FSGSBASE() });
features.push_back({ "FXSR", CPUIdent::FXSR() });
features.push_back({ "HLE", CPUIdent::HLE() });
features.push_back({ "INVPCID", CPUIdent::INVPCID() });
features.push_back({ "LAHF", CPUIdent::LAHF() });
features.push_back({ "LZCNT", CPUIdent::LZCNT() });
features.push_back({ "MMX", CPUIdent::MMX() });
features.push_back({ "MMXEXT", CPUIdent::MMXEXT() });
features.push_back({ "MONITOR", CPUIdent::MONITOR() });
features.push_back({ "MOVBE", CPUIdent::MOVBE() });
features.push_back({ "MSR", CPUIdent::MSR() });
features.push_back({ "OSXSAVE", CPUIdent::OSXSAVE() });
features.push_back({ "PCLMULQDQ", CPUIdent::PCLMULQDQ() });
features.push_back({ "POPCNT", CPUIdent::POPCNT() });
features.push_back({ "PREFETCHWT1", CPUIdent::PREFETCHWT1() });
features.push_back({ "RDRAND", CPUIdent::RDRAND() });
features.push_back({ "RDSEED", CPUIdent::RDSEED() });
features.push_back({ "RDTSCP", CPUIdent::RDTSCP() });
features.push_back({ "RTM", CPUIdent::RTM() });
features.push_back({ "SEP", CPUIdent::SEP() });
features.push_back({ "SHA", CPUIdent::SHA() });
features.push_back({ "SSE", CPUIdent::SSE() });
features.push_back({ "SSE2", CPUIdent::SSE2() });
features.push_back({ "SSE3", CPUIdent::SSE3() });
features.push_back({ "SSE4.1", CPUIdent::SSE41() });
features.push_back({ "SSE4.2", CPUIdent::SSE42() });
features.push_back({ "SSE4a", CPUIdent::SSE4a() });
features.push_back({ "SSSE3", CPUIdent::SSSE3() });
features.push_back({ "SYSCALL", CPUIdent::SYSCALL() });
features.push_back({ "TBM", CPUIdent::TBM() });
features.push_back({ "XOP", CPUIdent::XOP() });
features.push_back({ "XSAVE", CPUIdent::XSAVE() });
return features;
};
#endif

View file

@ -1,5 +1,5 @@
// //
// CPUID.h // CPUIdent.h
// //
// Adapted from Microsoft's example for using the cpuid intrinsic, // Adapted from Microsoft's example for using the cpuid intrinsic,
// found at https://msdn.microsoft.com/en-us/library/hskdteyh.aspx // found at https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
@ -15,8 +15,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#ifndef hifi_CPUID_h #ifndef hifi_CPUIdent_h
#define hifi_CPUID_h #define hifi_CPUIdent_h
#include <QtCore/QtGlobal> #include <QtCore/QtGlobal>
@ -29,10 +29,10 @@
#include <intrin.h> #include <intrin.h>
class CPUID class CPUIdent
{ {
// forward declarations // forward declarations
class CPUID_Internal; class CPUIdent_Internal;
public: public:
struct Feature { struct Feature {
@ -104,12 +104,12 @@ public:
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
private: private:
static const CPUID_Internal CPU_Rep; static const CPUIdent_Internal CPU_Rep;
class CPUID_Internal class CPUIdent_Internal
{ {
public: public:
CPUID_Internal() CPUIdent_Internal()
: nIds_ { 0 }, : nIds_ { 0 },
nExIds_ { 0 }, nExIds_ { 0 },
isIntel_ { false }, isIntel_ { false },
@ -209,4 +209,4 @@ private:
#endif #endif
#endif // hifi_CPUID_h #endif // hifi_CPUIdent_h

View file

@ -24,7 +24,7 @@
#endif #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "CPUID.h" #include "CPUIdent.h"
#endif #endif
@ -758,10 +758,10 @@ void printSystemInformation() {
qDebug() << "CPUID"; qDebug() << "CPUID";
qDebug() << "\tCPU Vendor: " << CPUID::Vendor().c_str(); qDebug() << "\tCPU Vendor: " << CPUIdent::Vendor().c_str();
qDebug() << "\tCPU Brand: " << CPUID::Brand().c_str(); qDebug() << "\tCPU Brand: " << CPUIdent::Brand().c_str();
for (auto& feature : CPUID::getAllFeatures()) { for (auto& feature : CPUIdent::getAllFeatures()) {
qDebug().nospace().noquote() << "\t[" << (feature.supported ? "x" : " ") << "] " << feature.name.c_str(); qDebug().nospace().noquote() << "\t[" << (feature.supported ? "x" : " ") << "] " << feature.name.c_str();
} }
#endif #endif
@ -777,4 +777,4 @@ void printSystemInformation() {
qDebug().noquote().nospace() << "\t" << qDebug().noquote().nospace() << "\t" <<
(envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND"); (envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND");
} }
} }

View file

@ -0,0 +1,49 @@
//
// Created by Bradley Austin Davis 2016/04/10
// Copyright 2013-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
//
#pragma once
#ifndef hifi_Shared_RateCounter_h
#define hifi_Shared_RateCounter_h
#include <stdint.h>
#include <functional>
#include <QtCore/QElapsedTimer>
#include "../SharedUtil.h"
#include "../NumericalConstants.h"
template <uint32_t INTERVAL = MSECS_PER_SECOND, uint8_t PRECISION = 2>
class RateCounter {
public:
void increment(size_t count = 1) {
auto now = usecTimestampNow();
auto currentIntervalMs = (uint32_t)((now - _start) / USECS_PER_MSEC);
if (currentIntervalMs > INTERVAL) {
float currentCount = _count;
float intervalSeconds = (float)currentIntervalMs / (float)MSECS_PER_SECOND;
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
_start = now;
_count = 0;
};
_count += count;
}
float rate() const { return _rate; }
uint8_t precision() const { return PRECISION; }
uint32_t interval() const { return INTERVAL; }
private:
uint64_t _start { usecTimestampNow() };
size_t _count { 0 };
float _rate { 0 };
const float _scale { powf(10, PRECISION) };
};
#endif

View file

@ -15,6 +15,8 @@
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
#include <QtQml/QtQml> #include <QtQml/QtQml>
#include <gl/GLHelpers.h>
#include <AbstractUriHandler.h> #include <AbstractUriHandler.h>
#include <AccountManager.h> #include <AccountManager.h>

View file

@ -21,7 +21,7 @@ public:
// Stereo specific methods // Stereo specific methods
virtual void resetSensors() override final; virtual void resetSensors() override final;
virtual void beginFrameRender(uint32_t frameIndex) override; virtual void beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() override { return _hmdDesc.DisplayRefreshRate; } float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
protected: protected:

View file

@ -181,7 +181,7 @@ int OculusLegacyDisplayPlugin::getHmdScreen() const {
return _hmdScreen; return _hmdScreen;
} }
float OculusLegacyDisplayPlugin::getTargetFrameRate() { float OculusLegacyDisplayPlugin::getTargetFrameRate() const {
return TARGET_RATE_OculusLegacy; return TARGET_RATE_OculusLegacy;
} }

View file

@ -28,7 +28,7 @@ public:
virtual void resetSensors() override; virtual void resetSensors() override;
virtual void beginFrameRender(uint32_t frameIndex) override; virtual void beginFrameRender(uint32_t frameIndex) override;
virtual float getTargetFrameRate() override; virtual float getTargetFrameRate() const override;
protected: protected:
virtual bool internalActivate() override; virtual bool internalActivate() override;

View file

@ -21,7 +21,7 @@ public:
virtual bool isSupported() const override; virtual bool isSupported() const override;
virtual const QString& getName() const override { return NAME; } virtual const QString& getName() const override { return NAME; }
virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; } virtual float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
virtual void customizeContext() override; virtual void customizeContext() override;