mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 12:13:40 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into restore-text3d-overlay
This commit is contained in:
commit
f60bb1faa5
43 changed files with 1396 additions and 916 deletions
|
@ -16,7 +16,7 @@ Contributing
|
|||
git checkout -b new_branch_name
|
||||
```
|
||||
4. Code
|
||||
* Follow the [coding standard](http://docs.highfidelity.io/v1.0/docs/coding-standard)
|
||||
* Follow the [coding standard](https://readme.highfidelity.com/v1.0/docs/coding-standard)
|
||||
5. Commit
|
||||
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
6. Update your branch
|
||||
|
|
|
@ -235,6 +235,11 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
||||
|
||||
if (_currentAssignment) {
|
||||
qCWarning(assigmnentclient) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring.";
|
||||
return;
|
||||
}
|
||||
|
||||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects.
|
||||
// Also supports touch and equipping objects.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -305,7 +304,6 @@ function MyController(hand) {
|
|||
switch (this.state) {
|
||||
case STATE_OFF:
|
||||
this.off();
|
||||
this.touchTest();
|
||||
break;
|
||||
case STATE_SEARCHING:
|
||||
case STATE_HOLD_SEARCHING:
|
||||
|
@ -495,7 +493,8 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
this.searchIndicatorOn = function(handPosition, distantPickRay) {
|
||||
this.searchIndicatorOn = function(distantPickRay) {
|
||||
var handPosition = distantPickRay.origin;
|
||||
var SEARCH_SPHERE_SIZE = 0.011;
|
||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
||||
|
||||
|
@ -857,7 +856,9 @@ function MyController(hand) {
|
|||
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
var currentControllerPosition = Controller.getPoseValue(controllerHandInput).position;
|
||||
var currentControllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation,
|
||||
Controller.getPoseValue(controllerHandInput).translation),
|
||||
MyAvatar.position);
|
||||
var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation));
|
||||
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ?
|
||||
|
@ -865,19 +866,13 @@ function MyController(hand) {
|
|||
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
|
||||
|
||||
var distantPickRay = {
|
||||
origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position,
|
||||
origin: PICK_WITH_HAND_RAY ? currentControllerPosition : Camera.position,
|
||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(controllerRotation) : Vec3.mix(Quat.getUp(controllerRotation),
|
||||
Quat.getFront(Camera.orientation),
|
||||
HAND_HEAD_MIX_RATIO),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
var searchVisualizationPickRay = {
|
||||
origin: currentControllerPosition,
|
||||
direction: Quat.getUp(this.getHandRotation()),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
// Pick at some maximum rate, not always
|
||||
var pickRays = [];
|
||||
var now = Date.now();
|
||||
|
@ -1086,7 +1081,7 @@ function MyController(hand) {
|
|||
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
this.searchIndicatorOn(handPosition, distantPickRay);
|
||||
this.searchIndicatorOn(distantPickRay);
|
||||
Reticle.setVisible(false);
|
||||
|
||||
};
|
||||
|
@ -1668,78 +1663,13 @@ function MyController(hand) {
|
|||
if (intersection.intersects) {
|
||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
}
|
||||
this.searchIndicatorOn(handPosition, pickRay);
|
||||
this.searchIndicatorOn(pickRay);
|
||||
}
|
||||
}
|
||||
|
||||
this.callEntityMethodOnGrabbed("continueFarTrigger");
|
||||
};
|
||||
|
||||
_this.allTouchedIDs = {};
|
||||
|
||||
this.touchTest = function() {
|
||||
var maxDistance = 0.05;
|
||||
var leftHandPosition = MyAvatar.getLeftPalmPosition();
|
||||
var rightHandPosition = MyAvatar.getRightPalmPosition();
|
||||
var leftEntities = Entities.findEntities(leftHandPosition, maxDistance);
|
||||
var rightEntities = Entities.findEntities(rightHandPosition, maxDistance);
|
||||
var ids = [];
|
||||
|
||||
if (leftEntities.length !== 0) {
|
||||
leftEntities.forEach(function(entity) {
|
||||
ids.push(entity);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (rightEntities.length !== 0) {
|
||||
rightEntities.forEach(function(entity) {
|
||||
ids.push(entity);
|
||||
});
|
||||
}
|
||||
|
||||
ids.forEach(function(id) {
|
||||
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
|
||||
if (!props ||
|
||||
!props.boundingBox ||
|
||||
props.name === 'pointer') {
|
||||
return;
|
||||
}
|
||||
var entityMinPoint = props.boundingBox.brn;
|
||||
var entityMaxPoint = props.boundingBox.tfl;
|
||||
var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint);
|
||||
var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint);
|
||||
|
||||
if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) {
|
||||
// we haven't been touched before, but either right or left is touching us now
|
||||
_this.allTouchedIDs[id] = true;
|
||||
_this.startTouch(id);
|
||||
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) {
|
||||
// we have been touched before and are still being touched
|
||||
// continue touch
|
||||
_this.continueTouch(id);
|
||||
} else if (_this.allTouchedIDs[id]) {
|
||||
delete _this.allTouchedIDs[id];
|
||||
_this.stopTouch(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.startTouch = function(entityID) {
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "startTouch", args);
|
||||
};
|
||||
|
||||
this.continueTouch = function(entityID) {
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "continueTouch", args);
|
||||
};
|
||||
|
||||
this.stopTouch = function(entityID) {
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "stopTouch", args);
|
||||
};
|
||||
|
||||
this.release = function() {
|
||||
this.turnLightsOff();
|
||||
this.turnOffVisualizations();
|
||||
|
|
21
examples/utilities/cache/cacheStats.js
vendored
Normal file
21
examples/utilities/cache/cacheStats.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// cacheStats.js
|
||||
// examples/utilities/cache
|
||||
//
|
||||
// Zach Pomerantz, created on 4/1/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('stats.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Cache Stats',
|
||||
source: qml,
|
||||
width: 300,
|
||||
height: 200
|
||||
});
|
||||
window.setPosition(500, 50);
|
||||
window.closed.connect(function() { Script.stop(); });
|
77
examples/utilities/cache/stats.qml
vendored
Normal file
77
examples/utilities/cache/stats.qml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// stats.qml
|
||||
// examples/utilities/cache
|
||||
//
|
||||
// Created by Zach Pomerantz on 4/1/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../lib/plotperf"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
property var caches: [["Animation", AnimationCache], ["Model", ModelCache], ["Texture", TextureCache], ["Sound", SoundCache]]
|
||||
|
||||
Grid {
|
||||
id: grid
|
||||
rows: root.caches.length; columns: 1; spacing: 8
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: root.caches
|
||||
|
||||
Row {
|
||||
PlotPerf {
|
||||
title: modelData[0] + " Count"
|
||||
anchors.left: parent
|
||||
height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length
|
||||
width: grid.width / 2 - grid.spacing * 1.5
|
||||
object: modelData[1]
|
||||
valueNumDigits: "1"
|
||||
plots: [
|
||||
{
|
||||
prop: "numTotal",
|
||||
label: "total",
|
||||
color: "#00B4EF"
|
||||
},
|
||||
{
|
||||
prop: "numCached",
|
||||
label: "cached",
|
||||
color: "#1AC567"
|
||||
}
|
||||
]
|
||||
}
|
||||
PlotPerf {
|
||||
title: modelData[0] + " Size"
|
||||
anchors.right: parent
|
||||
height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length
|
||||
width: grid.width / 2 - grid.spacing * 1.5
|
||||
object: modelData[1]
|
||||
valueScale: 1048576
|
||||
valueUnit: "Mb"
|
||||
valueNumDigits: "1"
|
||||
plots: [
|
||||
{
|
||||
prop: "sizeTotal",
|
||||
label: "total",
|
||||
color: "#00B4EF"
|
||||
},
|
||||
{
|
||||
prop: "sizeCached",
|
||||
label: "cached",
|
||||
color: "#1AC567"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,12 +19,8 @@ Item {
|
|||
// The title of the graph
|
||||
property string title
|
||||
|
||||
// THe object used as the default source object for the prop plots
|
||||
// The object used as the default source object for the prop plots
|
||||
property var object
|
||||
|
||||
// THis is my hack to get a property and assign it to a trigger var in order to get
|
||||
// a signal called whenever the value changed
|
||||
property var trigger
|
||||
|
||||
// Plots is an array of plot descriptor
|
||||
// a default plot descriptor expects the following object:
|
||||
|
@ -55,45 +51,38 @@ Item {
|
|||
property var tick : 0
|
||||
|
||||
function createValues() {
|
||||
print("trigger is: " + JSON.stringify(trigger))
|
||||
if (Array.isArray(plots)) {
|
||||
for (var i =0; i < plots.length; i++) {
|
||||
var plot = plots[i];
|
||||
print(" a pnew Plot:" + JSON.stringify(plot));
|
||||
_values.push( {
|
||||
object: (plot["object"] !== undefined ? plot["object"] : root.object),
|
||||
value: plot["prop"],
|
||||
valueMax: 1,
|
||||
numSamplesConstantMax: 0,
|
||||
valueHistory: new Array(),
|
||||
label: (plot["label"] !== undefined ? plot["label"] : ""),
|
||||
color: (plot["color"] !== undefined ? plot["color"] : "white"),
|
||||
scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
|
||||
unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
|
||||
})
|
||||
}
|
||||
for (var i =0; i < plots.length; i++) {
|
||||
var plot = plots[i];
|
||||
_values.push( {
|
||||
object: (plot["object"] !== undefined ? plot["object"] : root.object),
|
||||
value: plot["prop"],
|
||||
valueMax: 1,
|
||||
numSamplesConstantMax: 0,
|
||||
valueHistory: new Array(),
|
||||
label: (plot["label"] !== undefined ? plot["label"] : ""),
|
||||
color: (plot["color"] !== undefined ? plot["color"] : "white"),
|
||||
scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
|
||||
unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
|
||||
})
|
||||
}
|
||||
print("in creator" + JSON.stringify(_values));
|
||||
|
||||
pullFreshValues();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
createValues();
|
||||
print(JSON.stringify(_values));
|
||||
|
||||
}
|
||||
|
||||
function pullFreshValues() {
|
||||
//print("pullFreshValues");
|
||||
// Wait until values are created to begin pulling
|
||||
if (!_values) { return; }
|
||||
|
||||
var VALUE_HISTORY_SIZE = 100;
|
||||
var UPDATE_CANVAS_RATE = 20;
|
||||
tick++;
|
||||
|
||||
|
||||
var currentValueMax = 0
|
||||
for (var i = 0; i < _values.length; i++) {
|
||||
|
||||
var currentVal = _values[i].object[_values[i].value] * _values[i].scale;
|
||||
var currentVal = (+_values[i].object[_values[i].value]) * _values[i].scale;
|
||||
|
||||
_values[i].valueHistory.push(currentVal)
|
||||
_values[i].numSamplesConstantMax++;
|
||||
|
@ -125,11 +114,13 @@ Item {
|
|||
valueMax = currentValueMax;
|
||||
}
|
||||
|
||||
if (tick % UPDATE_CANVAS_RATE == 0) {
|
||||
mycanvas.requestPaint()
|
||||
}
|
||||
mycanvas.requestPaint()
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 100; running: true; repeat: true
|
||||
onTriggered: pullFreshValues()
|
||||
}
|
||||
onTriggerChanged: pullFreshValues()
|
||||
|
||||
Canvas {
|
||||
id: mycanvas
|
||||
|
@ -165,9 +156,9 @@ Item {
|
|||
ctx.fillStyle = val.color;
|
||||
var bestValue = val.valueHistory[val.valueHistory.length -1];
|
||||
ctx.textAlign = "right";
|
||||
ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1.5);
|
||||
ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1);
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1.5);
|
||||
ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1);
|
||||
}
|
||||
|
||||
function displayTitle(ctx, text, maxVal) {
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "plotperf"
|
||||
import "../lib/plotperf"
|
||||
|
||||
Item {
|
||||
id: statsUI
|
||||
|
@ -32,7 +32,6 @@ Item {
|
|||
title: "Num Buffers"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["bufferCPUCount"]
|
||||
plots: [
|
||||
{
|
||||
prop: "bufferCPUCount",
|
||||
|
@ -50,7 +49,6 @@ Item {
|
|||
title: "gpu::Buffer Memory"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["bufferCPUMemoryUsage"]
|
||||
valueScale: 1048576
|
||||
valueUnit: "Mb"
|
||||
valueNumDigits: "1"
|
||||
|
@ -71,7 +69,6 @@ Item {
|
|||
title: "Num Textures"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["textureCPUCount"]
|
||||
plots: [
|
||||
{
|
||||
prop: "textureCPUCount",
|
||||
|
@ -94,7 +91,6 @@ Item {
|
|||
title: "gpu::Texture Memory"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["textureCPUMemoryUsage"]
|
||||
valueScale: 1048576
|
||||
valueUnit: "Mb"
|
||||
valueNumDigits: "1"
|
||||
|
@ -116,7 +112,6 @@ Item {
|
|||
title: "Triangles"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["frameTriangleCount"]
|
||||
valueScale: 1000
|
||||
valueUnit: "K"
|
||||
plots: [
|
||||
|
@ -138,7 +133,6 @@ Item {
|
|||
title: "Drawcalls"
|
||||
height: parent.evalEvenHeight()
|
||||
object: stats.config
|
||||
trigger: stats.config["frameDrawcallCount"]
|
||||
plots: [
|
||||
{
|
||||
prop: "frameAPIDrawcallCount",
|
||||
|
@ -168,7 +162,6 @@ Item {
|
|||
title: "Items"
|
||||
height: parent.evalEvenHeight()
|
||||
object: parent.drawOpaqueConfig
|
||||
trigger: Render.getConfig("DrawOpaqueDeferred")["numDrawn"]
|
||||
plots: [
|
||||
{
|
||||
object: Render.getConfig("DrawOpaqueDeferred"),
|
||||
|
|
|
@ -53,6 +53,10 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
QTimer* inactivePeerTimer = new QTimer(this);
|
||||
connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
|
||||
inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
|
||||
|
||||
// handle public keys when they arrive from the QNetworkAccessManager
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
|
||||
}
|
||||
|
||||
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
||||
|
@ -62,8 +66,6 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
|||
if (headerVersion == versionForPacketType(headerType)) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet version mismatch for packet" << headerType << " from" << packet.getSenderSockAddr();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +204,6 @@ bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& pla
|
|||
void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
||||
// send a request to the metaverse API for the public key for this domain
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
|
||||
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
||||
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
|
|
|
@ -107,12 +107,15 @@ elseif(WIN32)
|
|||
# add an executable that also has the icon itself and the configured rc file as resources
|
||||
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
||||
COMMENT "Adding OS version support manifest to exe"
|
||||
)
|
||||
if ( NOT DEV_BUILD )
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
||||
COMMENT "Adding OS version support manifest to exe"
|
||||
)
|
||||
endif()
|
||||
|
||||
else()
|
||||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif()
|
||||
|
|
|
@ -18,23 +18,12 @@
|
|||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <Qt>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QAbstractNativeEventFilter>
|
||||
#include <QtCore/QMimeData>
|
||||
#include <QtCore/QThreadPool>
|
||||
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QWheelEvent>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
@ -44,28 +33,20 @@
|
|||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <QtWidgets/QActionGroup>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include <QtMultimedia/QMediaPlayer>
|
||||
|
||||
#include <QtNetwork/QNetworkDiskCache>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <shared/JSONHelpers.h>
|
||||
|
||||
#include <ResourceScriptingInterface.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <BuildInfo.h>
|
||||
#include <AssetClient.h>
|
||||
#include <AssetUpload.h>
|
||||
#include <AutoUpdater.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
#include <CursorManager.h>
|
||||
|
@ -110,12 +91,9 @@
|
|||
#include <ScriptCache.h>
|
||||
#include <SoundCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <TextureCache.h>
|
||||
#include <Tooltip.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
#include <VrMenu.h>
|
||||
#include <recording/Deck.h>
|
||||
#include <recording/Recorder.h>
|
||||
#include <QmlWebWindowClass.h>
|
||||
|
@ -137,7 +115,6 @@
|
|||
#include "InterfaceActionFactory.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "LODManager.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelPackager.h"
|
||||
#include "PluginContainerProxy.h"
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
|
@ -169,9 +146,7 @@
|
|||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
|
||||
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
#if defined(Q_OS_WIN)
|
||||
extern "C" {
|
||||
|
@ -254,14 +229,14 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
void updateHeartbeat() {
|
||||
static void updateHeartbeat() {
|
||||
auto now = usecTimestampNow();
|
||||
auto elapsed = now - _heartbeat;
|
||||
_movingAverage.addSample(elapsed);
|
||||
_heartbeat = now;
|
||||
}
|
||||
|
||||
void deadlockDetectionCrash() {
|
||||
static void deadlockDetectionCrash() {
|
||||
uint32_t* crashTrigger = nullptr;
|
||||
*crashTrigger = 0xDEAD10CC;
|
||||
}
|
||||
|
@ -390,7 +365,7 @@ public:
|
|||
LambdaEvent(std::function<void()> && fun) :
|
||||
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
|
||||
}
|
||||
void call() { _fun(); }
|
||||
void call() const { _fun(); }
|
||||
};
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
|
@ -528,7 +503,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
|
||||
_lastFaceTrackerUpdate(0)
|
||||
{
|
||||
// FIXME this may be excessivly conservative. On the other hand
|
||||
// FIXME this may be excessively conservative. On the other hand
|
||||
// maybe I'm used to having an 8-core machine
|
||||
// Perhaps find the ideal thread count and subtract 2 or 3
|
||||
// (main thread, present thread, random OS load)
|
||||
|
@ -588,9 +563,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
// Model background downloads need to happen on the Datagram Processor Thread. The idle loop will
|
||||
// emit checkBackgroundDownloads to cause the ModelCache to check it's queue for requested background
|
||||
// downloads.
|
||||
QSharedPointer<ModelCache> modelCacheP = DependencyManager::get<ModelCache>();
|
||||
ResourceCache* modelCache = modelCacheP.data();
|
||||
connect(this, &Application::checkBackgroundDownloads, modelCache, &ResourceCache::checkAsynchronousGets);
|
||||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
connect(this, &Application::checkBackgroundDownloads, modelCache.data(), &ModelCache::checkAsynchronousGets);
|
||||
|
||||
// put the audio processing on a separate thread
|
||||
QThread* audioThread = new QThread();
|
||||
|
@ -1014,12 +988,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||
applicationUpdater->checkForUpdate();
|
||||
|
||||
// Now that menu is initalized we can sync myAvatar with it's state.
|
||||
// Now that menu is initialized we can sync myAvatar with it's state.
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
|
||||
// FIXME spacemouse code still needs cleanup
|
||||
#if 0
|
||||
// the 3Dconnexion device wants to be initiliazed after a window is displayed.
|
||||
// the 3Dconnexion device wants to be initialized after a window is displayed.
|
||||
SpacemouseManager::getInstance().init();
|
||||
#endif
|
||||
|
||||
|
@ -1035,6 +1009,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
||||
if (webEntity) {
|
||||
webEntity->setProxyWindow(_window->windowHandle());
|
||||
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||
}
|
||||
_keyboardFocusedItem = entityItemID;
|
||||
_lastAcceptedKeyPress = usecTimestampNow();
|
||||
if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
|
||||
|
@ -1124,7 +1101,7 @@ void Application::showCursor(const QCursor& cursor) {
|
|||
_cursorNeedsChanging = true;
|
||||
}
|
||||
|
||||
void Application::updateHeartbeat() {
|
||||
void Application::updateHeartbeat() const {
|
||||
static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();
|
||||
}
|
||||
|
||||
|
@ -1147,6 +1124,10 @@ void Application::aboutToQuit() {
|
|||
}
|
||||
|
||||
void Application::cleanupBeforeQuit() {
|
||||
// add a logline indicating if QTWEBENGINE_REMOTE_DEBUGGING is set or not
|
||||
QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false");
|
||||
qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging;
|
||||
|
||||
// Stop third party processes so that they're not left running in the event of a subsequent shutdown crash.
|
||||
#ifdef HAVE_DDE
|
||||
DependencyManager::get<DdeFaceTracker>()->setEnabled(false);
|
||||
|
@ -1218,12 +1199,12 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
Application::~Application() {
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
tree->setSimulation(NULL);
|
||||
tree->setSimulation(nullptr);
|
||||
|
||||
_octreeProcessor.terminate();
|
||||
_entityEditSender.terminate();
|
||||
|
||||
_physicsEngine->setCharacterController(NULL);
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
ModelEntityItem::cleanupLoadedAnimations();
|
||||
|
||||
|
@ -1264,7 +1245,7 @@ Application::~Application() {
|
|||
|
||||
_window->deleteLater();
|
||||
|
||||
qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
|
||||
qInstallMessageHandler(nullptr); // NOTE: Do this as late as possible so we continue to get our log messages
|
||||
}
|
||||
|
||||
void Application::initializeGL() {
|
||||
|
@ -1280,6 +1261,9 @@ void Application::initializeGL() {
|
|||
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
|
||||
gpu::Context::init<gpu::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
// The gpu context can make child contexts for transfers, so
|
||||
// we need to restore primary rendering context
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
@ -1353,7 +1337,6 @@ void Application::initializeUi() {
|
|||
// though I can't find it. Hence, "ApplicationInterface"
|
||||
rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());
|
||||
rootContext->setContextProperty("ApplicationInterface", this);
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
|
@ -1383,8 +1366,13 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
|
||||
// Caches
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||
rootContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
|
@ -1495,10 +1483,6 @@ void Application::paintGL() {
|
|||
// update the avatar with a fresh HMD pose
|
||||
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
|
||||
|
||||
// update sensorToWorldMatrix for camera and hand controllers
|
||||
getMyAvatar()->updateSensorToWorldMatrix();
|
||||
|
||||
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
|
||||
|
||||
|
@ -1744,7 +1728,7 @@ void Application::runTests() {
|
|||
runUnitTests();
|
||||
}
|
||||
|
||||
void Application::audioMuteToggled() {
|
||||
void Application::audioMuteToggled() const {
|
||||
QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio);
|
||||
Q_CHECK_PTR(muteAction);
|
||||
muteAction->setChecked(DependencyManager::get<AudioClient>()->isMuted());
|
||||
|
@ -1826,7 +1810,7 @@ bool Application::event(QEvent* event) {
|
|||
}
|
||||
|
||||
if ((int)event->type() == (int)Lambda) {
|
||||
((LambdaEvent*)event)->call();
|
||||
static_cast<LambdaEvent*>(event)->call();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1860,25 +1844,25 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)event);
|
||||
mouseMoveEvent(static_cast<QMouseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
mousePressEvent((QMouseEvent*)event);
|
||||
mousePressEvent(static_cast<QMouseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
mouseDoublePressEvent((QMouseEvent*)event);
|
||||
mouseDoublePressEvent(static_cast<QMouseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::MouseButtonRelease:
|
||||
mouseReleaseEvent((QMouseEvent*)event);
|
||||
mouseReleaseEvent(static_cast<QMouseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::KeyPress:
|
||||
keyPressEvent((QKeyEvent*)event);
|
||||
keyPressEvent(static_cast<QKeyEvent*>(event));
|
||||
return true;
|
||||
case QEvent::KeyRelease:
|
||||
keyReleaseEvent((QKeyEvent*)event);
|
||||
keyReleaseEvent(static_cast<QKeyEvent*>(event));
|
||||
return true;
|
||||
case QEvent::FocusOut:
|
||||
focusOutEvent((QFocusEvent*)event);
|
||||
focusOutEvent(static_cast<QFocusEvent*>(event));
|
||||
return true;
|
||||
case QEvent::TouchBegin:
|
||||
touchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||
|
@ -2306,13 +2290,13 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
|
||||
// synthesize events for keys currently pressed, since we may not get their release events
|
||||
foreach (int key, _keysPressed) {
|
||||
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
|
||||
keyReleaseEvent(&event);
|
||||
QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
|
||||
keyReleaseEvent(&keyEvent);
|
||||
}
|
||||
_keysPressed.clear();
|
||||
}
|
||||
|
||||
void Application::maybeToggleMenuVisible(QMouseEvent* event) {
|
||||
void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
|
||||
#ifndef Q_OS_MAC
|
||||
// If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow
|
||||
// then show the menubar.
|
||||
|
@ -2428,7 +2412,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::mouseDoublePressEvent(QMouseEvent* event) {
|
||||
void Application::mouseDoublePressEvent(QMouseEvent* event) const {
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface->isMouseCaptured()) {
|
||||
return;
|
||||
|
@ -2528,7 +2512,7 @@ void Application::touchEndEvent(QTouchEvent* event) {
|
|||
// put any application specific touch behavior below here..
|
||||
}
|
||||
|
||||
void Application::wheelEvent(QWheelEvent* event) {
|
||||
void Application::wheelEvent(QWheelEvent* event) const {
|
||||
_altPressed = false;
|
||||
_controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts
|
||||
|
||||
|
@ -2592,6 +2576,15 @@ void Application::idle(uint64_t now) {
|
|||
_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
|
||||
|
@ -2707,9 +2700,11 @@ float Application::getAverageSimsPerSecond() {
|
|||
}
|
||||
return _simsPerSecondReport;
|
||||
}
|
||||
|
||||
void Application::setAvatarSimrateSample(float sample) {
|
||||
_avatarSimsPerSecond.updateAverage(sample);
|
||||
}
|
||||
|
||||
float Application::getAvatarSimrate() {
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
||||
|
@ -2724,7 +2719,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
|||
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
||||
}
|
||||
|
||||
ivec2 Application::getMouse() {
|
||||
ivec2 Application::getMouse() const {
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
|
||||
// in the HMD, the reticlePosition is the mouse position
|
||||
|
@ -2741,11 +2736,11 @@ FaceTracker* Application::getActiveFaceTracker() {
|
|||
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||
|
||||
return (dde->isActive() ? static_cast<FaceTracker*>(dde.data()) :
|
||||
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : NULL));
|
||||
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : nullptr));
|
||||
}
|
||||
|
||||
FaceTracker* Application::getSelectedFaceTracker() {
|
||||
FaceTracker* faceTracker = NULL;
|
||||
FaceTracker* faceTracker = nullptr;
|
||||
#ifdef HAVE_FACESHIFT
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)) {
|
||||
faceTracker = DependencyManager::get<Faceshift>().data();
|
||||
|
@ -2759,7 +2754,7 @@ FaceTracker* Application::getSelectedFaceTracker() {
|
|||
return faceTracker;
|
||||
}
|
||||
|
||||
void Application::setActiveFaceTracker() {
|
||||
void Application::setActiveFaceTracker() const {
|
||||
#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
|
||||
bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking);
|
||||
#endif
|
||||
|
@ -2896,7 +2891,7 @@ void Application::loadSettings() {
|
|||
_settingsLoaded = true;
|
||||
}
|
||||
|
||||
void Application::saveSettings() {
|
||||
void Application::saveSettings() const {
|
||||
sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
|
||||
DependencyManager::get<AudioClient>()->saveSettings();
|
||||
DependencyManager::get<LODManager>()->saveSettings();
|
||||
|
@ -2986,7 +2981,7 @@ void Application::init() {
|
|||
connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
}
|
||||
|
||||
void Application::updateLOD() {
|
||||
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()) {
|
||||
|
@ -3188,13 +3183,13 @@ void Application::reloadResourceCaches() {
|
|||
getMyAvatar()->resetFullAvatarURL();
|
||||
}
|
||||
|
||||
void Application::rotationModeChanged() {
|
||||
void Application::rotationModeChanged() const {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
getMyAvatar()->setHeadPitch(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateDialogs(float deltaTime) {
|
||||
void Application::updateDialogs(float deltaTime) const {
|
||||
PerformanceTimer perfTimer("updateDialogs");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateDialogs()");
|
||||
|
@ -3230,7 +3225,7 @@ void Application::update(float deltaTime) {
|
|||
if (!_physicsEnabled && _processOctreeStatsCounter > 0) {
|
||||
|
||||
// process octree stats packets are sent in between full sends of a scene.
|
||||
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
|
||||
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
||||
// scene is ready to compute its collision shape.
|
||||
|
||||
if (nearbyEntitiesAreReadyForPhysics()) {
|
||||
|
@ -3429,6 +3424,9 @@ void Application::update(float deltaTime) {
|
|||
avatarManager->updateOtherAvatars(deltaTime);
|
||||
}
|
||||
|
||||
// update sensorToWorldMatrix for camera and hand controllers
|
||||
getMyAvatar()->updateSensorToWorldMatrix();
|
||||
|
||||
qApp->updateMyAvatarLookAtPosition();
|
||||
|
||||
{
|
||||
|
@ -3535,7 +3533,7 @@ int Application::sendNackPackets() {
|
|||
|
||||
QSet<OCTREE_PACKET_SEQUENCE> missingSequenceNumbers;
|
||||
_octreeServerSceneStats.withReadLock([&] {
|
||||
// retreive octree scene stats of this node
|
||||
// retrieve octree scene stats of this node
|
||||
if (_octreeServerSceneStats.find(nodeUUID) == _octreeServerSceneStats.end()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3735,7 +3733,7 @@ bool Application::isHMDMode() const {
|
|||
}
|
||||
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
|
||||
|
||||
QRect Application::getDesirableApplicationGeometry() {
|
||||
QRect Application::getDesirableApplicationGeometry() const {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
||||
// If our parent window is on the HMD, then don't use its geometry, instead use
|
||||
|
@ -3748,7 +3746,7 @@ QRect Application::getDesirableApplicationGeometry() {
|
|||
|
||||
// if our app's screen is the hmd screen, we don't want to place the
|
||||
// running scripts widget on it. So we need to pick a better screen.
|
||||
// we will use the screen for the HMDTools since it's a guarenteed
|
||||
// we will use the screen for the HMDTools since it's a guaranteed
|
||||
// better screen.
|
||||
if (appScreen == hmdScreen) {
|
||||
QScreen* betterScreen = hmdTools->windowHandle()->screen();
|
||||
|
@ -3777,7 +3775,7 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
|||
viewFrustum.calculate();
|
||||
}
|
||||
|
||||
glm::vec3 Application::getSunDirection() {
|
||||
glm::vec3 Application::getSunDirection() const {
|
||||
// Sun direction is in fact just the location of the sun relative to the origin
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
return skyStage->getSunLight()->getDirection();
|
||||
|
@ -3979,7 +3977,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
|
||||
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
|
||||
renderDebugFlags = static_cast<RenderArgs::DebugFlags>(renderDebugFlags |
|
||||
static_cast<int>(RenderArgs::RENDER_DEBUG_HULLS));
|
||||
}
|
||||
renderArgs->_debugFlags = renderDebugFlags;
|
||||
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
|
||||
|
@ -4044,7 +4043,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi
|
|||
myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale());
|
||||
|
||||
} else { // HEAD zoom level
|
||||
// FIXME note that the positioing of the camera relative to the avatar can suffer limited
|
||||
// FIXME note that the positioning of the camera relative to the avatar can suffer limited
|
||||
// precision as the user's position moves further away from the origin. Thus at
|
||||
// /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
|
||||
// wildly as you rotate your avatar because the floating point values are becoming
|
||||
|
@ -4090,7 +4089,7 @@ void Application::resetSensors(bool andReload) {
|
|||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Application::updateWindowTitle(){
|
||||
void Application::updateWindowTitle() const {
|
||||
|
||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -4148,14 +4147,14 @@ void Application::resettingDomain() {
|
|||
_notifiedPacketVersionMismatchThisDomain = false;
|
||||
}
|
||||
|
||||
void Application::nodeAdded(SharedNodePointer node) {
|
||||
void Application::nodeAdded(SharedNodePointer node) const {
|
||||
if (node->getType() == NodeType::AvatarMixer) {
|
||||
// new avatar mixer, send off our identity packet right away
|
||||
getMyAvatar()->sendIdentityPacket();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::nodeActivated(SharedNodePointer node) {
|
||||
void Application::nodeActivated(SharedNodePointer node) const {
|
||||
if (node->getType() == NodeType::AssetServer) {
|
||||
// asset server just connected - check if we have the asset browser showing
|
||||
|
||||
|
@ -4289,7 +4288,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
statsMessageLength = octreeStats.unpackFromPacket(message);
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
NodeToJurisdictionMap* jurisdiction = NULL;
|
||||
NodeToJurisdictionMap* jurisdiction = nullptr;
|
||||
QString serverType;
|
||||
if (sendingNode->getType() == NodeType::EntityServer) {
|
||||
jurisdiction = &_entityServerJurisdictions;
|
||||
|
@ -4375,8 +4374,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
|
||||
// Caches
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
|
||||
|
@ -4445,7 +4449,7 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
|||
return defaultUpload;
|
||||
}
|
||||
|
||||
void Application::setSessionUUID(const QUuid& sessionUUID) {
|
||||
void Application::setSessionUUID(const QUuid& sessionUUID) const {
|
||||
// HACK: until we swap the library dependency order between physics and entities
|
||||
// we cache the sessionID in two distinct places for physics.
|
||||
Physics::setSessionUUID(sessionUUID); // TODO: remove this one
|
||||
|
@ -4586,7 +4590,7 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name)
|
|||
}
|
||||
}
|
||||
|
||||
void Application::toggleRunningScriptsWidget() {
|
||||
void Application::toggleRunningScriptsWidget() const {
|
||||
static const QUrl url("hifi/dialogs/RunningScripts.qml");
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");
|
||||
//if (_runningScriptsWidget->isVisible()) {
|
||||
|
@ -4623,7 +4627,7 @@ void Application::packageModel() {
|
|||
ModelPackager::package();
|
||||
}
|
||||
|
||||
void Application::openUrl(const QUrl& url) {
|
||||
void Application::openUrl(const QUrl& url) const {
|
||||
if (!url.isEmpty()) {
|
||||
if (url.scheme() == HIFI_URL_SCHEME) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
|
@ -4653,7 +4657,7 @@ void Application::setPreviousScriptLocation(const QString& location) {
|
|||
_previousScriptLocation.set(location);
|
||||
}
|
||||
|
||||
void Application::loadScriptURLDialog() {
|
||||
void Application::loadScriptURLDialog() const {
|
||||
auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL");
|
||||
if (!newScript.isEmpty()) {
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript);
|
||||
|
@ -4718,7 +4722,7 @@ void Application::notifyPacketVersionMismatch() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::checkSkeleton() {
|
||||
void Application::checkSkeleton() const {
|
||||
if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) {
|
||||
qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
|
||||
|
||||
|
@ -4799,7 +4803,7 @@ void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
|||
}
|
||||
}
|
||||
|
||||
int Application::getMaxOctreePacketsPerSecond() {
|
||||
int Application::getMaxOctreePacketsPerSecond() const {
|
||||
return _maxOctreePPS;
|
||||
}
|
||||
|
||||
|
@ -4823,7 +4827,7 @@ DisplayPlugin* Application::getActiveDisplayPlugin() {
|
|||
}
|
||||
|
||||
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
|
||||
return ((Application*)this)->getActiveDisplayPlugin();
|
||||
return const_cast<Application*>(this)->getActiveDisplayPlugin();
|
||||
}
|
||||
|
||||
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
|
||||
|
@ -4903,7 +4907,7 @@ void Application::updateDisplayMode() {
|
|||
}
|
||||
}
|
||||
|
||||
// concactonate the groupings into a single list in the order: standard, advanced, developer
|
||||
// concatenate the groupings into a single list in the order: standard, advanced, developer
|
||||
standard.insert(std::end(standard), std::begin(advanced), std::end(advanced));
|
||||
standard.insert(std::end(standard), std::begin(developer), std::end(developer));
|
||||
|
||||
|
@ -4915,7 +4919,7 @@ void Application::updateDisplayMode() {
|
|||
first = false;
|
||||
}
|
||||
|
||||
// after all plugins have been added to the menu, add a seperator to the menu
|
||||
// after all plugins have been added to the menu, add a separator to the menu
|
||||
auto menu = Menu::getInstance();
|
||||
auto parent = menu->getMenu(MenuOption::OutputMenu);
|
||||
parent->addSeparator();
|
||||
|
@ -4986,7 +4990,7 @@ void Application::updateDisplayMode() {
|
|||
|
||||
emit activeDisplayPluginChanged();
|
||||
|
||||
// reset the avatar, to set head and hand palms back to a resonable default pose.
|
||||
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
||||
getMyAvatar()->reset(false);
|
||||
|
||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
|
@ -5115,7 +5119,7 @@ void Application::setActiveDisplayPlugin(const QString& pluginName) {
|
|||
updateDisplayMode();
|
||||
}
|
||||
|
||||
void Application::handleLocalServerConnection() {
|
||||
void Application::handleLocalServerConnection() const {
|
||||
auto server = qobject_cast<QLocalServer*>(sender());
|
||||
|
||||
qDebug() << "Got connection on local server from additional instance - waiting for parameters";
|
||||
|
@ -5128,7 +5132,7 @@ void Application::handleLocalServerConnection() {
|
|||
qApp->getWindow()->activateWindow();
|
||||
}
|
||||
|
||||
void Application::readArgumentsFromLocalSocket() {
|
||||
void Application::readArgumentsFromLocalSocket() const {
|
||||
auto socket = qobject_cast<QLocalSocket*>(sender());
|
||||
|
||||
auto message = socket->readAll();
|
||||
|
|
|
@ -135,14 +135,14 @@ public:
|
|||
const ViewFrustum* getDisplayViewFrustum() const;
|
||||
ViewFrustum* getShadowViewFrustum() override { return &_shadowViewFrustum; }
|
||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||
EntityTreeRenderer* getEntities() { return DependencyManager::get<EntityTreeRenderer>().data(); }
|
||||
EntityTreeRenderer* getEntities() const { return DependencyManager::get<EntityTreeRenderer>().data(); }
|
||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||
MainWindow* getWindow() { return _window; }
|
||||
EntityTreePointer getEntityClipboard() { return _entityClipboard; }
|
||||
MainWindow* getWindow() const { return _window; }
|
||||
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
|
||||
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
||||
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
||||
|
||||
ivec2 getMouse();
|
||||
ivec2 getMouse() const;
|
||||
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
FaceTracker* getSelectedFaceTracker();
|
||||
|
@ -155,7 +155,7 @@ public:
|
|||
|
||||
bool isForeground() const { return _isForeground; }
|
||||
|
||||
uint32_t getFrameCount() { return _frameCount; }
|
||||
uint32_t getFrameCount() const { return _frameCount; }
|
||||
float getFps() const { return _fps; }
|
||||
float getTargetFrameRate(); // frames/second
|
||||
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
||||
|
@ -179,7 +179,7 @@ public:
|
|||
DisplayPlugin* getActiveDisplayPlugin();
|
||||
const DisplayPlugin* getActiveDisplayPlugin() const;
|
||||
|
||||
FileLogger* getLogger() { return _logger; }
|
||||
FileLogger* getLogger() const { return _logger; }
|
||||
|
||||
glm::vec2 getViewportDimensions() const;
|
||||
|
||||
|
@ -189,7 +189,7 @@ public:
|
|||
|
||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||
|
||||
// the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display
|
||||
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
||||
// rendering of several elements depend on that
|
||||
// TODO: carry that information on the Camera as a setting
|
||||
bool isHMDMode() const;
|
||||
|
@ -197,14 +197,14 @@ public:
|
|||
glm::mat4 getEyeOffset(int eye) const;
|
||||
glm::mat4 getEyeProjection(int eye) const;
|
||||
|
||||
QRect getDesirableApplicationGeometry();
|
||||
QRect getDesirableApplicationGeometry() const;
|
||||
Bookmarks* getBookmarks() const { return _bookmarks; }
|
||||
|
||||
virtual bool canAcceptURL(const QString& url) const override;
|
||||
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
|
||||
|
||||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||
int getMaxOctreePacketsPerSecond();
|
||||
int getMaxOctreePacketsPerSecond() const;
|
||||
|
||||
render::ScenePointer getMain3DScene() override { return _main3DScene; }
|
||||
render::ScenePointer getMain3DScene() const { return _main3DScene; }
|
||||
|
@ -239,22 +239,22 @@ public slots:
|
|||
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
bool importEntities(const QString& url);
|
||||
|
||||
void setLowVelocityFilter(bool lowVelocityFilter);
|
||||
static void setLowVelocityFilter(bool lowVelocityFilter);
|
||||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
void toggleLogDialog();
|
||||
void toggleRunningScriptsWidget();
|
||||
void toggleRunningScriptsWidget() const;
|
||||
void toggleAssetServerWidget(QString filePath = "");
|
||||
|
||||
void handleLocalServerConnection();
|
||||
void readArgumentsFromLocalSocket();
|
||||
void handleLocalServerConnection() const;
|
||||
void readArgumentsFromLocalSocket() const;
|
||||
|
||||
void packageModel();
|
||||
static void packageModel();
|
||||
|
||||
void openUrl(const QUrl& url);
|
||||
void openUrl(const QUrl& url) const;
|
||||
|
||||
void resetSensors(bool andReload = false);
|
||||
void setActiveFaceTracker();
|
||||
void setActiveFaceTracker() const;
|
||||
|
||||
#ifdef HAVE_IVIEWHMD
|
||||
void setActiveEyeTracker();
|
||||
|
@ -264,7 +264,7 @@ public slots:
|
|||
#endif
|
||||
|
||||
void aboutApp();
|
||||
void showHelp();
|
||||
static void showHelp();
|
||||
|
||||
void cycleCamera();
|
||||
void cameraMenuChanged();
|
||||
|
@ -273,14 +273,14 @@ public slots:
|
|||
|
||||
void reloadResourceCaches();
|
||||
|
||||
void updateHeartbeat();
|
||||
void updateHeartbeat() const;
|
||||
|
||||
void crashApplication();
|
||||
void deadlockApplication();
|
||||
static void crashApplication();
|
||||
static void deadlockApplication();
|
||||
|
||||
void rotationModeChanged();
|
||||
void rotationModeChanged() const;
|
||||
|
||||
void runTests();
|
||||
static void runTests();
|
||||
|
||||
private slots:
|
||||
void showDesktop();
|
||||
|
@ -290,7 +290,7 @@ private slots:
|
|||
|
||||
void resettingDomain();
|
||||
|
||||
void audioMuteToggled();
|
||||
void audioMuteToggled() const;
|
||||
void faceTrackerMuteToggled();
|
||||
|
||||
void activeChanged(Qt::ApplicationState state);
|
||||
|
@ -298,7 +298,7 @@ private slots:
|
|||
void notifyPacketVersionMismatch();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
bool acceptSnapshot(const QString& urlString);
|
||||
bool askToSetAvatarUrl(const QString& url);
|
||||
|
@ -308,18 +308,18 @@ private slots:
|
|||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
void setSessionUUID(const QUuid& sessionUUID) const;
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
void nodeAdded(SharedNodePointer node);
|
||||
void nodeActivated(SharedNodePointer node);
|
||||
void updateWindowTitle() const;
|
||||
void nodeAdded(SharedNodePointer node) const;
|
||||
void nodeActivated(SharedNodePointer node) const;
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
void packetSent(quint64 length);
|
||||
static void packetSent(quint64 length);
|
||||
void updateDisplayMode();
|
||||
void updateInputModes();
|
||||
|
||||
private:
|
||||
void initDisplay();
|
||||
static void initDisplay();
|
||||
void init();
|
||||
|
||||
void cleanupBeforeQuit();
|
||||
|
@ -327,14 +327,14 @@ private:
|
|||
void update(float deltaTime);
|
||||
|
||||
// Various helper functions called during update()
|
||||
void updateLOD();
|
||||
void updateLOD() const;
|
||||
void updateThreads(float deltaTime);
|
||||
void updateDialogs(float deltaTime);
|
||||
void updateDialogs(float deltaTime) const;
|
||||
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
glm::vec3 getSunDirection();
|
||||
glm::vec3 getSunDirection() const;
|
||||
|
||||
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region);
|
||||
|
||||
|
@ -344,7 +344,7 @@ private:
|
|||
|
||||
MyAvatar* getMyAvatar() const;
|
||||
|
||||
void checkSkeleton();
|
||||
void checkSkeleton() const;
|
||||
|
||||
void initializeAcceptedFiles();
|
||||
|
||||
|
@ -366,18 +366,18 @@ private:
|
|||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoublePressEvent(QMouseEvent* event);
|
||||
void mouseDoublePressEvent(QMouseEvent* event) const;
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
void touchBeginEvent(QTouchEvent* event);
|
||||
void touchEndEvent(QTouchEvent* event);
|
||||
void touchUpdateEvent(QTouchEvent* event);
|
||||
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
void wheelEvent(QWheelEvent* event) const;
|
||||
void dropEvent(QDropEvent* event);
|
||||
void dragEnterEvent(QDragEnterEvent* event);
|
||||
static void dragEnterEvent(QDragEnterEvent* event);
|
||||
|
||||
void maybeToggleMenuVisible(QMouseEvent* event);
|
||||
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||
|
||||
MainWindow* _window;
|
||||
QElapsedTimer& _sessionRunTimer;
|
||||
|
@ -423,7 +423,7 @@ private:
|
|||
int _avatarSimsPerSecondReport {0};
|
||||
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _mirrorCamera; // Cammera for mirror view
|
||||
Camera _mirrorCamera; // Camera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
|
@ -518,6 +518,8 @@ private:
|
|||
std::mutex _preRenderLambdasLock;
|
||||
|
||||
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
||||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -63,6 +63,11 @@ bool Bookmarks::contains(const QString& name) const {
|
|||
void Bookmarks::readFromFile() {
|
||||
QFile loadFile(_bookmarksFilename);
|
||||
|
||||
if (!loadFile.exists()) {
|
||||
// User has not yet saved bookmarks
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loadFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Couldn't open bookmarks file for reading");
|
||||
return;
|
||||
|
|
|
@ -434,6 +434,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
_showCollisionHull = shouldShowCollisionHull;
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||
|
|
|
@ -9,17 +9,20 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <QtDebug>
|
||||
#include <QtEndian>
|
||||
#include <QFileInfo>
|
||||
#include "FBXReader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QtEndian>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
template<class T> int streamSize() {
|
||||
return sizeof(T);
|
||||
}
|
||||
|
@ -299,6 +302,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
|||
}
|
||||
|
||||
FBXNode FBXReader::parseFBX(QIODevice* device) {
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, device);
|
||||
// verify the prolog
|
||||
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
||||
|
|
|
@ -12,17 +12,20 @@
|
|||
// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/
|
||||
// http://paulbourke.net/dataformats/obj/
|
||||
|
||||
#include "OBJReader.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QIODevice>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QEventLoop>
|
||||
#include <ctype.h> // .obj files are not locale-specific. The C/ASCII charset applies.
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f},
|
||||
|
@ -404,7 +407,7 @@ done:
|
|||
|
||||
|
||||
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr);
|
||||
QBuffer buffer { &model };
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
||||
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
||||
format.setVersion(4, 1);
|
||||
format.setVersion(4, 5);
|
||||
#ifdef DEBUG
|
||||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
#endif
|
||||
|
@ -27,7 +27,7 @@ const QGLFormat& getDefaultGLFormat() {
|
|||
static QGLFormat glFormat;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
glFormat.setVersion(4, 1);
|
||||
glFormat.setVersion(4, 5);
|
||||
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
||||
glFormat.setSampleBuffers(false);
|
||||
glFormat.setDepth(false);
|
||||
|
|
|
@ -83,3 +83,8 @@ void OffscreenGLCanvas::doneCurrent() {
|
|||
QObject* OffscreenGLCanvas::getContextObject() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
|
||||
moveToThread(thread);
|
||||
_context->moveToThread(thread);
|
||||
}
|
|
@ -26,6 +26,7 @@ public:
|
|||
bool create(QOpenGLContext* sharedContext = nullptr);
|
||||
bool makeCurrent();
|
||||
void doneCurrent();
|
||||
void moveToThreadWithContext(QThread* thread);
|
||||
QOpenGLContext* getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OglplusHelpers.h"
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <set>
|
||||
#include <oglplus/shapes/plane.hpp>
|
||||
#include <oglplus/shapes/sky_box.hpp>
|
||||
|
||||
using namespace oglplus;
|
||||
using namespace oglplus::shapes;
|
||||
|
@ -20,11 +22,13 @@ uniform mat4 mvp = mat4(1);
|
|||
in vec3 Position;
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec3 vPosition;
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = mvp * vec4(Position, 1);
|
||||
vTexCoord = TexCoord ;
|
||||
vTexCoord = TexCoord;
|
||||
vPosition = Position;
|
||||
}
|
||||
|
||||
)VS";
|
||||
|
@ -35,7 +39,9 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
|
|||
uniform sampler2D sampler;
|
||||
uniform float alpha = 1.0;
|
||||
|
||||
in vec3 vPosition;
|
||||
in vec2 vTexCoord;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
|
@ -47,12 +53,38 @@ void main() {
|
|||
)FS";
|
||||
|
||||
|
||||
static const char * SIMPLE_TEXTURED_CUBEMAP_FS = R"FS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
uniform samplerCube sampler;
|
||||
uniform float alpha = 1.0;
|
||||
|
||||
in vec3 vPosition;
|
||||
in vec3 vTexCoord;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
|
||||
FragColor = texture(sampler, vPosition);
|
||||
FragColor.a *= alpha;
|
||||
}
|
||||
|
||||
)FS";
|
||||
|
||||
|
||||
ProgramPtr loadDefaultShader() {
|
||||
ProgramPtr result;
|
||||
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
||||
return result;
|
||||
}
|
||||
|
||||
ProgramPtr loadCubemapShader() {
|
||||
ProgramPtr result;
|
||||
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_CUBEMAP_FS);
|
||||
return result;
|
||||
}
|
||||
|
||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
||||
using namespace oglplus;
|
||||
try {
|
||||
|
@ -93,6 +125,10 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) {
|
|||
);
|
||||
}
|
||||
|
||||
ShapeWrapperPtr loadSkybox(ProgramPtr program) {
|
||||
return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program));
|
||||
}
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
static glm::vec3 getPoint(float yaw, float pitch) {
|
||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include <oglplus/bound/framebuffer.hpp>
|
||||
#include <oglplus/bound/renderbuffer.hpp>
|
||||
#include <oglplus/shapes/wrapper.hpp>
|
||||
#include <oglplus/shapes/plane.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(pop)
|
||||
|
@ -55,7 +54,9 @@ using ProgramPtr = std::shared_ptr<oglplus::Program>;
|
|||
using Mat4Uniform = oglplus::Uniform<mat4>;
|
||||
|
||||
ProgramPtr loadDefaultShader();
|
||||
ProgramPtr loadCubemapShader();
|
||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
||||
ShapeWrapperPtr loadSkybox(ProgramPtr program);
|
||||
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
||||
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32);
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
#include <QOpenGLContext>
|
||||
|
||||
QOpenGLContext* QOpenGLContextWrapper::currentContext() {
|
||||
return QOpenGLContext::currentContext();
|
||||
}
|
||||
|
||||
QOpenGLContextWrapper::QOpenGLContextWrapper() :
|
||||
_context(new QOpenGLContext)
|
||||
|
|
|
@ -19,7 +19,6 @@ class QSurfaceFormat;
|
|||
class QOpenGLContextWrapper {
|
||||
public:
|
||||
QOpenGLContextWrapper();
|
||||
|
||||
void setFormat(const QSurfaceFormat& format);
|
||||
bool create();
|
||||
void swapBuffers(QSurface* surface);
|
||||
|
@ -27,6 +26,8 @@ public:
|
|||
void doneCurrent();
|
||||
void setShareContext(QOpenGLContext* otherContext);
|
||||
|
||||
static QOpenGLContext* currentContext();
|
||||
|
||||
QOpenGLContext* getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ GLBackend::GLBackend() {
|
|||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
||||
initInput();
|
||||
initTransform();
|
||||
initTextureTransferHelper();
|
||||
}
|
||||
|
||||
GLBackend::~GLBackend() {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
namespace gpu {
|
||||
|
||||
class GLTextureTransferHelper;
|
||||
|
||||
class GLBackend : public Backend {
|
||||
|
||||
// Context Backend static interface required
|
||||
|
@ -35,7 +37,6 @@ class GLBackend : public Backend {
|
|||
explicit GLBackend(bool syncCache);
|
||||
GLBackend();
|
||||
public:
|
||||
|
||||
virtual ~GLBackend();
|
||||
|
||||
virtual void render(Batch& batch);
|
||||
|
@ -75,25 +76,63 @@ public:
|
|||
|
||||
class GLTexture : public GPUObject {
|
||||
public:
|
||||
Stamp _storageStamp;
|
||||
Stamp _contentStamp;
|
||||
GLuint _texture;
|
||||
GLenum _target;
|
||||
const Stamp _storageStamp;
|
||||
Stamp _contentStamp { 0 };
|
||||
const GLuint _texture;
|
||||
const GLenum _target;
|
||||
|
||||
GLTexture();
|
||||
GLTexture(const gpu::Texture& gpuTexture);
|
||||
~GLTexture();
|
||||
|
||||
void setSize(GLuint size);
|
||||
GLuint size() const { return _size; }
|
||||
|
||||
enum SyncState {
|
||||
// The texture is currently undergoing no processing, although it's content
|
||||
// may be out of date, or it's storage may be invalid relative to the
|
||||
// owning GPU texture
|
||||
Idle,
|
||||
// The texture has been queued for transfer to the GPU
|
||||
Pending,
|
||||
// The texture has been transferred to the GPU, but is awaiting
|
||||
// any post transfer operations that may need to occur on the
|
||||
// primary rendering thread
|
||||
Transferred,
|
||||
};
|
||||
|
||||
void setSyncState(SyncState syncState) { _syncState = syncState; }
|
||||
SyncState getSyncState() const { return _syncState; }
|
||||
|
||||
// Is the storage out of date relative to the gpu texture?
|
||||
bool isInvalid() const;
|
||||
|
||||
// Is the content out of date relative to the gpu texture?
|
||||
bool isOutdated() const;
|
||||
|
||||
// Is the texture in a state where it can be rendered with no work?
|
||||
bool isReady() const;
|
||||
|
||||
// Move the image bits from the CPU to the GPU
|
||||
void transfer() const;
|
||||
|
||||
// Execute any post-move operations that must occur only on the main thread
|
||||
void postTransfer();
|
||||
|
||||
static const size_t CUBE_NUM_FACES = 6;
|
||||
static const GLenum CUBE_FACE_LAYOUT[6];
|
||||
|
||||
private:
|
||||
GLuint _size;
|
||||
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 Texture& texture);
|
||||
static GLTexture* syncGPUObject(const TexturePointer& texture);
|
||||
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
||||
|
||||
// very specific for now
|
||||
static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object);
|
||||
static void syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object);
|
||||
|
||||
class GLShader : public GPUObject {
|
||||
public:
|
||||
|
@ -241,6 +280,11 @@ protected:
|
|||
void renderPassTransfer(Batch& batch);
|
||||
void renderPassDraw(Batch& batch);
|
||||
|
||||
void initTextureTransferHelper();
|
||||
static void transferGPUObject(const TexturePointer& texture);
|
||||
|
||||
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
|
||||
|
||||
// Draw Stage
|
||||
void do_draw(Batch& batch, size_t paramOffset);
|
||||
void do_drawIndexed(Batch& batch, size_t paramOffset);
|
||||
|
@ -484,6 +528,7 @@ protected:
|
|||
|
||||
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
|
||||
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
if (gltexture) {
|
||||
|
|
|
@ -255,7 +255,7 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
|
|||
_stats._RSNumTextureBounded++;
|
||||
|
||||
// Always make sure the GLObject is in sync
|
||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
||||
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||
if (object) {
|
||||
GLuint to = object->_texture;
|
||||
GLuint target = object->_target;
|
||||
|
|
|
@ -9,18 +9,79 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "GPULogging.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include "GLBackendShared.h"
|
||||
#include "GLTexelFormat.h"
|
||||
#include "GLBackendTextureTransfer.h"
|
||||
|
||||
using namespace gpu;
|
||||
|
||||
GLBackend::GLTexture::GLTexture() :
|
||||
_storageStamp(0),
|
||||
_contentStamp(0),
|
||||
_texture(0),
|
||||
_target(GL_TEXTURE_2D),
|
||||
_size(0)
|
||||
GLenum gpuToGLTextureType(const Texture& texture) {
|
||||
switch (texture.getType()) {
|
||||
case Texture::TEX_2D:
|
||||
return GL_TEXTURE_2D;
|
||||
break;
|
||||
|
||||
case Texture::TEX_CUBE:
|
||||
return GL_TEXTURE_CUBE_MAP;
|
||||
break;
|
||||
|
||||
default:
|
||||
qFatal("Unsupported texture type");
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
return GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
GLuint allocateSingleTexture() {
|
||||
GLuint result;
|
||||
glGenTextures(1, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = {
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
levels = std::max(1, std::min(texture.maxMip() + 1, levels));
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
GLBackend::GLTexture::~GLTexture() {
|
||||
|
@ -31,562 +92,163 @@ GLBackend::GLTexture::~GLTexture() {
|
|||
Backend::decrementTextureGPUCount();
|
||||
}
|
||||
|
||||
void GLBackend::GLTexture::setSize(GLuint size) {
|
||||
Backend::updateTextureGPUMemoryUsage(_size, size);
|
||||
_size = size;
|
||||
bool GLBackend::GLTexture::isInvalid() const {
|
||||
return _storageStamp < _gpuTexture.getStamp();
|
||||
}
|
||||
|
||||
class GLTexelFormat {
|
||||
public:
|
||||
GLenum internalFormat;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
bool GLBackend::GLTexture::isOutdated() const {
|
||||
return _contentStamp < _gpuTexture.getDataStamp();
|
||||
}
|
||||
|
||||
static 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_RED;
|
||||
break;
|
||||
case gpu::DEPTH:
|
||||
texel.internalFormat = GL_DEPTH_COMPONENT;
|
||||
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_RG;
|
||||
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_RGB;
|
||||
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_RGB;
|
||||
break;
|
||||
case gpu::RGBA:
|
||||
texel.internalFormat = GL_RGBA;
|
||||
break;
|
||||
case gpu::SRGB:
|
||||
texel.internalFormat = GL_SRGB;
|
||||
break;
|
||||
case gpu::SRGBA:
|
||||
texel.internalFormat = GL_SRGB_ALPHA;
|
||||
break;
|
||||
default:
|
||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||
}
|
||||
return texel;
|
||||
} else {
|
||||
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:
|
||||
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_RED;
|
||||
break;
|
||||
}
|
||||
case gpu::NINT32: {
|
||||
texel.internalFormat = GL_RED_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_SLUMINANCE;
|
||||
} 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_COMPONENT;
|
||||
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_RG;
|
||||
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_RGB;
|
||||
break;
|
||||
case gpu::SRGB:
|
||||
case gpu::SRGBA:
|
||||
texel.internalFormat = GL_SRGB; // 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_RGB;
|
||||
break;
|
||||
case gpu::RGBA:
|
||||
texel.internalFormat = GL_RGBA;
|
||||
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_SRGB;
|
||||
break;
|
||||
case gpu::SRGBA:
|
||||
texel.internalFormat = GL_SRGB_ALPHA; // 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;
|
||||
}
|
||||
bool GLBackend::GLTexture::isReady() const {
|
||||
// If we have an invalid texture, we're never ready
|
||||
if (isInvalid()) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// If we're out of date, but the transfer is in progress, report ready
|
||||
// as a special case
|
||||
auto syncState = _syncState.load();
|
||||
|
||||
GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
||||
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
|
||||
if (isOutdated()) {
|
||||
return Pending == syncState;
|
||||
}
|
||||
|
||||
// If GPU object already created and in sync
|
||||
bool needUpdate = false;
|
||||
if (object && (object->_storageStamp == texture.getStamp())) {
|
||||
// If gpu object info is in sync with sysmem version
|
||||
if (object->_contentStamp >= texture.getDataStamp()) {
|
||||
// Then all good, GPU object is ready to be used
|
||||
return object;
|
||||
} else {
|
||||
// Need to update the content of the GPU object from the source sysmem of the texture
|
||||
needUpdate = true;
|
||||
}
|
||||
} else if (!texture.isDefined()) {
|
||||
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());
|
||||
(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;
|
||||
// Need to update the content of the GPU object from the source sysmem of the texture
|
||||
if (_contentStamp >= _gpuTexture.getDataStamp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindTexture(_target, _texture);
|
||||
// GO through the process of allocating the correct storage and/or update the content
|
||||
switch (_gpuTexture.getType()) {
|
||||
case Texture::TEX_2D:
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(0)) {
|
||||
transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0));
|
||||
}
|
||||
break;
|
||||
|
||||
case Texture::TEX_CUBE:
|
||||
// transfer pixels from each faces
|
||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
|
||||
if (_gpuTexture.isStoredMipFaceAvailable(0, f)) {
|
||||
transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported";
|
||||
break;
|
||||
}
|
||||
|
||||
if (_gpuTexture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(_target);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// Do any post-transfer operations that might be required on the main context / rendering thread
|
||||
void GLBackend::GLTexture::postTransfer() {
|
||||
setSyncState(GLTexture::Idle);
|
||||
// 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:
|
||||
_gpuTexture.notifyMipFaceGPULoaded(0, 0);
|
||||
break;
|
||||
|
||||
case Texture::TEX_CUBE:
|
||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) {
|
||||
_gpuTexture.notifyMipFaceGPULoaded(0, f);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||
const Texture& texture = *texturePointer;
|
||||
if (!texture.isDefined()) {
|
||||
// NO texture definition yet so let's avoid thinking
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the object hasn't been created, or the object definition is out of date, drop and re-create
|
||||
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
|
||||
if (object && object->isReady()) {
|
||||
return object;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
// This automatically destroys the old texture
|
||||
object = new GLTexture(texture);
|
||||
}
|
||||
|
||||
// need to have a gpu object?
|
||||
if (!object) {
|
||||
object = new GLTexture();
|
||||
glGenTextures(1, &object->_texture);
|
||||
(void) CHECK_GL_ERROR();
|
||||
Backend::setGPUObject(texture, object);
|
||||
if (texture.getNumSlices() != 1) {
|
||||
return object;
|
||||
}
|
||||
|
||||
// GO through the process of allocating the correct storage and/or update the content
|
||||
switch (texture.getType()) {
|
||||
case Texture::TEX_2D: {
|
||||
if (texture.getNumSlices() == 1) {
|
||||
GLint boundTex = -1;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
|
||||
glBindTexture(GL_TEXTURE_2D, object->_texture);
|
||||
|
||||
if (needUpdate) {
|
||||
if (texture.isStoredMipFaceAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
||||
const GLvoid* bytes = mip->readData();
|
||||
Element srcFormat = mip->getFormat();
|
||||
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, object->_texture);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
||||
texelFormat.format, texelFormat.type, bytes);
|
||||
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_2D;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
|
||||
// At this point the mip piels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
}
|
||||
} else {
|
||||
const GLvoid* bytes = 0;
|
||||
Element srcFormat = texture.getTexelFormat();
|
||||
if (texture.isStoredMipFaceAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
||||
|
||||
bytes = mip->readData();
|
||||
srcFormat = mip->getFormat();
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
}
|
||||
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
||||
texelFormat.format, texelFormat.type, bytes);
|
||||
|
||||
if (bytes && texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
object->_target = GL_TEXTURE_2D;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
|
||||
object->_storageStamp = texture.getStamp();
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
object->setSize((GLuint)texture.getSize());
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, boundTex);
|
||||
}
|
||||
break;
|
||||
// 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()) {
|
||||
_textureTransferHelper->transferTexture(texturePointer);
|
||||
}
|
||||
case Texture::TEX_CUBE: {
|
||||
if (texture.getNumSlices() == 1) {
|
||||
GLint boundTex = -1;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
const int NUM_FACES = 6;
|
||||
const GLenum FACE_LAYOUT[] = {
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
|
||||
if (needUpdate) {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
|
||||
// transfer pixels from each faces
|
||||
for (int f = 0; f < NUM_FACES; f++) {
|
||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
||||
Element srcFormat = mipFace->getFormat();
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, f);
|
||||
}
|
||||
}
|
||||
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
|
||||
// transfer pixels from each faces
|
||||
for (int f = 0; f < NUM_FACES; f++) {
|
||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
||||
Element srcFormat = mipFace->getFormat();
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, f);
|
||||
}
|
||||
}
|
||||
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
object->_storageStamp = texture.getStamp();
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
object->setSize((GLuint)texture.getSize());
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, boundTex);
|
||||
}
|
||||
break;
|
||||
if (GLTexture::Transferred == object->getSyncState()) {
|
||||
object->postTransfer();
|
||||
}
|
||||
default:
|
||||
qCDebug(gpulogging) << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported";
|
||||
}
|
||||
(void) CHECK_GL_ERROR();
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
std::shared_ptr<GLTextureTransferHelper> GLBackend::_textureTransferHelper;
|
||||
|
||||
void GLBackend::initTextureTransferHelper() {
|
||||
_textureTransferHelper = std::make_shared<GLTextureTransferHelper>();
|
||||
}
|
||||
|
||||
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||
if (!texture) {
|
||||
|
@ -594,7 +256,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
|||
}
|
||||
GLTexture* object { nullptr };
|
||||
if (sync) {
|
||||
object = GLBackend::syncGPUObject(*texture);
|
||||
object = GLBackend::syncGPUObject(texture);
|
||||
} else {
|
||||
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
||||
}
|
||||
|
@ -605,38 +267,37 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
|||
}
|
||||
}
|
||||
|
||||
void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object) {
|
||||
void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object) {
|
||||
if (!object) return;
|
||||
if (!object->_texture) return;
|
||||
|
||||
class GLFilterMode {
|
||||
public:
|
||||
GLint minFilter;
|
||||
GLint magFilter;
|
||||
};
|
||||
static const GLFilterMode filterModes[] = {
|
||||
{GL_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_POINT,
|
||||
{GL_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR,
|
||||
{GL_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT,
|
||||
{GL_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR,
|
||||
|
||||
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT,
|
||||
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT,
|
||||
{GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
||||
{GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
||||
{GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
||||
{GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
||||
{GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
||||
{GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
||||
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_MIP_LINEAR,
|
||||
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} //FILTER_ANISOTROPIC,
|
||||
static const GLFilterMode filterModes[] = {
|
||||
{ GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT,
|
||||
{ GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR,
|
||||
{ GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT,
|
||||
{ GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR,
|
||||
|
||||
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT,
|
||||
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT,
|
||||
{ GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
||||
{ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
||||
{ GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
||||
{ GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
||||
{ GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
||||
{ GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
||||
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR,
|
||||
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC,
|
||||
};
|
||||
|
||||
auto fm = filterModes[sampler.getFilter()];
|
||||
glTexParameteri(object->_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
||||
glTexParameteri(object->_target, GL_TEXTURE_MAG_FILTER, fm.magFilter);
|
||||
|
||||
static const GLenum comparisonFuncs[] = {
|
||||
static const GLenum comparisonFuncs[] = {
|
||||
GL_NEVER,
|
||||
GL_LESS,
|
||||
GL_EQUAL,
|
||||
|
@ -653,7 +314,7 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
|||
glTexParameteri(object->_target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
||||
}
|
||||
|
||||
static const GLenum wrapModes[] = {
|
||||
static const GLenum wrapModes[] = {
|
||||
GL_REPEAT, // WRAP_REPEAT,
|
||||
GL_MIRRORED_REPEAT, // WRAP_MIRROR,
|
||||
GL_CLAMP_TO_EDGE, // WRAP_CLAMP,
|
||||
|
@ -664,23 +325,20 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
|||
glTexParameteri(object->_target, GL_TEXTURE_WRAP_T, wrapModes[sampler.getWrapModeV()]);
|
||||
glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]);
|
||||
|
||||
glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*) &sampler.getBorderColor());
|
||||
glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
||||
glTexParameteri(object->_target, GL_TEXTURE_BASE_LEVEL, sampler.getMipOffset());
|
||||
glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float) sampler.getMinMip());
|
||||
glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||
glTexParameterf(object->_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
if (!resourceTexture) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
||||
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
|
@ -695,7 +353,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
|||
|
||||
if (freeSlot < 0) {
|
||||
// If had to use slot 0 then restore state
|
||||
GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]);
|
||||
GLTexture* boundObject = GLBackend::syncGPUObject(_resource._textures[0]);
|
||||
if (boundObject) {
|
||||
glBindTexture(boundObject->_target, boundObject->_texture);
|
||||
}
|
||||
|
|
126
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
126
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// 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 "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
|
||||
|
||||
using namespace gpu;
|
||||
|
||||
GLTextureTransferHelper::GLTextureTransferHelper() {
|
||||
#ifdef THREADED_TEXTURE_TRANSFER
|
||||
_canvas = std::make_shared<OffscreenGLCanvas>();
|
||||
_canvas->create(QOpenGLContextWrapper::currentContext());
|
||||
if (!_canvas->makeCurrent()) {
|
||||
qFatal("Unable to create texture transfer context");
|
||||
}
|
||||
_canvas->doneCurrent();
|
||||
initialize(true, QThread::LowPriority);
|
||||
_canvas->moveToThreadWithContext(_thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
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();
|
||||
object->setSyncState(GLBackend::GLTexture::Pending);
|
||||
queueItem(package);
|
||||
#else
|
||||
object->transfer();
|
||||
object->postTransfer();
|
||||
#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
|
||||
}
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
61
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
61
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// 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 <GenericQueueThread.h>
|
||||
#include "GLBackendShared.h"
|
||||
|
||||
#define THREADED_TEXTURE_TRANSFER
|
||||
|
||||
class OffscreenGLCanvas;
|
||||
|
||||
namespace gpu {
|
||||
|
||||
struct TextureTransferPackage {
|
||||
std::weak_ptr<Texture> texture;
|
||||
GLsync fence;
|
||||
};
|
||||
|
||||
class GLTextureTransferHelper : public GenericQueueThread<TextureTransferPackage> {
|
||||
public:
|
||||
GLTextureTransferHelper();
|
||||
void transferTexture(const gpu::TexturePointer& texturePointer);
|
||||
void postTransfer(const gpu::TexturePointer& texturePointer);
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
bool processQueueItems(const Queue& messages) override;
|
||||
void transferTextureSynchronous(const gpu::Texture& texture);
|
||||
|
||||
private:
|
||||
std::shared_ptr<OffscreenGLCanvas> _canvas;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void withPreservedTexture(GLenum target, F f) {
|
||||
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();
|
||||
|
||||
f();
|
||||
|
||||
glBindTexture(target, boundTex);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
}
|
375
libraries/gpu/src/gpu/GLTexelFormat.h
Normal file
375
libraries/gpu/src/gpu/GLTexelFormat.h
Normal file
|
@ -0,0 +1,375 @@
|
|||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include "ModelCache.h"
|
||||
#include <Finally.h>
|
||||
#include <FSTReader.h>
|
||||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
|
@ -117,6 +118,9 @@ void GeometryReader::run() {
|
|||
originalPriority = QThread::NormalPriority;
|
||||
}
|
||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||
Finally setPriorityBackToNormal([originalPriority]() {
|
||||
QThread::currentThread()->setPriority(originalPriority);
|
||||
});
|
||||
|
||||
if (!_resource.data()) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; resource was deleted";
|
||||
|
@ -167,8 +171,6 @@ void GeometryReader::run() {
|
|||
QMetaObject::invokeMethod(resource.data(), "finishedLoading", Qt::BlockingQueuedConnection, Q_ARG(bool, false));
|
||||
}
|
||||
}
|
||||
|
||||
QThread::currentThread()->setPriority(originalPriority);
|
||||
}
|
||||
|
||||
class GeometryDefinitionResource : public GeometryResource {
|
||||
|
|
|
@ -13,18 +13,22 @@
|
|||
|
||||
#include <mutex>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QPainter>
|
||||
#include <QRunnable>
|
||||
#include <QThreadPool>
|
||||
#include <qimagereader.h>
|
||||
#include <PathUtils.h>
|
||||
#include <QImageReader>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
#include <Finally.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "ModelNetworkingLogging.h"
|
||||
|
||||
TextureCache::TextureCache() {
|
||||
|
@ -196,7 +200,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
|
|||
{
|
||||
_textureLoader = textureLoader;
|
||||
}
|
||||
|
||||
|
||||
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||
switch (_type) {
|
||||
case CUBE_TEXTURE: {
|
||||
|
@ -240,14 +244,14 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
|||
class ImageReader : public QRunnable {
|
||||
public:
|
||||
|
||||
ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data, const QUrl& url = QUrl());
|
||||
ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data, const QUrl& url = QUrl());
|
||||
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
static void listSupportedImageFormats();
|
||||
|
||||
QWeakPointer<Resource> _texture;
|
||||
QWeakPointer<Resource> _resource;
|
||||
QUrl _url;
|
||||
QByteArray _content;
|
||||
};
|
||||
|
@ -261,9 +265,9 @@ void NetworkTexture::loadContent(const QByteArray& content) {
|
|||
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data,
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data,
|
||||
const QUrl& url) :
|
||||
_texture(texture),
|
||||
_resource(resource),
|
||||
_url(url),
|
||||
_content(data)
|
||||
{
|
||||
|
@ -278,31 +282,34 @@ void ImageReader::listSupportedImageFormats() {
|
|||
}
|
||||
|
||||
void ImageReader::run() {
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr);
|
||||
auto originalPriority = QThread::currentThread()->priority();
|
||||
if (originalPriority == QThread::InheritPriority) {
|
||||
originalPriority = QThread::NormalPriority;
|
||||
}
|
||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||
Finally restorePriority([originalPriority]{
|
||||
QThread::currentThread()->setPriority(originalPriority);
|
||||
});
|
||||
|
||||
auto texture = _texture.toStrongRef();
|
||||
if (!texture) {
|
||||
qCWarning(modelnetworking) << "Could not get strong ref";
|
||||
if (!_resource.data()) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||
return;
|
||||
}
|
||||
|
||||
listSupportedImageFormats();
|
||||
|
||||
// try to help the QImage loader by extracting the image file format from the url filename ext
|
||||
// Some tga are not created properly for example without it
|
||||
// Help the QImage loader by extracting the image file format from the url filename ext.
|
||||
// Some tga are not created properly without it.
|
||||
auto filename = _url.fileName().toStdString();
|
||||
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
||||
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
||||
|
||||
// Note that QImage.format is the pixel format which is different from the "format" of the image file...
|
||||
auto imageFormat = image.format();
|
||||
auto imageFormat = image.format();
|
||||
int originalWidth = image.width();
|
||||
int originalHeight = image.height();
|
||||
|
||||
|
||||
if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
|
||||
if (filenameExtension.empty()) {
|
||||
qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url;
|
||||
|
@ -312,25 +319,40 @@ void ImageReader::run() {
|
|||
return;
|
||||
}
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
auto ntex = texture.dynamicCast<NetworkTexture>();
|
||||
if (ntex) {
|
||||
theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString());
|
||||
gpu::Texture* texture = nullptr;
|
||||
{
|
||||
// Double-check the resource still exists between long operations.
|
||||
auto resource = _resource.toStrongRef();
|
||||
if (!resource) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||
return;
|
||||
}
|
||||
|
||||
auto url = _url.toString().toStdString();
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
||||
texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(texture.data(), "setImage",
|
||||
Q_ARG(void*, theTexture),
|
||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||
QThread::currentThread()->setPriority(originalPriority);
|
||||
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
||||
auto resource = _resource.toStrongRef();
|
||||
if (!resource) {
|
||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||
delete texture;
|
||||
} else {
|
||||
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(void*, texture),
|
||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||
int originalHeight) {
|
||||
_originalWidth = originalWidth;
|
||||
_originalHeight = originalHeight;
|
||||
|
||||
|
||||
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
|
||||
|
||||
|
||||
// Passing ownership
|
||||
_textureSource->resetTexture(texture);
|
||||
auto gpuTexture = _textureSource->getGPUTexture();
|
||||
|
@ -338,7 +360,7 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
|||
if (gpuTexture) {
|
||||
_width = gpuTexture->getWidth();
|
||||
_height = gpuTexture->getHeight();
|
||||
setBytes(gpuTexture->getStoredSize());
|
||||
setSize(gpuTexture->getStoredSize());
|
||||
} else {
|
||||
// FIXME: If !gpuTexture, we failed to load!
|
||||
_width = _height = 0;
|
||||
|
|
|
@ -336,6 +336,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||
// so emit our signal that says that
|
||||
qDebug() << "Limit of silent domain checkins reached";
|
||||
emit limitOfSilentDomainCheckInsReached();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ ResourceCache::~ResourceCache() {
|
|||
void ResourceCache::refreshAll() {
|
||||
// Clear all unused resources so we don't have to reload them
|
||||
clearUnusedResource();
|
||||
resetResourceCounters();
|
||||
|
||||
// Refresh all remaining resources in use
|
||||
foreach (auto resource, _resources) {
|
||||
|
@ -53,9 +54,27 @@ void ResourceCache::refresh(const QUrl& url) {
|
|||
resource->refresh();
|
||||
} else {
|
||||
_resources.remove(url);
|
||||
resetResourceCounters();
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList ResourceCache::getResourceList() {
|
||||
QVariantList list;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// NOTE: invokeMethod does not allow a const QObject*
|
||||
QMetaObject::invokeMethod(this, "getResourceList", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVariantList, list));
|
||||
} else {
|
||||
auto resources = _resources.uniqueKeys();
|
||||
list.reserve(resources.size());
|
||||
for (auto& resource : resources) {
|
||||
list << resource;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void ResourceCache::setRequestLimit(int limit) {
|
||||
_requestLimit = limit;
|
||||
|
||||
|
@ -114,6 +133,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
|||
void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
|
||||
_unusedResourcesMaxSize = clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE);
|
||||
reserveUnusedResource(0);
|
||||
resetResourceCounters();
|
||||
}
|
||||
|
||||
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||
|
@ -127,6 +147,8 @@ void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource)
|
|||
resource->setLRUKey(++_lastLRUKey);
|
||||
_unusedResources.insert(resource->getLRUKey(), resource);
|
||||
_unusedResourcesSize += resource->getBytes();
|
||||
|
||||
resetResourceCounters();
|
||||
}
|
||||
|
||||
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||
|
@ -134,6 +156,7 @@ void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resourc
|
|||
_unusedResources.remove(resource->getLRUKey());
|
||||
_unusedResourcesSize -= resource->getBytes();
|
||||
}
|
||||
resetResourceCounters();
|
||||
}
|
||||
|
||||
void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
||||
|
@ -142,8 +165,13 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
|||
// unload the oldest resource
|
||||
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||
|
||||
_unusedResourcesSize -= it.value()->getBytes();
|
||||
it.value()->setCache(nullptr);
|
||||
auto size = it.value()->getBytes();
|
||||
|
||||
_totalResourcesSize -= size;
|
||||
_resources.remove(it.value()->getURL());
|
||||
|
||||
_unusedResourcesSize -= size;
|
||||
_unusedResources.erase(it);
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +187,17 @@ void ResourceCache::clearUnusedResource() {
|
|||
}
|
||||
}
|
||||
|
||||
void ResourceCache::resetResourceCounters() {
|
||||
_numTotalResources = _resources.size();
|
||||
_numUnusedResources = _unusedResources.size();
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void ResourceCache::updateTotalSize(const qint64& oldSize, const qint64& newSize) {
|
||||
_totalResourcesSize += (newSize - oldSize);
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) {
|
||||
Lock lock(_mutex);
|
||||
_loadingRequests.append(resource);
|
||||
|
@ -377,6 +416,11 @@ void Resource::finishedLoading(bool success) {
|
|||
emit finished(success);
|
||||
}
|
||||
|
||||
void Resource::setSize(const qint64& bytes) {
|
||||
QMetaObject::invokeMethod(_cache.data(), "updateTotalSize", Q_ARG(qint64, _bytes), Q_ARG(qint64, bytes));
|
||||
_bytes = bytes;
|
||||
}
|
||||
|
||||
void Resource::reinsert() {
|
||||
_cache->_resources.insert(_url, _self);
|
||||
}
|
||||
|
@ -412,7 +456,7 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
|
|||
void Resource::handleReplyFinished() {
|
||||
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
||||
|
||||
_bytes = _bytesTotal;
|
||||
setSize(_bytesTotal);
|
||||
|
||||
if (!_request || _request != sender()) {
|
||||
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#ifndef hifi_ResourceCache_h
|
||||
#define hifi_ResourceCache_h
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QObject>
|
||||
|
@ -29,6 +31,8 @@
|
|||
|
||||
#include "ResourceManager.h"
|
||||
|
||||
Q_DECLARE_METATYPE(size_t)
|
||||
|
||||
class QNetworkReply;
|
||||
class QTimer;
|
||||
|
||||
|
@ -79,8 +83,20 @@ private:
|
|||
/// Base class for resource caches.
|
||||
class ResourceCache : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
|
||||
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
|
||||
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
|
||||
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
|
||||
|
||||
public:
|
||||
size_t getNumTotalResources() const { return _numTotalResources; }
|
||||
size_t getSizeTotalResources() const { return _totalResourcesSize; }
|
||||
|
||||
size_t getNumCachedResources() const { return _numUnusedResources; }
|
||||
size_t getSizeCachedResources() const { return _unusedResourcesSize; }
|
||||
|
||||
Q_INVOKABLE QVariantList getResourceList();
|
||||
|
||||
static void setRequestLimit(int limit);
|
||||
static int getRequestLimit() { return _requestLimit; }
|
||||
|
||||
|
@ -101,15 +117,21 @@ public:
|
|||
void refreshAll();
|
||||
void refresh(const QUrl& url);
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
||||
public slots:
|
||||
void checkAsynchronousGets();
|
||||
|
||||
protected slots:
|
||||
void updateTotalSize(const qint64& oldSize, const qint64& newSize);
|
||||
|
||||
protected:
|
||||
/// Loads a resource from the specified URL.
|
||||
/// \param fallback a fallback URL to load if the desired one is unavailable
|
||||
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
||||
/// \param extra extra data to pass to the creator, if appropriate
|
||||
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||
bool delayLoad = false, void* extra = NULL);
|
||||
|
||||
/// Creates a new resource.
|
||||
|
@ -118,18 +140,20 @@ protected:
|
|||
|
||||
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
void removeUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
void reserveUnusedResource(qint64 resourceSize);
|
||||
void clearUnusedResource();
|
||||
|
||||
/// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading
|
||||
/// \return true if the resource began loading, otherwise false if the resource is in the pending queue
|
||||
Q_INVOKABLE static bool attemptRequest(Resource* resource);
|
||||
static bool attemptRequest(Resource* resource);
|
||||
static void requestCompleted(Resource* resource);
|
||||
static bool attemptHighestPriorityRequest();
|
||||
|
||||
private:
|
||||
friend class Resource;
|
||||
|
||||
void reserveUnusedResource(qint64 resourceSize);
|
||||
void clearUnusedResource();
|
||||
void resetResourceCounters();
|
||||
|
||||
QHash<QUrl, QWeakPointer<Resource>> _resources;
|
||||
int _lastLRUKey = 0;
|
||||
|
||||
|
@ -140,8 +164,13 @@ private:
|
|||
QReadWriteLock _resourcesToBeGottenLock;
|
||||
QQueue<QUrl> _resourcesToBeGotten;
|
||||
|
||||
std::atomic<size_t> _numTotalResources { 0 };
|
||||
std::atomic<size_t> _numUnusedResources { 0 };
|
||||
|
||||
std::atomic<qint64> _totalResourcesSize { 0 };
|
||||
std::atomic<qint64> _unusedResourcesSize { 0 };
|
||||
|
||||
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
|
||||
qint64 _unusedResourcesSize = 0;
|
||||
QMap<int, QSharedPointer<Resource>> _unusedResources;
|
||||
};
|
||||
|
||||
|
@ -226,7 +255,7 @@ protected:
|
|||
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
|
||||
|
||||
/// Called when the download is finished and processed, sets the number of actual bytes.
|
||||
void setBytes(qint64 bytes) { _bytes = bytes; }
|
||||
void setSize(const qint64& bytes);
|
||||
|
||||
/// Called when the download is finished and processed.
|
||||
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
|
||||
|
|
|
@ -384,9 +384,10 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig
|
|||
// check the exists mask to see if we have a child to traverse into
|
||||
|
||||
if (oneAtBit(childInBufferMask, childIndex)) {
|
||||
if (!destinationElement->getChildAtIndex(childIndex)) {
|
||||
auto childAt = destinationElement->getChildAtIndex(childIndex);
|
||||
if (!childAt) {
|
||||
// add a child at that index, if it doesn't exist
|
||||
destinationElement->addChildAtIndex(childIndex);
|
||||
childAt = destinationElement->addChildAtIndex(childIndex);
|
||||
bool nodeIsDirty = destinationElement->isDirty();
|
||||
if (nodeIsDirty) {
|
||||
_isDirty = true;
|
||||
|
@ -394,8 +395,7 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig
|
|||
}
|
||||
|
||||
// tell the child to read the subsequent data
|
||||
int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex),
|
||||
nodeData + bytesRead, bytesLeftToRead, args);
|
||||
int lowerLevelBytes = readElementData(childAt, nodeData + bytesRead, bytesLeftToRead, args);
|
||||
|
||||
bytesRead += lowerLevelBytes;
|
||||
bytesLeftToRead -= lowerLevelBytes;
|
||||
|
|
|
@ -46,6 +46,8 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) {
|
|||
_thread->start();
|
||||
|
||||
_thread->setPriority(priority);
|
||||
} else {
|
||||
setup();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,10 +62,16 @@ void GenericThread::terminate() {
|
|||
_thread->deleteLater();
|
||||
_thread = NULL;
|
||||
}
|
||||
} else {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void GenericThread::threadRoutine() {
|
||||
if (_isThreaded) {
|
||||
setup();
|
||||
}
|
||||
|
||||
while (!_stopThread) {
|
||||
|
||||
// override this function to do whatever your class actually does, return false to exit thread early
|
||||
|
@ -78,8 +86,13 @@ void GenericThread::threadRoutine() {
|
|||
}
|
||||
}
|
||||
|
||||
// If we were on a thread, then quit our thread
|
||||
if (_isThreaded && _thread) {
|
||||
_thread->quit();
|
||||
if (_isThreaded) {
|
||||
shutdown();
|
||||
|
||||
// If we were on a thread, then quit our thread
|
||||
if (_thread) {
|
||||
_thread->quit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,9 +33,6 @@ public:
|
|||
/// Call to stop the thread
|
||||
void terminate();
|
||||
|
||||
/// Override this function to do whatever your class actually does, return false to exit thread early.
|
||||
virtual bool process() = 0;
|
||||
|
||||
virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately
|
||||
|
||||
bool isThreaded() const { return _isThreaded; }
|
||||
|
@ -48,6 +45,10 @@ signals:
|
|||
void finished();
|
||||
|
||||
protected:
|
||||
/// Override this function to do whatever your class actually does, return false to exit thread early.
|
||||
virtual bool process() = 0;
|
||||
virtual void setup() {};
|
||||
virtual void shutdown() {};
|
||||
|
||||
/// Locks all the resources of the thread.
|
||||
void lock() { _mutex.lock(); }
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
};
|
||||
|
||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload);
|
||||
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, (uint64_t)payload);
|
||||
#else
|
||||
#define PROFILE_RANGE(name)
|
||||
#define PROFILE_RANGE_EX(name, argbColor, payload)
|
||||
|
|
|
@ -447,7 +447,7 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
|||
|
||||
new KeyboardFocusHack();
|
||||
|
||||
connect(_desktop, SIGNAL(showDesktop()), this, SLOT(showDesktop()));
|
||||
connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop()));
|
||||
}
|
||||
|
||||
QQuickItem* OffscreenUi::getDesktop() {
|
||||
|
|
|
@ -52,6 +52,16 @@ void OculusDisplayPlugin::customizeContext() {
|
|||
}
|
||||
|
||||
void OculusDisplayPlugin::uncustomizeContext() {
|
||||
using namespace oglplus;
|
||||
|
||||
// Present a final black frame to the HMD
|
||||
_compositeFramebuffer->Bound(FramebufferTarget::Draw, [] {
|
||||
Context::ClearColor(0, 0, 0, 1);
|
||||
Context::Clear().ColorBuffer();
|
||||
});
|
||||
|
||||
hmdPresent();
|
||||
|
||||
#if (OVR_MAJOR_VERSION >= 6)
|
||||
_sceneFbo.reset();
|
||||
#endif
|
||||
|
|
|
@ -90,12 +90,16 @@ ovrSession acquireOculusSession() {
|
|||
|
||||
void releaseOculusSession() {
|
||||
Q_ASSERT(refCount > 0 && session);
|
||||
// HACK the Oculus runtime doesn't seem to play well with repeated shutdown / restart.
|
||||
// So for now we'll just hold on to the session
|
||||
#if 0
|
||||
if (!--refCount) {
|
||||
qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session";
|
||||
ovr_Destroy(session);
|
||||
ovr_Shutdown();
|
||||
session = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue