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);
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({
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
});
@ -164,52 +167,85 @@ void Agent::requestScript() {
scriptURL = QUrl(_payload);
}
// setup a network access manager and
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
// make sure this is not a script request for the file scheme
if (scriptURL.scheme() == URL_SCHEME_FILE) {
qWarning() << "Cannot load script for Agent from local filesystem.";
scriptRequestFinished();
return;
}
QNetworkDiskCache* cache = new QNetworkDiskCache();
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
networkAccessManager.setCache(cache);
auto request = ResourceManager::createResourceRequest(this, scriptURL);
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
if (!request) {
qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();
scriptRequestFinished();
return;
}
// setup a timeout for script request
static const int SCRIPT_TIMEOUT_MS = 10000;
_scriptRequestTimeout = new QTimer(this);
_scriptRequestTimeout = new QTimer;
connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished);
_scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS);
qDebug() << "Downloading script at" << scriptURL.toString();
QNetworkReply* reply = networkAccessManager.get(networkRequest);
connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished);
connect(request, &ResourceRequest::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() {
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) {
_scriptContents = reply->readAll();
qDebug() << "Downloaded script:" << _scriptContents;
if (request && request->getResult() == ResourceRequest::Success) {
_scriptContents = request->getData();
qInfo() << "Downloaded script:" << _scriptContents;
// we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished
// to return before calling executeScript
QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection);
} else {
if (reply) {
qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment.";
qDebug() << "QNetworkReply error was" << reply->errorString();
if (request) {
qWarning() << "Failed to download script at" << request->getUrl().toString() << " - bailing on assignment.";
qWarning() << "ResourceRequest error was" << request->getResult();
} 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);
}
reply->deleteLater();
request->deleteLater();
}
void Agent::executeScript() {

View file

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

View file

@ -122,67 +122,49 @@ void AssetServer::completeSetup() {
}
// 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
auto files = _filesDirectory.entryList(QDir::Files);
QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING };
auto hashedFiles = files.filter(hashFileRegex);
QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING };
auto hashedFiles = files.filter(hashFileRegex);
qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory.";
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() {
QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}(\\.[\\w]+)+$" };
void AssetServer::cleanupUnmappedFiles() {
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) {
if (hashFileRegex.exactMatch(fileInfo.fileName())) {
// we have a pre-mapping file that we should migrate to the new mapping system
qDebug() << "Migrating pre-mapping file" << fileInfo.fileName();
if (!mappedHashes.contains(fileInfo.fileName())) {
// remove the unmapped file
QFile removeableFile { fileInfo.absoluteFilePath() };
// rename the file to the same name with no extension
QFile oldFile { fileInfo.absoluteFilePath() };
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;
}
if (removeableFile.remove()) {
qDebug() << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped.";
} else {
qDebug() << "\tCould not add migration mapping for" << hash << "since a mapping for" << fakeFileName
<< "already exists.";
qDebug() << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed";
}
}
}
@ -451,7 +433,7 @@ void AssetServer::sendStatsPacket() {
static const QString MAP_FILE_NAME = "map.json";
void AssetServer::loadMappingsFromFile() {
bool AssetServer::loadMappingsFromFile() {
auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME);
@ -488,15 +470,17 @@ void AssetServer::loadMappingsFromFile() {
}
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.";
setFinished(true);
qCritical() << "Failed to read mapping file at" << mapFilePath;
return false;
} else {
qInfo() << "No existing mappings loaded from file since no file was found at" << mapFilePath;
}
return true;
}
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
auto oldMappings = _fileMappings;
QSet<QString> hashesToCheckForDeletion;
// enumerate the paths to delete and remove them all
for (auto& path : paths) {
@ -579,6 +565,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
while (it != _fileMappings.end()) {
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);
} else {
++it;
@ -595,6 +584,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
} else {
auto oldMapping = _fileMappings.take(path);
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();
} else {
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
if (writeMappingsToFile()) {
// 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;
} else {
qWarning() << "Failed to persist deleted mappings, rolling back";

View file

@ -48,7 +48,7 @@ private:
void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
// Mapping file operations must be called from main assignment thread only
void loadMappingsFromFile();
bool loadMappingsFromFile();
bool writeMappingsToFile();
/// Set the mapping for path to hash
@ -60,7 +60,8 @@ private:
/// Rename mapping from `oldPath` to `newPath`. Returns true if successful
bool renameMapping(AssetPath oldPath, AssetPath newPath);
void performMappingMigration();
// deletes any unmapped files from the local asset directory
void cleanupUnmappedFiles();
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
if (formJSON["security"]) {
var password = formJSON["security"]["http_password"];
if (password.length > 0) {
if (password && password.length > 0) {
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
QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString();
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : "";
QString hexHeaderPassword = QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) {
if (settingsUsername == headerUsername
&& (settingsPassword.isEmpty() || hexHeaderPassword == settingsPassword)) {
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_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 isActive = false;
@ -432,7 +428,7 @@ var toolBar = (function() {
}
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 !== "") {
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",
label: "Frame",
color: "#E2334D"
},
{
prop: "textureGPUTransferCount",
label: "Transfer",
color: "#9495FF"
}
]
}
@ -104,7 +109,18 @@ Item {
prop: "textureGPUMemoryUsage",
label: "GPU",
color: "#1AC567"
},
{
prop: "textureGPUVirtualMemoryUsage",
label: "GPU Virtual",
color: "#9495FF"
},
{
prop: "frameTextureMemoryUsage",
label: "Frame",
color: "#E2334D"
}
]
}
@ -179,7 +195,7 @@ Item {
object: Render.getConfig("DrawLight"),
prop: "numDrawn",
label: "Lights",
color: "#E2334D"
color: "#FED959"
}
]
}

