Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scene data serialization #427

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ option(WITH_AL_INFO "Build magnum-al-info utility" OFF)
option(WITH_ANYIMAGEIMPORTER "Build AnyImageImporter plugin" OFF)
option(WITH_ANYAUDIOIMPORTER "Build AnyAudioImporter plugin" OFF)
option(WITH_ANYIMAGECONVERTER "Build AnyImageConverter plugin" OFF)
option(WITH_ANYSCENECONVERTER "Build AnySceneConverter plugin" OFF)
option(WITH_ANYSCENEIMPORTER "Build AnySceneImporter plugin" OFF)
option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF)
option(WITH_MAGNUMFONT "Build MagnumFont plugin" OFF)
Expand Down Expand Up @@ -356,6 +357,10 @@ set(MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINA
set(MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/importers)
set(MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/importers)
set(MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/importers)
set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/audioimporters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/audioimporters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/audioimporters)
Expand Down Expand Up @@ -394,6 +399,7 @@ if(MAGNUM_PLUGINS_DIR)
set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/fontconverters)
set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/imageconverters)
set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/importers)
set(MAGNUM_PLUGINS_SCENECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/audioimporters)
endif()
if(MAGNUM_PLUGINS_DEBUG_DIR)
Expand All @@ -402,12 +408,14 @@ if(MAGNUM_PLUGINS_DEBUG_DIR)
set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/imageconverters)
set(MAGNUM_PLUGINS_IMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/importers)
set(MAGNUM_PLUGINS_FONT_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fonts)
set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/audioimporters)
endif()
if(MAGNUM_PLUGINS_RELEASE_DIR)
set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fontconverters)
set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/imageconverters)
set(MAGNUM_PLUGINS_IMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/importers)
set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/sceneconverters)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/audioimporters)
endif()

Expand Down
107 changes: 107 additions & 0 deletions doc/blob.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
This file is part of Magnum.

Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
Vladimír Vondruš <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

namespace Magnum {
/** @page blob Magnum's memory-mappable serialization format
@brief Efficient and extensible format for storing binary data
@m_since_latest

@tableofcontents
@m_footernavigation

Apart from various data import and conversion plugins, described in the
@ref plugins "previous chapter", Magnum provides its own binary format. Files
stored in this format have a `*.blob` extension and are identified by various
permutations of the letters `BLOB` in their first few bytes.

The goal of the format is being usable directly without having to process the
data payload in any way. That allows for example the file contents to be
memory-mapped and operated on directly. In order to achieve this, there's four
different variants of the format based on whether it's running on a 32-bit or
64-bit system and whether the machine is Little- or Big-Endian. The @ref Trade
library itself provides serialization and deserialization of blob formats
matching the platform it's running on. Import and conversion of blobs with
different endianness or bitness (as well as compatibility with previous format
versions as the format will evolve) is handled by the
@ref Trade::MagnumImporter "MagnumImporter" and
@ref Trade::MagnumSceneConverter "MagnumSceneConverter" plugins --- since this
functionality is not strictly needed when shipping an application, it's
provided separately.

@section blob-implementation Implementation

The binary format consists of "chunks" similar to [RIFF](https://en.wikipedia.org/wiki/Resource_Interchange_File_Format),
and the main property is an ability to combine arbitrary chunks together in the
most trivial way possible as well as extracting them back. Each chunk has a
@ref Trade::DataChunkHeader containing a [FourCC](https://en.wikipedia.org/wiki/FourCC)-like @ref Trade::DataChunkType identifier and a chunk size, allowing applications to pick chunks that they're interested in and reliably skip the
others. Compared to RIFF the file doesn't have any "global" chunk in order to
make trivial file concatenation possible:

@code{.sh}
cat chair.blob table.blob > furniture.blob
@endcode

@section blob-iteration Chunk iteration

To be designed & written first.

@section blob-meshdata Mesh data

Currently there's just a single serializable data type, @ref Trade::MeshData.
You can create serialized blobs using @ref Trade::MeshData::serialize() or
alternatively using the @ref magnum-sceneconverter "magnum-sceneconverter"
tool, for example:

@code{.sh}
magnum-sceneconverter avocado.glb avocado.blob
@endcode

Deserialization is then done with @ref Trade::MeshData::deserialize(). The
function takes a memory view as an input and returns a @ref Trade::MeshData
instance pointing to that view, without copying or processing the data in any
way. A recommended way to access serialized data is thus via memory-mapping the
file (for example using @ref Utility::Directory::mapRead() or any other way
your platform allows), and keeping it around for as long as you need:

@snippet MagnumTrade.cpp blob-deserialize-mesh

@section blob-custom Custom chunk types

As said above, the format is designed to allow custom chunk types to be mixed
together with data recognized by Magnum. To make a custom chunk, create your
own @ref Trade::DataChunkType using @ref Corrade::Utility::Endianness::fourCC()
--- identifiers starting with an uppercase letter are reserved for Magnum
itself, custom application-specific data types should use a lowercase first
letter instead.

Then write a serialization/deserialization API similar to
@ref Trade::MeshData::serialize() / @ref Trade::MeshData::deserialize() with
the help of low-level @ref Trade::dataChunkHeaderSerializeInto() and
@ref Trade::dataChunkHeaderDeserialize(). Those functions will take care of
properly filling in required chunk header fields when serializing and checking
chunk validity when deserializing. Validation of the chunk data itself is then
up to you.
*/
}
2 changes: 2 additions & 0 deletions doc/building.dox
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,8 @@ default.
building of the @ref Trade library.
- `WITH_ANYIMAGEIMPORTER` --- Build the @ref Trade::AnyImageImporter "AnyImageImporter"
plugin. Enables also building of the @ref Trade library.
- `WITH_ANYSCENECONVERTER` --- Build the @ref Trade::AnySceneConverter "AnySceneConverter"
plugin. Enables also building of the @ref Trade library.
- `WITH_ANYSCENEIMPORTER` --- Build the @ref Trade::AnySceneImporter "AnySceneImporter"
plugin. Enables also building of the @ref Trade library.
- `WITH_MAGNUMFONT` --- Build the @ref Text::MagnumFont "MagnumFont" plugin.
Expand Down
8 changes: 8 additions & 0 deletions doc/changelog.dox
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ See also:

