mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 00:13:53 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into create
This commit is contained in:
commit
d43f35df22
26 changed files with 829 additions and 91 deletions
|
@ -136,7 +136,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
} else {
|
||||
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
|
||||
<< "with hardware address" << nodeConnection.hardwareAddress
|
||||
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
|
||||
<< "and machine fingerprint" << nodeConnection.machineFingerprint
|
||||
<< "sysinfo" << nodeConnection.SystemInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,11 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
|
|||
dataStream >> newHeader.machineFingerprint;
|
||||
|
||||
// and the operating system type
|
||||
dataStream >> newHeader.SystemInfo;
|
||||
QByteArray compressedSystemInfo;
|
||||
dataStream >> compressedSystemInfo;
|
||||
if (!compressedSystemInfo.isEmpty()) {
|
||||
newHeader.SystemInfo = qUncompress(compressedSystemInfo);
|
||||
}
|
||||
|
||||
dataStream >> newHeader.connectReason;
|
||||
|
||||
|
|
|
@ -122,12 +122,22 @@ Flickable {
|
|||
}
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
text: "<b>CPU:</b> " + PlatformInfo.getCPUBrand()
|
||||
text: "<b>CPU:</b>"
|
||||
Layout.maximumWidth: parent.width
|
||||
height: paintedHeight
|
||||
size: 16
|
||||
color: simplifiedUI.colors.text.white
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
Component.onCompleted: {
|
||||
var cpu = JSON.parse(PlatformInfo.getCPU(0));
|
||||
var cpuModel = cpu.model;
|
||||
if (cpuModel.length === 0) {
|
||||
cpuModel = "Unknown";
|
||||
}
|
||||
|
||||
text = "<b>CPU:</b> " + cpuModel;
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
|
@ -158,12 +168,22 @@ Flickable {
|
|||
}
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
text: "<b>GPU:</b> " + PlatformInfo.getGraphicsCardType()
|
||||
text: "<b>GPU:</b> "
|
||||
Layout.maximumWidth: parent.width
|
||||
height: paintedHeight
|
||||
size: 16
|
||||
color: simplifiedUI.colors.text.white
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
Component.onCompleted: {
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(0));
|
||||
var gpuModel = gpu.model;
|
||||
if (gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
}
|
||||
|
||||
text = "<b>GPU:</b> " + gpuModel;
|
||||
}
|
||||
}
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
|
@ -180,9 +200,11 @@ Flickable {
|
|||
width: 200
|
||||
height: 32
|
||||
text: "Copy to Clipboard"
|
||||
temporaryText: "Copied!"
|
||||
|
||||
onClicked: {
|
||||
Window.copyToClipboard(root.buildPlatformInfoTextToCopy());
|
||||
showTemporaryText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,12 +228,29 @@ Flickable {
|
|||
textToCopy += "Computer Vendor/Model: " + computerVendor + "/" + computerModel + "\n";
|
||||
textToCopy += "Profiled Platform Tier: " + PlatformInfo.getTierProfiled() + "\n";
|
||||
textToCopy += "OS Type: " + PlatformInfo.getOperatingSystemType() + "\n";
|
||||
textToCopy += "CPU: " + PlatformInfo.getCPUBrand() + "\n";
|
||||
|
||||
var cpu = JSON.parse(PlatformInfo.getCPU(0));
|
||||
var cpuModel = cpu.model;
|
||||
if (cpuModel.length === 0) {
|
||||
cpuModel = "Unknown";
|
||||
}
|
||||
|
||||
textToCopy += "CPU: " + cpuModel + "\n";
|
||||
textToCopy += "# CPUs: " + PlatformInfo.getNumCPUs() + "\n";
|
||||
textToCopy += "# CPU Cores: " + PlatformInfo.getNumLogicalCores() + "\n";
|
||||
textToCopy += "RAM: " + PlatformInfo.getTotalSystemMemoryMB() + " MB\n";
|
||||
textToCopy += "GPU: " + PlatformInfo.getGraphicsCardType() + "\n";
|
||||
textToCopy += "VR Hand Controllers: " + (PlatformInfo.hasRiftControllers() ? "Rift" : (PlatformInfo.hasViveControllers() ? "Vive" : "None"));
|
||||
|
||||
var gpu = JSON.parse(PlatformInfo.getGPU(0));
|
||||
var gpuModel = gpu.model;
|
||||
if (gpuModel.length === 0) {
|
||||
gpuModel = "Unknown";
|
||||
}
|
||||
|
||||
textToCopy += "GPU: " + gpuModel + "\n";
|
||||
textToCopy += "VR Hand Controllers: " + (PlatformInfo.hasRiftControllers() ? "Rift" : (PlatformInfo.hasViveControllers() ? "Vive" : "None")) + "\n";
|
||||
|
||||
textToCopy += "\n**All Platform Info**\n";
|
||||
textToCopy += JSON.stringify(JSON.parse(PlatformInfo.getPlatform()), null, 4);
|
||||
|
||||
return textToCopy;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ import TabletScriptingInterface 1.0
|
|||
|
||||
Original.Button {
|
||||
id: root
|
||||
// The two properties below are used when calling showTemporaryText()
|
||||
property string originalText: ""
|
||||
property string temporaryText: ""
|
||||
|
||||
SimplifiedConstants.SimplifiedConstants {
|
||||
id: simplifiedUI
|
||||
|
@ -103,4 +106,31 @@ Original.Button {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.text
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: showTemporaryTextTimer
|
||||
interval: 1500
|
||||
repeat: false
|
||||
running: false
|
||||
|
||||
onTriggered: {
|
||||
buttonText.text = root.originalText;
|
||||
root.originalText = "";
|
||||
}
|
||||
}
|
||||
|
||||
function showTemporaryText() {
|
||||
if (root.temporaryText === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showTemporaryTextTimer.running) {
|
||||
showTemporaryTextTimer.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
root.originalText = buttonText.text;
|
||||
buttonText.text = root.temporaryText;
|
||||
showTemporaryTextTimer.start();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
set(ENV{MACOSX_DEPLOYMENT_TARGET} 10.9)
|
||||
project(HQLauncher)
|
||||
set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
|
||||
set(src_files
|
||||
|
@ -26,6 +27,8 @@ set(src_files
|
|||
src/LatestBuildRequest.m
|
||||
src/OrganizationRequest.m
|
||||
src/OrganizationRequest.h
|
||||
src/Interface.h
|
||||
src/Interface.m
|
||||
src/ErrorViewController.h
|
||||
src/ErrorViewController.m
|
||||
src/Settings.h
|
||||
|
@ -67,7 +70,7 @@ add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
|
|||
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME}
|
||||
MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME})
|
||||
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
|
||||
if (LAUNCHER_HMAC_SECRET STREQUAL "")
|
||||
if ("${LAUNCHER_HMAC_SECRET}" STREQUAL "")
|
||||
message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
|
||||
endif()
|
||||
|
||||
|
|
|
@ -32,6 +32,6 @@
|
|||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>CFBundleName</string>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -69,8 +69,17 @@
|
|||
<action selector="hyperLink:" target="YVh-OH-vU8" id="JfS-ot-wAP"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0CN-kS-xZ5">
|
||||
<rect key="frame" x="66" y="68" width="266" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="By signing in, you agree to the High Fidelity" id="zSf-YA-osu">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xeX-qc-ccB" customClass="HFButton">
|
||||
<rect key="frame" x="205" y="84" width="104" height="42"/>
|
||||
<rect key="frame" x="205" y="111" width="104" height="42"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="square" title="LOG IN" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Ba-S9-5qW">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -81,6 +90,18 @@
|
|||
<action selector="login:" target="YVh-OH-vU8" id="uzF-yC-tu1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QtF-Xs-rht" customClass="Hyperlink">
|
||||
<rect key="frame" x="336" y="68" width="115" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Terms of Service" id="O5x-BW-FLk">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="linkColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="linkColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="termsOfService:" target="YVh-OH-vU8" id="bgc-08-8Lj"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="138.5" y="154"/>
|
||||
</customView>
|
||||
|
@ -91,5 +112,14 @@
|
|||
<outlet property="smallLogo" destination="j8K-TD-h7e" id="OVd-p3-nu6"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="wWT-mi-6An">
|
||||
<rect key="frame" x="0.0" y="0.0" width="37" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="1Uh-1I-CcU">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
|
@ -31,4 +31,9 @@
|
|||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/hq-support"]];
|
||||
}
|
||||
|
||||
- (IBAction)termsOfService:(id)sender
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/termsofservice"]];
|
||||
}
|
||||
@end
|
||||
|
|
8
launchers/darwin/src/Interface.h
Normal file
8
launchers/darwin/src/Interface.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface Interface : NSObject
|
||||
|
||||
-(id _Nonnull) initWith:(NSString * _Nonnull) aPathToInterface;
|
||||
-(NSInteger) getVersion:(out NSError * _Nullable * _Nonnull) anError;
|
||||
|
||||
@end
|
90
launchers/darwin/src/Interface.m
Normal file
90
launchers/darwin/src/Interface.m
Normal file
|
@ -0,0 +1,90 @@
|
|||
#import "Interface.h"
|
||||
|
||||
@implementation Interface
|
||||
{
|
||||
NSString *pathTo;
|
||||
}
|
||||
|
||||
-(id) initWith:(NSString*)aPathToInterface
|
||||
{
|
||||
[self init];
|
||||
self->pathTo = [NSString stringWithFormat:@"%@/Contents/MacOS/interface", aPathToInterface];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSInteger) getVersion:(out NSError * _Nullable *) outError
|
||||
{
|
||||
NSTask * interface = [[NSTask alloc] init];
|
||||
NSPipe * standardOut = [NSPipe pipe];
|
||||
|
||||
interface.launchPath = self->pathTo;
|
||||
interface.arguments = @[ @"--version" ];
|
||||
interface.standardOutput = standardOut;
|
||||
|
||||
NSLog(@"calling interface at %@", self->pathTo);
|
||||
|
||||
NSError *error = nil;
|
||||
[interface launch];
|
||||
[interface waitUntilExit];
|
||||
if (0 != [interface terminationStatus]) {
|
||||
*outError = [NSError errorWithDomain:@"interface"
|
||||
code:-1
|
||||
userInfo:@{NSUnderlyingErrorKey: error}];
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSFileHandle * fh = [standardOut fileHandleForReading];
|
||||
NSData * data = [fh readDataToEndOfFile];
|
||||
NSString * output = [NSString stringWithUTF8String:[data bytes]];
|
||||
if (output == nil) {
|
||||
NSDictionary * userInfo = @{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't start interface", nil)
|
||||
};
|
||||
*outError = [NSError errorWithDomain:@"interface"
|
||||
code:-1
|
||||
userInfo:userInfo];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Interface returns the build version as a string like this:
|
||||
// "Interface 33333-DEADBEEF". This code grabs the substring
|
||||
// between "Interface " and the hyphon ("-")
|
||||
NSRange start = [output rangeOfString:@"Interface "];
|
||||
if (start.length == 0) {
|
||||
NSDictionary * userInfo = @{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil)
|
||||
};
|
||||
*outError = [NSError errorWithDomain:@"interface"
|
||||
code:-2
|
||||
userInfo:userInfo];
|
||||
return 0;
|
||||
}
|
||||
NSRange end = [output rangeOfString:@"-"];
|
||||
if (end.length == 0) {
|
||||
NSDictionary * userInfo = @{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil)
|
||||
};
|
||||
*outError = [NSError errorWithDomain:@"interface"
|
||||
code:-2
|
||||
userInfo:userInfo];
|
||||
return 0;
|
||||
}
|
||||
NSRange subRange = {start.length, end.location - start.length};
|
||||
NSString * versionStr;
|
||||
@try {
|
||||
versionStr = [output substringWithRange:subRange];
|
||||
}
|
||||
@catch (NSException *) {
|
||||
NSDictionary * userInfo = @{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't read interface's version", nil)
|
||||
};
|
||||
*outError = [NSError errorWithDomain:@"interface"
|
||||
code:-2
|
||||
userInfo:userInfo];
|
||||
return 0;
|
||||
}
|
||||
|
||||
return versionStr.integerValue;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,17 +1,30 @@
|
|||
#import "LatestBuildRequest.h"
|
||||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
#import "Interface.h"
|
||||
|
||||
@implementation LatestBuildRequest
|
||||
|
||||
- (NSInteger) getCurrentVersion {
|
||||
NSString* interfaceAppPath = [[Launcher.sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
|
||||
NSError * error = nil;
|
||||
Interface * interface = [[Interface alloc] initWith:interfaceAppPath];
|
||||
NSInteger currentVersion = [interface getVersion:&error];
|
||||
if (currentVersion == 0 && error != nil) {
|
||||
NSLog(@"can't get version from interface, falling back to settings: %@", error);
|
||||
currentVersion = [Settings.sharedSettings latestBuildVersion];
|
||||
}
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
- (void) requestLatestBuildInfo {
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
|
||||
NSURLSession* session = [NSURLSession sharedSession];
|
||||
// We're using an ephermeral session here to ensure the tags api response is never cached.
|
||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
|
||||
|
@ -45,8 +58,7 @@
|
|||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
Settings* settings = [Settings sharedSettings];
|
||||
NSInteger currentVersion = [settings latestBuildVersion];
|
||||
NSInteger currentVersion = [self getCurrentVersion];
|
||||
NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE");
|
||||
NSLog(@"Latest Build Request -> current version: %ld", currentVersion);
|
||||
NSLog(@"Latest Build Request -> latest version: %ld", buildNumber.integerValue);
|
||||
|
@ -105,11 +117,10 @@
|
|||
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
|
||||
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
|
||||
|
||||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
|
||||
|
||||
Settings* settings = [Settings sharedSettings];
|
||||
NSInteger currentVersion = [settings latestBuildVersion];
|
||||
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
|
||||
NSString* interfaceAppPath = [[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
|
||||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:interfaceAppPath];
|
||||
|
||||
BOOL latestVersionAvailable = ([self getCurrentVersion] != buildNumber.integerValue);
|
||||
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
|
||||
|
||||
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
|
||||
|
|
|
@ -207,7 +207,7 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
|
|||
|
||||
if (entityItem && !entityItem->getScript().isEmpty()) {
|
||||
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
|
||||
if (entityItem->contains(_avatarPosition)) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
|
@ -222,6 +222,7 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
|
|||
|
||||
auto sessionUUID = getTree()->getMyAvatarSessionUUID();
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
|
||||
std::unordered_set<EntityRendererPointer> savedRenderables;
|
||||
// remove all entities from the scene
|
||||
auto scene = _viewState->getMain3DScene();
|
||||
if (scene) {
|
||||
|
@ -232,11 +233,12 @@ void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
|
|||
fadeOutRenderable(renderer);
|
||||
} else {
|
||||
savedEntities[entry.first] = entry.second;
|
||||
savedRenderables.insert(entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_renderablesToUpdate = savedEntities;
|
||||
_renderablesToUpdate = savedRenderables;
|
||||
_entitiesInScene = savedEntities;
|
||||
|
||||
if (_layeredZones.clearDomainAndNonOwnedZones(sessionUUID)) {
|
||||
|
@ -389,13 +391,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
PerformanceTimer pt("change");
|
||||
std::unordered_set<EntityItemID> changedEntities;
|
||||
_changedEntitiesGuard.withWriteLock([&] {
|
||||
#if 0
|
||||
// FIXME Weird build failure in latest VC update that fails to compile when using std::swap
|
||||
changedEntities.swap(_changedEntities);
|
||||
#else
|
||||
changedEntities.insert(_changedEntities.begin(), _changedEntities.end());
|
||||
_changedEntities.clear();
|
||||
#endif
|
||||
});
|
||||
|
||||
{
|
||||
|
@ -404,7 +400,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
auto renderable = renderableForEntityId(entityId);
|
||||
if (renderable) {
|
||||
// only add valid renderables _renderablesToUpdate
|
||||
_renderablesToUpdate.insert({ entityId, renderable });
|
||||
_renderablesToUpdate.insert(renderable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -414,8 +410,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
// we expect to update all renderables within available time budget
|
||||
PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
uint64_t updateStart = usecTimestampNow();
|
||||
for (const auto& entry : _renderablesToUpdate) {
|
||||
const auto& renderable = entry.second;
|
||||
for (const auto& renderable : _renderablesToUpdate) {
|
||||
assert(renderable); // only valid renderables are added to _renderablesToUpdate
|
||||
renderable->updateInScene(scene, transaction);
|
||||
}
|
||||
|
@ -424,8 +419,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
|
||||
// compute average per-renderable update cost
|
||||
float cost = (float)(usecTimestampNow() - updateStart) / (float)(numRenderables);
|
||||
const float blend = 0.1f;
|
||||
_avgRenderableUpdateCost = (1.0f - blend) * _avgRenderableUpdateCost + blend * cost;
|
||||
const float BLEND = 0.1f;
|
||||
_avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost;
|
||||
} else {
|
||||
// we expect the cost to updating all renderables to exceed available time budget
|
||||
// so we first sort by priority and update in order until out of time
|
||||
|
@ -450,43 +445,40 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
|
||||
sortedRenderables.reserve(_renderablesToUpdate.size());
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
|
||||
while (itr != _renderablesToUpdate.end()) {
|
||||
assert(itr->second); // only valid renderables are added to _renderablesToUpdate
|
||||
sortedRenderables.push(SortableRenderer(itr->second));
|
||||
++itr;
|
||||
PROFILE_RANGE_EX(simulation_physics, "BuildSortedRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
for (const auto& renderable : _renderablesToUpdate) {
|
||||
assert(renderable); // only valid renderables are added to _renderablesToUpdate
|
||||
sortedRenderables.push(SortableRenderer(renderable));
|
||||
}
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, sortedRenderables.size());
|
||||
PROFILE_RANGE_EX(simulation_physics, "SortAndUpdateRenderables", 0xffff00ff, sortedRenderables.size());
|
||||
|
||||
// compute remaining time budget
|
||||
const auto& sortedRenderablesVector = sortedRenderables.getSortedVector();
|
||||
uint64_t updateStart = usecTimestampNow();
|
||||
uint64_t timeBudget = MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET;
|
||||
uint64_t sortCost = updateStart - sortStart;
|
||||
uint64_t timeBudget = MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET;
|
||||
if (sortCost < MAX_UPDATE_RENDERABLES_TIME_BUDGET - MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET) {
|
||||
timeBudget = MAX_UPDATE_RENDERABLES_TIME_BUDGET - sortCost;
|
||||
}
|
||||
uint64_t expiry = updateStart + timeBudget;
|
||||
|
||||
// process the sorted renderables
|
||||
size_t numSorted = sortedRenderables.size();
|
||||
const auto& sortedRenderablesVector = sortedRenderables.getSortedVector();
|
||||
for (const auto& sortedRenderable : sortedRenderablesVector) {
|
||||
if (usecTimestampNow() > expiry) {
|
||||
break;
|
||||
}
|
||||
const auto& renderable = sortedRenderable.getRenderer();
|
||||
renderable->updateInScene(scene, transaction);
|
||||
_renderablesToUpdate.erase(renderable->getEntity()->getID());
|
||||
_renderablesToUpdate.erase(renderable);
|
||||
}
|
||||
|
||||
// compute average per-renderable update cost
|
||||
size_t numUpdated = numSorted - sortedRenderables.size() + 1; // add one to avoid divide by zero
|
||||
size_t numUpdated = sortedRenderables.size() - _renderablesToUpdate.size() + 1; // add one to avoid divide by zero
|
||||
float cost = (float)(usecTimestampNow() - updateStart) / (float)(numUpdated);
|
||||
const float blend = 0.1f;
|
||||
_avgRenderableUpdateCost = (1.0f - blend) * _avgRenderableUpdateCost + blend * cost;
|
||||
const float BLEND = 0.1f;
|
||||
_avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -990,7 +982,6 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||
// If it's in a pending queue, remove it
|
||||
_renderablesToUpdate.erase(entityID);
|
||||
_entitiesToAdd.erase(entityID);
|
||||
|
||||
auto itr = _entitiesInScene.find(entityID);
|
||||
|
@ -1000,7 +991,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
|||
}
|
||||
|
||||
if (_tree && !_shuttingDown && _entitiesScriptEngine && !itr->second->getEntity()->getScript().isEmpty()) {
|
||||
if (itr->second->getEntity()->contains(_avatarPosition)) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
|
@ -1013,6 +1004,7 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
|||
}
|
||||
|
||||
auto renderable = itr->second;
|
||||
_renderablesToUpdate.erase(renderable);
|
||||
_entitiesInScene.erase(itr);
|
||||
|
||||
if (!renderable) {
|
||||
|
@ -1047,7 +1039,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
QString scriptUrl = entity->getScript();
|
||||
if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) {
|
||||
if (_entitiesScriptEngine) {
|
||||
if (entity->contains(_avatarPosition)) {
|
||||
if (_currentEntitiesInside.contains(entityID)) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
|
||||
}
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
|
|
|
@ -246,7 +246,7 @@ private:
|
|||
ReadWriteLockable _changedEntitiesGuard;
|
||||
std::unordered_set<EntityItemID> _changedEntities;
|
||||
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer> _renderablesToUpdate;
|
||||
std::unordered_set<EntityRendererPointer> _renderablesToUpdate;
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer> _entitiesInScene;
|
||||
std::unordered_map<EntityItemID, EntityItemWeakPointer> _entitiesToAdd;
|
||||
|
||||
|
|
|
@ -2121,9 +2121,10 @@ void EntityTree::fixupNeedsParentFixups() {
|
|||
_needsParentFixup.clear();
|
||||
}
|
||||
|
||||
std::unordered_set<QUuid> seenEntityIds;
|
||||
QMutableVectorIterator<EntityItemWeakPointer> iter(entitiesToFixup);
|
||||
while (iter.hasNext()) {
|
||||
EntityItemWeakPointer entityWP = iter.next();
|
||||
const auto& entityWP = iter.next();
|
||||
EntityItemPointer entity = entityWP.lock();
|
||||
if (!entity) {
|
||||
// entity was deleted before we found its parent
|
||||
|
@ -2131,6 +2132,17 @@ void EntityTree::fixupNeedsParentFixups() {
|
|||
continue;
|
||||
}
|
||||
|
||||
const auto id = entity->getID();
|
||||
// BUGZ-771 some entities seem to never be removed by the below logic and further seem to accumulate dupes within the _needsParentFixup list
|
||||
// This block ensures that duplicates are removed from entitiesToFixup before it's re-appended to _needsParentFixup
|
||||
if (0 != seenEntityIds.count(id)) {
|
||||
// Entity was duplicated inside entitiesToFixup
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
seenEntityIds.insert(id);
|
||||
|
||||
entity->requiresRecalcBoxes();
|
||||
bool queryAACubeSuccess { false };
|
||||
bool maxAACubeSuccess { false };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME networking)
|
||||
setup_hifi_library(Network)
|
||||
link_hifi_libraries(shared)
|
||||
link_hifi_libraries(shared platform)
|
||||
|
||||
target_openssl()
|
||||
target_tbb()
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <ThreadHelpers.h>
|
||||
#include <LogHandler.h>
|
||||
#include <UUID.h>
|
||||
#include <platform/Platform.h>
|
||||
#include <platform/PlatformKeys.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "AddressManager.h"
|
||||
|
@ -42,6 +44,7 @@
|
|||
using namespace std::chrono;
|
||||
|
||||
const int KEEPALIVE_PING_INTERVAL_MS = 1000;
|
||||
const int MAX_SYSTEM_INFO_SIZE = 1000;
|
||||
|
||||
NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) :
|
||||
LimitedNodeList(socketListenPort, dtlsListenPort),
|
||||
|
@ -418,19 +421,20 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
packetStream << FingerprintUtils::getMachineFingerprint();
|
||||
|
||||
QString systemInfo;
|
||||
#if defined Q_OS_WIN
|
||||
systemInfo = "OS:Windows";
|
||||
#elif defined Q_OS_OSX
|
||||
systemInfo = "OS:OSX";
|
||||
#elif defined Q_OS_LINUX
|
||||
systemInfo = "OS:Linux";
|
||||
#elif defined Q_OS_ANDROID
|
||||
systemInfo = "OS:Android";
|
||||
#else
|
||||
systemInfo = "OS:Unknown";
|
||||
#endif
|
||||
packetStream << systemInfo;
|
||||
auto desc = platform::getAll();
|
||||
|
||||
QByteArray systemInfo(desc.dump().c_str());
|
||||
QByteArray compressedSystemInfo = qCompress(systemInfo);
|
||||
|
||||
if (compressedSystemInfo.size() > MAX_SYSTEM_INFO_SIZE) {
|
||||
// Highly unlikely, as not even unreasonable machines will
|
||||
// overflow the max size, but prevent MTU overflow anyway.
|
||||
// We could do something sophisticated like clearing specific
|
||||
// values if they're too big, but we'll save that for later.
|
||||
compressedSystemInfo.clear();
|
||||
}
|
||||
|
||||
packetStream << compressedSystemInfo;
|
||||
|
||||
packetStream << _connectReason;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <LogHandler.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
||||
#include <platform/Platform.h>
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) :
|
||||
|
@ -38,6 +39,16 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) :
|
|||
// if the NL tells us we got a DS response, clear our member variable of queued check-ins
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
connect(nodeList.data(), &NodeList::receivedDomainServerList, this, &ThreadedAssignment::clearQueuedCheckIns);
|
||||
|
||||
platform::create();
|
||||
if (!platform::enumeratePlatform()) {
|
||||
qCDebug(networking) << "Failed to enumerate platform.";
|
||||
}
|
||||
}
|
||||
|
||||
ThreadedAssignment::~ThreadedAssignment() {
|
||||
stop();
|
||||
platform::destroy();
|
||||
}
|
||||
|
||||
void ThreadedAssignment::setFinished(bool isFinished) {
|
||||
|
|
|
@ -22,7 +22,7 @@ class ThreadedAssignment : public Assignment {
|
|||
Q_OBJECT
|
||||
public:
|
||||
ThreadedAssignment(ReceivedMessage& message);
|
||||
~ThreadedAssignment() { stop(); }
|
||||
~ThreadedAssignment();
|
||||
|
||||
virtual void aboutToFinish() { };
|
||||
void addPacketStatsAndSendStatsPacket(QJsonObject statsObject);
|
||||
|
|
|
@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
|
||||
|
||||
case PacketType::DomainConnectRequest:
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasSystemInfo);
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasCompressedSystemInfo);
|
||||
|
||||
case PacketType::DomainServerAddedNode:
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);
|
||||
|
|
|
@ -347,7 +347,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
|
|||
AlwaysHasMachineFingerprint,
|
||||
HasTimestamp,
|
||||
HasReason,
|
||||
HasSystemInfo
|
||||
HasSystemInfo,
|
||||
HasCompressedSystemInfo
|
||||
};
|
||||
|
||||
enum class DomainConnectionDeniedVersion : PacketVersion {
|
||||
|
|
|
@ -5,3 +5,11 @@ link_hifi_libraries(shared)
|
|||
|
||||
GroupSources("src")
|
||||
target_json()
|
||||
|
||||
if (APPLE)
|
||||
# link in required OS X frameworks and include the right GL headers
|
||||
find_library(OpenGL OpenGL)
|
||||
find_library(AppKit AppKit)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${OpenGL} ${AppKit})
|
||||
endif ()
|
||||
|
|
|
@ -1691,10 +1691,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
using packBlendshapeOffsetTo = void(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked);
|
||||
|
||||
void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) {
|
||||
static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) {
|
||||
float len = glm::compMax(glm::abs(unpacked.positionOffset));
|
||||
glm::vec3 normalizedPos(unpacked.positionOffset);
|
||||
if (len > 0.0f) {
|
||||
|
@ -1711,6 +1708,37 @@ void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& pac
|
|||
);
|
||||
}
|
||||
|
||||
static void packBlendshapeOffsets_ref(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
|
||||
++unpacked;
|
||||
++packed;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
//
|
||||
// Runtime CPU dispatch
|
||||
//
|
||||
#include <CPUDetect.h>
|
||||
|
||||
void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size);
|
||||
|
||||
static void packBlendshapeOffsets(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) {
|
||||
static bool _cpuSupportsAVX2 = cpuSupportsAVX2();
|
||||
if (_cpuSupportsAVX2) {
|
||||
static_assert(sizeof(BlendshapeOffsetUnpacked) == 9 * sizeof(float), "struct BlendshapeOffsetUnpacked size doesn't match.");
|
||||
static_assert(sizeof(BlendshapeOffsetPacked) == 4 * sizeof(uint32_t), "struct BlendshapeOffsetPacked size doesn't match.");
|
||||
packBlendshapeOffsets_AVX2((float(*)[9])unpacked, (uint32_t(*)[4])packed, size);
|
||||
} else {
|
||||
packBlendshapeOffsets_ref(unpacked, packed, size);
|
||||
}
|
||||
}
|
||||
|
||||
#else // portable reference code
|
||||
static auto& packBlendshapeOffsets = packBlendshapeOffsets_ref;
|
||||
#endif
|
||||
|
||||
class Blender : public QRunnable {
|
||||
public:
|
||||
|
||||
|
@ -1735,21 +1763,28 @@ Blender::Blender(ModelPointer model, HFMModel::ConstPointer hfmModel, int blendN
|
|||
void Blender::run() {
|
||||
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
|
||||
int numBlendshapeOffsets = 0; // number of offsets required for all meshes.
|
||||
int maxBlendshapeOffsets = 0; // number of offsets in the largest mesh.
|
||||
int numMeshes = 0; // number of meshes in this model.
|
||||
for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) {
|
||||
numMeshes++;
|
||||
if (meshIter->blendshapes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
int numVertsInMesh = meshIter->vertices.size();
|
||||
numBlendshapeOffsets += numVertsInMesh;
|
||||
maxBlendshapeOffsets = std::max(maxBlendshapeOffsets, numVertsInMesh);
|
||||
}
|
||||
|
||||
// all elements are default constructed to zero offsets.
|
||||
QVector<BlendshapeOffset> packedBlendshapeOffsets(numBlendshapeOffsets);
|
||||
QVector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(numBlendshapeOffsets);
|
||||
|
||||
// allocate the required size
|
||||
// allocate the required sizes
|
||||
QVector<int> blendedMeshSizes;
|
||||
blendedMeshSizes.reserve(numMeshes);
|
||||
|
||||
QVector<BlendshapeOffset> packedBlendshapeOffsets;
|
||||
packedBlendshapeOffsets.reserve(numBlendshapeOffsets);
|
||||
|
||||
QVector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets;
|
||||
unpackedBlendshapeOffsets.reserve(maxBlendshapeOffsets); // reuse for all meshes
|
||||
|
||||
int offset = 0;
|
||||
for (auto meshIter = _hfmModel->meshes.cbegin(); meshIter != _hfmModel->meshes.cend(); ++meshIter) {
|
||||
if (meshIter->blendshapes.isEmpty()) {
|
||||
|
@ -1759,6 +1794,9 @@ void Blender::run() {
|
|||
int numVertsInMesh = meshIter->vertices.size();
|
||||
blendedMeshSizes.push_back(numVertsInMesh);
|
||||
|
||||
// initialize offsets to zero
|
||||
memset(unpackedBlendshapeOffsets.data(), 0, numVertsInMesh * sizeof(BlendshapeOffsetUnpacked));
|
||||
|
||||
// for each blendshape in this mesh, accumulate the offsets into unpackedBlendshapeOffsets.
|
||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), meshIter->blendshapes.size()); i < n; i++) {
|
||||
|
@ -1773,7 +1811,7 @@ void Blender::run() {
|
|||
for (int j = 0; j < blendshape.indices.size(); ++j) {
|
||||
int index = blendshape.indices.at(j);
|
||||
|
||||
auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[offset + index];
|
||||
auto& currentBlendshapeOffset = unpackedBlendshapeOffsets[index];
|
||||
currentBlendshapeOffset.positionOffset += blendshape.vertices.at(j) * vertexCoefficient;
|
||||
currentBlendshapeOffset.normalOffset += blendshape.normals.at(j) * normalCoefficient;
|
||||
if (j < blendshape.tangents.size()) {
|
||||
|
@ -1781,20 +1819,15 @@ void Blender::run() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu.
|
||||
auto unpacked = unpackedBlendshapeOffsets.data();
|
||||
auto packed = packedBlendshapeOffsets.data() + offset;
|
||||
packBlendshapeOffsets(unpacked, packed, numVertsInMesh);
|
||||
|
||||
offset += numVertsInMesh;
|
||||
}
|
||||
|
||||
// convert unpackedBlendshapeOffsets into packedBlendshapeOffsets for the gpu.
|
||||
// FIXME it feels like we could be more effectively using SIMD here
|
||||
{
|
||||
auto unpacked = unpackedBlendshapeOffsets.data();
|
||||
auto packed = packedBlendshapeOffsets.data();
|
||||
for (int i = 0; i < unpackedBlendshapeOffsets.size(); ++i) {
|
||||
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
|
||||
++unpacked;
|
||||
++packed;
|
||||
}
|
||||
}
|
||||
Q_ASSERT(offset == numBlendshapeOffsets);
|
||||
|
||||
// post the result to the ModelBlender, which will dispatch to the model if still alive
|
||||
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace PrioritySortUtil {
|
|||
class PriorityQueue {
|
||||
public:
|
||||
PriorityQueue() = delete;
|
||||
PriorityQueue(const ConicalViewFrustums& views) : _views(views) { }
|
||||
PriorityQueue(const ConicalViewFrustums& views) : _views(views), _usecCurrentTime(usecTimestampNow()) { }
|
||||
PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
|
||||
: _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
|
||||
, _usecCurrentTime(usecTimestampNow()) {
|
||||
|
|
285
libraries/shared/src/avx2/BlendshapePacking_avx2.cpp
Normal file
285
libraries/shared/src/avx2/BlendshapePacking_avx2.cpp
Normal file
|
@ -0,0 +1,285 @@
|
|||
//
|
||||
// BlendshapePacking_avx2.cpp
|
||||
//
|
||||
// Created by Ken Cooke on 6/22/19.
|
||||
// Copyright 2019 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
|
||||
//
|
||||
|
||||
#ifdef __AVX2__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <immintrin.h>
|
||||
|
||||
void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size) {
|
||||
|
||||
int i = 0;
|
||||
for (; i < size - 7; i += 8) { // blocks of 8
|
||||
|
||||
//
|
||||
// deinterleave (8x9 to 9x8 matrix transpose)
|
||||
//
|
||||
__m256 s0 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+0][0])), _mm_load_ps(&unpacked[i+4][0]), 1);
|
||||
__m256 s1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+1][0])), _mm_load_ps(&unpacked[i+5][0]), 1);
|
||||
__m256 s2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+2][0])), _mm_load_ps(&unpacked[i+6][0]), 1);
|
||||
__m256 s3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+3][0])), _mm_load_ps(&unpacked[i+7][0]), 1);
|
||||
__m256 s4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+0][4])), _mm_load_ps(&unpacked[i+4][4]), 1);
|
||||
__m256 s5 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+1][4])), _mm_load_ps(&unpacked[i+5][4]), 1);
|
||||
__m256 s6 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+2][4])), _mm_load_ps(&unpacked[i+6][4]), 1);
|
||||
__m256 s7 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&unpacked[i+3][4])), _mm_load_ps(&unpacked[i+7][4]), 1);
|
||||
|
||||
__m256 t0 = _mm256_unpacklo_ps(s0, s1);
|
||||
__m256 t1 = _mm256_unpackhi_ps(s0, s1);
|
||||
__m256 t2 = _mm256_unpacklo_ps(s2, s3);
|
||||
__m256 t3 = _mm256_unpackhi_ps(s2, s3);
|
||||
__m256 t4 = _mm256_unpacklo_ps(s4, s5);
|
||||
__m256 t5 = _mm256_unpackhi_ps(s4, s5);
|
||||
__m256 t6 = _mm256_unpacklo_ps(s6, s7);
|
||||
__m256 t7 = _mm256_unpackhi_ps(s6, s7);
|
||||
|
||||
__m256 px = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
|
||||
__m256 py = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
|
||||
__m256 pz = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
|
||||
__m256 nx = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
|
||||
__m256 ny = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(1,0,1,0));
|
||||
__m256 nz = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(3,2,3,2));
|
||||
__m256 tx = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(1,0,1,0));
|
||||
__m256 ty = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(3,2,3,2));
|
||||
|
||||
__m256 tz = _mm256_i32gather_ps(unpacked[i+0], _mm256_setr_epi32(8,17,26,35,44,53,62,71), sizeof(float));
|
||||
|
||||
// abs(pos)
|
||||
__m256 apx = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), px);
|
||||
__m256 apy = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), py);
|
||||
__m256 apz = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), pz);
|
||||
|
||||
// len = compMax(abs(pos))
|
||||
__m256 len = _mm256_max_ps(_mm256_max_ps(apx, apy), apz);
|
||||
|
||||
// detect zeros
|
||||
__m256 mask = _mm256_cmp_ps(len, _mm256_setzero_ps(), _CMP_EQ_OQ);
|
||||
|
||||
// rcp = 1.0f / len
|
||||
__m256 rcp = _mm256_div_ps(_mm256_set1_ps(1.0f), len);
|
||||
|
||||
// replace +inf with 1.0f
|
||||
rcp = _mm256_blendv_ps(rcp, _mm256_set1_ps(1.0f), mask);
|
||||
len = _mm256_blendv_ps(len, _mm256_set1_ps(1.0f), mask);
|
||||
|
||||
// pos *= 1.0f / len
|
||||
px = _mm256_mul_ps(px, rcp);
|
||||
py = _mm256_mul_ps(py, rcp);
|
||||
pz = _mm256_mul_ps(pz, rcp);
|
||||
|
||||
// clamp(vec, -1.0f, 1.0f)
|
||||
px = _mm256_min_ps(_mm256_max_ps(px, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
py = _mm256_min_ps(_mm256_max_ps(py, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
pz = _mm256_min_ps(_mm256_max_ps(pz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
nx = _mm256_min_ps(_mm256_max_ps(nx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
ny = _mm256_min_ps(_mm256_max_ps(ny, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
nz = _mm256_min_ps(_mm256_max_ps(nz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
tx = _mm256_min_ps(_mm256_max_ps(tx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
ty = _mm256_min_ps(_mm256_max_ps(ty, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
tz = _mm256_min_ps(_mm256_max_ps(tz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
|
||||
// vec *= 511.0f
|
||||
px = _mm256_mul_ps(px, _mm256_set1_ps(511.0f));
|
||||
py = _mm256_mul_ps(py, _mm256_set1_ps(511.0f));
|
||||
pz = _mm256_mul_ps(pz, _mm256_set1_ps(511.0f));
|
||||
nx = _mm256_mul_ps(nx, _mm256_set1_ps(511.0f));
|
||||
ny = _mm256_mul_ps(ny, _mm256_set1_ps(511.0f));
|
||||
nz = _mm256_mul_ps(nz, _mm256_set1_ps(511.0f));
|
||||
tx = _mm256_mul_ps(tx, _mm256_set1_ps(511.0f));
|
||||
ty = _mm256_mul_ps(ty, _mm256_set1_ps(511.0f));
|
||||
tz = _mm256_mul_ps(tz, _mm256_set1_ps(511.0f));
|
||||
|
||||
// veci = lrint(vec) & 03ff
|
||||
__m256i pxi = _mm256_and_si256(_mm256_cvtps_epi32(px), _mm256_set1_epi32(0x3ff));
|
||||
__m256i pyi = _mm256_and_si256(_mm256_cvtps_epi32(py), _mm256_set1_epi32(0x3ff));
|
||||
__m256i pzi = _mm256_and_si256(_mm256_cvtps_epi32(pz), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nxi = _mm256_and_si256(_mm256_cvtps_epi32(nx), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nyi = _mm256_and_si256(_mm256_cvtps_epi32(ny), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nzi = _mm256_and_si256(_mm256_cvtps_epi32(nz), _mm256_set1_epi32(0x3ff));
|
||||
__m256i txi = _mm256_and_si256(_mm256_cvtps_epi32(tx), _mm256_set1_epi32(0x3ff));
|
||||
__m256i tyi = _mm256_and_si256(_mm256_cvtps_epi32(ty), _mm256_set1_epi32(0x3ff));
|
||||
__m256i tzi = _mm256_and_si256(_mm256_cvtps_epi32(tz), _mm256_set1_epi32(0x3ff));
|
||||
|
||||
// pack = (xi << 0) | (yi << 10) | (zi << 20);
|
||||
__m256i li = _mm256_castps_si256(len); // length
|
||||
__m256i pi = _mm256_or_si256(_mm256_or_si256(pxi, _mm256_slli_epi32(pyi, 10)), _mm256_slli_epi32(pzi, 20)); // position
|
||||
__m256i ni = _mm256_or_si256(_mm256_or_si256(nxi, _mm256_slli_epi32(nyi, 10)), _mm256_slli_epi32(nzi, 20)); // normal
|
||||
__m256i ti = _mm256_or_si256(_mm256_or_si256(txi, _mm256_slli_epi32(tyi, 10)), _mm256_slli_epi32(tzi, 20)); // tangent
|
||||
|
||||
//
|
||||
// interleave (4x4 matrix transpose)
|
||||
//
|
||||
__m256i u0 = _mm256_unpacklo_epi32(li, pi);
|
||||
__m256i u1 = _mm256_unpackhi_epi32(li, pi);
|
||||
__m256i u2 = _mm256_unpacklo_epi32(ni, ti);
|
||||
__m256i u3 = _mm256_unpackhi_epi32(ni, ti);
|
||||
|
||||
__m256i v0 = _mm256_unpacklo_epi64(u0, u2);
|
||||
__m256i v1 = _mm256_unpackhi_epi64(u0, u2);
|
||||
__m256i v2 = _mm256_unpacklo_epi64(u1, u3);
|
||||
__m256i v3 = _mm256_unpackhi_epi64(u1, u3);
|
||||
|
||||
__m256i w0 = _mm256_permute2f128_si256(v0, v1, 0x20);
|
||||
__m256i w1 = _mm256_permute2f128_si256(v2, v3, 0x20);
|
||||
__m256i w2 = _mm256_permute2f128_si256(v0, v1, 0x31);
|
||||
__m256i w3 = _mm256_permute2f128_si256(v2, v3, 0x31);
|
||||
|
||||
// store pack x 8
|
||||
_mm256_storeu_si256((__m256i*)packed[i+0], w0);
|
||||
_mm256_storeu_si256((__m256i*)packed[i+2], w1);
|
||||
_mm256_storeu_si256((__m256i*)packed[i+4], w2);
|
||||
_mm256_storeu_si256((__m256i*)packed[i+6], w3);
|
||||
}
|
||||
|
||||
if (i < size) { // remainder
|
||||
int rem = size - i;
|
||||
|
||||
//
|
||||
// deinterleave (8x9 to 9x8 matrix transpose)
|
||||
//
|
||||
__m256 s0 = _mm256_setzero_ps();
|
||||
__m256 s1 = _mm256_setzero_ps();
|
||||
__m256 s2 = _mm256_setzero_ps();
|
||||
__m256 s3 = _mm256_setzero_ps();
|
||||
__m256 s4 = _mm256_setzero_ps();
|
||||
__m256 s5 = _mm256_setzero_ps();
|
||||
__m256 s6 = _mm256_setzero_ps();
|
||||
__m256 s7 = _mm256_setzero_ps();
|
||||
|
||||
switch (rem) {
|
||||
case 7: s6 = _mm256_loadu_ps(unpacked[i+6]);
|
||||
case 6: s5 = _mm256_loadu_ps(unpacked[i+5]);
|
||||
case 5: s4 = _mm256_loadu_ps(unpacked[i+4]);
|
||||
case 4: s3 = _mm256_loadu_ps(unpacked[i+3]);
|
||||
case 3: s2 = _mm256_loadu_ps(unpacked[i+2]);
|
||||
case 2: s1 = _mm256_loadu_ps(unpacked[i+1]);
|
||||
case 1: s0 = _mm256_loadu_ps(unpacked[i+0]);
|
||||
}
|
||||
|
||||
__m256 t0 = _mm256_unpacklo_ps(s0, s1);
|
||||
__m256 t1 = _mm256_unpackhi_ps(s0, s1);
|
||||
__m256 t2 = _mm256_unpacklo_ps(s2, s3);
|
||||
__m256 t3 = _mm256_unpackhi_ps(s2, s3);
|
||||
__m256 t4 = _mm256_unpacklo_ps(s4, s5);
|
||||
__m256 t5 = _mm256_unpackhi_ps(s4, s5);
|
||||
__m256 t6 = _mm256_unpacklo_ps(s6, s7);
|
||||
__m256 t7 = _mm256_unpackhi_ps(s6, s7);
|
||||
|
||||
s0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
|
||||
s1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
|
||||
s2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
|
||||
s3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
|
||||
s4 = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(1,0,1,0));
|
||||
s5 = _mm256_shuffle_ps(t4, t6, _MM_SHUFFLE(3,2,3,2));
|
||||
s6 = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(1,0,1,0));
|
||||
s7 = _mm256_shuffle_ps(t5, t7, _MM_SHUFFLE(3,2,3,2));
|
||||
|
||||
__m256 px = _mm256_permute2f128_ps(s0, s4, 0x20);
|
||||
__m256 py = _mm256_permute2f128_ps(s1, s5, 0x20);
|
||||
__m256 pz = _mm256_permute2f128_ps(s2, s6, 0x20);
|
||||
__m256 nx = _mm256_permute2f128_ps(s3, s7, 0x20);
|
||||
__m256 ny = _mm256_permute2f128_ps(s0, s4, 0x31);
|
||||
__m256 nz = _mm256_permute2f128_ps(s1, s5, 0x31);
|
||||
__m256 tx = _mm256_permute2f128_ps(s2, s6, 0x31);
|
||||
__m256 ty = _mm256_permute2f128_ps(s3, s7, 0x31);
|
||||
|
||||
__m256i loadmask = _mm256_cvtepi8_epi32(_mm_cvtsi64_si128(0xffffffffffffffffULL >> (64 - 8 * rem)));
|
||||
__m256 tz = _mm256_mask_i32gather_ps(_mm256_setzero_ps(), unpacked[i+0], _mm256_setr_epi32(8,17,26,35,44,53,62,71),
|
||||
_mm256_castsi256_ps(loadmask), sizeof(float));
|
||||
// abs(pos)
|
||||
__m256 apx = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), px);
|
||||
__m256 apy = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), py);
|
||||
__m256 apz = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), pz);
|
||||
|
||||
// len = compMax(abs(pos))
|
||||
__m256 len = _mm256_max_ps(_mm256_max_ps(apx, apy), apz);
|
||||
|
||||
// detect zeros
|
||||
__m256 mask = _mm256_cmp_ps(len, _mm256_setzero_ps(), _CMP_EQ_OQ);
|
||||
|
||||
// rcp = 1.0f / len
|
||||
__m256 rcp = _mm256_div_ps(_mm256_set1_ps(1.0f), len);
|
||||
|
||||
// replace +inf with 1.0f
|
||||
rcp = _mm256_blendv_ps(rcp, _mm256_set1_ps(1.0f), mask);
|
||||
len = _mm256_blendv_ps(len, _mm256_set1_ps(1.0f), mask);
|
||||
|
||||
// pos *= 1.0f / len
|
||||
px = _mm256_mul_ps(px, rcp);
|
||||
py = _mm256_mul_ps(py, rcp);
|
||||
pz = _mm256_mul_ps(pz, rcp);
|
||||
|
||||
// clamp(vec, -1.0f, 1.0f)
|
||||
px = _mm256_min_ps(_mm256_max_ps(px, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
py = _mm256_min_ps(_mm256_max_ps(py, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
pz = _mm256_min_ps(_mm256_max_ps(pz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
nx = _mm256_min_ps(_mm256_max_ps(nx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
ny = _mm256_min_ps(_mm256_max_ps(ny, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
nz = _mm256_min_ps(_mm256_max_ps(nz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
tx = _mm256_min_ps(_mm256_max_ps(tx, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
ty = _mm256_min_ps(_mm256_max_ps(ty, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
tz = _mm256_min_ps(_mm256_max_ps(tz, _mm256_set1_ps(-1.0f)), _mm256_set1_ps(1.0f));
|
||||
|
||||
// vec *= 511.0f
|
||||
px = _mm256_mul_ps(px, _mm256_set1_ps(511.0f));
|
||||
py = _mm256_mul_ps(py, _mm256_set1_ps(511.0f));
|
||||
pz = _mm256_mul_ps(pz, _mm256_set1_ps(511.0f));
|
||||
nx = _mm256_mul_ps(nx, _mm256_set1_ps(511.0f));
|
||||
ny = _mm256_mul_ps(ny, _mm256_set1_ps(511.0f));
|
||||
nz = _mm256_mul_ps(nz, _mm256_set1_ps(511.0f));
|
||||
tx = _mm256_mul_ps(tx, _mm256_set1_ps(511.0f));
|
||||
ty = _mm256_mul_ps(ty, _mm256_set1_ps(511.0f));
|
||||
tz = _mm256_mul_ps(tz, _mm256_set1_ps(511.0f));
|
||||
|
||||
// veci = lrint(vec) & 03ff
|
||||
__m256i pxi = _mm256_and_si256(_mm256_cvtps_epi32(px), _mm256_set1_epi32(0x3ff));
|
||||
__m256i pyi = _mm256_and_si256(_mm256_cvtps_epi32(py), _mm256_set1_epi32(0x3ff));
|
||||
__m256i pzi = _mm256_and_si256(_mm256_cvtps_epi32(pz), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nxi = _mm256_and_si256(_mm256_cvtps_epi32(nx), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nyi = _mm256_and_si256(_mm256_cvtps_epi32(ny), _mm256_set1_epi32(0x3ff));
|
||||
__m256i nzi = _mm256_and_si256(_mm256_cvtps_epi32(nz), _mm256_set1_epi32(0x3ff));
|
||||
__m256i txi = _mm256_and_si256(_mm256_cvtps_epi32(tx), _mm256_set1_epi32(0x3ff));
|
||||
__m256i tyi = _mm256_and_si256(_mm256_cvtps_epi32(ty), _mm256_set1_epi32(0x3ff));
|
||||
__m256i tzi = _mm256_and_si256(_mm256_cvtps_epi32(tz), _mm256_set1_epi32(0x3ff));
|
||||
|
||||
// pack = (xi << 0) | (yi << 10) | (zi << 20);
|
||||
__m256i li = _mm256_castps_si256(len); // length
|
||||
__m256i pi = _mm256_or_si256(_mm256_or_si256(pxi, _mm256_slli_epi32(pyi, 10)), _mm256_slli_epi32(pzi, 20)); // position
|
||||
__m256i ni = _mm256_or_si256(_mm256_or_si256(nxi, _mm256_slli_epi32(nyi, 10)), _mm256_slli_epi32(nzi, 20)); // normal
|
||||
__m256i ti = _mm256_or_si256(_mm256_or_si256(txi, _mm256_slli_epi32(tyi, 10)), _mm256_slli_epi32(tzi, 20)); // tangent
|
||||
|
||||
//
|
||||
// interleave (4x4 matrix transpose)
|
||||
//
|
||||
__m256i u0 = _mm256_unpacklo_epi32(li, pi);
|
||||
__m256i u1 = _mm256_unpackhi_epi32(li, pi);
|
||||
__m256i u2 = _mm256_unpacklo_epi32(ni, ti);
|
||||
__m256i u3 = _mm256_unpackhi_epi32(ni, ti);
|
||||
|
||||
__m256i v0 = _mm256_unpacklo_epi64(u0, u2);
|
||||
__m256i v1 = _mm256_unpackhi_epi64(u0, u2);
|
||||
__m256i v2 = _mm256_unpacklo_epi64(u1, u3);
|
||||
__m256i v3 = _mm256_unpackhi_epi64(u1, u3);
|
||||
|
||||
// store pack x 8
|
||||
switch (rem) {
|
||||
case 7: _mm_storeu_si128((__m128i*)packed[i+6], _mm256_extractf128_si256(v2, 1));
|
||||
case 6: _mm_storeu_si128((__m128i*)packed[i+5], _mm256_extractf128_si256(v1, 1));
|
||||
case 5: _mm_storeu_si128((__m128i*)packed[i+4], _mm256_extractf128_si256(v0, 1));
|
||||
case 4: _mm_storeu_si128((__m128i*)packed[i+3], _mm256_castsi256_si128(v3));
|
||||
case 3: _mm_storeu_si128((__m128i*)packed[i+2], _mm256_castsi256_si128(v2));
|
||||
case 2: _mm_storeu_si128((__m128i*)packed[i+1], _mm256_castsi256_si128(v1));
|
||||
case 1: _mm_storeu_si128((__m128i*)packed[i+0], _mm256_castsi256_si128(v0));
|
||||
}
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
#endif
|
148
tests/shared/src/BlendshapePackingTests.cpp
Normal file
148
tests/shared/src/BlendshapePackingTests.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// BlendshapePackingTests.cpp
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Ken Cooke on 6/24/19.
|
||||
// Copyright 2019 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 "BlendshapePackingTests.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <test-utils/QTestExtensions.h>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
struct BlendshapeOffsetUnpacked {
|
||||
glm::vec3 positionOffset;
|
||||
glm::vec3 normalOffset;
|
||||
glm::vec3 tangentOffset;
|
||||
};
|
||||
|
||||
struct BlendshapeOffsetPacked {
|
||||
glm::uvec4 packedPosNorTan;
|
||||
};
|
||||
|
||||
QTEST_MAIN(BlendshapePackingTests)
|
||||
|
||||
static void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) {
|
||||
float len = glm::compMax(glm::abs(unpacked.positionOffset));
|
||||
glm::vec3 normalizedPos(unpacked.positionOffset);
|
||||
if (len > 0.0f) {
|
||||
normalizedPos /= len;
|
||||
} else {
|
||||
len = 1.0f;
|
||||
}
|
||||
|
||||
packed = glm::uvec4(
|
||||
glm::floatBitsToUint(len),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(normalizedPos, 0.0f)),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.normalOffset, 0.0f)),
|
||||
glm_packSnorm3x10_1x2(glm::vec4(unpacked.tangentOffset, 0.0f))
|
||||
);
|
||||
}
|
||||
|
||||
static void packBlendshapeOffsets_ref(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10((*packed).packedPosNorTan, (*unpacked));
|
||||
++unpacked;
|
||||
++packed;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
//
|
||||
// Runtime CPU dispatch
|
||||
//
|
||||
#include <CPUDetect.h>
|
||||
|
||||
void packBlendshapeOffsets_AVX2(float (*unpacked)[9], uint32_t (*packed)[4], int size);
|
||||
|
||||
static void packBlendshapeOffsets(BlendshapeOffsetUnpacked* unpacked, BlendshapeOffsetPacked* packed, int size) {
|
||||
static bool _cpuSupportsAVX2 = cpuSupportsAVX2();
|
||||
if (_cpuSupportsAVX2) {
|
||||
static_assert(sizeof(BlendshapeOffsetUnpacked) == 9 * sizeof(float), "struct BlendshapeOffsetUnpacked size doesn't match.");
|
||||
static_assert(sizeof(BlendshapeOffsetPacked) == 4 * sizeof(uint32_t), "struct BlendshapeOffsetPacked size doesn't match.");
|
||||
packBlendshapeOffsets_AVX2((float(*)[9])unpacked, (uint32_t(*)[4])packed, size);
|
||||
} else {
|
||||
packBlendshapeOffsets_ref(unpacked, packed, size);
|
||||
}
|
||||
}
|
||||
|
||||
#else // portable reference code
|
||||
static auto& packBlendshapeOffsets = packBlendshapeOffsets_ref;
|
||||
#endif
|
||||
|
||||
void comparePacked(BlendshapeOffsetPacked& ref, BlendshapeOffsetPacked& tst) {
|
||||
union i10i10i10i2 {
|
||||
struct {
|
||||
int x : 10;
|
||||
int y : 10;
|
||||
int z : 10;
|
||||
int w : 2;
|
||||
} data;
|
||||
uint32_t pack;
|
||||
} Ref[4], Tst[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Ref[i].pack = ref.packedPosNorTan[i];
|
||||
Tst[i].pack = tst.packedPosNorTan[i];
|
||||
}
|
||||
|
||||
// allow 1 ULP due to rounding differences
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[0].pack, Ref[0].pack, 1);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[1].data.x, Ref[1].data.x, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[1].data.y, Ref[1].data.y, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[1].data.z, Ref[1].data.z, 1);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[2].data.x, Ref[2].data.x, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[2].data.y, Ref[2].data.y, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[2].data.z, Ref[2].data.z, 1);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[3].data.x, Ref[3].data.x, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[3].data.y, Ref[3].data.y, 1);
|
||||
QCOMPARE_WITH_ABS_ERROR(Tst[3].data.z, Ref[3].data.z, 1);
|
||||
}
|
||||
|
||||
void BlendshapePackingTests::testAVX2() {
|
||||
|
||||
for (int numBlendshapeOffsets = 0; numBlendshapeOffsets < 4096; ++numBlendshapeOffsets) {
|
||||
|
||||
std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(numBlendshapeOffsets);
|
||||
std::vector<BlendshapeOffsetPacked> packedBlendshapeOffsets1(numBlendshapeOffsets);
|
||||
std::vector<BlendshapeOffsetPacked> packedBlendshapeOffsets2(numBlendshapeOffsets);
|
||||
|
||||
// init test data
|
||||
if (numBlendshapeOffsets > 0) {
|
||||
unpackedBlendshapeOffsets[0] = {
|
||||
glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
};
|
||||
}
|
||||
for (int i = 1; i < numBlendshapeOffsets; ++i) {
|
||||
unpackedBlendshapeOffsets[i] = {
|
||||
glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)),
|
||||
glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)),
|
||||
glm::linearRand(glm::vec3(-2.0f, -2.0f, -2.0f), glm::vec3(2.0f, 2.0f, 2.0f)),
|
||||
};
|
||||
}
|
||||
|
||||
// ref version
|
||||
packBlendshapeOffsets_ref(unpackedBlendshapeOffsets.data(), packedBlendshapeOffsets1.data(), numBlendshapeOffsets);
|
||||
|
||||
// AVX2 version, if supported by CPU
|
||||
packBlendshapeOffsets(unpackedBlendshapeOffsets.data(), packedBlendshapeOffsets2.data(), numBlendshapeOffsets);
|
||||
|
||||
// verify
|
||||
for (int i = 0; i < numBlendshapeOffsets; ++i) {
|
||||
auto ref = packedBlendshapeOffsets1.at(i);
|
||||
auto tst = packedBlendshapeOffsets2.at(i);
|
||||
comparePacked(ref, tst);
|
||||
}
|
||||
}
|
||||
}
|
23
tests/shared/src/BlendshapePackingTests.h
Normal file
23
tests/shared/src/BlendshapePackingTests.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// BlendshapePackingTests.h
|
||||
// tests/shared/src
|
||||
//
|
||||
// Created by Ken Cooke on 6/24/19.
|
||||
// Copyright 2019 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
|
||||
//
|
||||
|
||||
#ifndef hifi_BlendshapePackingTests_h
|
||||
#define hifi_BlendshapePackingTests_h
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class BlendshapePackingTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void testAVX2();
|
||||
};
|
||||
|
||||
#endif // hifi_BlendshapePackingTests_h
|
Loading…
Reference in a new issue