View file

@ -48,12 +48,22 @@ Item {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Render Rate: " + root.renderrate
text: "Render Rate: " + root.renderrate.toFixed(2);
}
Text {
color: root.fontColor;
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 {
color: root.fontColor;

View file

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

View file

@ -34,6 +34,10 @@ FocusScope {
// The VR version of the primary menu
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
QtObject {
id: zLevels;

View file

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

View file

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

View file

@ -511,8 +511,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_sessionRunTimer(startupTimer),
_previousSessionCrashed(setupEssentials(argc, argv)),
_undoStackScriptingInterface(&_undoStack),
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
@ -1332,11 +1330,13 @@ void Application::initializeGL() {
_idleLoopStdev.reset();
// update before the first render
update(1.0f / _fps);
update(0);
InfoView::show(INFO_HELP_PATH, true);
}
extern void setupPreferences();
void Application::initializeUi() {
AddressBarDialog::registerType();
ErrorDialog::registerType();
@ -1348,6 +1348,9 @@ void Application::initializeUi() {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_offscreenContext->getContext());
auto rootContext = offscreenUi->getRootContext();
offscreenUi->setProxyWindow(_window->windowHandle());
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
// 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
offscreenUi->setNavigationFocused(false);
auto rootContext = offscreenUi->getRootContext();
auto engine = rootContext->engine();
connect(engine, &QQmlEngine::quit, [] {
qApp->quit();
@ -1463,7 +1465,6 @@ void Application::initializeUi() {
}
void Application::paintGL() {
updateHeartbeat();
// Some plugins process message events, potentially leading to
@ -1481,24 +1482,7 @@ void Application::paintGL() {
return;
}
_frameCount++;
// 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;
}
_frameCounter.increment();
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
PerformanceTimer perfTimer("paintGL");
@ -1655,7 +1639,7 @@ void Application::paintGL() {
}
// Update camera position
if (!isHMDMode()) {
_myCamera.update(1.0f / _fps);
_myCamera.update(1.0f / _frameCounter.rate());
}
}
@ -1752,8 +1736,6 @@ void Application::paintGL() {
batch.resetStages();
});
}
_lastInstantaneousFps = instantaneousFps;
}
void Application::runTests() {
@ -2625,32 +2607,19 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME };
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();
if (_aboutToQuit || _inPaint) {
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();
// 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
@ -2684,8 +2653,36 @@ void Application::idle(uint64_t now) {
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__);
// 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
_lastTimeUpdated.start();
@ -2694,14 +2691,7 @@ void Application::idle(uint64_t now) {
Stats::getInstance()->updateStats();
AvatarInputs::getInstance()->update();
{
static uint64_t lastIdleStart{ now };
uint64_t idleStartToStartDuration = now - lastIdleStart;
if (idleStartToStartDuration != 0) {
_simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration);
}
lastIdleStart = now;
}
_simCounter.increment();
PerformanceTimer perfTimer("idle");
// Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
@ -2757,30 +2747,6 @@ void Application::idle(uint64_t now) {
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) {
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
}
@ -3051,7 +3017,7 @@ void Application::updateLOD() const {
PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!isThrottleRendering()) {
DependencyManager::get<LODManager>()->autoAdjustLOD(_fps);
DependencyManager::get<LODManager>()->autoAdjustLOD(_frameCounter.rate());
} else {
DependencyManager::get<LODManager>()->resetLODAdjust();
}
@ -3482,17 +3448,13 @@ void Application::update(float deltaTime) {
// AvatarManager update
{
PerformanceTimer perfTimer("AvatarManger");
qApp->setAvatarSimrateSample(1.0f / deltaTime);
_avatarSimCounter.increment();
{
PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
avatarManager->updateOtherAvatars(deltaTime);
}
// update sensorToWorldMatrix for camera and hand controllers
getMyAvatar()->updateSensorToWorldMatrix();
qApp->updateMyAvatarLookAtPosition();
{
@ -3805,7 +3767,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
bool Application::isHMDMode() const {
return getActiveDisplayPlugin()->isHmd();
}
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
float Application::getTargetFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); }
QRect Application::getDesirableApplicationGeometry() const {
QRect applicationGeometry = getWindow()->geometry();

View file

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

View file

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

View file

@ -15,6 +15,8 @@
#include "AbstractLoggerInterface.h"
#include <GenericQueueThread.h>
#include <QtCore/QFile>
class FileLogger : public AbstractLoggerInterface {
Q_OBJECT
@ -27,11 +29,32 @@ public:
virtual QString getLogData() override;
virtual void locateLog() override;
signals:
void rollingLogFile(QString newFilename);
private:
const QString _fileName;
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

View file

@ -294,20 +294,18 @@ void MyAvatar::update(float deltaTime) {
auto audio = DependencyManager::get<AudioClient>();
head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
simulate(deltaTime);
simulate(deltaTime);
currentEnergy += energyChargeRate;
currentEnergy -= getAccelerationEnergy();
currentEnergy -= getAudioEnergy();
if(didTeleport()) {
currentEnergy = 0.0f;
}
currentEnergy = max(0.0f, min(currentEnergy,1.0f));
emit energyChanged(currentEnergy);
}
extern QByteArray avatarStateToFrame(const AvatarData* _avatar);
@ -333,6 +331,10 @@ void MyAvatar::simulate(float deltaTime) {
updatePosition(deltaTime);
}
// update sensorToWorldMatrix for camera and hand controllers
// before we perform rig animations and IK.
updateSensorToWorldMatrix();
{
PerformanceTimer perfTimer("skeleton");
_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 {
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;
auto cameraMode = qApp->getCamera()->getMode();
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
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 {

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),
nullptr, BUG_SPLAT_FLAGS };
#endif
QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
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
// not made more complicated than it already is.
bool override = false;
QString glVersion;
QJsonObject glData;
{
OpenGLVersionChecker openGLVersionChecker(argc, const_cast<char**>(argv));
bool valid = true;
glVersion = openGLVersionChecker.checkVersion(valid, override);
glData = openGLVersionChecker.checkVersion(valid, override);
if (!valid) {
if (override) {
auto glVersion = glData["version"].toString();
qCDebug(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
} else {
qCDebug(interfaceapp, "Early exit due to OpenGL version.");
@ -148,12 +149,12 @@ int main(int argc, const char* argv[]) {
if (override) {
auto& accountManager = AccountManager::getInstance();
if (accountManager.isLoggedIn()) {
UserActivityLogger::getInstance().insufficientGLVersion(glVersion);
UserActivityLogger::getInstance().insufficientGLVersion(glData);
} else {
QObject::connect(&AccountManager::getInstance(), &AccountManager::loginComplete, [glVersion](){
QObject::connect(&AccountManager::getInstance(), &AccountManager::loginComplete, [glData](){
static bool loggedInsufficientGL = false;
if (!loggedInsufficientGL) {
UserActivityLogger::getInstance().insufficientGLVersion(glVersion);
UserActivityLogger::getInstance().insufficientGLVersion(glData);
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.
auto logPath = QDir::toNativeSeparators(app.getLogger()->getFilename());
auto logger = app.getLogger();
auto logPath = QDir::toNativeSeparators(logger->getFilename());
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
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
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE(renderrate, (int)qApp->getFps());
STAT_UPDATE(renderrate, qApp->getFps());
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 {
STAT_UPDATE(presentrate, -1);
STAT_UPDATE(presentnewrate, -1);
STAT_UPDATE(presentdroprate, -1);
}
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());

View file

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

View file

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

View file

@ -20,7 +20,7 @@ class Basic2DWindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
public:
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;

View file

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

View file

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

View file

@ -90,6 +90,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
}
if (!entity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(entity);
}
@ -256,10 +257,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
bool success;
AACube queryCube = childEntity->getQueryAACube(success);
if (!success) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity);
continue;
}
if (!childEntity->isParentIDValid()) {
QWriteLocker locker(&_missingParentLock);
_missingParent.append(childEntity);
}
@ -346,6 +349,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (result->getAncestorMissing()) {
// 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.
QWriteLocker locker(&_missingParentLock);
_missingParent.append(result);
}
@ -1008,10 +1012,27 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
void EntityTree::fixupMissingParents() {
MovingEntitiesOperator moveOperator(getThisPointer());
QMutableVectorIterator<EntityItemWeakPointer> iter(_missingParent);
while (iter.hasNext()) {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
QList<EntityItemPointer> missingParents;
{
QWriteLocker locker(&_missingParentLock);
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) {
bool queryAACubeSuccess;
AACube newCube = entity->getQueryAACube(queryAACubeSuccess);
@ -1040,12 +1061,8 @@ void EntityTree::fixupMissingParents() {
if (queryAACubeSuccess && doMove) {
moveOperator.addEntityToMoveList(entity, newCube);
iter.remove();
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
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.
QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server
QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars

View file

@ -4,6 +4,7 @@
#include <QtGui/QSurfaceFormat>
#include <QtOpenGL/QGL>
#include <QOpenGLContext>
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
static QSurfaceFormat format;
@ -37,3 +38,21 @@ const QGLFormat& getDefaultGLFormat() {
});
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
#define hifi_GLHelpers_h
#include <QJsonObject>
// 16 bits of depth precision
#define DEFAULT_GL_DEPTH_BUFFER_BITS 16
// 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 QGLFormat& getDefaultGLFormat();
QJsonObject getGLContextData();
#endif

View file

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

View file

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

View file

@ -19,7 +19,7 @@ class OpenGLVersionChecker : public QApplication {
public:
OpenGLVersionChecker(int& argc, char** argv);
static QString checkVersion(bool& valid, bool& override);
static QJsonObject checkVersion(bool& valid, bool& override);
};
#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<Texture::Size> Context::_textureGPUMemoryUsage{ 0 };
std::atomic<Texture::Size> Context::_textureGPUVirtualMemoryUsage{ 0 };
std::atomic<uint32_t> Context::_textureGPUTransferCount{ 0 };
void Context::incrementBufferGPUCount() {
_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() {
return _bufferGPUCount.load();
}
@ -165,10 +185,20 @@ Context::Size Context::getTextureGPUMemoryUsage() {
return _textureGPUMemoryUsage.load();
}
Context::Size Context::getTextureGPUVirtualMemoryUsage() {
return _textureGPUVirtualMemoryUsage.load();
}
uint32_t Context::getTextureGPUTransferCount() {
return _textureGPUTransferCount.load();
}
void Backend::incrementBufferGPUCount() { Context::incrementBufferGPUCount(); }
void Backend::decrementBufferGPUCount() { Context::decrementBufferGPUCount(); }
void Backend::updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateBufferGPUMemoryUsage(prevObjectSize, newObjectSize); }
void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); }
void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); }
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 _RSNumTextureBounded = 0;
int _RSAmountTextureMemoryBounded = 0;
int _DSNumAPIDrawcalls = 0;
int _DSNumDrawcalls = 0;
@ -128,6 +129,9 @@ public:
static void incrementTextureGPUCount();
static void decrementTextureGPUCount();
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:
StereoState _stereo;
@ -177,6 +181,8 @@ public:
static uint32_t getTextureGPUCount();
static Size getTextureGPUMemoryUsage();
static Size getTextureGPUVirtualMemoryUsage();
static uint32_t getTextureGPUTransferCount();
protected:
Context(const Context& context);
@ -202,6 +208,9 @@ protected:
static void incrementTextureGPUCount();
static void decrementTextureGPUCount();
static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
static void updateTextureGPUVirtualMemoryUsage(Size prevObjectSize, Size newObjectSize);
static void incrementTextureGPUTransferCount();
static void decrementTextureGPUTransferCount();
// Buffer and Texture Counters
static std::atomic<uint32_t> _bufferGPUCount;
@ -209,6 +218,9 @@ protected:
static std::atomic<uint32_t> _textureGPUCount;
static std::atomic<Size> _textureGPUMemoryUsage;
static std::atomic<Size> _textureGPUVirtualMemoryUsage;
static std::atomic<uint32_t> _textureGPUTransferCount;
friend class Backend;
};

View file

@ -192,6 +192,25 @@ enum Semantic {
SRGBA,
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,
UNIFORM,
@ -224,6 +243,7 @@ public:
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; }
bool isNormalized() const { return (getType() >= NORMALIZED_START); }

View file

@ -76,15 +76,22 @@ public:
class GLTexture : public GPUObject {
public:
// The public gl texture object
GLuint _texture{ 0 };
const Stamp _storageStamp;
Stamp _contentStamp { 0 };
const GLuint _texture;
const GLenum _target;
GLTexture(const gpu::Texture& gpuTexture);
~GLTexture();
void createTexture();
GLuint size() const { return _size; }
GLuint virtualSize() const { return _virtualSize; }
void updateSize();
enum SyncState {
// 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 GLenum CUBE_FACE_LAYOUT[6];
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;
const GLuint _size;
// The owning texture
const Texture& _gpuTexture;
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);
// very specific for now

View file

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

View file

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

View file

@ -45,6 +45,20 @@ static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = {
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
#define CHECK_GL_ERROR_HELPER(x) #x
// 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 "GLBackendShared.h"
#include "GLTexelFormat.h"
#include "GLBackendTextureTransfer.h"
using namespace gpu;
@ -49,49 +48,145 @@ const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = {
// Create the texture and allocate storage
GLBackend::GLTexture::GLTexture(const Texture& texture) :
_storageStamp(texture.getStamp()), _texture(allocateSingleTexture()),
_target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture)
_storageStamp(texture.getStamp()),
_target(gpuToGLTextureType(texture)),
_size(0),
_virtualSize(0),
_numLevels(texture.maxMip() + 1),
_gpuTexture(texture)
{
Backend::incrementTextureGPUCount();
Backend::updateTextureGPUMemoryUsage(0, _size);
Backend::setGPUObject(texture, this);
GLsizei width = texture.getWidth();
GLsizei height = texture.getHeight();
GLsizei levels = 1;
if (texture.maxMip() > 0) {
if (texture.isAutogenerateMips()) {
while ((width | height) >> levels) {
++levels;
// updateSize();
GLuint virtualSize = _gpuTexture.evalTotalSize();
setVirtualSize(virtualSize);
setSize(virtualSize);
}
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());
withPreservedTexture(_target, [&] {
glBindTexture(_target, _texture);
(void)CHECK_GL_ERROR();
// GO through the process of allocating the correct storage
if (GLEW_VERSION_4_2) {
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();
});
syncSampler(_gpuTexture.getSampler(), _gpuTexture.getType(), this);
(void)CHECK_GL_ERROR();
glBindTexture(_target, boundTex);
(void)CHECK_GL_ERROR();
}
GLBackend::GLTexture::~GLTexture() {
if (_texture != 0) {
glDeleteTextures(1, &_texture);
if (_privateTexture != 0) {
glDeleteTextures(1, &_privateTexture);
}
Backend::updateTextureGPUMemoryUsage(_size, 0);
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);
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 {
return _storageStamp < _gpuTexture.getStamp();
}
@ -117,44 +212,25 @@ bool GLBackend::GLTexture::isReady() const {
return Idle == syncState;
}
//#define USE_PBO
// 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 {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat());
#ifdef USE_PBO
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());
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());
(void)CHECK_GL_ERROR();
#endif
}
// Move content bits from the CPU to the GPU
void GLBackend::GLTexture::transfer() const {
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
if (_contentStamp >= _gpuTexture.getDataStamp()) {
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
switch (_gpuTexture.getType()) {
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
void GLBackend::GLTexture::postTransfer() {
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
switch (_gpuTexture.getType()) {
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;
if (!texture.isDefined()) {
// 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...
// Create the texture if need be (force re-creation if the storage stamp changes
// for easier use of immutable storage)
if (!object || object->isInvalid()) {
@ -226,18 +306,23 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin
object = new GLTexture(texture);
}
// need to have a gpu object?
if (texture.getNumSlices() != 1) {
// Object maybe doens't neet to be tranasferred after creation
if (!needTransfer) {
object->createTexture();
object->_contentStamp = texturePointer->getDataStamp();
object->postTransfer();
return object;
}
// Object might be outdated, if so, start the transfer
// (outdated objects that are already in transfer will have reported 'true' for ready()
if (object->isOutdated()) {
Backend::incrementTextureGPUTransferCount();
_textureTransferHelper->transferTexture(texturePointer);
}
if (GLTexture::Transferred == object->getSyncState()) {
Backend::decrementTextureGPUTransferCount();
object->postTransfer();
}
@ -338,7 +423,8 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
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) {
return;
}

View file

@ -9,26 +9,12 @@
#include "GLBackendTextureTransfer.h"
#include "GLBackendShared.h"
#include "GLTexelFormat.h"
#ifdef THREADED_TEXTURE_TRANSFER
#include <gl/OffscreenGLCanvas.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
@ -50,30 +36,18 @@ GLTextureTransferHelper::GLTextureTransferHelper() {
void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) {
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
#ifdef THREADED_TEXTURE_TRANSFER
TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) };
glFlush();
TextureTransferPackage package{ texturePointer, 0};
object->setSyncState(GLBackend::GLTexture::Pending);
queueItem(package);
#else
object->transfer();
object->postTransfer();
object->setSyncState(GLBackend::GLTexture::Transferred);
#endif
}
void GLTextureTransferHelper::setup() {
#ifdef THREADED_TEXTURE_TRANSFER
_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
}
@ -85,8 +59,6 @@ void GLTextureTransferHelper::shutdown() {
bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
for (auto package : messages) {
glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(package.fence);
TexturePointer texturePointer = package.texture.lock();
// Texture no longer exists, move on to the next
if (!texturePointer) {
@ -94,37 +66,17 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
}
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
object->createTexture();
object->transfer();
#ifdef FORCE_DRAW_AFTER_TRANSFER
// 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
object->updateSize();
glBindTexture(object->_target, 0);
auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(writeSync);
object->_contentStamp = texturePointer->getDataStamp();
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() {
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) :
_format(format),

View file

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

View file

@ -417,7 +417,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
{
_textures = Textures(MapChannel::NUM_MAP_CHANNELS);
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;
map->setTextureTransform(_albedoTransform);
@ -488,7 +488,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!albedoName.isEmpty()) {
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);
// when reassigning the albedo texture we also check for the alpha channel used as opacity
map->setUseAlphaChannel(true);
@ -497,7 +497,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
if (!normalName.isEmpty()) {
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);
}

View file

@ -203,6 +203,18 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
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: {
return TextureLoaderFunc(model::TextureUsage::createCubeTextureFromImage);
break;
@ -232,7 +244,6 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
break;
}
case DEFAULT_TEXTURE:
case EMISSIVE_TEXTURE:
default: {
return TextureLoaderFunc(model::TextureUsage::create2DTextureFromImage);
break;

View file

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

View file

@ -19,6 +19,9 @@
using namespace model;
using namespace gpu;
// FIXME: Declare this to enable compression
//#define COMPRESS_TEXTURES
void TextureMap::setTextureSource(TextureSourcePointer& textureSource) {
_textureSource = textureSource;
@ -49,12 +52,10 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) {
_lightmapOffsetScale.y = scale;
}
// 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) {
const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) {
QImage image = srcImage;
bool validAlpha = false;
bool alphaAsMask = true;
validAlpha = false;
alphaAsMask = true;
const uint8 OPAQUE_ALPHA = 255;
const uint8 TRANSPARENT_ALPHA = 0;
if (image.hasAlphaChannel()) {
@ -63,7 +64,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// Actual alpha channel? create the histogram
for (int y = 0; y < image.height(); ++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);
}
}
}
if (!validAlpha && image.format() != 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;
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 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));
}
gpu::Element formatGPU;
gpu::Element formatMip;
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
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->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;
}
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) {
QImage image = srcImage;
if (!image.hasAlphaChannel()) {
if (image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888);
}
} else {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
if (image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888);
}
gpu::Texture* theTexture = nullptr;
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->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
@ -179,7 +240,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
const double pStrength = 2.0;
int width = image.width();
int height = image.height();
QImage result(width, height, image.format());
QImage result(width, height, QImage::Format_RGB888);
for (int i = 0; i < width; i++) {
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
@ -227,23 +288,16 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
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));
}
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
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;
}
@ -263,8 +317,11 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma
gpu::Texture* theTexture = nullptr;
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);
#endif
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)));
@ -297,9 +354,13 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s
gpu::Texture* theTexture = nullptr;
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);
#endif
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->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1);
@ -327,7 +388,11 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag
gpu::Texture* theTexture = nullptr;
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);
#endif
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)));
@ -372,315 +437,181 @@ public:
_faceYNeg(fYN),
_faceZPos(fZP),
_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) {
QImage image = srcImage;
int imageArea = image.width() * image.height();
qCDebug(modelLog) << "Cube map size:" << QString(srcImageName.c_str()) << 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);
const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
//
// WIDTH
// <------>
// ^ +------+
// | | |
// | | +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 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;
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);
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 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));
std::vector<QImage> faces;
// If found, go extract the faces as separate images
if (foundLayout >= 0) {
auto& layout = CubeLayout::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;
}
const CubeLayout CUBEMAP_LAYOUTS[] = {
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
//
// WIDTH
// <------>
// ^ +------+
// | | |
// | | +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;
}
// 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));
int f = 0;
for (auto& face : faces) {
theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
f++;
}
std::vector<QImage> faces;
// 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));
if (generateMips) {
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();
}
}
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;
}
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;
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* createNormalTextureFromBumpImage(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* 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);
return;
}
Q_ASSERT(_state == NotStarted);
_state = InProgress;

