//// Created by Bradley Austin Davis on 2016/07/01// Copyright 2014 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 "KtxTests.h" #include <mutex> #include <QtTest/QtTest> #include <ktx/KTX.h>#include <gpu/Texture.h>#include <image/Image.h>#include <image/TextureProcessing.h>#include "SerDes.h"#include <QDebug> QTEST_GUILESS_MAIN(KtxTests) #if 0ktx::Byte* serializeSPH(ktx::Byte* data, const gpu::IrradianceKTXPayload &payload) const { *(ktx::IrradianceKTXPayload::Version*)data = IrradianceKTXPayload::CURRENT_VERSION; data += sizeof(ktx::IrradianceKTXPayload::Version); memcpy(data, &payload._irradianceSH, sizeof(ktx::SphericalHarmonics)); data += sizeof(SphericalHarmonics); return data + PADDING;}#endif void KtxTests::initTestCase() {} void KtxTests::cleanupTestCase() {} void KtxTests::testKhronosCompressionFunctions() { using namespace khronos::gl::texture; QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0); QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1); QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1); QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2); QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00); QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01); QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01); QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02); QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400); QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error);} void KtxTests::testKtxEvalFunctions() { QCOMPARE(sizeof(ktx::Header), (size_t)64); QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0); QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3); QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2); QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1); QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0); QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0); QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3); QCOMPARE(ktx::evalPaddedSize(0x0), 0x0); QCOMPARE(ktx::evalPaddedSize(0x1), 0x4); QCOMPARE(ktx::evalPaddedSize(0x2), 0x4); QCOMPARE(ktx::evalPaddedSize(0x3), 0x4); QCOMPARE(ktx::evalPaddedSize(0x4), 0x4); QCOMPARE(ktx::evalPaddedSize(0x400), 0x400); QCOMPARE(ktx::evalPaddedSize(0x401), 0x404); QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0); QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1); QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1); QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2);} void KtxTests::testKtxSerialization() { const QString TEST_IMAGE = QCoreApplication::applicationDirPath() + "/cube_texture.png"; QImage image(TEST_IMAGE); std::atomic<bool> abortSignal; gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(std::move(image), TEST_IMAGE.toStdString(), true, gpu::BackendTarget::GL45, true, abortSignal); if (!testTexture) { qWarning() << "Failed to process2DTextureColorFromImage:" << TEST_IMAGE; QFAIL("Failed to process2DTextureColorFromImage"); } auto ktxMemory = gpu::Texture::serialize(*testTexture, glm::ivec2(testTexture->getWidth(), testTexture->getHeight())); QVERIFY(ktxMemory.get()); // Serialize the image to a file QTemporaryFile TEST_IMAGE_KTX; { const auto& ktxStorage = ktxMemory->getStorage(); QVERIFY(ktx::KTX::validate(ktxStorage)); QVERIFY(ktxMemory->isValid()); auto& outFile = TEST_IMAGE_KTX; if (!outFile.open()) { QFAIL("Unable to open file"); } auto ktxSize = ktxStorage->size(); outFile.resize(ktxSize); auto dest = outFile.map(0, ktxSize); memcpy(dest, ktxStorage->data(), ktxSize); outFile.unmap(dest); outFile.close(); } { auto ktxStorage = std::make_shared<storage::FileStorage>(TEST_IMAGE_KTX.fileName()); QVERIFY(ktx::KTX::validate(ktxStorage)); auto ktxFile = ktx::KTX::create(ktxStorage); QVERIFY(ktxFile.get()); QVERIFY(ktxFile->isValid()); { const auto& memStorage = ktxMemory->getStorage(); const auto& fileStorage = ktxFile->getStorage(); QVERIFY(memStorage->size() == fileStorage->size()); QVERIFY(memStorage->data() != fileStorage->data()); QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size()); auto imageCount = ktxFile->_images.size(); auto startMemory = ktxMemory->_storage->data(); auto startFile = ktxFile->_storage->data(); for (size_t i = 0; i < imageCount; ++i) { auto memImages = ktxMemory->_images[i]; auto fileImages = ktxFile->_images[i]; QVERIFY(memImages._padding == fileImages._padding); QVERIFY(memImages._numFaces == fileImages._numFaces); QVERIFY(memImages._imageSize == fileImages._imageSize); QVERIFY(memImages._faceSize == fileImages._faceSize); QVERIFY(memImages._faceBytes.size() == memImages._numFaces); QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces); auto faceCount = fileImages._numFaces; for (uint32_t face = 0; face < faceCount; ++face) { auto memFace = memImages._faceBytes[face]; auto memOffset = memFace - startMemory; auto fileFace = fileImages._faceBytes[face]; auto fileOffset = fileFace - startFile; QVERIFY(memOffset % 4 == 0); QVERIFY(memOffset == fileOffset); } } } } testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString());} void KtxTests::testKtxNewSerializationSphericalHarmonics() { DataSerializer ser; } #if 0 static const QString TEST_FOLDER { "H:/ktx_cacheold" };//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; //static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };static const QString EXTENSIONS { "*.ktx" }; int mainTemp(int, char**) { setupHifiApplication("KTX Tests"); auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); for (auto fileInfo : fileInfoList) { qDebug() << fileInfo.filePath(); std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } }; if (!ktx::KTX::validate(storage)) { qDebug() << "KTX invalid"; } auto ktxFile = ktx::KTX::create(storage); ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; for (const auto& kv : ktxDescriptor.keyValues) { qDebug() << "\t" << kv._key.c_str(); } auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); if (offsetToMinMipKV) { auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; auto minMipLevelAvailable = *data; qDebug() << "\tMin mip available " << minMipLevelAvailable; assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); } auto storageSize = storage->size(); for (const auto& faceImageDesc : ktxDescriptor.images) { //assert(0 == (faceImageDesc._faceSize % 4)); for (const auto& faceOffset : faceImageDesc._faceOffsets) { assert(0 == (faceOffset % 4)); auto faceEndOffset = faceOffset + faceImageDesc._faceSize; assert(faceEndOffset <= storageSize); } } for (const auto& faceImage : ktxFile->_images) { for (const ktx::Byte* faceBytes : faceImage._faceBytes) { assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4)); } } } return 0;}#endif
//
// Created by Bradley Austin Davis on 2016/07/01
// Copyright 2014 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 "KtxTests.h"
#include <mutex>
#include <QtTest/QtTest>
#include <ktx/KTX.h>
#include <gpu/Texture.h>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include "SerDes.h"
#include <QDebug>
QTEST_GUILESS_MAIN(KtxTests)
#if 0
ktx::Byte* serializeSPH(ktx::Byte* data, const gpu::IrradianceKTXPayload &payload) const {
*(ktx::IrradianceKTXPayload::Version*)data = IrradianceKTXPayload::CURRENT_VERSION;
data += sizeof(ktx::IrradianceKTXPayload::Version);
memcpy(data, &payload._irradianceSH, sizeof(ktx::SphericalHarmonics));
data += sizeof(SphericalHarmonics);
return data + PADDING;
}
#endif
void KtxTests::initTestCase() {
}
void KtxTests::cleanupTestCase() {
}
void KtxTests::testKhronosCompressionFunctions() {
using namespace khronos::gl::texture;
QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0);
QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1);
QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1);
QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02);
QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400);
QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error);
}
void KtxTests::testKtxEvalFunctions() {
QCOMPARE(sizeof(ktx::Header), (size_t)64);
QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3);
QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2);
QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1);
QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0);
QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3);
QCOMPARE(ktx::evalPaddedSize(0x0), 0x0);
QCOMPARE(ktx::evalPaddedSize(0x1), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x2), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x3), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x4), 0x4);
QCOMPARE(ktx::evalPaddedSize(0x400), 0x400);
QCOMPARE(ktx::evalPaddedSize(0x401), 0x404);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1);
QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2);
}
void KtxTests::testKtxSerialization() {
const QString TEST_IMAGE = QCoreApplication::applicationDirPath() + "/cube_texture.png";
QImage image(TEST_IMAGE);
std::atomic<bool> abortSignal;
gpu::TexturePointer testTexture =
image::TextureUsage::process2DTextureColorFromImage(std::move(image), TEST_IMAGE.toStdString(), true, gpu::BackendTarget::GL45, true, abortSignal);
if (!testTexture) {
qWarning() << "Failed to process2DTextureColorFromImage:" << TEST_IMAGE;
QFAIL("Failed to process2DTextureColorFromImage");
}
auto ktxMemory = gpu::Texture::serialize(*testTexture, glm::ivec2(testTexture->getWidth(), testTexture->getHeight()));
QVERIFY(ktxMemory.get());
// Serialize the image to a file
QTemporaryFile TEST_IMAGE_KTX;
{
const auto& ktxStorage = ktxMemory->getStorage();
QVERIFY(ktx::KTX::validate(ktxStorage));
QVERIFY(ktxMemory->isValid());
auto& outFile = TEST_IMAGE_KTX;
if (!outFile.open()) {
QFAIL("Unable to open file");
}
auto ktxSize = ktxStorage->size();
outFile.resize(ktxSize);
auto dest = outFile.map(0, ktxSize);
memcpy(dest, ktxStorage->data(), ktxSize);
outFile.unmap(dest);
outFile.close();
}
{
auto ktxStorage = std::make_shared<storage::FileStorage>(TEST_IMAGE_KTX.fileName());
QVERIFY(ktx::KTX::validate(ktxStorage));
auto ktxFile = ktx::KTX::create(ktxStorage);
QVERIFY(ktxFile.get());
QVERIFY(ktxFile->isValid());
{
const auto& memStorage = ktxMemory->getStorage();
const auto& fileStorage = ktxFile->getStorage();
QVERIFY(memStorage->size() == fileStorage->size());
QVERIFY(memStorage->data() != fileStorage->data());
QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size());
auto imageCount = ktxFile->_images.size();
auto startMemory = ktxMemory->_storage->data();
auto startFile = ktxFile->_storage->data();
for (size_t i = 0; i < imageCount; ++i) {
auto memImages = ktxMemory->_images[i];
auto fileImages = ktxFile->_images[i];
QVERIFY(memImages._padding == fileImages._padding);
QVERIFY(memImages._numFaces == fileImages._numFaces);
QVERIFY(memImages._imageSize == fileImages._imageSize);
QVERIFY(memImages._faceSize == fileImages._faceSize);
QVERIFY(memImages._faceBytes.size() == memImages._numFaces);
QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces);
auto faceCount = fileImages._numFaces;
for (uint32_t face = 0; face < faceCount; ++face) {
auto memFace = memImages._faceBytes[face];
auto memOffset = memFace - startMemory;
auto fileFace = fileImages._faceBytes[face];
auto fileOffset = fileFace - startFile;
QVERIFY(memOffset % 4 == 0);
QVERIFY(memOffset == fileOffset);
}
}
}
}
testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString());
}
void KtxTests::testKtxNewSerializationSphericalHarmonics() {
DataSerializer ser;
}
#if 0
static const QString TEST_FOLDER { "H:/ktx_cacheold" };
//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" };
//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" };
static const QString EXTENSIONS { "*.ktx" };
int mainTemp(int, char**) {
setupHifiApplication("KTX Tests");
auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
for (auto fileInfo : fileInfoList) {
qDebug() << fileInfo.filePath();
std::shared_ptr<storage::Storage> storage { new storage::FileStorage { fileInfo.filePath() } };
if (!ktx::KTX::validate(storage)) {
qDebug() << "KTX invalid";
}
auto ktxFile = ktx::KTX::create(storage);
ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor();
qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs";
for (const auto& kv : ktxDescriptor.keyValues) {
qDebug() << "\t" << kv._key.c_str();
}
auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
if (offsetToMinMipKV) {
auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV;
auto minMipLevelAvailable = *data;
qDebug() << "\tMin mip available " << minMipLevelAvailable;
assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels);
}
auto storageSize = storage->size();
for (const auto& faceImageDesc : ktxDescriptor.images) {
//assert(0 == (faceImageDesc._faceSize % 4));
for (const auto& faceOffset : faceImageDesc._faceOffsets) {
assert(0 == (faceOffset % 4));
auto faceEndOffset = faceOffset + faceImageDesc._faceSize;
assert(faceEndOffset <= storageSize);
}
}
for (const auto& faceImage : ktxFile->_images) {
for (const ktx::Byte* faceBytes : faceImage._faceBytes) {
assert(0 == (reinterpret_cast<size_t>(faceBytes) % 4));
}
}
}
return 0;
}
#endif