Merge branch 'master' of https://github.com/highfidelity/hifi into commerce_daveIntegration3

This commit is contained in:
Zach Fox 2017-10-10 12:36:06 -07:00
commit 1c0a564155
33 changed files with 1578 additions and 1537 deletions

9
.gitignore vendored
View File

@ -17,6 +17,15 @@ Makefile
local.properties
android/libraries
# VSCode
# List taken from Github Global Ignores master@435c4d92
# https://github.com/github/gitignore/commits/master/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Xcode
*.xcodeproj
*.xcworkspace

View File

@ -29,12 +29,22 @@ RowLayout {
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: Audio.playSystemSound should not require position
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(function() { isPlaying = false; sample = null; });
if (sample === null && !isPlaying) {
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(reset);
}
}
function stopSound() {
sample && sample.stop();
if (sample && isPlaying) {
sample.stop();
}
}
function reset() {
sample.finished.disconnect(reset);
isPlaying = false;
sample = null;
}
Component.onCompleted: createSampleSound();

View File

@ -15,7 +15,7 @@ import "."
Rectangle {
id: modalWindow
layer.enabled: true
property var title: "Modal"
property var title: "Open"
width: tabletRoot.width
height: tabletRoot.height
color: "#80000000"

View File

@ -1638,12 +1638,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
QJsonObject bytesDownloaded;
bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt();
bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt();
bytesDownloaded["file"] = statTracker->getStat(STAT_FILE_RESOURCE_TOTAL_BYTES).toInt();
bytesDownloaded["total"] = bytesDownloaded["atp"].toInt() + bytesDownloaded["http"].toInt()
+ bytesDownloaded["file"].toInt();
properties["bytesDownloaded"] = bytesDownloaded;
auto atpBytes = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toLongLong();
auto httpBytes = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toLongLong();
auto fileBytes = statTracker->getStat(STAT_FILE_RESOURCE_TOTAL_BYTES).toLongLong();
bytesDownloaded["atp"] = atpBytes;
bytesDownloaded["http"] = httpBytes;
bytesDownloaded["file"] = fileBytes;
bytesDownloaded["total"] = atpBytes + httpBytes + fileBytes;
properties["bytes_downloaded"] = bytesDownloaded;
auto myAvatar = getMyAvatar();
glm::vec3 avatarPosition = myAvatar->getPosition();
@ -5634,14 +5636,37 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
return false;
}
// We don't want to use EntityTree::findEntities(AABox, ...) method because that scan will snarf parented entities
// whose bounding boxes cannot be computed (it is too loose for our purposes here). Instead we manufacture
// custom filters and use the general-purpose EntityTree::findEntities(filter, ...)
QVector<EntityItemPointer> entities;
AABox avatarBox(getMyAvatar()->getPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
// create two functions that use avatarBox (entityScan and elementScan), the second calls the first
std::function<bool (EntityItemPointer&)> entityScan = [=](EntityItemPointer& entity) {
if (entity->shouldBePhysical()) {
bool success = false;
AABox entityBox = entity->getAABox(success);
// important: bail for entities that cannot supply a valid AABox
return success && avatarBox.touches(entityBox);
}
return false;
};
std::function<bool(const OctreeElementPointer&, void*)> elementScan = [&](const OctreeElementPointer& element, void* unused) {
if (element->getAACube().touches(avatarBox)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(entityScan, entities);
return true;
}
return false;
};
entityTree->withReadLock([&] {
AABox box(getMyAvatar()->getPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
entityTree->findEntities(box, entities);
// Pass the second function to the general-purpose EntityTree::findEntities()
// which will traverse the tree, apply the two filter functions (to element, then to entities)
// as it traverses. The end result will be a list of entities that match.
entityTree->findEntities(elementScan, entities);
});
// For reasons I haven't found, we don't necessarily have the full scene when we receive a stats packet. Apply
// a heuristic to try to decide when we actually know about all of the nearby entities.
uint32_t nearbyCount = entities.size();
if (nearbyCount == _nearbyEntitiesCountAtLastPhysicsCheck) {
_nearbyEntitiesStabilityCount++;

View File

@ -78,7 +78,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
request->send();
} else {
++_errorCount;
qWarning() << "Count not create request for asset at" << migrationURL.toString();
qWarning() << "Could not create request for asset at" << migrationURL.toString();
}
};

View File

@ -21,6 +21,7 @@
#include <NetworkLogging.h>
#include <NodeList.h>
#include <OffscreenUi.h>
#include <UserActivityLogger.h>
static const int AUTO_REFRESH_INTERVAL = 1000;
@ -104,6 +105,21 @@ void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping,
startedCallback.call();
QFileInfo fileInfo { path };
int64_t size { fileInfo.size() };
QString extension = "";
auto idx = path.lastIndexOf(".");
if (idx >= 0) {
extension = path.mid(idx + 1);
}
UserActivityLogger::getInstance().logAction("uploading_asset", {
{ "size", (qint64)size },
{ "mapping", mapping },
{ "extension", extension}
});
auto upload = DependencyManager::get<AssetClient>()->createUpload(path);
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
if (upload->getError() != AssetUpload::NoError) {

View File

@ -1240,6 +1240,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
mapJoints(entity, model->getJointNames());
}
animate(entity);
emit requestRenderUpdate();
}
}

View File

@ -864,6 +864,12 @@ void EntityTree::findEntities(const ViewFrustum& frustum, QVector<EntityItemPoin
foundEntities.swap(args.entities);
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(RecurseOctreeOperation& elementFilter,
QVector<EntityItemPointer>& foundEntities) {
recurseTreeWithOperation(elementFilter, nullptr);
}
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) {
EntityItemID entityID(id);
return findEntityByEntityItemID(entityID);

View File

@ -165,6 +165,11 @@ public:
/// \param foundEntities[out] vector of EntityItemPointer
void findEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that match scanOperator
/// \parameter scanOperator function that scans entities that match criteria
/// \parameter foundEntities[out] vector of EntityItemPointer
void findEntities(RecurseOctreeOperation& scanOperator, QVector<EntityItemPointer>& foundEntities);
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);

