diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 74d36101db..55a15dadf0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include @@ -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(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(*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) { diff --git a/libraries/gpu/src/gpu/FrameIO.cpp b/libraries/gpu/src/gpu/FrameIO.cpp new file mode 100644 index 0000000000..a0f21df881 --- /dev/null +++ b/libraries/gpu/src/gpu/FrameIO.cpp @@ -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 +#include + +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 +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 +static void writeChunk(uint8_t*& dest, uint32_t chunkType, const T& chunkData) { + uint32_t chunkSize = static_cast(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(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(0, nullptr) }; + auto storage = builder(); + if (!storage) { + storage = EMPTY_STORAGE; + } + writeChunk(ptr, gpu::hfb::CHUNK_TYPE_KTX, *storage); + } + assert((ptr - output->data()) == size); +} diff --git a/libraries/gpu/src/gpu/FrameIO.h b/libraries/gpu/src/gpu/FrameIO.h index 4de4cf5fe7..cf653cc4ae 100644 --- a/libraries/gpu/src/gpu/FrameIO.h +++ b/libraries/gpu/src/gpu/FrameIO.h @@ -12,17 +12,72 @@ #include "Forward.h" #include "Format.h" +#include + #include namespace gpu { -using TextureCapturer = std::function; -using TextureLoader = std::function; +using TextureCapturer = std::function; +//using TextureLoader = std::function; 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 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; + +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; + +struct Descriptor { + using Pointer = std::shared_ptr; + + 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 diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 5d5f8a5bd9..1a98d0decd 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -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", diff --git a/libraries/gpu/src/gpu/FrameReader.cpp b/libraries/gpu/src/gpu/FrameReader.cpp index 740ec2b26f..d636c6aaca 100644 --- a/libraries/gpu/src/gpu/FrameReader.cpp +++ b/libraries/gpu/src/gpu/FrameReader.cpp @@ -10,9 +10,7 @@ #include #include -#include -#include - +#include #include #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(filename.c_str())), + externalTexture(externalTexture) { + descriptor = std::make_shared(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 shaders; std::vector programs; std::vector textures; @@ -69,10 +63,13 @@ public: std::vector 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 - static bool readBatchCacheTransformed(typename Batch::Cache::Vector& dest, - const json& node, - const std::string& name, - std::function f = [](const json& node) -> TT { - return node.get(); - }) { + static bool readBatchCacheTransformed( + typename Batch::Cache::Vector& dest, + const json& node, + const std::string& name, + std::function f = [](const json& node) -> TT { return node.get(); }) { 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(data.flags, node, keys::flags, &readStateFlags); - Deserializer::readOptionalTransformed(data.blendFunction, node, keys::blendFunction, &readBlendFunction); + Deserializer::readOptionalTransformed(data.blendFunction, node, keys::blendFunction, + &readBlendFunction); Deserializer::readOptionalTransformed(data.depthTest, node, keys::depthTest, &readDepthTest); - Deserializer::readOptionalTransformed(data.stencilActivation, node, keys::stencilActivation, &readStencilActivation); - Deserializer::readOptionalTransformed(data.stencilTestFront, node, keys::stencilTestFront, &readStencilTest); - Deserializer::readOptionalTransformed(data.stencilTestBack, node, keys::stencilTestBack, &readStencilTest); + Deserializer::readOptionalTransformed(data.stencilActivation, node, keys::stencilActivation, + &readStencilActivation); + Deserializer::readOptionalTransformed(data.stencilTestFront, node, keys::stencilTestFront, + &readStencilTest); + Deserializer::readOptionalTransformed(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(); 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(frameNode, keys::formats, [](const json& node) { return readFormat(node); }); - auto textureReader = [this](const json& node) { return readTexture(node, externalTexture); }; - textures = readArray(frameNode, keys::textures, textureReader); - if (textureLoader) { - std::vector capturedTextures = readNumericVector(frameNode[keys::capturedTextures]); - for (const auto& index : capturedTextures) { - const auto& texturePointer = textures[index]; - uint16 layers = std::max(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(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 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 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()); - } -} diff --git a/libraries/gpu/src/gpu/FrameWriter.cpp b/libraries/gpu/src/gpu/FrameWriter.cpp index f348827385..761f37a620 100644 --- a/libraries/gpu/src/gpu/FrameWriter.cpp +++ b/libraries/gpu/src/gpu/FrameWriter.cpp @@ -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 shaderMap; std::unordered_map programMap; @@ -32,8 +32,11 @@ public: std::unordered_map framebufferMap; std::unordered_map swapchainMap; std::unordered_map queryMap; + std::unordered_set 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 static uint32_t getGlobalIndex(const T& value, std::unordered_map& 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& 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(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(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 writtenRenderbuffers; - std::unordered_set 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(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"); - } } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 5e2485941d..debedf02a5 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -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::shared_ptr>> _cachedKtxFiles; static std::mutex _cachedKtxFilesMutex; + storage::StoragePointer _storage; std::string _filename; cache::FilePointer _cacheEntry; std::atomic _minMipLevelAvailable; @@ -543,6 +545,7 @@ public: Size getStoredSize() const; void setStorage(std::unique_ptr& newStorage); + void setKtxBacking(const storage::StoragePointer& storage); void setKtxBacking(const std::string& filename); void setKtxBacking(const cache::FilePointer& cacheEntry); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index f471baf2c7..a5cea3e60e 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -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 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 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(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 diff --git a/libraries/ktx/src/ktx/KTX.h b/libraries/ktx/src/ktx/KTX.h index d755a482e3..0165113ec5 100644 --- a/libraries/ktx/src/ktx/KTX.h +++ b/libraries/ktx/src/ktx/KTX.h @@ -96,7 +96,7 @@ namespace ktx { using GLBaseInternalFormat = khronos::gl::texture::BaseInternalFormat; using Storage = storage::Storage; - using StoragePointer = std::shared_ptr; + using StoragePointer = std::shared_ptr; struct ImageDescriptor; using ImageDescriptors = std::vector; diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index f2a4925351..164af091de 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -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(); +} diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index 2f5e11f005..d4ff819e75 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -12,20 +12,23 @@ #ifndef hifi_FileUtils_h #define hifi_FileUtils_h -#include -#include +#include + 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 diff --git a/libraries/shared/src/shared/Storage.h b/libraries/shared/src/shared/Storage.h index 0e5032bb62..6a2cecf8b9 100644 --- a/libraries/shared/src/shared/Storage.h +++ b/libraries/shared/src/shared/Storage.h @@ -10,15 +10,22 @@ #ifndef hifi_Storage_h #define hifi_Storage_h -#include +#include #include #include -#include -#include +#include + +#include +#include namespace storage { class Storage; - using StoragePointer = std::shared_ptr; + using Pointer = std::shared_ptr; + 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; + using Builders = std::vector; // 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 { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 75b08ba44f..040bb750d0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -330,7 +330,6 @@ QObject* TabletScriptingInterface::getFlags() { // static const char* TABLET_HOME_SOURCE_URL = "hifi/tablet/TabletHome.qml"; -static const char* WEB_VIEW_SOURCE_URL = "hifi/tablet/TabletWebView.qml"; static const char* VRMENU_SOURCE_URL = "hifi/tablet/TabletMenu.qml"; class TabletRootWindow : public QmlWindowClass { @@ -827,58 +826,24 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) { void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) { bool localSafeContext = hifi::scripting::isLocalAccessSafeThread(); if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, localSafeContext)); + QMetaObject::invokeMethod(this, "loadHTMLSourceOnTopImpl", Q_ARG(QString, url.toString()), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, false), Q_ARG(bool, localSafeContext)); return; } - loadHTMLSourceImpl(url, injectJavaScriptUrl, localSafeContext); -} - - - -void TabletProxy::loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext) { - if (QThread::currentThread() != thread()) { - qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts"; - return; - - } - - - QObject* root = nullptr; - if (!_toolbarMode && _qmlTabletRoot) { - root = _qmlTabletRoot; - } else if (_toolbarMode && _desktopWindow) { - root = _desktopWindow->asQuickItem(); - } - - if (root) { - if (localSafeContext) { - hifi::scripting::setLocalAccessSafeThread(true); - } - QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); - if (_toolbarMode && _desktopWindow) { - QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); - } - QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl))); - hifi::scripting::setLocalAccessSafeThread(false); - } - _state = State::Web; + loadHTMLSourceOnTopImpl(url.toString(), injectJavaScriptUrl, false, localSafeContext); } void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) { bool localSafeContext = hifi::scripting::isLocalAccessSafeThread(); if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext)); + QMetaObject::invokeMethod(this, "loadHTMLSourceOnTopImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext)); return; } - - loadHTMLSourceImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext); + loadHTMLSourceOnTopImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext); } -void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) { - +void TabletProxy::loadHTMLSourceOnTopImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) { QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -911,7 +876,6 @@ void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injected _initialWebPathParams.first = injectedJavaScriptUrl; _initialWebPathParams.second = loadOtherBase; _initialScreen = true; - } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 58596d6d92..ab51bf203c 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -273,7 +273,9 @@ public: Q_INVOKABLE void gotoHomeScreen(); /**jsdoc - * Opens a web page or app on the tablet. + * Opens a web app or page in addition to any current app. In tablet mode, the app or page is displayed over the top of the + * current app; in toolbar mode, the app is opened in a new window that replaces any current window open. If in tablet + * mode, the app or page can be closed using {@link TabletProxy#returnToPreviousApp}. * @function TabletProxy#gotoWebScreen * @param {string} url - The URL of the web page or app. * @param {string} [injectedJavaScriptUrl=""] - The URL of JavaScript to inject into the web page. @@ -294,29 +296,31 @@ public: Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false); /**jsdoc - * Internal function, do not call from scripts * @function TabletProxy#loadQMLSourceImpl + * @deprecated This function is deprecated and will be removed. */ + // Internal function, do not call from scripts. Q_INVOKABLE void loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext); - /**jsdoc - * Internal function, do not call from scripts - * @function TabletProxy#loadHTMLSourceImpl + /**jsdoc + * @function TabletProxy#loadHTMLSourceOnTopImpl + * @deprecated This function is deprecated and will be removed. */ - Q_INVOKABLE void loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext); + // Internal function, do not call from scripts. + Q_INVOKABLE void loadHTMLSourceOnTopImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext); - /**jsdoc - * Internal function, do not call from scripts - * @function TabletProxy#loadHTMLSourceImpl - */ - Q_INVOKABLE void loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext); - - /**jsdoc - * Internal function, do not call from scripts + /**jsdoc * @function TabletProxy#returnToPreviousAppImpl + * @deprecated This function is deprecated and will be removed. */ + // Internal function, do not call from scripts. Q_INVOKABLE void returnToPreviousAppImpl(bool localSafeContext); + /**jsdoc + *@function TabletProxy#loadQMLOnTopImpl + * @deprecated This function is deprecated and will be removed. + */ + // Internal function, do not call from scripts. Q_INVOKABLE void loadQMLOnTopImpl(const QVariant& path, bool localSafeContext); // FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success); @@ -355,8 +359,8 @@ public: /**jsdoc * Opens a web app or page in addition to any current app. In tablet mode, the app or page is displayed over the top of the - * current app; in toolbar mode, the app is opened in a new window. If in tablet mode, the app or page can be closed using - * {@link TabletProxy#returnToPreviousApp}. + * current app; in toolbar mode, the app is opened in a new window that replaces any current window open. If in tablet + * mode, the app or page can be closed using {@link TabletProxy#returnToPreviousApp}. * @function TabletProxy#loadWebScreenOnTop * @param {string} path - The URL of the web page or HTML app. * @param {string} [injectedJavaScriptURL=""] - The URL of JavaScript to inject into the web page. diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index fd74786a5e..4942ecbd63 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -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() diff --git a/tools/frame-optimizer/CMakeLists.txt b/tools/frame-optimizer/CMakeLists.txt deleted file mode 100644 index cc268c5baf..0000000000 --- a/tools/frame-optimizer/CMakeLists.txt +++ /dev/null @@ -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() diff --git a/tools/frame-optimizer/src/main.cpp b/tools/frame-optimizer/src/main.cpp deleted file mode 100644 index a4200c3d97..0000000000 --- a/tools/frame-optimizer/src/main.cpp +++ /dev/null @@ -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 - -#ifdef Q_OS_WIN -#include -#endif - -#include -#include -#include - - -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; -} diff --git a/tools/gpu-frame-player/src/PlayerWindow.cpp b/tools/gpu-frame-player/src/PlayerWindow.cpp index e74caddd5e..8e7f730181 100644 --- a/tools/gpu-frame-player/src/PlayerWindow.cpp +++ b/tools/gpu-frame-player/src/PlayerWindow.cpp @@ -8,6 +8,8 @@ #include "PlayerWindow.h" +#include +#include #include #include #include @@ -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()) { diff --git a/tools/gpu-frame-player/src/PlayerWindow.h b/tools/gpu-frame-player/src/PlayerWindow.h index 4dfbca0855..a519fd9339 100644 --- a/tools/gpu-frame-player/src/PlayerWindow.h +++ b/tools/gpu-frame-player/src/PlayerWindow.h @@ -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& filename, const gpu::TexturePointer& texture, uint16_t layer); QSettings _settings; RenderThread _renderThread; };