mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 16:43:33 +02:00
Merge branch 'master' into 20872
This commit is contained in:
commit
b33d28852b
69 changed files with 1774 additions and 1234 deletions
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
133
examples/golfclub/golfClub.js
Normal file
133
examples/golfclub/golfClub.js
Normal 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);
|
||||
}
|
||||
|
||||
});
|
36
examples/golfclub/golfClub.json
Normal file
36
examples/golfclub/golfClub.json
Normal 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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -30,8 +30,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
|
|||
}
|
||||
}, true, false);
|
||||
|
||||
updateFramerate();
|
||||
|
||||
return Parent::internalActivate();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
426
libraries/gpu/src/gpu/GLBackendTexelFormat.cpp
Normal file
426
libraries/gpu/src/gpu/GLBackendTexelFormat.cpp
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -31,6 +31,7 @@ typedef QSharedPointer<NetworkTexture> NetworkTexturePointer;
|
|||
|
||||
enum TextureType {
|
||||
DEFAULT_TEXTURE,
|
||||
ALBEDO_TEXTURE,
|
||||
NORMAL_TEXTURE,
|
||||
BUMP_TEXTURE,
|
||||
SPECULAR_TEXTURE,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ void ResourceRequest::send() {
|
|||
QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(_state == NotStarted);
|
||||
|
||||
_state = InProgress;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
75
libraries/shared/src/CPUIdent.cpp
Normal file
75
libraries/shared/src/CPUIdent.cpp
Normal 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
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
49
libraries/shared/src/shared/RateCounter.h
Normal file
49
libraries/shared/src/shared/RateCounter.h
Normal 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
|
|
@ -15,6 +15,8 @@
|
|||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQml/QtQml>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
|
||||
#include <AbstractUriHandler.h>
|
||||
#include <AccountManager.h>
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -181,7 +181,7 @@ int OculusLegacyDisplayPlugin::getHmdScreen() const {
|
|||
return _hmdScreen;
|
||||
}
|
||||
|
||||
float OculusLegacyDisplayPlugin::getTargetFrameRate() {
|
||||
float OculusLegacyDisplayPlugin::getTargetFrameRate() const {
|
||||
return TARGET_RATE_OculusLegacy;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue