Merge pull request #16297 from jherico/frame-capture

DEV-632: New format for captured GPU frames
This commit is contained in:
Sam Gateau 2019-10-08 09:43:44 -07:00 committed by GitHub
commit 6db302990c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 571 additions and 457 deletions

View file

@ -13,6 +13,7 @@
#include <gl/Config.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QBuffer>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QFileInfo>
@ -36,6 +37,7 @@
#include <shaders/Shaders.h>
#include <gpu/gl/GLShared.h>
#include <gpu/gl/GLBackend.h>
#include <gpu/gl/GLTexelFormat.h>
#include <GeometryCache.h>
#include <CursorManager.h>
@ -475,30 +477,48 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
});
}
ktx::StoragePointer textureToKtx(const gpu::Texture& texture) {
ktx::Header header;
{
auto gpuDims = texture.getDimensions();
header.pixelWidth = gpuDims.x;
header.pixelHeight = gpuDims.y;
header.pixelDepth = 0;
}
{
auto gltexelformat = gpu::gl::GLTexelFormat::evalGLTexelFormat(texture.getStoredMipFormat());
header.glInternalFormat = gltexelformat.internalFormat;
header.glFormat = gltexelformat.format;
header.glBaseInternalFormat = gltexelformat.format;
header.glType = gltexelformat.type;
header.glTypeSize = 1;
header.numberOfMipmapLevels = 1 + texture.getMaxMip();
}
auto memKtx = ktx::KTX::createBare(header);
auto storage = memKtx->_storage;
uint32_t faceCount = std::max(header.numberOfFaces, 1u);
uint32_t mipCount = std::max(header.numberOfMipmapLevels, 1u);
for (uint32_t mip = 0; mip < mipCount; ++mip) {
for (uint32_t face = 0; face < faceCount; ++face) {
const auto& image = memKtx->_images[mip];
auto& faceBytes = const_cast<gpu::Byte*&>(image._faceBytes[face]);
if (texture.isStoredMipFaceAvailable(mip, face)) {
auto storedImage = texture.accessStoredMipFace(mip, face);
auto storedSize = storedImage->size();
memcpy(faceBytes, storedImage->data(), storedSize);
}
}
}
return storage;
}
void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
withOtherThreadContext([&] {
using namespace gpu;
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") };
TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) {
QImage image;
if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) {
image = QImage{ 1, 1, QImage::Format_ARGB32 };
auto storedImage = texture->accessStoredMipFace(0, 0);
memcpy(image.bits(), storedImage->data(), image.sizeInBytes());
//if (texture == textureCache->getWhiteTexture()) {
//} else if (texture == textureCache->getBlackTexture()) {
//} else if (texture == textureCache->getBlueTexture()) {
//} else if (texture == textureCache->getGrayTexture()) {
} else {
ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() };
framebuffer->setRenderBuffer(0, texture, layer);
glBackend->syncGPUObject(*framebuffer);
image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 };
glBackend->downloadFramebuffer(framebuffer, rect, image);
}
QImageWriter(filename.c_str()).write(image);
TextureCapturer captureLambda = [&](const gpu::TexturePointer& texture)->storage::StoragePointer {
return textureToKtx(*texture);
};
if (_currentFrame) {

View file

@ -0,0 +1,136 @@
//
// Created by Bradley Austin Davis on 2019/10/03
// Copyright 2013-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 "FrameIO.h"
#include <shared/Storage.h>
#include <stdexcept>
using namespace gpu::hfb;
static bool skip(const uint8_t*& ptr, size_t& remaining, uint32_t size) {
if (remaining < size) {
return false;
}
ptr += size;
remaining -= size;
return true;
}
template <typename T>
static bool read(const uint8_t*& ptr, size_t& remaining, T& output) {
uint32_t readSize = (uint32_t)sizeof(T);
if (remaining < readSize) {
return false;
}
memcpy(&output, ptr, readSize);
return skip(ptr, remaining, readSize);
}
Descriptor::Descriptor(const StoragePointer& storage) : storage(storage) {
const auto* const start = storage->data();
const auto* ptr = storage->data();
auto remaining = storage->size();
try {
// Can't parse files more than 4GB
if (remaining > UINT32_MAX) {
throw std::runtime_error("File too large");
}
if (!read(ptr, remaining, header)) {
throw std::runtime_error("Couldn't read binary header");
}
if (header.length != storage->size()) {
throw std::runtime_error("Header/Actual size mismatch");
}
while (remaining != 0) {
chunks.emplace_back();
auto& chunk = chunks.back();
ChunkHeader& chunkHeader = chunk;
if (!read(ptr, remaining, chunkHeader)) {
throw std::runtime_error("Coulnd't read chunk header");
}
chunk.offset = (uint32_t)(ptr - start);
if (chunk.end() > storage->size()) {
throw std::runtime_error("Chunk too large for file");
}
if (!skip(ptr, remaining, chunk.length)) {
throw std::runtime_error("Skip chunk data failed");
}
}
} catch (const std::runtime_error&) {
// LOG somnething
header.magic = 0;
}
}
size_t Chunk::end() const {
size_t result = offset;
result += length;
return result;
}
StoragePointer Descriptor::getChunk(uint32_t chunkIndex) const {
if (chunkIndex >= chunks.size()) {
return {};
}
const auto& chunk = chunks[chunkIndex];
if (chunk.end() > storage->size()) {
return {};
}
return storage->createView(chunk.length, chunk.offset);
}
static void writeUint(uint8_t*& dest, uint32_t value) {
memcpy(dest, &value, sizeof(uint32_t));
dest += sizeof(uint32_t);
}
template <typename T>
static void writeChunk(uint8_t*& dest, uint32_t chunkType, const T& chunkData) {
uint32_t chunkSize = static_cast<uint32_t>(chunkData.size());
writeUint(dest, chunkSize);
writeUint(dest, chunkType);
memcpy(dest, chunkData.data(), chunkSize);
dest += chunkSize;
}
void gpu::hfb::writeFrame(const std::string& filename,
const std::string& json,
const Buffer& binaryBuffer,
const StorageBuilders& ktxBuilders) {
uint32_t strLen = (uint32_t)json.size();
uint32_t size = gpu::hfb::HEADER_SIZE + gpu::hfb::CHUNK_HEADER_SIZE + strLen;
size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)binaryBuffer.size();
for (const auto& builder : ktxBuilders) {
auto storage = builder();
if (storage) {
size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)storage->size();
}
}
auto outputConst = storage::FileStorage::create(filename.c_str(), size, nullptr);
auto output = std::const_pointer_cast<storage::Storage>(outputConst);
auto ptr = output->mutableData();
writeUint(ptr, gpu::hfb::MAGIC);
writeUint(ptr, gpu::hfb::VERSION);
writeUint(ptr, size);
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_JSON, json);
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_BIN, binaryBuffer);
for (const auto& builder : ktxBuilders) {
static StoragePointer EMPTY_STORAGE{ std::make_shared<storage::MemoryStorage>(0, nullptr) };
auto storage = builder();
if (!storage) {
storage = EMPTY_STORAGE;
}
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_KTX, *storage);
}
assert((ptr - output->data()) == size);
}