View File

@ -869,6 +869,14 @@ void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector<EntityIt
});
}
void EntityTreeElement::getEntities(EntityItemFilter& filter, QVector<EntityItemPointer>& foundEntities) {
forEachEntity([&](EntityItemPointer entity) {
if (filter(entity)) {
foundEntities.push_back(entity);
}
});
}
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
EntityItemPointer foundEntity = NULL;
withReadLock([&] {

View File

@ -27,6 +27,7 @@ class EntityTreeElement;
using EntityItems = QVector<EntityItemPointer>;
using EntityTreeElementWeakPointer = std::weak_ptr<EntityTreeElement>;
using EntityTreeElementPointer = std::shared_ptr<EntityTreeElement>;
using EntityItemFilter = std::function<bool(EntityItemPointer&)>;
class EntityTreeUpdateArgs {
public:
@ -199,6 +200,11 @@ public:
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that match filter
/// \param filter function that adds matching entities to foundEntities
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(EntityItemFilter& filter, QVector<EntityItemPointer>& foundEntities);
EntityItemPointer getEntityWithID(uint32_t id) const;
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);

View File

@ -19,6 +19,7 @@ bool GLTexelFormat::isCompressed() const {
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
return true;
default:
return false;
@ -94,6 +95,11 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
result = GL_R11F_G11F_B10F;
break;
case gpu::RGB9E5:
// the type should be float
result = GL_RGB9_E5;
break;
case gpu::DEPTH:
result = GL_DEPTH_COMPONENT32;
switch (dstFormat.getType()) {
@ -244,6 +250,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::COMPRESSED_BC5_XY:
result = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC6_RGB:
result = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
break;
case gpu::COMPRESSED_BC7_SRGBA:
result = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
@ -396,6 +405,9 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC6_RGB:
texel.internalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;
@ -495,10 +507,16 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::R11G11B10:
texel.format = GL_RGB;
// the type should be float
texel.type = GL_UNSIGNED_INT_10F_11F_11F_REV;
texel.internalFormat = GL_R11F_G11F_B10F;
break;
case gpu::RGB9E5:
texel.format = GL_RGB;
texel.type = GL_UNSIGNED_INT_5_9_9_9_REV;
texel.internalFormat = GL_RGB9_E5;
break;
case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
texel.internalFormat = GL_DEPTH_COMPONENT32;
@ -694,6 +712,9 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
case gpu::COMPRESSED_BC5_XY:
texel.internalFormat = GL_COMPRESSED_RG_RGTC2;
break;
case gpu::COMPRESSED_BC6_RGB:
texel.internalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
break;
case gpu::COMPRESSED_BC7_SRGBA:
texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
break;

View File

@ -114,6 +114,7 @@ Size GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
@ -131,6 +132,7 @@ Size GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;

View File

@ -143,6 +143,7 @@ Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
@ -158,6 +159,7 @@ Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
if (glCompressedTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat,

View File

@ -24,11 +24,13 @@ const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_B
const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA };
const Element Element::COLOR_COMPRESSED_SRGBA { TILE4x4, COMPRESSED, COMPRESSED_BC3_SRGBA };
const Element Element::COLOR_COMPRESSED_XY { TILE4x4, COMPRESSED, COMPRESSED_BC5_XY };
const Element Element::COLOR_COMPRESSED_SRGBA_HIGH { TILE4x4, COMPRESSED, COMPRESSED_BC7_SRGBA };
const Element Element::COLOR_COMPRESSED_SRGBA_HIGH{ TILE4x4, COMPRESSED, COMPRESSED_BC7_SRGBA };
const Element Element::COLOR_COMPRESSED_HDR_RGB{ TILE4x4, COMPRESSED, COMPRESSED_BC6_RGB };
const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY };
const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 };
const Element Element::COLOR_RGB9E5{ SCALAR, FLOAT, RGB9E5 };
const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA };
const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
const Element Element::VEC2F_XY{ VEC2, FLOAT, XY };

View File

@ -187,11 +187,13 @@ enum Semantic : uint8_t {
COMPRESSED_BC3_SRGBA,
COMPRESSED_BC4_RED,
COMPRESSED_BC5_XY,
COMPRESSED_BC6_RGB,
COMPRESSED_BC7_SRGBA,
_LAST_COMPRESSED,
R11G11B10,
RGB9E5,
UNIFORM,
UNIFORM_BUFFER,
@ -240,11 +242,13 @@ static const int SEMANTIC_SIZE_FACTOR[NUM_SEMANTICS] = {
16, //COMPRESSED_BC3_SRGBA, 1 byte/pixel * 4x4 pixels = 16 bytes
8, //COMPRESSED_BC4_RED, 1/2 byte/pixel * 4x4 pixels = 8 bytes
16, //COMPRESSED_BC5_XY, 1 byte/pixel * 4x4 pixels = 16 bytes
16, //COMPRESSED_BC6_RGB, 1 byte/pixel * 4x4 pixels = 16 bytes
16, //COMPRESSED_BC7_SRGBA, 1 byte/pixel * 4x4 pixels = 16 bytes
1, //_LAST_COMPRESSED,
1, //R11G11B10,
1, //RGB9E5
1, //UNIFORM,
1, //UNIFORM_BUFFER,
@ -306,12 +310,14 @@ public:
static const Element COLOR_BGRA_32;
static const Element COLOR_SBGRA_32;
static const Element COLOR_R11G11B10;
static const Element COLOR_RGB9E5;
static const Element COLOR_COMPRESSED_RED;
static const Element COLOR_COMPRESSED_SRGB;
static const Element COLOR_COMPRESSED_SRGBA_MASK;
static const Element COLOR_COMPRESSED_SRGBA;
static const Element COLOR_COMPRESSED_XY;
static const Element COLOR_COMPRESSED_SRGBA_HIGH;
static const Element COLOR_COMPRESSED_HDR_RGB;
static const Element VEC2NU8_XY;
static const Element VEC4F_COLOR_RGBA;
static const Element VEC2F_UV;

View File

@ -14,6 +14,7 @@
#include <glm/gtc/constants.hpp>
#include <glm/gtx/component_wise.hpp>
#include <glm/gtc/packing.hpp>
#include <QtCore/QDebug>
#include <QtCore/QThread>
@ -683,6 +684,21 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
auto mipFormat = cubeTexture.getStoredMipFormat();
std::function<glm::vec3(uint32)> unpackFunc;
switch (mipFormat.getSemantic()) {
case gpu::R11G11B10:
unpackFunc = glm::unpackF2x11_1x10;
break;
case gpu::RGB9E5:
unpackFunc = glm::unpackF3x9_E1x5;
break;
default:
assert(false);
break;
}
const uint sqOrder = order*order;
// allocate memory for calculations
@ -716,17 +732,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
PROFILE_RANGE(render_gpu, "ProcessFace");
auto mipFormat = cubeTexture.getStoredMipFormat();
auto numComponents = mipFormat.getScalarCount();
int roffset { 0 };
int goffset { 1 };
int boffset { 2 };
if ((mipFormat.getSemantic() == gpu::BGRA) || (mipFormat.getSemantic() == gpu::SBGRA)) {
roffset = 2;
boffset = 0;
}
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
if (data == nullptr) {
continue;
}
@ -806,29 +812,24 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
// index of texel in texture
// get color from texture and map to range [0, 1]
float red { 0.0f };
float green { 0.0f };
float blue { 0.0f };
// get color from texture
glm::vec3 color{ 0.0f, 0.0f, 0.0f };
for (int i = 0; i < stride; ++i) {
for (int j = 0; j < stride; ++j) {
int k = (int)(x + i - halfStride + (y + j - halfStride) * width) * numComponents;
red += ColorUtils::sRGB8ToLinearFloat(data[k + roffset]);
green += ColorUtils::sRGB8ToLinearFloat(data[k + goffset]);
blue += ColorUtils::sRGB8ToLinearFloat(data[k + boffset]);
int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
color += unpackFunc(data[k]);
}
}
glm::vec3 clr(red, green, blue);
// scale color and add to previously accumulated coefficients
// red
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.r * fDiffSolid);
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.r * fDiffSolid);
sphericalHarmonicsAdd(resultR.data(), order, resultR.data(), shBuffB.data());
// green
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.g * fDiffSolid);
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.g * fDiffSolid);
sphericalHarmonicsAdd(resultG.data(), order, resultG.data(), shBuffB.data());
// blue
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.b * fDiffSolid);
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.b * fDiffSolid);
sphericalHarmonicsAdd(resultB.data(), order, resultB.data(), shBuffB.data());
}
}