View file

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

View file

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

View file

@ -66,7 +66,7 @@ public:
/// By default, all HMDs are stereo
virtual bool isStereo() const { return isHmd(); }
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
/// to the user. For monitor displays, false might indicate that a screensaver,
@ -142,7 +142,12 @@ public:
virtual void abandonCalibration() {}
virtual void resetSensors() {}
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; }
virtual void cycleDebugOutput() {}

View file

@ -32,6 +32,8 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte
config->textureGPUCount = gpu::Texture::getTextureGPUCount();
config->textureCPUMemoryUsage = gpu::Texture::getTextureCPUMemoryUsage();
config->textureGPUMemoryUsage = gpu::Texture::getTextureGPUMemoryUsage();
config->textureGPUVirtualMemoryUsage = gpu::Texture::getTextureGPUVirtualMemoryUsage();
config->textureGPUTransferCount = gpu::Texture::getTextureGPUTransferCount();
gpu::ContextStats gpuStats(_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->frameTextureRate = config->frameTextureCount * frequency;
config->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded;
config->emitDirty();
}

View file

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

View file

@ -40,6 +40,19 @@ ScriptEngines::ScriptEngines()
_scriptsModelFilter.setSourceModel(&_scriptsModel);
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
_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) {

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,
// 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
//
#ifndef hifi_CPUID_h
#define hifi_CPUID_h
#ifndef hifi_CPUIdent_h
#define hifi_CPUIdent_h
#include <QtCore/QtGlobal>
@ -29,10 +29,10 @@
#include <intrin.h>
class CPUID
class CPUIdent
{
// forward declarations
class CPUID_Internal;
class CPUIdent_Internal;
public:
struct Feature {
@ -104,12 +104,12 @@ public:
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
private:
static const CPUID_Internal CPU_Rep;
static const CPUIdent_Internal CPU_Rep;
class CPUID_Internal
class CPUIdent_Internal
{
public:
CPUID_Internal()
CPUIdent_Internal()
: nIds_ { 0 },
nExIds_ { 0 },
isIntel_ { false },
@ -209,4 +209,4 @@ private:
#endif
#endif // hifi_CPUID_h
#endif // hifi_CPUIdent_h

View file

@ -24,7 +24,7 @@
#endif
#ifdef Q_OS_WIN
#include "CPUID.h"
#include "CPUIdent.h"
#endif
@ -758,10 +758,10 @@ void printSystemInformation() {
qDebug() << "CPUID";
qDebug() << "\tCPU Vendor: " << CPUID::Vendor().c_str();
qDebug() << "\tCPU Brand: " << CPUID::Brand().c_str();
qDebug() << "\tCPU Vendor: " << CPUIdent::Vendor().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();
}
#endif
@ -777,4 +777,4 @@ void printSystemInformation() {
qDebug().noquote().nospace() << "\t" <<
(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 <QtQml/QtQml>
#include <gl/GLHelpers.h>
#include <AbstractUriHandler.h>
#include <AccountManager.h>

View file

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

View file

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

View file

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

View file

@ -21,7 +21,7 @@ public:
virtual bool isSupported() const override;
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;