Improving test code for KTX library

This commit is contained in:
Brad Davis 2017-05-30 10:00:30 -07:00
parent 66415dadc6
commit 765bd3890e
5 changed files with 225 additions and 236 deletions

View file

@ -211,6 +211,8 @@ namespace khronos {
template <uint32_t ALIGNMENT>
inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {
enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) };
static_assert(val, "template parameter ALIGNMENT must be a power of 2");
// FIXME add static assert that ALIGNMENT is a power of 2
static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1;
return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;

View file

@ -1,15 +1,9 @@
# Declare dependencies
macro (SETUP_TESTCASE_DEPENDENCIES)
# link in the shared libraries
link_hifi_libraries(shared ktx gpu image)
set(TARGET_NAME ktx-test)
if (WIN32)
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
endif()
package_libraries_for_deployment()
endmacro ()
# This is not a testcase -- just set it up as a regular hifi project
setup_hifi_project(Quick Gui OpenGL)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)
package_libraries_for_deployment()
setup_hifi_testcase()

195
tests/ktx/src/KtxTests.cpp Normal file
View file

@ -0,0 +1,195 @@
//
// 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>
QTEST_GUILESS_MAIN(KtxTests)
QString getRootPath() {
static std::once_flag once;
static QString result;
std::call_once(once, [&] {
QFileInfo file(__FILE__);
QDir parent = file.absolutePath();
result = QDir::cleanPath(parent.currentPath() + "/../../..");
});
return result;
}
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 = getRootPath() + "/scripts/developer/tests/cube_texture.png";
QImage image(TEST_IMAGE);
gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
auto ktxMemory = gpu::Texture::serialize(*testTexture);
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());
}
#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**) {
qInstallMessageHandler(messageHandler);
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

21
tests/ktx/src/KtxTests.h Normal file
View file

@ -0,0 +1,21 @@
//
// 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 <QtCore/QObject>
class KtxTests : public QObject {
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void testKtxEvalFunctions();
void testKhronosCompressionFunctions();
void testKtxSerialization();
};

View file

@ -1,223 +0,0 @@
//
// 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 <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <QProcessEnvironment>
#include <QtCore/QDir>
#include <QtCore/QElapsedTimer>
#include <QtCore/QLoggingCategory>
#include <QtCore/QRegularExpression>
#include <QtCore/QSettings>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtCore/QThreadPool>
#include <QtCore/QSaveFile>
#include <QtGui/QGuiApplication>
#include <QtGui/QResizeEvent>
#include <QtGui/QWindow>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QApplication>
#include <shared/RateCounter.h>
#include <shared/NetworkUtils.h>
#include <shared/FileLogger.h>
#include <shared/FileUtils.h>
#include <StatTracker.h>
#include <LogHandler.h>
#include <gpu/Texture.h>
#include <gl/Config.h>
#include <model/TextureMap.h>
#include <ktx/KTX.h>
#include <image/Image.h>
QSharedPointer<FileLogger> logger;
gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true);
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message);
if (!logMessage.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
if (logger) {
logger->addMessage(qPrintable(logMessage + "\n"));
}
}
}
const char * LOG_FILTER_RULES = R"V0G0N(
hifi.gpu=true
)V0G0N";
QString getRootPath() {
static std::once_flag once;
static QString result;
std::call_once(once, [&] {
QFileInfo file(__FILE__);
QDir parent = file.absolutePath();
result = QDir::cleanPath(parent.currentPath() + "/../../..");
});
return result;
}
const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx";
int main(int argc, char** argv) {
QApplication app(argc, argv);
QCoreApplication::setApplicationName("KTX");
QCoreApplication::setOrganizationName("High Fidelity");
QCoreApplication::setOrganizationDomain("highfidelity.com");
logger.reset(new FileLogger());
Q_ASSERT(ktx::evalPadding(0) == 0);
Q_ASSERT(ktx::evalPadding(1) == 3);
Q_ASSERT(ktx::evalPadding(2) == 2);
Q_ASSERT(ktx::evalPadding(3) == 1);
Q_ASSERT(ktx::evalPadding(4) == 0);
Q_ASSERT(ktx::evalPadding(1024) == 0);
Q_ASSERT(ktx::evalPadding(1025) == 3);
Q_ASSERT(ktx::evalPaddedSize(0) == 0);
Q_ASSERT(ktx::evalPaddedSize(1) == 4);
Q_ASSERT(ktx::evalPaddedSize(2) == 4);
Q_ASSERT(ktx::evalPaddedSize(3) == 4);
Q_ASSERT(ktx::evalPaddedSize(4) == 4);
Q_ASSERT(ktx::evalPaddedSize(1024) == 1024);
Q_ASSERT(ktx::evalPaddedSize(1025) == 1028);
Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13));
DependencyManager::set<tracing::Tracer>();
qInstallMessageHandler(messageHandler);
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
QImage image(TEST_IMAGE);
gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
auto ktxMemory = gpu::Texture::serialize(*testTexture);
{
const auto& ktxStorage = ktxMemory->getStorage();
Q_ASSERT_X(ktx::KTX::validate(ktxStorage), __FUNCTION__, "KTX storage validation failed");
Q_ASSERT_X(ktxMemory->isValid(), __FUNCTION__, "KTX self-validation failed");
QSaveFile outFile(TEST_IMAGE_KTX);
if (!outFile.open(QFile::WriteOnly)) {
throw std::runtime_error("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.commit();
}
{
auto ktxFile = ktx::KTX::create(std::shared_ptr<storage::Storage>(new storage::FileStorage(TEST_IMAGE_KTX)));
{
const auto& memStorage = ktxMemory->getStorage();
const auto& fileStorage = ktxFile->getStorage();
Q_ASSERT(memStorage->size() == fileStorage->size());
Q_ASSERT(memStorage->data() != fileStorage->data());
Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size()));
Q_ASSERT(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];
Q_ASSERT(memImages._padding == fileImages._padding);
Q_ASSERT(memImages._numFaces == fileImages._numFaces);
Q_ASSERT(memImages._imageSize == fileImages._imageSize);
Q_ASSERT(memImages._faceSize == fileImages._faceSize);
Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces);
Q_ASSERT(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;
Q_ASSERT(memOffset % 4 == 0);
Q_ASSERT(memOffset == fileOffset);
}
}
}
}
testTexture->setKtxBacking(TEST_IMAGE_KTX.toStdString());
return 0;
}
#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**) {
qInstallMessageHandler(messageHandler);
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
#include "main.moc"