View file

@ -12,17 +12,72 @@
#include "Forward.h"
#include "Format.h"
#include <shared/Storage.h>
#include <functional>
namespace gpu {
using TextureCapturer = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
using TextureLoader = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
using TextureCapturer = std::function<storage::StoragePointer(const TexturePointer&)>;
//using TextureLoader = std::function<void(const storage::StoragePointer& storage, const TexturePointer&)>;
void writeFrame(const std::string& filename, const FramePointer& frame, const TextureCapturer& capturer = nullptr);
FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader = nullptr);
FramePointer readFrame(const std::string& filename, uint32_t externalTexture);
using IndexOptimizer = std::function<void(Primitive, uint32_t faceCount, uint32_t indexCount, uint32_t* indices )>;
void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer);
namespace hfb {
using Storage = storage::Storage;
using StoragePointer = storage::Pointer;
using StorageBuilders = storage::Builders;
constexpr const char* const EXTENSION{ ".hfb" };
constexpr uint32_t HEADER_SIZE{ sizeof(uint32_t) * 3 };
constexpr uint32_t CHUNK_HEADER_SIZE = sizeof(uint32_t) * 2;
constexpr uint32_t MAGIC{ 0x49464948 };
constexpr uint32_t VERSION{ 0x01 };
constexpr uint32_t CHUNK_TYPE_JSON{ 0x4E4F534A };
constexpr uint32_t CHUNK_TYPE_KTX{ 0x0058544b };
constexpr uint32_t CHUNK_TYPE_BIN{ 0x004E4942 };
constexpr uint32_t CHUNK_TYPE_PNG{ 0x00474E50 };
using Buffer = std::vector<uint8_t>;
struct Header {
uint32_t magic{ 0 };
uint32_t version{ 0 };
uint32_t length{ 0 };
};
struct ChunkHeader {
uint32_t length{ 0 };
uint32_t type{ 0 };
};
struct Chunk : public ChunkHeader {
uint32_t offset{ 0 };
size_t end() const;
};
using Chunks = std::vector<Chunk>;
struct Descriptor {
using Pointer = std::shared_ptr<Descriptor>;
Header header;
Chunks chunks;
StoragePointer storage;
Descriptor(const StoragePointer& storage);
operator bool() const { return header.magic == MAGIC; }
StoragePointer getChunk(uint32_t chunk) const;
};
void writeFrame(const std::string& filename,
const std::string& json,
const Buffer& binaryBuffer,
const StorageBuilders& pngBuffers);
} // namespace hfb
} // namespace gpu

View file

