May 02, 2018

Magnum 2018.04 released

with custom renderer support

The new re­lease brings more flex­ib­il­ity to as­set man­age­ment and ren­der­ing ab­strac­tions, im­proves plu­gin hand­ling and show­cases Leap Mo­tion in­teg­ra­tion.

Two months after the pre­vi­ous re­lease, Mag­num comes with the biggest change in five years of its pub­lic his­tory. With 2018.04 the OpenGL wrap­ping lay­er and as­set man­age­ment APIs are no longer a core part of the lib­rary, but com­pletely op­tion­al — this fi­nally al­lows for the Vulkan port im­ple­ment­a­tion and also makes it pos­sible to use Mag­num in con­texts where it was not pos­sible be­fore. The changelog since 2018.02 is al­most as long as list of changes for whole three years be­fore, so let’s dive right in!

OpenGL wrap­ping lay­er made op­tion­al

There’s a new GL lib­rary con­tain­ing all buf­fer, mesh, tex­ture and oth­er APIs that were pre­vi­ously part of the core lib­rary. It’s built by de­fault, but can be com­pletely dis­abled. Some Mag­num lib­rar­ies still have a hard de­pend­ency on it, at the mo­ment it’s Text and Shaders. This will change dur­ing the next re­leases, es­pe­cially the Text lib­rary is sched­uled to re­ceive up­dates that sep­ar­ate the text lay­out­ing from GPU ren­der­ing. How­ever, these up­dates will be rather in­cre­ment­al com­pared to this re­lease.

Some Mag­num lib­rar­ies provide func­tion­al­ity that makes use of the GL lib­rary (for ex­ample MeshTools::com­pile() or De­bug­Tools::tex­ture­SubIm­age()). This doesn’t make them tied to it, how­ever — you can dis­able OpenGL in­teg­ra­tion and leave just the API-ag­nost­ic fea­tures there. Look in the doc­u­ment­a­tion for de­tails.

API-in­de­pend­ent as­set man­age­ment

Be­sides hav­ing the GL lib­rary op­tion­al, the Trade lib­rary is now op­tion­al as well — so if you use Mag­num for GPU data pro­cessing and don’t need any im­age de­cod­ing plu­gins or mesh im­port, you can use Mag­num without any of that, with faster build times and smal­ler de­ploy sizes.

To make the as­set man­age­ment APIs in­de­pend­ent on OpenGL, there are now gen­er­ic Pixel­Format / Com­pressed­Pixel­Format, MeshPrim­it­ive, Sampler­Fil­ter, Sampler­Mi­pmap and Sampler­Wrap­ping enums. Each of these con­tains a set of most widely used formats/types and all im­age and scene im­port­ers now de­scribe data us­ing those gen­er­ic types. The new mesh prim­it­ive and sampler types re­tain their ori­gin­al nam­ing, be­cause that’s in line with how most graph­ics APIs in­clud­ing Vulkan, Met­al, or D3D name them. How­ever I dropped the un­ne­ces­sary pixel format / pixel type dis­tinc­tion en­forced by OpenGL leg­acy and made just a single Pixel­Format enum. Nam­ing of its val­ues is a res­ult of find­ing a good com­prom­ise between read­ab­il­ity and clar­ity with un­am­bigu­ous im­pli­cit map­ping to GPU APIs. It re­sembles MTLPixel­Format the most, which it­self is a more read­able al­tern­at­ive to what Vk­Format or D3D formats of­fer.

const Color3 data[]{
    0xff0000_rgbf, 0x00ff00_rgbf,
    0x00ffff_rgbf, 0xffff00_rgbf
};

/* Three-component 32-bit floating point pixels */
ImageView2D image{PixelFormat::RGB32F, {2, 2}, data};

How­ever, that does not mean you are now re­stric­ted to some “low­est com­mon de­nom­in­at­or” sub­set, as is un­for­tu­nately the case in some graph­ics API ab­strac­tions — if you feel the need for a BGR565 type or simply want to use the OpenGL types dir­ectly like be­fore, just do it:

ImageView2D image{GL::PixelFormat::BGR, GL::PixelType::UnsignedShort565Rev,
    {32, 32}, data};

The Im­age, Im­ageView and Trade::Im­ageData APIs sup­port both — if you pass them a gen­er­ic format, the GL lib­rary con­verts it to the well-known GL::Pixel­Format / GL::Pixel­Type pair that OpenGL un­der­stands; if you pass it an im­ple­ment­a­tion-spe­cif­ic format, it will use it as-is.

The trans­la­tion to OpenGL-spe­cif­ic formats is done im­pli­citly (i.e., all APIs in the GL lib­rary that take the gen­er­ic enum have an over­load that takes the OpenGL-spe­cif­ic value as well), but you can also re­quest ex­pli­cit con­ver­sion us­ing GL::pixel­Format(), GL::pixel­Type(), GL::sampler­Wrap­ping() and oth­er trans­la­tion func­tions.

This is not lim­ited to OpenGL, though — if you want to use the Mag­num im­age classes with Met­al-spe­cif­ic formats, for ex­ample, you can do so, too. In this case the con­struct­or re­quires you to spe­cify pixel size manu­ally, as it needs it for vari­ous checks and op­er­a­tions later:

/* Default pixel storage, 8-bit sRGB + alpha, four bytes per pixel */
ImageView2D view{{}, MTLPixelFormatRGBA8Unorm_sRGB, {}, 4, {256, 256}, data};

Us­ing cus­tom ren­der­ers with Mag­num