View File

@ -515,8 +515,6 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
return texture;
}
TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(cacheEntry->getFilepath().c_str()));
if (!ktxPointer) {
@ -536,7 +534,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile) {
if (!ktxPointer) {
return nullptr;
}
auto texture = build(ktxPointer->toDescriptor());
if (texture) {
texture->setKtxBacking(ktxfile);
@ -570,6 +568,12 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
header.setCompressed(ktx::GLInternalFormat::COMPRESSED_RG_RGTC2, ktx::GLBaseInternalFormat::RG);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH && mipFormat == Format::COLOR_COMPRESSED_SRGBA_HIGH) {
header.setCompressed(ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM, ktx::GLBaseInternalFormat::RGBA);
} else if (texelFormat == Format::COLOR_COMPRESSED_HDR_RGB && mipFormat == Format::COLOR_COMPRESSED_HDR_RGB) {
header.setCompressed(ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, ktx::GLBaseInternalFormat::RGB);
} else if (texelFormat == Format::COLOR_RGB9E5 && mipFormat == Format::COLOR_RGB9E5) {
header.setUncompressed(ktx::GLType::UNSIGNED_INT_5_9_9_9_REV, 1, ktx::GLFormat::RGB, ktx::GLInternalFormat::RGB9_E5, ktx::GLBaseInternalFormat::RGB);
} else if (texelFormat == Format::COLOR_R11G11B10 && mipFormat == Format::COLOR_R11G11B10) {
header.setUncompressed(ktx::GLType::UNSIGNED_INT_10F_11F_11F_REV, 1, ktx::GLFormat::RGB, ktx::GLInternalFormat::R11F_G11F_B10F, ktx::GLBaseInternalFormat::RGB);
} else {
return false;
}
@ -612,6 +616,12 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
} else {
return false;
}
} else if (header.getGLFormat() == ktx::GLFormat::RGB && header.getGLType() == ktx::GLType::UNSIGNED_INT_10F_11F_11F_REV) {
mipFormat = Format::COLOR_R11G11B10;
texelFormat = Format::COLOR_R11G11B10;
} else if (header.getGLFormat() == ktx::GLFormat::RGB && header.getGLType() == ktx::GLType::UNSIGNED_INT_5_9_9_9_REV) {
mipFormat = Format::COLOR_RGB9E5;
texelFormat = Format::COLOR_RGB9E5;
} else if (header.isCompressed()) {
if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
mipFormat = Format::COLOR_COMPRESSED_SRGB;
@ -631,6 +641,9 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
} else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
mipFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH;
texelFormat = Format::COLOR_COMPRESSED_SRGBA_HIGH;
} else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) {
mipFormat = Format::COLOR_COMPRESSED_HDR_RGB;
texelFormat = Format::COLOR_COMPRESSED_HDR_RGB;
} else {
return false;
}

View File