@ -11,128 +11,130 @@
namespace gpu { namespace keys {
static const char* binary = "binary";
static const char* L00 = "L00";
static const char* L1m1 = "L1m1";
static const char* L10 = "L10";
static const char* L11 = "L11";
static const char* L2m2 = "L2m2";
static const char* L2m1 = "L2m1";
static const char* L20 = "L20";
static const char* L21 = "L21";
static const char* L22 = "L22";
static const char* eyeProjections = "eyeProjections";
static const char* eyeViews = "eyeViews";
static const char* alphaToCoverageEnable = "alphaToCoverageEnable";
static const char* antialisedLineEnable = "antialisedLineEnable";
static const char* attributes = "attributes";
static const char* batches = "batches";
static const char* blendFunction = "blendFunction";
static const char* borderColor = "borderColor";
static const char* bufferMask = "bufferMask";
static const char* buffers = "buffers";
static const char* capturedTextures = "capturedTextures";
static const char* channel = "channel";
static const char* colorAttachments = "colorAttachments";
static const char* colorWriteMask = "colorWriteMask";
static const char* commands = "commands";
static const char* comparisonFunction = "comparisonFunction";
static const char* cullMode = "cullMode";
static const char* data = "data";
static const char* depth = "depth";
static const char* depthBias = "depthBias";
static const char* depthBiasSlopeScale = "depthBiasSlopeScale";
static const char* depthClampEnable = "depthClampEnable";
static const char* depthStencilAttachment = "depthStencilAttachment";
static const char* depthTest = "depthTest";
static const char* drawCallInfos = "drawCallInfos";
static const char* drawcallUniform = "drawcallUniform";
static const char* drawcallUniformReset = "drawcallUniformReset";
static const char* element = "element";
static const char* fillMode = "fillMode";
static const char* filter = "filter";
static const char* formats = "formats";
static const char* frameIndex = "frameIndex";
static const char* framebuffer = "framebuffer";
static const char* framebuffers = "framebuffers";
static const char* frequency = "frequency";
static const char* frontFaceClockwise = "frontFaceClockwise";
static const char* height = "height";
static const char* id = "id";
static const char* ktxFile = "ktxFile";
static const char* layers = "layers";
static const char* maxAnisotropy = "maxAnisotropy";
static const char* maxMip = "maxMip";
static const char* minMip = "minMip";
static const char* mipOffset = "mipOffset";
static const char* mips = "mips";
static const char* multisampleEnable = "multisampleEnable";
static const char* name = "name";
static const char* namedData = "namedData";
static const char* names = "names";
static const char* objects = "objects";
static const char* offset = "offset";
static const char* pipelines = "pipelines";
static const char* pose = "pose";
static const char* profileRanges = "profileRanges";
static const char* program = "program";
static const char* programs = "programs";
static const char* projectionJitter = "projectionJitter";
static const char* queries = "queries";
static const char* sampleCount = "sampleCount";
static const char* sampleMask = "sampleMask";
static const char* sampler = "sampler";
static const char* samples = "samples";
static const char* scissorEnable = "scissorEnable";
static const char* shaders = "shaders";
static const char* size = "size";
static const char* skybox = "skybox";
static const char* slot = "slot";
static const char* source = "source";
static const char* state = "state";
static const char* stencilActivation = "stencilActivation";
static const char* stencilTestBack = "stencilTestBack";
static const char* stencilTestFront = "stencilTestFront";
static const char* stereo = "stereo";
static const char* subresource = "subresource";
static const char* swapchains = "swapchains";
static const char* texelFormat = "texelFormat";
static const char* texture = "texture";
static const char* textureTables = "textureTables";
static const char* textures = "textures";
static const char* transforms = "transforms";
static const char* type = "type";
static const char* usageType = "usageType";
static const char* view = "view";
static const char* width = "width";
static const char* wrapModeU = "wrapModeU";
static const char* wrapModeV = "wrapModeV";
static const char* wrapModeW = "wrapModeW";
constexpr const char* binary = "binary";
constexpr const char* L00 = "L00";
constexpr const char* L1m1 = "L1m1";
constexpr const char* L10 = "L10";
constexpr const char* L11 = "L11";
constexpr const char* L2m2 = "L2m2";
constexpr const char* L2m1 = "L2m1";
constexpr const char* L20 = "L20";
constexpr const char* L21 = "L21";
constexpr const char* L22 = "L22";
constexpr const char* eyeProjections = "eyeProjections";
constexpr const char* eyeViews = "eyeViews";
constexpr const char* alphaToCoverageEnable = "alphaToCoverageEnable";
constexpr const char* antialisedLineEnable = "antialisedLineEnable";
constexpr const char* attributes = "attributes";
constexpr const char* batches = "batches";
constexpr const char* blendFunction = "blendFunction";
constexpr const char* borderColor = "borderColor";
constexpr const char* bufferMask = "bufferMask";
constexpr const char* buffers = "buffers";
constexpr const char* capturedTextures = "capturedTextures";
constexpr const char* channel = "channel";
constexpr const char* chunk = "chunk";
constexpr const char* colorAttachments = "colorAttachments";
constexpr const char* colorWriteMask = "colorWriteMask";
constexpr const char* commands = "commands";
constexpr const char* comparisonFunction = "comparisonFunction";
constexpr const char* cullMode = "cullMode";
constexpr const char* data = "data";
constexpr const char* depth = "depth";
constexpr const char* depthBias = "depthBias";
constexpr const char* depthBiasSlopeScale = "depthBiasSlopeScale";
constexpr const char* depthClampEnable = "depthClampEnable";
constexpr const char* depthStencilAttachment = "depthStencilAttachment";
constexpr const char* depthTest = "depthTest";
constexpr const char* drawCallInfos = "drawCallInfos";
constexpr const char* drawcallUniform = "drawcallUniform";
constexpr const char* drawcallUniformReset = "drawcallUniformReset";
constexpr const char* element = "element";
constexpr const char* fillMode = "fillMode";
constexpr const char* filter = "filter";
constexpr const char* formats = "formats";
constexpr const char* frameIndex = "frameIndex";
constexpr const char* framebuffer = "framebuffer";
constexpr const char* framebuffers = "framebuffers";
constexpr const char* frequency = "frequency";
constexpr const char* frontFaceClockwise = "frontFaceClockwise";
constexpr const char* height = "height";
constexpr const char* id = "id";
constexpr const char* ktxFile = "ktxFile";
constexpr const char* layers = "layers";
constexpr const char* maxAnisotropy = "maxAnisotropy";
constexpr const char* maxMip = "maxMip";
constexpr const char* minMip = "minMip";
constexpr const char* mipOffset = "mipOffset";
constexpr const char* mips = "mips";
constexpr const char* multisampleEnable = "multisampleEnable";
constexpr const char* name = "name";
constexpr const char* namedData = "namedData";
constexpr const char* names = "names";
constexpr const char* objects = "objects";
constexpr const char* offset = "offset";
constexpr const char* pipelines = "pipelines";
constexpr const char* pose = "pose";
constexpr const char* profileRanges = "profileRanges";
constexpr const char* program = "program";
constexpr const char* programs = "programs";
constexpr const char* projectionJitter = "projectionJitter";
constexpr const char* queries = "queries";
constexpr const char* sampleCount = "sampleCount";
constexpr const char* sampleMask = "sampleMask";
constexpr const char* sampler = "sampler";
constexpr const char* samples = "samples";
constexpr const char* scissorEnable = "scissorEnable";
constexpr const char* shaders = "shaders";
constexpr const char* size = "size";
constexpr const char* skybox = "skybox";
constexpr const char* slot = "slot";
constexpr const char* source = "source";
constexpr const char* state = "state";
constexpr const char* stencilActivation = "stencilActivation";
constexpr const char* stencilTestBack = "stencilTestBack";
constexpr const char* stencilTestFront = "stencilTestFront";
constexpr const char* stereo = "stereo";
constexpr const char* subresource = "subresource";
constexpr const char* swapchains = "swapchains";
constexpr const char* texelFormat = "texelFormat";
constexpr const char* texture = "texture";
constexpr const char* textureTables = "textureTables";
constexpr const char* textures = "textures";
constexpr const char* transforms = "transforms";
constexpr const char* type = "type";
constexpr const char* usageType = "usageType";
constexpr const char* view = "view";
constexpr const char* width = "width";
constexpr const char* wrapModeU = "wrapModeU";
constexpr const char* wrapModeV = "wrapModeV";
constexpr const char* wrapModeW = "wrapModeW";
static const char* backWriteMask = "backWriteMask";
static const char* frontWriteMask = "frontWriteMask";
static const char* reference = "reference";
static const char* readMask = "readMask";
static const char* failOp = "failOp";
static const char* depthFailOp = "depthFailOp";
static const char* passOp = "passOp";
static const char* enabled = "enabled";
static const char* blend = "blend";
static const char* flags = "flags";
static const char* writeMask = "writeMask";
static const char* function = "function";
static const char* sourceColor = "sourceColor";
static const char* sourceAlpha = "sourceAlpha";
static const char* destColor = "destColor";
static const char* destAlpha = "destAlpha";
static const char* opColor = "opColor";
static const char* opAlpha = "opAlpha";
static const char* enable = "enable";
static const char* contextDisable = "contextDisable";
constexpr const char* backWriteMask = "backWriteMask";
constexpr const char* frontWriteMask = "frontWriteMask";
constexpr const char* reference = "reference";
constexpr const char* readMask = "readMask";
constexpr const char* failOp = "failOp";
constexpr const char* depthFailOp = "depthFailOp";
constexpr const char* passOp = "passOp";
constexpr const char* enabled = "enabled";
constexpr const char* blend = "blend";
constexpr const char* flags = "flags";
constexpr const char* writeMask = "writeMask";
constexpr const char* function = "function";
constexpr const char* sourceColor = "sourceColor";
constexpr const char* sourceAlpha = "sourceAlpha";
constexpr const char* destColor = "destColor";
constexpr const char* destAlpha = "destAlpha";
constexpr const char* opColor = "opColor";
constexpr const char* opAlpha = "opAlpha";
constexpr const char* enable = "enable";
constexpr const char* contextDisable = "contextDisable";
static const char* COMMAND_NAMES[] = {
constexpr const char* COMMAND_NAMES[] = {
"draw",
"drawIndexed",
"drawInstanced",

View file

@ -10,9 +10,7 @@
#include <nlohmann/json.hpp>
#include <unordered_map>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <shared/FileUtils.h>
#include <ktx/KTX.h>
#include "Frame.h"
#include "Batch.h"
@ -22,24 +20,18 @@
namespace gpu {
using json = nlohmann::json;
using StoragePointer = storage::StoragePointer;
using FileStorage = storage::FileStorage;
class Deserializer {
public:
static std::string getBaseName(const std::string& filename) {
static const std::string ext{ ".json" };
if (std::string::npos != filename.rfind(ext)) {
return filename.substr(0, filename.size() - ext.size());
}
return filename;
}
static std::string getBaseDir(const std::string& filename) {
std::string result;
if (0 == filename.find("assets:")) {
auto lastSlash = filename.rfind('/');
result = filename.substr(0, lastSlash + 1);
} else {
result = QFileInfo(filename.c_str()).absoluteDir().canonicalPath().toStdString();
result = FileUtils::getParentPath(filename.c_str()).toStdString();
if (*result.rbegin() != '/') {
result += '/';
}
@ -47,15 +39,17 @@ public:
return result;
}
Deserializer(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) :
basename(getBaseName(filename)), basedir(getBaseDir(filename)), externalTexture(externalTexture), textureLoader(loader) {
Deserializer(const std::string& filename_, uint32_t externalTexture = 0) :
filename(filename_), basedir(getBaseDir(filename_)), mappedFile(std::make_shared<FileStorage>(filename.c_str())),
externalTexture(externalTexture) {
descriptor = std::make_shared<hfb::Descriptor>(mappedFile);
}
const std::string basename;
const std::string filename;
const std::string basedir;
std::string binaryFile;
const StoragePointer mappedFile;
const uint32_t externalTexture;
TextureLoader textureLoader;
hfb::Descriptor::Pointer descriptor;
std::vector<ShaderPointer> shaders;
std::vector<ShaderPointer> programs;
std::vector<TexturePointer> textures;
@ -69,10 +63,13 @@ public:
std::vector<QueryPointer> queries;
json frameNode;
FramePointer readFrame();
void optimizeFrame(const IndexOptimizer& optimizer);
FramePointer deserializeFrame();
std::string getStringChunk(size_t chunkIndex) {
auto storage = descriptor->getChunk((uint32_t)chunkIndex);
return std::string{ (const char*)storage->data(), storage->size() };
}
void readBuffers(const json& node);
@ -148,12 +145,11 @@ public:
}
template <typename T, typename TT = T>
static bool readBatchCacheTransformed(typename Batch::Cache<T>::Vector& dest,
const json& node,
const std::string& name,
std::function<TT(const json&)> f = [](const json& node) -> TT {
return node.get<TT>();
}) {
static bool readBatchCacheTransformed(
typename Batch::Cache<T>::Vector& dest,
const json& node,
const std::string& name,
std::function<TT(const json&)> f = [](const json& node) -> TT { return node.get<TT>(); }) {
if (node.count(name)) {
const auto& arrayNode = node[name];
for (const auto& entry : arrayNode) {
@ -230,12 +226,8 @@ public:
static void readCommand(const json& node, Batch& batch);
};
FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) {
return Deserializer(filename, externalTexture, loader).readFrame();
}
void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer) {
return Deserializer(filename, 0, {}).optimizeFrame(optimizer);
FramePointer readFrame(const std::string& filename, uint32_t externalTexture) {
return Deserializer(filename, externalTexture).readFrame();
}
} // namespace gpu
@ -243,9 +235,9 @@ void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer)
using namespace gpu;
void Deserializer::readBuffers(const json& buffersNode) {
storage::FileStorage mappedFile(binaryFile.c_str());
const auto mappedSize = mappedFile.size();
const auto* mapped = mappedFile.data();
const auto& binaryChunk = descriptor->chunks[1];
const auto* mapped = mappedFile->data() + binaryChunk.offset;
const auto mappedSize = binaryChunk.length;
size_t bufferCount = buffersNode.size();
buffers.reserve(buffersNode.size());
size_t offset = 0;
@ -311,6 +303,8 @@ Sampler Deserializer::readSampler(const json& node) {
return result;
}
constexpr uint32_t INVALID_CHUNK_INDEX{ (uint32_t)-1 };
TexturePointer Deserializer::readTexture(const json& node, uint32_t external) {
if (node.is_null()) {
return nullptr;
@ -319,8 +313,17 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) {
std::string source;
readOptional(source, node, keys::source);
uint32_t chunkIndex = INVALID_CHUNK_INDEX;
readOptional(chunkIndex, node, keys::chunk);
std::string ktxFile;
readOptional(ktxFile, node, keys::ktxFile);
if (!ktxFile.empty()) {
if (!FileUtils::exists(ktxFile.c_str())) {
qDebug() << "Warning" << ktxFile.c_str() << " not found, ignoring";
ktxFile = {};
}
}
Element ktxTexelFormat, ktxMipFormat;
if (!ktxFile.empty()) {
// If we get a texture that starts with ":" we need to re-route it to the resources directory
@ -330,8 +333,8 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) {
frameReaderPath.replace("libraries/gpu/src/gpu/framereader.cpp", "interface/resources", Qt::CaseInsensitive);
ktxFile.replace(0, 1, frameReaderPath.toStdString());
}
if (QFileInfo(ktxFile.c_str()).isRelative()) {
ktxFile = basedir + ktxFile;
if (FileUtils::isRelative(ktxFile.c_str())) {
ktxFile = basedir + "/" + ktxFile;
}
ktx::StoragePointer ktxStorage{ new storage::FileStorage(ktxFile.c_str()) };
auto ktxObject = ktx::KTX::create(ktxStorage);
@ -364,12 +367,14 @@ TexturePointer Deserializer::readTexture(const json& node, uint32_t external) {
auto& texture = *result;
readOptional(texture._source, node, keys::source);
if (!ktxFile.empty()) {
if (QFileInfo(ktxFile.c_str()).isRelative()) {
ktxFile = basedir + "/" + ktxFile;
}
if (chunkIndex != INVALID_CHUNK_INDEX) {
auto ktxChunk = descriptor->getChunk(chunkIndex);
texture.setKtxBacking(ktxChunk);
} else if (!ktxFile.empty()) {
texture.setSource(ktxFile);
texture.setKtxBacking(ktxFile);
}
}
return result;
}
@ -405,11 +410,11 @@ ShaderPointer Deserializer::readShader(const json& node) {
// FIXME support procedural shaders
Shader::Type type = node[keys::type];
std::string name = node[keys::name];
// Using the serialized ID is bad, because it's generated at
// cmake time, and can change across platforms or when
// Using the serialized ID is bad, because it's generated at
// cmake time, and can change across platforms or when
// shaders are added or removed
// uint32_t id = node[keys::id];
uint32_t id = shadersIdsByName[name];
ShaderPointer result;
switch (type) {
@ -555,11 +560,15 @@ StatePointer readState(const json& node) {
State::Data data;
Deserializer::readOptionalTransformed<State::Flags>(data.flags, node, keys::flags, &readStateFlags);
Deserializer::readOptionalTransformed<State::BlendFunction>(data.blendFunction, node, keys::blendFunction, &readBlendFunction);
Deserializer::readOptionalTransformed<State::BlendFunction>(data.blendFunction, node, keys::blendFunction,
&readBlendFunction);
Deserializer::readOptionalTransformed<State::DepthTest>(data.depthTest, node, keys::depthTest, &readDepthTest);
Deserializer::readOptionalTransformed<State::StencilActivation>(data.stencilActivation, node, keys::stencilActivation, &readStencilActivation);
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestFront, node, keys::stencilTestFront, &readStencilTest);
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestBack, node, keys::stencilTestBack, &readStencilTest);
Deserializer::readOptionalTransformed<State::StencilActivation>(data.stencilActivation, node, keys::stencilActivation,
&readStencilActivation);
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestFront, node, keys::stencilTestFront,
&readStencilTest);
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestBack, node, keys::stencilTestBack,
&readStencilTest);
Deserializer::readOptional(data.colorWriteMask, node, keys::colorWriteMask);
Deserializer::readOptional(data.cullMode, node, keys::cullMode);
Deserializer::readOptional(data.depthBias, node, keys::depthBias);
@ -799,25 +808,15 @@ StereoState readStereoState(const json& node) {
FramePointer Deserializer::deserializeFrame() {
{
std::string filename{ basename + ".json" };
storage::FileStorage mappedFile(filename.c_str());
frameNode = json::parse(std::string((const char*)mappedFile.data(), mappedFile.size()));
if (!descriptor.operator bool()) {
return {};
}
frameNode = json::parse(getStringChunk(0));
FramePointer result = std::make_shared<Frame>();
auto& frame = *result;
if (frameNode[keys::binary].is_string()) {
binaryFile = frameNode[keys::binary];
if (QFileInfo(binaryFile.c_str()).isRelative()) {
binaryFile = basedir + "/" + binaryFile;
}
} else {
binaryFile = basename + ".bin";
}
if (frameNode.count(keys::buffers)) {
readBuffers(frameNode[keys::buffers]);
}
@ -830,19 +829,7 @@ FramePointer Deserializer::deserializeFrame() {
formats = readArray<Stream::FormatPointer>(frameNode, keys::formats, [](const json& node) { return readFormat(node); });
auto textureReader = [this](const json& node) { return readTexture(node, externalTexture); };
textures = readArray<TexturePointer>(frameNode, keys::textures, textureReader);
if (textureLoader) {
std::vector<uint32_t> capturedTextures = readNumericVector<uint32_t>(frameNode[keys::capturedTextures]);
for (const auto& index : capturedTextures) {
const auto& texturePointer = textures[index];
uint16 layers = std::max<uint16>(texturePointer->getNumSlices(), 1);
for (uint16 layer = 0; layer < layers; ++layer) {
std::string filename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png";
textureLoader(filename, texturePointer, layer);
}
}
}
textures = readArray<TexturePointer>(frameNode, keys::textures, [this](const json& node) { return readTexture(node, externalTexture); });
// Must come after textures
auto textureTableReader = [this](const json& node) { return readTextureTable(node); };
@ -868,87 +855,22 @@ FramePointer Deserializer::deserializeFrame() {
}
}
for (uint32_t i = 0; i < textures.size(); ++i) {
const auto& texturePtr = textures[i];
if (!texturePtr) {
continue;
}
const auto& texture = *texturePtr;
if (texture.getUsageType() == gpu::TextureUsageType::RESOURCE && texture.source().empty()) {
qDebug() << "Empty source ";
}
}
return result;
}
FramePointer Deserializer::readFrame() {
auto result = deserializeFrame();
result->finish();
return result;
}
void Deserializer::optimizeFrame(const IndexOptimizer& optimizer) {
auto result = deserializeFrame();
auto& frame = *result;
// optimize the index buffers?
struct CurrentIndexBuffer {
Offset offset{ 0 };
BufferPointer buffer;
Type type{ gpu::Type::INT32 };
Primitive primitve{ Primitive::TRIANGLES };
uint32_t numIndices{ 0 };
uint32_t startIndex{ 0 };
};
std::vector<CurrentIndexBuffer> captured;
for (auto& batch : frame.batches) {
CurrentIndexBuffer currentIndexBuffer;
batch->forEachCommand([&](Batch::Command cmd, const Batch::Param* params){
switch(cmd) {
case Batch::Command::COMMAND_setIndexBuffer:
currentIndexBuffer.offset = params[0]._size;
currentIndexBuffer.buffer = batch->_buffers.get(params[1]._int);
currentIndexBuffer.type = (Type)params[2]._int;
break;
case Batch::Command::COMMAND_drawIndexed:
currentIndexBuffer.startIndex = params[0]._int;
currentIndexBuffer.numIndices = params[1]._int;
currentIndexBuffer.primitve = (Primitive)params[2]._int;
captured.emplace_back(currentIndexBuffer);
break;
case Batch::Command::COMMAND_drawIndexedInstanced:
currentIndexBuffer.startIndex = params[1]._int;
currentIndexBuffer.numIndices = params[2]._int;
currentIndexBuffer.primitve = (Primitive)params[3]._int;
captured.emplace_back(currentIndexBuffer);
break;
default:
break;
}
});
}
std::string optimizedBinaryFile = basename + "_optimized.bin";
QFile(binaryFile.c_str()).copy(optimizedBinaryFile.c_str());
{
storage::FileStorage mappedFile(optimizedBinaryFile.c_str());
std::set<BufferPointer> uniqueBuffers;
for (const auto& capturedIndexData : captured) {
if (uniqueBuffers.count(capturedIndexData.buffer)) {
continue;
}
uniqueBuffers.insert(capturedIndexData.buffer);
auto bufferOffset = bufferOffsets[capturedIndexData.buffer];
auto& buffer = *capturedIndexData.buffer;
const auto& count = capturedIndexData.numIndices;
auto indices = (uint32_t*)buffer.editData();
optimizer(capturedIndexData.primitve, count / 3, count, indices);
memcpy(mappedFile.mutableData() + bufferOffset, indices, sizeof(uint32_t) * count);
}
}
frameNode[keys::binary] = optimizedBinaryFile;
{
std::string frameJson = frameNode.dump();
std::string filename = basename + "_optimized.json";
storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data());
}
}

View file

@ -20,7 +20,7 @@ using json = nlohmann::json;
class Serializer {
public:
const std::string basename;
const std::string filename;
const TextureCapturer textureCapturer;
std::unordered_map<ShaderPointer, uint32_t> shaderMap;
std::unordered_map<ShaderPointer, uint32_t> programMap;
@ -32,8 +32,11 @@ public:
std::unordered_map<FramebufferPointer, uint32_t> framebufferMap;
std::unordered_map<SwapChainPointer, uint32_t> swapchainMap;
std::unordered_map<QueryPointer, uint32_t> queryMap;
std::unordered_set<TexturePointer> captureTextures;
hfb::Buffer binaryBuffer;
hfb::StorageBuilders ktxBuilders;
Serializer(const std::string& basename, const TextureCapturer& capturer) : basename(basename), textureCapturer(capturer) {}
Serializer(const std::string& basename, const TextureCapturer& capturer) : filename(basename + hfb::EXTENSION), textureCapturer(capturer) {}
template <typename T>
static uint32_t getGlobalIndex(const T& value, std::unordered_map<T, uint32_t>& map) {
@ -129,7 +132,7 @@ public:
json writeProgram(const ShaderPointer& program);
json writeNamedBatchData(const Batch::NamedBatchData& namedData);
json writeCapturableTextures(const Frame& frame);
void findCapturableTextures(const Frame& frame);
void writeBinaryBlob();
static std::string toBase64(const std::vector<uint8_t>& v);
static json writeIrradiance(const SHPointer& irradiance);
@ -146,7 +149,7 @@ public:
static json writeTransform(const Transform& t) { return writeMat4(t.getMatrix()); }
static json writeCommand(size_t index, const Batch& batch);
static json writeSampler(const Sampler& sampler);
static json writeTexture(const TexturePointer& texture);
json writeTexture(const TexturePointer& texture);
static json writeFormat(const Stream::FormatPointer& format);
static json writeQuery(const QueryPointer& query);
static json writeShader(const ShaderPointer& shader);
@ -389,9 +392,17 @@ json Serializer::writeTexture(const TexturePointer& texturePointer) {
const auto* storage = texture._storage.get();
const auto* ktxStorage = dynamic_cast<const Texture::KtxStorage*>(storage);
if (ktxStorage) {
result[keys::ktxFile] = ktxStorage->_filename;
} else {
// TODO serialize the backing storage
result[keys::chunk] = 2 + ktxBuilders.size();
auto filename = ktxStorage->_filename;
ktxBuilders.push_back([=] {
return std::make_shared<storage::FileStorage>(filename.c_str());
});
} else if (textureCapturer && captureTextures.count(texturePointer) != 0) {
result[keys::chunk] = 2 + ktxBuilders.size();
auto storage = textureCapturer(texturePointer);
ktxBuilders.push_back([=] {
return storage;
});
}
}
return result;
@ -673,14 +684,8 @@ json Serializer::writeQuery(const QueryPointer& queryPointer) {
return result;
}
json Serializer::writeCapturableTextures(const Frame& frame) {
if (!textureCapturer) {
return json::array();
}
void Serializer::findCapturableTextures(const Frame& frame) {
std::unordered_set<TexturePointer> writtenRenderbuffers;
std::unordered_set<TexturePointer> captureTextures;
auto maybeCaptureTexture = [&](const TexturePointer& texture) {
// Not a valid texture
if (!texture) {
@ -755,20 +760,6 @@ json Serializer::writeCapturableTextures(const Frame& frame) {
}
}
}
json result = json::array();
for (const auto& texture : captureTextures) {
if (textureCapturer) {
auto index = textureMap[texture];
auto layers = std::max<uint16>(texture->getNumSlices(), 1);
for (uint16 layer = 0; layer < layers; ++layer) {
std::string textureFilename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png";
textureCapturer(textureFilename, texture, layer);
}
result.push_back(index);
}
}
return result;
}
void Serializer::writeFrame(const Frame& frame) {
@ -780,7 +771,7 @@ void Serializer::writeFrame(const Frame& frame) {
}
frameNode[keys::stereo] = writeStereoState(frame.stereoState);
frameNode[keys::capturedTextures] = writeCapturableTextures(frame);
findCapturableTextures(frame);
frameNode[keys::frameIndex] = frame.frameIndex;
frameNode[keys::view] = writeMat4(frame.view);
frameNode[keys::pose] = writeMat4(frame.pose);
@ -797,35 +788,21 @@ void Serializer::writeFrame(const Frame& frame) {
// Serialize textures and buffers last, since the maps they use can be populated by some of the above code
// Serialize textures
serializeMap(frameNode, keys::textures, textureMap, writeTexture);
serializeMap(frameNode, keys::textures, textureMap, std::bind(&Serializer::writeTexture, this, _1));
// Serialize buffers
serializeMap(frameNode, keys::buffers, bufferMap, writeBuffer);
{
std::string frameJson = frameNode.dump();
std::string filename = basename + ".json";
storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data());
}
writeBinaryBlob();
frameNode[keys::binary] = basename + ".bin";
hfb::writeFrame(filename, frameNode.dump(), binaryBuffer, ktxBuilders);
}
void Serializer::writeBinaryBlob() {
const auto buffers = mapToVector(bufferMap);
auto accumulator = [](size_t total, const BufferPointer& buffer) { return total + (buffer ? buffer->getSize() : 0); };
size_t totalSize = std::accumulate(buffers.begin(), buffers.end(), (size_t)0, accumulator);
const auto blobFilename = basename + ".bin";
QFile file(blobFilename.c_str());
if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) {
throw std::runtime_error("Unable to open file for writing");
}
if (!file.resize(totalSize)) {
throw std::runtime_error("Unable to resize file");
}
auto mapped = file.map(0, totalSize);
binaryBuffer.resize(totalSize);
auto mapped = binaryBuffer.data();
size_t offset = 0;
for (const auto& bufferPointer : buffers) {
@ -838,7 +815,4 @@ void Serializer::writeBinaryBlob() {
memcpy(mapped + offset, bufferData, bufferSize);
offset += bufferSize;
}
if (!file.unmap(mapped)) {
throw std::runtime_error("Unable to unmap file");
}
}

View file

@ -343,6 +343,7 @@ public:
class KtxStorage : public Storage {
public:
KtxStorage(const storage::StoragePointer& storage);
KtxStorage(const std::string& filename);
KtxStorage(const cache::FilePointer& file);
PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override;
@ -366,6 +367,7 @@ public:
static std::vector<std::pair<std::shared_ptr<storage::FileStorage>, std::shared_ptr<std::mutex>>> _cachedKtxFiles;
static std::mutex _cachedKtxFilesMutex;
storage::StoragePointer _storage;
std::string _filename;
cache::FilePointer _cacheEntry;
std::atomic<uint8_t> _minMipLevelAvailable;
@ -543,6 +545,7 @@ public:
Size getStoredSize() const;
void setStorage(std::unique_ptr<Storage>& newStorage);
void setKtxBacking(const storage::StoragePointer& storage);
void setKtxBacking(const std::string& filename);
void setKtxBacking(const cache::FilePointer& cacheEntry);

View file

@ -159,7 +159,31 @@ struct IrradianceKTXPayload {
};
const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" };
KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) {
KtxStorage::KtxStorage(const storage::StoragePointer& storage) : _storage(storage) {
auto ktxPointer = ktx::KTX::create(storage);
_ktxDescriptor.reset(new ktx::KTXDescriptor(ktxPointer->toDescriptor()));
if (_ktxDescriptor->images.size() < _ktxDescriptor->header.numberOfMipmapLevels) {
qWarning() << "Bad images found in ktx";
}
_offsetToMinMipKV = _ktxDescriptor->getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY);
if (_offsetToMinMipKV) {
auto data = storage->data() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV;
_minMipLevelAvailable = *data;
} else {
// Assume all mip levels are available
_minMipLevelAvailable = 0;
}
// now that we know the ktx, let's get the header info to configure this Texture::Storage:
Format mipFormat = Format::COLOR_BGRA_32;
Format texelFormat = Format::COLOR_SRGBA_32;
if (Texture::evalTextureFormat(_ktxDescriptor->header, mipFormat, texelFormat)) {
_format = mipFormat;
}
}
KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) {
_cacheEntry = cacheEntry;
}
@ -228,28 +252,31 @@ void KtxStorage::releaseOpenKtxFiles() {
PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face);
auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face);
storage::StoragePointer storageView;
if (faceSize != 0 && faceOffset != 0) {
std::lock_guard<std::mutex> lock(*_cacheFileMutex);
auto file = maybeOpenFile();
if (file) {
auto storageView = file->createView(faceSize, faceOffset);
if (storageView) {
return storageView->toMemoryStorage();
} else {
qWarning() << "Failed to get a valid storageView for faceSize=" << faceSize << " faceOffset=" << faceOffset << "out of valid file " << QString::fromStdString(_filename);
}
if (_storage) {
storageView = _storage->createView(faceSize, faceOffset);
} else {
qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename);
std::lock_guard<std::mutex> lock(*_cacheFileMutex);
auto file = maybeOpenFile();
if (file) {
storageView = file->createView(faceSize, faceOffset);
} else {
qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename);
}
}
}
return nullptr;
if (!storageView) {
qWarning() << "Failed to get a valid storageView for faceSize=" << faceSize << " faceOffset=" << faceOffset
<< "out of valid file " << QString::fromStdString(_filename);
}
return storageView->toMemoryStorage();
}
Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const {
return _ktxDescriptor->getMipFaceTexelsSize(level, face);
}
bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const {
return level >= _minMipLevelAvailable;
}
@ -271,7 +298,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
auto& imageDesc = _ktxDescriptor->images[level];
if (storage->size() != imageDesc._imageSize) {
qWarning() << "Invalid image size: " << storage->size() << ", expected: " << imageDesc._imageSize
<< ", level: " << level << ", filename: " << QString::fromStdString(_filename);
<< ", level: " << level << ", filename: " << QString::fromStdString(_filename);
return;
}
@ -311,8 +338,7 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor
throw std::runtime_error("Invalid call");
}
bool validKtx(const std::string& filename) {
ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) };
bool validKtx(const storage::StoragePointer& storage) {
auto ktxPointer = ktx::KTX::create(storage);
if (!ktxPointer) {
return false;
@ -320,6 +346,21 @@ bool validKtx(const std::string& filename) {
return true;
}
bool validKtx(const std::string& filename) {
ktx::StoragePointer storage{ new storage::FileStorage(filename.c_str()) };
return validKtx(storage);
}
void Texture::setKtxBacking(const storage::StoragePointer& storage) {
// Check the KTX file for validity before using it as backing storage
if (!validKtx(storage)) {
return;
}
auto newBacking = std::unique_ptr<Storage>(new KtxStorage(storage));
setStorage(newBacking);
}
void Texture::setKtxBacking(const std::string& filename) {
// Check the KTX file for validity before using it as backing storage
if (!validKtx(filename)) {
@ -355,7 +396,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
// Set Dimensions
uint32_t numFaces = 1;
switch (texture.getType()) {
case TEX_1D: {
case TEX_1D: {
if (texture.isArray()) {
header.set1DArray(texture.getWidth(), texture.getNumSlices());
} else {
@ -363,7 +404,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
}
break;
}
case TEX_2D: {
case TEX_2D: {
if (texture.isArray()) {
header.set2DArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices());
} else {
@ -371,7 +412,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
}
break;
}
case TEX_3D: {
case TEX_3D: {
if (texture.isArray()) {
header.set3DArray(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getNumSlices());
} else {
@ -379,7 +420,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
}
break;
}
case TEX_CUBE: {
case TEX_CUBE: {
if (texture.isArray()) {
header.setCubeArray(texture.getWidth(), texture.getHeight(), texture.getNumSlices());
} else {
@ -388,8 +429,8 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
numFaces = Texture::CUBE_FACE_COUNT;
break;
}
default:
return nullptr;
default:
return nullptr;
}
// Number level of mips coming

View file

@ -96,7 +96,7 @@ namespace ktx {
using GLBaseInternalFormat = khronos::gl::texture::BaseInternalFormat;
using Storage = storage::Storage;
using StoragePointer = std::shared_ptr<Storage>;
using StoragePointer = std::shared_ptr<const Storage>;
struct ImageDescriptor;
using ImageDescriptors = std::vector<ImageDescriptor>;

View file

@ -21,6 +21,7 @@
#include <QtCore/QUrl>
#include <QtCore/QTextStream>
#include <QtCore/QRegularExpression>
#include <QtCore/QFileSelector>
#include <QtGui/QDesktopServices>
@ -176,3 +177,15 @@ bool FileUtils::canCreateFile(const QString& fullPath) {
}
return true;
}
QString FileUtils::getParentPath(const QString& fullPath) {
return QFileInfo(fullPath).absoluteDir().canonicalPath();
}
bool FileUtils::exists(const QString& fileName) {
return QFileInfo(fileName).exists();
}
bool FileUtils::isRelative(const QString& fileName) {
return QFileInfo(fileName).isRelative();
}

View file

@ -12,20 +12,23 @@
#ifndef hifi_FileUtils_h
#define hifi_FileUtils_h
#include <QString>
#include <QtCore/QFileSelector>
#include <QtCore/QString>
class FileUtils {
public:
static const QStringList& getFileSelectors();
static QString selectFile(const QString& fileName);
static void locateFile(const QString& fileName);
static bool exists(const QString& fileName);
static bool isRelative(const QString& fileName);
static QString standardPath(QString subfolder);
static QString readFile(const QString& filename);
static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts);
static QString replaceDateTimeTokens(const QString& path);
static QString computeDocumentPath(const QString& path);
static bool canCreateFile(const QString& fullPath);
static QString getParentPath(const QString& fullPath);
};
#endif // hifi_FileUtils_h

View file

@ -10,15 +10,22 @@
#ifndef hifi_Storage_h
#define hifi_Storage_h
#include <stdint.h>
#include <cstdint>
#include <vector>
#include <memory>
#include <QFile>
#include <QString>
#include <functional>
#include <QtCore/QFile>
#include <QtCore/QString>
namespace storage {
class Storage;
using StoragePointer = std::shared_ptr<const Storage>;
using Pointer = std::shared_ptr<const Storage>;
using StoragePointer = Pointer;
// A function that can construct storage, useful for creating a list of
// things that can become storage without requiring that they all be instantiated at once
using Builder = std::function<StoragePointer()>;
using Builders = std::vector<Builder>;
// Abstract class to represent memory that stored _somewhere_ (in system memory or in a file, for example)
class Storage : public std::enable_shared_from_this<Storage> {

View file

@ -19,41 +19,31 @@ function(check_test name)
endfunction()
if (BUILD_TOOLS)
set(ALL_TOOLS
udt-test
vhacd-util
gpu-frame-player
ice-client
ktx-tool
ac-client
skeleton-dump
atp-client
oven
)
# Allow different tools for stable builds
if (STABLE_BUILD)
set(ALL_TOOLS
udt-test
vhacd-util
frame-optimizer
gpu-frame-player
ice-client
ktx-tool
ac-client
skeleton-dump
atp-client
oven
)
else()
set(ALL_TOOLS
udt-test
vhacd-util
frame-optimizer
gpu-frame-player
ice-client
ktx-tool
ac-client
skeleton-dump
atp-client
oven
nitpick
)
list(APPEND ALL_TOOLS nitpick)
endif()
foreach(TOOL ${ALL_TOOLS})
check_test(${TOOL})
if (${BUILD_TOOL_RESULT})
add_subdirectory(${TOOL})
set_target_properties(${TOOL} PROPERTIES FOLDER "Tools")
if (TARGET ${TOOL})
set_target_properties(${TOOL} PROPERTIES FOLDER "Tools")
endif()
endif()
endforeach()
endif()

View file

@ -1,6 +0,0 @@
set(TARGET_NAME frame-optimizer)
setup_memory_debugger()
setup_hifi_project(Gui Widgets)
link_hifi_libraries(shared ktx shaders gpu )
package_libraries_for_deployment()

View file

@ -1,39 +0,0 @@
//
// Created by Bradley Austin Davis on 2018/10/14
// 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/QCoreApplication>
#ifdef Q_OS_WIN
#include <Windows.h>
#endif
#include <iostream>
#include <gpu/FrameIO.h>
#include <gpu/Texture.h>
gpu::IndexOptimizer optimizer= [](gpu::Primitive primitive, uint32_t faceCount, uint32_t indexCount, uint32_t* indices ) {
// FIXME add a triangle index optimizer here
};
void messageHandler(QtMsgType type, const QMessageLogContext &, const QString & message) {
auto messageStr = message.toStdString();
#ifdef Q_OS_WIN
OutputDebugStringA(messageStr.c_str());
OutputDebugStringA("\n");
#endif
std::cerr << messageStr << std::endl;
}
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
qInstallMessageHandler(messageHandler);
gpu::optimizeFrame("D:/Frames/20190112_1647.json", optimizer);
return 0;
}

View file

@ -8,6 +8,8 @@
#include "PlayerWindow.h"
#include <QtCore/QByteArray>
#include <QtCore/QBuffer>
#include <QtGui/QResizeEvent>
#include <QtGui/QImageReader>
#include <QtGui/QScreen>
@ -55,7 +57,7 @@ void PlayerWindow::loadFrame() {
}
}
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("GPU Frames (*.json)"));
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("GPU Frames (*.hfb)"));
if (fileName.isNull()) {
return;
}
@ -104,17 +106,8 @@ void PlayerWindow::resizeEvent(QResizeEvent* ev) {
_renderThread.resize(ev->size());
}
void PlayerWindow::textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
QImage image;
QImageReader(filename.c_str()).read(&image);
if (layer > 0) {
return;
}
texture->assignStoredMip(0, image.byteCount(), image.constBits());
}
void PlayerWindow::loadFrame(const QString& path) {
auto frame = gpu::readFrame(path.toStdString(), _renderThread._externalTexture, &PlayerWindow::textureLoader);
auto frame = gpu::readFrame(path.toStdString(), _renderThread._externalTexture);
if (frame) {
_renderThread.submitFrame(frame);
if (!_renderThread.isThreaded()) {

View file

@ -28,7 +28,7 @@ protected:
void loadFrame(const QString& path);
private:
static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer);
static void textureLoader(const std::vector<uint8_t>& filename, const gpu::TexturePointer& texture, uint16_t layer);
QSettings _settings;
RenderThread _renderThread;
};