Mak­ing the GL lib­rary op­tion­al was primar­ily a first step to­wards Vulkan sup­port (work on which will now be re­sumed, you can watch the pro­gress in mosra/mag­num#234), but this opens up more pos­sib­ilites than that — you can now use Mag­num in pro­jects that have their own ren­der­ing en­gines! To prove the point, there’s now a new ex­ample that uses the sokol_g­fx single-file graph­ics API ab­strac­tion lib­rary to render a tri­angle on screen without re­ly­ing on Mag­num OpenGL sup­port at all.

sokol_gfx Triangle example screenshot
Tri­angle us­ing sokol_g­fx
Shows how to use cus­tom ren­der­ers with Mag­num.

The pos­sib­il­it­ies don’t end here — if the bg­fx lib­rary is your ren­der­er of choice, you can still make use of the well-doc­u­mented Mag­num Math lib­rary, SceneGraph or the OpenGex­Im­port­er and oth­er plu­gins. Or use the Test­Suite with Un­real En­gine and com­pare your shader out­put against a ground truth us­ing De­bug­Tools::Com­pareIm­age, for ex­ample.

Plu­gin work­flow im­prove­ments

A lot of ef­fort went in­to im­prov­ing the gen­er­al plu­gin work­flow. Un­til now, load­ing a file us­ing plu­gins in­volved quite a lot of setup in or­der to find and load the cor­rect plu­gin from the filesys­tem. Not any­more — load­ing a mod­el from a glTF file is now just a mat­ter of this:

PluginManager::Manager<Trade::AbstractImporter> manager;
std::unique_ptr<Trade::AbstractImporter> importer =
    manager.loadAndInstantiate("GltfImporter");

importer->openFile("cave.gltf");
Containers::Optional<Trade::MeshData3D> data =
    importer->mesh3D(importer->mesh3DForName("treasure-chest"));

And the work­flow is the same in­de­pend­ently on the plat­form — thanks to @Squareys the Vcp­kg pack­ages also re­ceived up­dates that make the plu­gin “just work” on Win­dows as well, in­clud­ing de­ploy­ment of all ne­ces­sary files.

There’s more — plu­gin ali­as pri­or­it­ies, plu­gin-spe­cif­ic con­fig­ur­a­tion and auto­ma­gic im­port of stat­ic plu­gins. And if you like glTF, there’s now a fresh Tiny­Glt­fIm­port­er plu­gin for im­port­ing this format. Fur­ther de­tails about all plu­gin us­ab­il­ity im­prove­ments are in a ded­ic­ated art­icle.

Pack­age up­dates

Thanks to @xyproto, the Arch­Linux [community] re­pos­it­ory now con­tains stable ver­sions of Cor­rade, Mag­num and Mag­num Plu­gins pack­ages, so if you are on Arch­Linux, you can just do this now:

sudo pacman -S magnum

The Homebrew pack­ages are now provid­ing stable ver­sions so you no longer need to in­stall --HEAD ver­sions:

brew install mosra/magnum/magnum

The Homebrew and Arch­Linux AUR pack­ages are already up­dated to 2018.04, the Arch­Linux [community] and Vcp­kg pack­ages are ex­pect­ing an up­date soon (see mi­crosoft/vcp­kg#3407). Be­sides these, Mag­num provides in-tree Debi­an/ Ubuntu and Gentoo pack­ages as usu­al, head over to the doc­u­ment­a­tion for de­tails.

More? Yes, there’s more!

Thanks to @Squareys (again!), we have a new fun ex­ample in­teg­rat­ing the Leap Mo­tion SDK. See its full source in the doc­u­ment­a­tion.

Leap Motion example screenshot
Leap Mo­tion
(In case you won­der how can @Squareys move his hands so fast, it’s be­cause he uses vim.)

An­droid sup­port got an over­haul, sup­port­ing the Gradle build and new Clang-based NDKs. There’s now a very de­tailed An­droid guide in the doc­u­ment­a­tion, ex­plain­ing all quirks and an­noy­ances you might en­counter. Oth­er plat­form-spe­cif­ic guides will get gradu­ally filled with use­ful in­form­a­tion re­gard­ing spe­cif­ic tar­gets.

The GL lib­rary has now an ini­tial sup­port for OpenGL ES 3.2 and OpenGL 4.6 and re­ceived vari­ous cla­ri­fic­a­tions and up­dates re­gard­ing float­ing-point formats on em­bed­ded sys­tems.

The Prim­it­ives namespace was sim­pli­fied to a bunch of free func­tions in­stead of the OOP-heavy in­ter­face be­fore. There are new cone, grid and 3D circle prim­it­ives and the doc­u­ment­a­tion now shows how every prim­it­ive looks:

Primitives namespace documentation

Among oth­er things the Oculus VR ex­ample got sim­pli­fied and there’s a second ex­per­i­ment­al ver­sion of the DartInteg­ra­tion lib­rary by @cost­ashatz.

Up­grad­ing from pre­vi­ous ver­sions

Be­cause the API changed quite heav­ily, up­grad­ing from pre­vi­ous ver­sions is not as smooth as usu­al. Nev­er­the­less, great care was taken to make this pro­cess as fric­tion­less as pos­sible.

First of all, if you are not on 2018.02 yet, please up­date to it first to re­duce the amount of un­wanted sur­prises. Next, in case you use Mag­num with the BUILD_DEPRECATED op­tion dis­abled (it’s usu­ally en­abled by de­fault), en­able it again — it will help with the up­grade a lot.

Up­date your cop­ies of FindCorrade.cmake, FindMagnum.cmake and oth­ers — in the ideal case just do­ing that should make your code com­pile and link again, al­though with a lot of de­prec­a­tion warn­ings. The de­prec­a­tion warn­ings in­clude help­ful mes­sages show­ing what to use in­stead, a high-level over­view of the changes needed is be­low:

CMake setup be­fore:

find_package(Magnum REQUIRED
    Sdl2Application)

add_exectutable(my-app main.cpp)
target_link_libraries(my-app PRIVATE
    Magnum::Application
    Magnum::Magnum)

With 2018.04:

find_package(Magnum REQUIRED
    GL
    Trade
    Sdl2Application)

add_exectutable(my-app main.cpp)
target_link_libraries(my-app PRIVATE
    Magnum::Application
    Magnum::GL
    Magnum::Trade
    Magnum::Magnum)

In­cludes be­fore:

#include <Magnum/ImageView.h>
#include <Magnum/PixelFormat.h>
#include <Magnum/Buffer.h>
#include <Magnum/Framebuffer.h>
#include <Magnum/Mesh.h>

With 2018.04:

#include <Magnum/ImageView.h>
#include <Magnum/PixelFormat.h>
#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/Framebuffer.h>
#include <Magnum/GL/Mesh.h>

Mesh setup be­fore:

Buffer vertices, indices;
vertices.setData(data,
    BufferUsage::StaticDraw);
indices.setData(indexData,
    BufferUsage::StaticDraw);

Mesh mesh{MeshPrimitive::Triangles};
mesh.addVertexBuffer(vertices, 0, ...)
    .setIndexBuffer(indices, 0,
        Mesh::IndexType::UnsignedShort);

With 2018.02:

GL::Buffer vertices;
vertices.setData(data,
    GL::BufferUsage::StaticDraw);
indices.setData(indexData,
    GL::BufferUsage::StaticDraw);

GL::Mesh mesh{MeshPrimitive::Triangles};
mesh.addVertexBuffer(vertices, 0, ...)
    .setIndexBuffer(indices, 0,
        MeshIndexType::UnsignedShort);

Tex­ture setup be­fore:

ImageView2D image{
    PixelFormat::Red, PixelType::UnsignedByte,
    size, data};

Texture2D t;
t.setMagnificationFilter(
    Sampler::Filter::Linear)
 .setMinificationFilter(
    Sampler::Filter::Linear)
 .setWrapping(Sampler::Wrapping::ClampToEdge)
 .setStorage(1, TextureFormat::R8, size)
 .setSubImage(0, {}, image);

With 2018.02:

ImageView2D image{
    PixelFormat::R8Unorm,
    size, data};

GL::Texture2D t;
t.setMagnificationFilter(
    SamplerFilter::Linear)
 .setMinificationFilter(
    SamplerFilter::Linear)
 .setWrapping(SamplerWrapping::ClampToEdge)
 .setStorage(1, GL::TextureFormat::R8, size)
 .setSubImage(0, {}, image);

The above snip­pets as­sume you’d want to use the gen­er­ic pixel format and sampler state enums as it’s less typ­ing, but you can use their GL-spe­cif­ic al­tern­at­ives as well. You also might not need to link to all the lib­rar­ies shown above if you don’t use any func­tion­al­ity from them.

Look in the changelog for a full over­view of all de­prec­ated fea­tures and also back­wards-in­com­pat­ible changes.

Com­plete changelog

There’s a lot of small changes that would be too bor­ing to men­tion in full here. As usu­al, you can find a com­plete list of changes since 2018.02 in the doc­u­ment­a­tion:

Spe­cial thanks

Again a lot of work in this re­lease is done thanks to ex­tern­al con­trib­ut­ors:

  • To­bi­as Stein (@Nussknack­er­XXL) — ini­tial im­ple­ment­a­tion of the Tiny­Glt­fIm­port­er plu­gin
  • Jonath­an Hale (@Squareys) — Oculus VR ex­ample sim­pli­fic­a­tion, con­tri­bu­tions to the tiny_gltf im­port­er plu­gin, Leap Mo­tion ex­ample, Vcp­kg pack­age main­ten­ance and much more
  • Kon­stanti­nos Chatzily­ger­oud­is (@cost­ashatz) — im­prove­ments to the DART in­teg­ra­tion lib­rary
  • Al­ex­an­der F Rød­seth (@xyproto) — Arch­Linux [community] pack­age main­ten­ance

Thanks a lot to every­one, not to for­get all people that re­por­ted is­sues or just wrote en­cour­aging mes­sages on the Git­ter chat. Cheers!