@ -11,9 +11,10 @@
#include "Image.h"
#include <nvtt/nvtt.h>
#include <glm/gtc/packing.hpp>
#include <QtCore/QtGlobal>
#include <QUrl>
#include <QImage>
#include <QBuffer>
@ -42,6 +43,8 @@ bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10;
static std::atomic<bool> compressColorTextures { false };
static std::atomic<bool> compressNormalTextures { false };
static std::atomic<bool> compressGrayscaleTextures { false };
@ -71,6 +74,10 @@ glm::uvec2 rectifyToSparseSize(const glm::uvec2& size) {
namespace image {
enum {
QIMAGE_HDR_FORMAT = QImage::Format_RGB30
};
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) {
case ALBEDO_TEXTURE:
@ -213,6 +220,25 @@ void setCubeTexturesCompressionEnabled(bool enabled) {
compressCubeTextures.store(enabled);
}
static float denormalize(float value, const float minValue) {
return value < minValue ? 0.0f : value;
}
uint32 packR11G11B10F(const glm::vec3& color) {
// Denormalize else unpacking gives high and incorrect values
// See https://www.khronos.org/opengl/wiki/Small_Float_Formats for this min value
static const auto minValue = 6.10e-5f;
static const auto maxValue = 6.50e4f;
glm::vec3 ucolor;
ucolor.r = denormalize(color.r, minValue);
ucolor.g = denormalize(color.g, minValue);
ucolor.b = denormalize(color.b, minValue);
ucolor.r = std::min(ucolor.r, maxValue);
ucolor.g = std::min(ucolor.g, maxValue);
ucolor.b = std::min(ucolor.b, maxValue);
return glm::packF2x11_1x10(ucolor);
}
gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename,
int maxNumPixels, TextureUsage::Type textureType,
const std::atomic<bool>& abortProcessing) {
@ -270,8 +296,6 @@ gpu::TexturePointer processImage(const QByteArray& content, const std::string& f
return texture;
}
QImage processSourceImage(const QImage& srcImage, bool cubemap) {
PROFILE_RANGE(resource_parse, "processSourceImage");
const glm::uvec2 srcImageSize = toGlm(srcImage.size());
@ -303,8 +327,8 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) {
}
#if defined(NVTT_API)
struct MyOutputHandler : public nvtt::OutputHandler {
MyOutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {}
struct OutputHandler : public nvtt::OutputHandler {
OutputHandler(gpu::Texture* texture, int face) : _texture(texture), _face(face) {}
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) override {
_size = size;
@ -313,12 +337,14 @@ struct MyOutputHandler : public nvtt::OutputHandler {
_data = static_cast<gpu::Byte*>(malloc(size));
_current = _data;
}
virtual bool writeData(const void* data, int size) override {
assert(_current + size <= _data + _size);
memcpy(_current, data, size);
_current += size;
return true;
}
virtual void endImage() override {
if (_face >= 0) {
_texture->assignStoredMipFace(_miplevel, _face, _size, static_cast<const gpu::Byte*>(_data));
@ -336,6 +362,51 @@ struct MyOutputHandler : public nvtt::OutputHandler {
int _size = 0;
int _face = -1;
};
struct PackedFloatOutputHandler : public OutputHandler {
PackedFloatOutputHandler(gpu::Texture* texture, int face, gpu::Element format) : OutputHandler(texture, face) {
if (format == gpu::Element::COLOR_RGB9E5) {
_packFunc = glm::packF3x9_E1x5;
} else if (format == gpu::Element::COLOR_R11G11B10) {
_packFunc = packR11G11B10F;
} else {
qCWarning(imagelogging) << "Unknown handler format";
Q_UNREACHABLE();
}
}
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) override {
// Divide by 3 because we will compress from 3*floats to 1 uint32
OutputHandler::beginImage(size / 3, width, height, depth, face, miplevel);
}
virtual bool writeData(const void* data, int size) override {
// Expecting to write multiple of floats
if (_packFunc) {
assert((size % sizeof(float)) == 0);
auto floatCount = size / sizeof(float);
const float* floatBegin = (const float*)data;
const float* floatEnd = floatBegin + floatCount;
while (floatBegin < floatEnd) {
_pixel[_coordIndex] = *floatBegin;
floatBegin++;
_coordIndex++;
if (_coordIndex == 3) {
uint32 packedRGB = _packFunc(_pixel);
_coordIndex = 0;
OutputHandler::writeData(&packedRGB, sizeof(packedRGB));
}
}
return true;
}
return false;
}
std::function<uint32(const glm::vec3&)> _packFunc;
glm::vec3 _pixel;
int _coordIndex{ 0 };
};
struct MyErrorHandler : public nvtt::ErrorHandler {
virtual void error(nvtt::Error e) override {
qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e);
@ -343,10 +414,115 @@ struct MyErrorHandler : public nvtt::ErrorHandler {
};
#endif
void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
#if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips");
class SequentialTaskDispatcher : public nvtt::TaskDispatcher {
public:
SequentialTaskDispatcher(const std::atomic<bool>& abortProcessing) : _abortProcessing(abortProcessing) {};
const std::atomic<bool>& _abortProcessing;
virtual void dispatch(nvtt::Task* task, void* context, int count) override {
for (int i = 0; i < count; i++) {
if (!_abortProcessing.load()) {
task(context, i);
} else {
break;
}
}
}
};
void generateHDRMips(gpu::Texture* texture, const QImage& image, const std::atomic<bool>& abortProcessing, int face) {
assert(image.format() == QIMAGE_HDR_FORMAT);
const int width = image.width(), height = image.height();
std::vector<glm::vec4> data;
std::vector<glm::vec4>::iterator dataIt;
auto mipFormat = texture->getStoredMipFormat();
std::function<glm::vec3(uint32)> unpackFunc;
nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_RGBA_32F;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
nvtt::RoundMode roundMode = nvtt::RoundMode_None;
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production);
if (mipFormat == gpu::Element::COLOR_COMPRESSED_HDR_RGB) {
compressionOptions.setFormat(nvtt::Format_BC6);
} else if (mipFormat == gpu::Element::COLOR_RGB9E5) {
compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_Float);
compressionOptions.setPixelFormat(32, 32, 32, 0);
} else if (mipFormat == gpu::Element::COLOR_R11G11B10) {
compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_Float);
compressionOptions.setPixelFormat(32, 32, 32, 0);
} else {
qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE();
return;
}
if (HDR_FORMAT == gpu::Element::COLOR_RGB9E5) {
unpackFunc = glm::unpackF3x9_E1x5;
} else if (HDR_FORMAT == gpu::Element::COLOR_R11G11B10) {
unpackFunc = glm::unpackF2x11_1x10;
} else {
qCWarning(imagelogging) << "Unknown HDR encoding format in QImage";
Q_UNREACHABLE();
return;
}
data.resize(width*height);
dataIt = data.begin();
for (auto lineNb = 0; lineNb < height; lineNb++) {
const uint32* srcPixelIt = reinterpret_cast<const uint32*>( image.constScanLine(lineNb) );
const uint32* srcPixelEnd = srcPixelIt + width;
while (srcPixelIt < srcPixelEnd) {
*dataIt = glm::vec4(unpackFunc(*srcPixelIt), 1.0f);
++srcPixelIt;
++dataIt;
}
}
assert(dataIt == data.end());
nvtt::OutputOptions outputOptions;
outputOptions.setOutputHeader(false);
std::unique_ptr<nvtt::OutputHandler> outputHandler;
MyErrorHandler errorHandler;
outputOptions.setErrorHandler(&errorHandler);
nvtt::Context context;
int mipLevel = 0;
if (mipFormat == gpu::Element::COLOR_RGB9E5 || mipFormat == gpu::Element::COLOR_R11G11B10) {
// Don't use NVTT (at least version 2.1) as it outputs wrong RGB9E5 and R11G11B10F values from floats
outputHandler.reset(new PackedFloatOutputHandler(texture, face, mipFormat));
} else {
outputHandler.reset( new OutputHandler(texture, face) );
}
outputOptions.setOutputHandler(outputHandler.get());
nvtt::Surface surface;
surface.setImage(inputFormat, width, height, 1, &(*data.begin()));
surface.setAlphaMode(alphaMode);
surface.setWrapMode(wrapMode);
SequentialTaskDispatcher dispatcher(abortProcessing);
nvtt::Compressor compressor;
context.setTaskDispatcher(&dispatcher);
context.compress(surface, face, mipLevel++, compressionOptions, outputOptions);
while (surface.canMakeNextMipmap() && !abortProcessing.load()) {
surface.buildNextMipmap(nvtt::MipmapFilter_Box);
context.compress(surface, face, mipLevel++, compressionOptions, outputOptions);
}
}
void generateLDRMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>& abortProcessing, int face) {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
@ -400,10 +576,10 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>&
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
inputGamma = 1.0f;
outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
@ -411,10 +587,10 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>&
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
inputGamma = 1.0f;
outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
@ -422,19 +598,19 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>&
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_R_8) {
compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
@ -454,32 +630,26 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>&
nvtt::OutputOptions outputOptions;
outputOptions.setOutputHeader(false);
MyOutputHandler outputHandler(texture, face);
OutputHandler outputHandler(texture, face);
outputOptions.setOutputHandler(&outputHandler);
MyErrorHandler errorHandler;
outputOptions.setErrorHandler(&errorHandler);
class SequentialTaskDispatcher : public nvtt::TaskDispatcher {
public:
SequentialTaskDispatcher(const std::atomic<bool>& abortProcessing) : _abortProcessing(abortProcessing) {};
const std::atomic<bool>& _abortProcessing;
virtual void dispatch(nvtt::Task* task, void* context, int count) override {
for (int i = 0; i < count; i++) {
if (!_abortProcessing.load()) {
task(context, i);
} else {
break;
}
}
}
};
SequentialTaskDispatcher dispatcher(abortProcessing);
nvtt::Compressor compressor;
compressor.setTaskDispatcher(&dispatcher);
compressor.process(inputOptions, compressionOptions, outputOptions);
}
void generateMips(gpu::Texture* texture, QImage& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
#if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips");
if (image.format() == QIMAGE_HDR_FORMAT) {
generateHDRMips(texture, image, abortProcessing, face);
} else {
generateLDRMips(texture, image, abortProcessing, face);
}
#else
texture->setAutoGenerateMips(true);
#endif
@ -961,6 +1131,62 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
};
const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
//#define DEBUG_COLOR_PACKING
QImage convertToHDRFormat(QImage srcImage, gpu::Element format) {
QImage hdrImage(srcImage.width(), srcImage.height(), (QImage::Format)QIMAGE_HDR_FORMAT);
std::function<uint32(const glm::vec3&)> packFunc;
#ifdef DEBUG_COLOR_PACKING
std::function<glm::vec3(uint32)> unpackFunc;
#endif
switch (format.getSemantic()) {
case gpu::R11G11B10:
packFunc = packR11G11B10F;
#ifdef DEBUG_COLOR_PACKING
unpackFunc = glm::unpackF2x11_1x10;
#endif
break;
case gpu::RGB9E5:
packFunc = glm::packF3x9_E1x5;
#ifdef DEBUG_COLOR_PACKING
unpackFunc = glm::unpackF3x9_E1x5;
#endif
break;
default:
qCWarning(imagelogging) << "Unsupported HDR format";
Q_UNREACHABLE();
return srcImage;
}
srcImage = srcImage.convertToFormat(QImage::Format_ARGB32);
for (auto y = 0; y < srcImage.height(); y++) {
const QRgb* srcLineIt = reinterpret_cast<const QRgb*>( srcImage.constScanLine(y) );
const QRgb* srcLineEnd = srcLineIt + srcImage.width();
uint32* hdrLineIt = reinterpret_cast<uint32*>( hdrImage.scanLine(y) );
glm::vec3 color;
while (srcLineIt < srcLineEnd) {
color.r = qRed(*srcLineIt);
color.g = qGreen(*srcLineIt);
color.b = qBlue(*srcLineIt);
// Normalize and apply gamma
color /= 255.0f;
color.r = powf(color.r, 2.2f);
color.g = powf(color.g, 2.2f);
color.b = powf(color.b, 2.2f);
*hdrLineIt = packFunc(color);
#ifdef DEBUG_COLOR_PACKING
glm::vec3 ucolor = unpackFunc(*hdrLineIt);
assert(glm::distance(color, ucolor) <= 5e-2);
#endif
++srcLineIt;
++hdrLineIt;
}
}
return hdrImage;
}
gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName,
bool generateIrradiance,
const std::atomic<bool>& abortProcessing) {
@ -969,18 +1195,19 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
gpu::TexturePointer theTexture = nullptr;
if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
QImage image = processSourceImage(srcImage, true);
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
gpu::Element formatMip;
gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) {
formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA_HIGH;
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA_HIGH;
formatMip = gpu::Element::COLOR_COMPRESSED_HDR_RGB;
formatGPU = gpu::Element::COLOR_COMPRESSED_HDR_RGB;
} else {
formatMip = gpu::Element::COLOR_SRGBA_32;
formatGPU = gpu::Element::COLOR_SRGBA_32;
formatMip = HDR_FORMAT;
formatGPU = HDR_FORMAT;
}
if (image.format() != QIMAGE_HDR_FORMAT) {
image = convertToHDRFormat(image, HDR_FORMAT);
}
// Find the layout of the cubemap in the 2D image
@ -1028,9 +1255,9 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
// Generate irradiance while we are at it
if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "generateIrradiance");
auto irradianceTexture = gpu::Texture::createCube(gpu::Element::COLOR_SRGBA_32, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
auto irradianceTexture = gpu::Texture::createCube(HDR_FORMAT, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
irradianceTexture->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(gpu::Element::COLOR_SBGRA_32);
irradianceTexture->setStoredMipFormat(HDR_FORMAT);
for (uint8 face = 0; face < faces.size(); ++face) {
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
}

View File

@ -209,6 +209,137 @@ namespace khronos {
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
};
inline uint8_t evalUncompressedBlockBitSize(InternalFormat format) {
switch (format) {
case InternalFormat::R8:
case InternalFormat::R8_SNORM:
return 8;
case InternalFormat::R16:
case InternalFormat::R16_SNORM:
case InternalFormat::RG8:
case InternalFormat::RG8_SNORM:
return 16;
case InternalFormat::RG16:
case InternalFormat::RG16_SNORM:
return 16;
case InternalFormat::R3_G3_B2:
return 8;
case InternalFormat::RGB4:
return 12;
case InternalFormat::RGB5:
case InternalFormat::RGB565:
return 16;
case InternalFormat::RGB8:
case InternalFormat::RGB8_SNORM:
return 24;
case InternalFormat::RGB10:
// TODO: check if this is really the case
return 32;
case InternalFormat::RGB12:
// TODO: check if this is really the case
return 48;
case InternalFormat::RGB16:
case InternalFormat::RGB16_SNORM:
return 48;
case InternalFormat::RGBA2:
return 8;
case InternalFormat::RGBA4:
case InternalFormat::RGB5_A1:
return 16;
case InternalFormat::RGBA8:
case InternalFormat::RGBA8_SNORM:
case InternalFormat::RGB10_A2:
case InternalFormat::RGB10_A2UI:
return 32;
case InternalFormat::RGBA12:
return 48;
case InternalFormat::RGBA16:
case InternalFormat::RGBA16_SNORM:
return 64;
case InternalFormat::SRGB8:
return 24;
case InternalFormat::SRGB8_ALPHA8:
return 32;
case InternalFormat::R16F:
return 16;
case InternalFormat::RG16F:
return 32;
case InternalFormat::RGB16F:
return 48;
case InternalFormat::RGBA16F:
return 64;
case InternalFormat::R32F:
return 32;
case InternalFormat::RG32F:
return 64;
case InternalFormat::RGB32F:
return 96;
case InternalFormat::RGBA32F:
return 128;
case InternalFormat::R11F_G11F_B10F:
case InternalFormat::RGB9_E5:
return 32;
case InternalFormat::R8I:
case InternalFormat::R8UI:
return 8;
case InternalFormat::R16I:
case InternalFormat::R16UI:
return 16;
case InternalFormat::R32I:
case InternalFormat::R32UI:
return 32;
case InternalFormat::RG8I:
case InternalFormat::RG8UI:
return 16;
case InternalFormat::RG16I:
case InternalFormat::RG16UI:
return 32;
case InternalFormat::RG32I:
case InternalFormat::RG32UI:
return 64;
case InternalFormat::RGB8I:
case InternalFormat::RGB8UI:
return 24;
case InternalFormat::RGB16I:
case InternalFormat::RGB16UI:
return 48;
case InternalFormat::RGB32I:
case InternalFormat::RGB32UI:
return 96;
case InternalFormat::RGBA8I:
case InternalFormat::RGBA8UI:
return 32;
case InternalFormat::RGBA16I:
case InternalFormat::RGBA16UI:
return 64;
case InternalFormat::RGBA32I:
case InternalFormat::RGBA32UI:
return 128;
case InternalFormat::DEPTH_COMPONENT16:
return 16;
case InternalFormat::DEPTH_COMPONENT24:
return 24;
case InternalFormat::DEPTH_COMPONENT32:
case InternalFormat::DEPTH_COMPONENT32F:
case InternalFormat::DEPTH24_STENCIL8:
return 32;
case InternalFormat::DEPTH32F_STENCIL8:
// TODO : check if this true
return 40;
case InternalFormat::STENCIL_INDEX1:
return 1;
case InternalFormat::STENCIL_INDEX4:
return 4;
case InternalFormat::STENCIL_INDEX8:
return 8;
case InternalFormat::STENCIL_INDEX16:
return 16;
default:
return 0;
}
}
template <uint32_t ALIGNMENT>
inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {
enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) };
@ -225,6 +356,7 @@ namespace khronos {
case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // BC3
case InternalFormat::COMPRESSED_RED_RGTC1: // BC4
case InternalFormat::COMPRESSED_RG_RGTC2: // BC5
case InternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: // BC6
case InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // BC7
return evalAlignedCompressedBlockCount<4>(value);
@ -241,6 +373,7 @@ namespace khronos {
return 8;
case InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case InternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case InternalFormat::COMPRESSED_RG_RGTC2:
return 16;
@ -250,6 +383,10 @@ namespace khronos {
}
}
inline uint8_t evalCompressedBlockBitSize(InternalFormat format) {
return evalCompressedBlockSize(format) * 8;
}
enum class BaseInternalFormat : uint32_t {
// GL 4.4 Table 8.11
DEPTH_COMPONENT = 0x1902,

View File

@ -54,15 +54,13 @@ uint32_t Header::evalPixelOrBlockDepth(uint32_t level) const {
return evalMipDimension(level, getPixelDepth());
}
size_t Header::evalPixelOrBlockSize() const {
size_t Header::evalPixelOrBlockBitSize() const {
size_t result = 0;
auto format = getGLInternaFormat();
if (isCompressed()) {
auto format = getGLInternaFormat();
result = khronos::gl::texture::evalCompressedBlockSize(format);
result = khronos::gl::texture::evalCompressedBlockBitSize(format);
} else {
// FIXME should really be using the internal format, not the base internal format
auto baseFormat = getGLBaseInternalFormat();
result = khronos::gl::texture::evalComponentCount(baseFormat);
result = khronos::gl::texture::evalUncompressedBlockBitSize(format);
}
if (0 == result) {
@ -73,11 +71,14 @@ size_t Header::evalPixelOrBlockSize() const {
size_t Header::evalRowSize(uint32_t level) const {
auto pixWidth = evalPixelOrBlockWidth(level);
auto pixSize = evalPixelOrBlockSize();
auto pixSize = evalPixelOrBlockBitSize();
if (pixSize == 0) {
return 0;
}
return evalPaddedSize(pixWidth * pixSize);
auto totalByteSize = pixWidth * pixSize;
// Round to the nearest upper byte size
totalByteSize = (totalByteSize / 8) + (((totalByteSize % 8) != 0) & 1);
return evalPaddedSize(totalByteSize);
}
size_t Header::evalFaceSize(uint32_t level) const {

View File

@ -170,7 +170,7 @@ namespace ktx {
uint32_t evalPixelOrBlockHeight(uint32_t level) const;
uint32_t evalPixelOrBlockDepth(uint32_t level) const;
size_t evalPixelOrBlockSize() const;
size_t evalPixelOrBlockBitSize() const;
size_t evalRowSize(uint32_t level) const;
size_t evalFaceSize(uint32_t level) const;
size_t evalImageSize(uint32_t level) const;

View File

@ -155,6 +155,7 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) {
case AssetRequest::Error::NoError:
_data = req->getData();
_result = Success;
recordBytesDownloadedInStats(STAT_ATP_RESOURCE_TOTAL_BYTES, _data.size());
break;
case AssetRequest::InvalidHash:
_result = InvalidURL;
@ -202,9 +203,8 @@ void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytes
emit progress(bytesReceived, bytesTotal);
auto now = p_high_resolution_clock::now();
// Recording ATP bytes downloaded in stats
DependencyManager::get<StatTracker>()->updateStat(STAT_ATP_RESOURCE_TOTAL_BYTES, bytesReceived);
recordBytesDownloadedInStats(STAT_ATP_RESOURCE_TOTAL_BYTES, bytesReceived);
// if we haven't received the full asset check if it is time to output progress to log
// we do so every X seconds to assist with ATP download tracking

View File

@ -41,6 +41,8 @@ private:
AssetRequest* _assetRequest { nullptr };
p_high_resolution_clock::time_point _lastProgressDebug;
int64_t _lastRecordedBytesDownloaded { 0 };
};
#endif

View File

@ -69,8 +69,7 @@ void FileResourceRequest::doSend() {
if (_result == ResourceRequest::Success) {
statTracker->incrementStat(STAT_FILE_REQUEST_SUCCESS);
// Recording FILE bytes downloaded in stats
statTracker->updateStat(STAT_FILE_RESOURCE_TOTAL_BYTES,fileSize);
statTracker->updateStat(STAT_FILE_RESOURCE_TOTAL_BYTES, fileSize);
} else {
statTracker->incrementStat(STAT_FILE_REQUEST_FAILED);
}

View File

@ -141,6 +141,8 @@ void HTTPResourceRequest::onRequestFinished() {
}
}
recordBytesDownloadedInStats(STAT_HTTP_RESOURCE_TOTAL_BYTES, _data.size());
break;
case QNetworkReply::TimeoutError:
@ -201,11 +203,8 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
_sendTimer->start();
emit progress(bytesReceived, bytesTotal);
// Recording HTTP bytes downloaded in stats
DependencyManager::get<StatTracker>()->updateStat(STAT_HTTP_RESOURCE_TOTAL_BYTES, bytesReceived);
recordBytesDownloadedInStats(STAT_HTTP_RESOURCE_TOTAL_BYTES, bytesReceived);
}
void HTTPResourceRequest::onTimeout() {

View File

@ -11,6 +11,9 @@
#include "ResourceRequest.h"
#include <DependencyManager.h>
#include <StatTracker.h>
#include <QtCore/QThread>
ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { }
@ -40,3 +43,11 @@ QString ResourceRequest::getResultString() const {
default: return "Unspecified Error";
}
}
void ResourceRequest::recordBytesDownloadedInStats(const QString& statName, int64_t bytesReceived) {
auto dBytes = bytesReceived - _lastRecordedBytesDownloaded;
if (dBytes > 0) {
_lastRecordedBytesDownloaded = bytesReceived;
DependencyManager::get<StatTracker>()->updateStat(statName, dBytes);
}
}

View File

@ -85,6 +85,7 @@ signals:
protected:
virtual void doSend() = 0;
void recordBytesDownloadedInStats(const QString& statName, int64_t bytesReceived);
QUrl _url;
QUrl _relativePathURL;
@ -97,6 +98,7 @@ protected:
ByteRange _byteRange;
bool _rangeRequestSuccessful { false };
uint64_t _totalSizeOfResource { 0 };
int64_t _lastRecordedBytesDownloaded { 0 };
};
#endif

View File

@ -14,15 +14,15 @@ StatTracker::StatTracker() {
QVariant StatTracker::getStat(const QString& name) {
std::lock_guard<std::mutex> lock(_statsLock);
return _stats[name];
return QVariant::fromValue<int64_t>(_stats[name]);
}
void StatTracker::setStat(const QString& name, int value) {
void StatTracker::setStat(const QString& name, int64_t value) {
Lock lock(_statsLock);
_stats[name] = value;
}
void StatTracker::updateStat(const QString& name, int value) {
void StatTracker::updateStat(const QString& name, int64_t value) {
Lock lock(_statsLock);
auto itr = _stats.find(name);
if (_stats.end() == itr) {

View File

@ -24,15 +24,15 @@ class StatTracker : public Dependency {
public:
StatTracker();
QVariant getStat(const QString& name);
void setStat(const QString& name, int value);
void updateStat(const QString& name, int mod);
void setStat(const QString& name, int64_t value);
void updateStat(const QString& name, int64_t mod);
void incrementStat(const QString& name);
void decrementStat(const QString& name);
private:
using Mutex = std::mutex;
using Lock = std::lock_guard<Mutex>;
Mutex _statsLock;
QHash<QString, int> _stats;
QHash<QString, int64_t> _stats;
};
class CounterStat {

View File

@ -119,6 +119,7 @@ Script.include("/~/system/libraries/controllers.js");
this.reticleMaxX;
this.reticleMinY = MARGIN;
this.reticleMaxY;
this.madeDynamic = false;
var ACTION_TTL = 15; // seconds
@ -344,6 +345,13 @@ Script.include("/~/system/libraries/controllers.js");
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args);
if (this.madeDynamic) {
var props = {};
props.dynamic = false;
props.localVelocity = {x: 0, y: 0, z: 0};
Entities.editEntity(this.grabbedThingID, props);
this.madeDynamic = false;
}
this.actionID = null;
this.grabbedThingID = null;
};
@ -503,7 +511,13 @@ Script.include("/~/system/libraries/controllers.js");
this.destroyContextOverlay();
}
if (entityIsDistanceGrabbable(targetProps)) {
if (entityIsGrabbable(targetProps)) {
if (!entityIsDistanceGrabbable(targetProps)) {
targetProps.dynamic = true;
Entities.editEntity(entityID, targetProps);
this.madeDynamic = true;
}
if (!this.distanceRotating) {
this.grabbedThingID = entityID;
this.grabbedDistance = rayPickInfo.distance;

View File

@ -15,12 +15,13 @@
//
/* global MyAvatar, Entities, Script, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller,
isInEditMode, HMD */
isInEditMode, HMD entityIsGrabbable*/
(function() { // BEGIN LOCAL_SCOPE
Script.include("/~/system/libraries/utils.js");
Script.include("/~/system/libraries/utils.js");
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed
var DELAY_FOR_30HZ = 33; // milliseconds
@ -330,9 +331,11 @@ Grabber.prototype.pressEvent = function(event) {
return;
}
var isDynamic = Entities.getEntityProperties(pickResults.objectID, "dynamic").dynamic;
if (!isDynamic) {
// only grab dynamic objects
var props = Entities.getEntityProperties(pickResults.objectID, ["dynamic", "userData", "locked", "type"]);
var isDynamic = props.dynamic;
var isGrabbable = props.grabbable;
if (!entityIsGrabbable(props)) {
// only grab grabbable objects
return;
}
@ -350,6 +353,7 @@ Grabber.prototype.pressEvent = function(event) {
var entityProperties = Entities.getEntityProperties(clickedEntity);
this.startPosition = entityProperties.position;
this.lastRotation = entityProperties.rotation;
this.madeDynamic = false;
var cameraPosition = Camera.getPosition();
var objectBoundingDiameter = Vec3.length(entityProperties.dimensions);
@ -361,6 +365,11 @@ Grabber.prototype.pressEvent = function(event) {
return;
}
if (entityIsGrabbable(props) && !isDynamic) {
entityProperties.dynamic = true;
Entities.editEntity(clickedEntity, entityProperties);
this.madeDynamic = true;
}
// this.activateEntity(clickedEntity, entityProperties);
this.isGrabbing = true;
@ -416,6 +425,14 @@ Grabber.prototype.releaseEvent = function(event) {
if (this.actionID) {
Entities.deleteAction(this.entityID, this.actionID);
}
if (this.madeDynamic) {
var entityProps = {};
entityProps.dynamic = false;
entityProps.localVelocity = {x: 0, y: 0, z: 0};
Entities.editEntity(this.entityID, entityProps);
}
this.actionID = null;
LaserPointers.setRenderState(this.mouseRayEntities, "");

File diff suppressed because it is too large Load Diff