- New @ref DebugTools::ColorMap namespace containing a few presets for
gradient visualization
- New @ref DebugTools::FrameProfiler utility for CPU and GPU profiling

@subsubsection changelog-latest-new-gl GL library

Expand Down Expand Up @@ -269,6 +270,11 @@ See also:
- New @ref magnum-sceneconverter "magnum-sceneconverter" tool, similar to
@ref magnum-imageconverter "magnum-imageconverter" but suited for general
scene formats
- New @ref Trade::AbstractSceneConverter plugin interface and an
@ref Trade::AnySceneConverter "AnySceneConverter" plugin
- Efficient and extensible memory-mappable serialization format for binary
data. See @ref blob for an introduction, see also
[mosra/magnum#427](https://github.com/mosra/magnum/pull/427).
- Ability to import image mip levels via an additional parameter in
@ref Trade::AbstractImporter::image2D(),
@ref Trade::AbstractImporter::image2DLevelCount() and similar APIs for 1D
Expand Down Expand Up @@ -630,6 +636,8 @@ See also:
This also means it's no longer
possible to override equality comparison epsilons at compile time, but that
was a rarely (if ever) used feature.
- @cpp DebugTools::Profiler @ce is obsolete, replaced with a much more
flexible and extensible @ref DebugTools::FrameProfiler

@subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs

Expand Down
2 changes: 2 additions & 0 deletions doc/cmake.dox
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ dependencies, you need to find the dependency and then link to it.
plugin
- `AnyImageImporter` --- @ref Trade::AnyImageImporter "AnyImageImporter"
plugin
- `AnySceneConverter` --- @ref Trade::AnySceneConverter "AnySceneConverter"
plugin
- `AnySceneImporter` --- @ref Trade::AnySceneImporter "AnySceneImporter"
plugin
- `MagnumFont` --- @ref Text::MagnumFont "MagnumFont" plugin
Expand Down
1 change: 1 addition & 0 deletions doc/features.dox
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ necessary to read through everything, pick only what you need.
- @subpage transformations --- @copybrief transformations
- @subpage animation --- @copybrief animation
- @subpage plugins --- @copybrief plugins
- @subpage blob --- @copybrief blob
- @subpage opengl-wrapping --- @copybrief opengl-wrapping
- @subpage shaders --- @copybrief shaders
- @subpage scenegraph --- @copybrief scenegraph
Expand Down
5 changes: 5 additions & 0 deletions doc/plugins.dox
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ of given type. Magnum provides these plugin interfaces:
- @ref Trade::AbstractImageConverter --- conversion among various image
formats. See `*ImageConverter` classes in the @ref Trade namespace for
available image converter plugins.
- @ref Trade::AbstractSceneConverter --- conversion among various scene
formats, mesh optimization etc. See `*SceneConverter` classes in the
@ref Trade namespace for available scene converter plugins.
- @ref Text::AbstractFont --- font loading and glyph layout. See `*Font`
classes in the @ref Text namespace for available font plugins.
- @ref Text::AbstractFontConverter --- font and glyph cache conversion. See
Expand Down Expand Up @@ -192,6 +195,8 @@ So far, the following plugins have the "any format" ability:
format
- @ref Trade::AnySceneImporter "AnySceneImporter" --- imports any scene
format
- @ref Trade::AnySceneConverter "AnySceneConverter" --- converts to any scene
format
- @ref Audio::AnyImporter "AnyImporter" --- imports any audio format

@section plugins-configuration Plugin-specific configuration
Expand Down
6 changes: 6 additions & 0 deletions doc/snippets/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ if(WITH_DEBUGTOOLS)
target_link_libraries(snippets-MagnumDebugTools PRIVATE MagnumDebugTools)
set_target_properties(snippets-MagnumDebugTools PROPERTIES FOLDER "Magnum/doc/snippets")

if(BUILD_GL_TESTS AND NOT MAGNUM_TARGET_GLES)
add_executable(debugtools-frameprofiler debugtools-frameprofiler.cpp)
target_link_libraries(debugtools-frameprofiler PRIVATE
MagnumDebugTools MagnumOpenGLTester)
endif()

# TODO: causes spurious linker errors on Travis iOS build, so I'm disabling it
if(NOT CORRADE_TARGET_IOS)
set(SNIPPETS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
Expand Down
41 changes: 41 additions & 0 deletions doc/snippets/MagnumDebugTools-gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "Magnum/DebugTools/TextureImage.h"
#include "Magnum/GL/Framebuffer.h"
#include "Magnum/GL/CubeMapTexture.h"
#include "Magnum/GL/SampleQuery.h"
#include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h"
#include "Magnum/Math/Range.h"
Expand All @@ -48,6 +49,10 @@
#include "Magnum/GL/BufferImage.h"
#endif

#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#include "Magnum/DebugTools/FrameProfiler.h"
#endif

using namespace Magnum;
using namespace Magnum::Math::Literals;

Expand Down Expand Up @@ -103,6 +108,33 @@ new DebugTools::ForceRenderer3D(manager, *object, {0.3f, 1.5f, -0.7f}, force,
/* [ForceRenderer] */
}

#ifndef MAGNUM_TARGET_GLES
{
/* [FrameProfiler-setup-delayed] */
GL::SampleQuery queries[3]{
GL::SampleQuery{GL::SampleQuery::Target::SamplesPassed},
GL::SampleQuery{GL::SampleQuery::Target::SamplesPassed},
GL::SampleQuery{GL::SampleQuery::Target::SamplesPassed}
};
DebugTools::FrameProfiler profiler{{
DebugTools::FrameProfiler::Measurement{"Samples",
DebugTools::FrameProfiler::Units::Count,
UnsignedInt(Containers::arraySize(queries)),
[](void* state, UnsignedInt current) {
static_cast<GL::SampleQuery*>(state)[current].begin();
},
[](void* state, UnsignedInt current) {
static_cast<GL::SampleQuery*>(state)[current].end();
},
[](void* state, UnsignedInt previous, UnsignedInt) {
return static_cast<GL::SampleQuery*>(state)[previous]
.result<UnsignedLong>();
}, queries}
}, 50};
/* [FrameProfiler-setup-delayed] */
}
#endif

{
SceneGraph::Object<SceneGraph::MatrixTransformation3D>* object{};
/* [ObjectRenderer] */
Expand All @@ -116,6 +148,15 @@ manager.set("my", DebugTools::ObjectRendererOptions{}.setSize(0.3f));
new DebugTools::ObjectRenderer3D{manager, *object, "my", &debugDrawables};
/* [ObjectRenderer] */
}

{
/* [GLFrameProfiler-usage] */
DebugTools::GLFrameProfiler profiler{
DebugTools::GLFrameProfiler::Value::FrameTime|
DebugTools::GLFrameProfiler::Value::GpuDuration, 50};
/* [GLFrameProfiler-usage] */
}

{
GL::Texture2D texture;
Range2Di rect;
Expand Down
63 changes: 61 additions & 2 deletions doc/snippets/MagnumDebugTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
DEALINGS IN THE SOFTWARE.
*/

#include <chrono>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/PluginManager/Manager.h>
Expand All @@ -31,6 +32,7 @@
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/DebugTools/CompareImage.h"
#include "Magnum/DebugTools/FrameProfiler.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/AbstractImporter.h"

Expand Down Expand Up @@ -101,5 +103,62 @@ CORRADE_COMPARE_WITH(actual.pixels<Color3ub>().flipped<0>(), expected,
}
};

/* To prevent macOS ranlib complaining that there are no symbols */
int main() {}
struct MyApp {
void drawEvent();
void drawEventAgain();
void swapBuffers();
void redraw();

DebugTools::FrameProfiler _profiler;
};

/* [FrameProfiler-usage] */
void MyApp::drawEvent() {
_profiler.beginFrame();

// actual drawing code …

_profiler.endFrame();

// possibly other code (such as UI) you don't want to have included in the
// measurements …

swapBuffers();
redraw();
}
/* [FrameProfiler-usage] */

void MyApp::drawEventAgain() {
/* [FrameProfiler-usage-console] */
_profiler.endFrame();
_profiler.printStatistics(10);

swapBuffers();
if(_profiler.isEnabled()) redraw();
}
/* [FrameProfiler-usage-console] */

int main() {
{
/* [FrameProfiler-setup-immediate] */
using std::chrono::high_resolution_clock;

high_resolution_clock::time_point frameBeginTime;
DebugTools::FrameProfiler profiler{{
DebugTools::FrameProfiler::Measurement{"CPU time",
DebugTools::FrameProfiler::Units::Nanoseconds,
[](void* state) {
*static_cast<high_resolution_clock::time_point*>(state)
= high_resolution_clock::now();
},
[](void* state) {
return UnsignedLong(
std::chrono::duration_cast<std::chrono::nanoseconds>(
*static_cast<high_resolution_clock::time_point*>(state)
- high_resolution_clock::now()).count());
}, &frameBeginTime}
}, 50};
/* [FrameProfiler-setup-immediate] */
}

}
Loading