New pow­er-ef­fi­cient ap­pli­ca­tion im­ple­men­ta­tion for Em­scripten While all Mag­num ap­pli­ca­tions his­tor­i­cal­ly de­fault­ed to re­draw­ing and mak­ing the CPU busy on­ly when need­ed in or­der to save pow­er, this was not re­al­ly the case on the web. Con­trib­uted by @Squareys, there’s a new Plat­form::Em­scripte­nAp­pli­ca­tion that aims to be more ef­fi­cient and small­er to down­load. Be­sides that, the Em­scripten SDL “em­u­la­tion” has a lot of lim­i­ta­tions and hav­ing an im­ple­men­ta­tion based di­rect­ly off the HTM­L5 APIs al­lows us to be more flex­i­ble. Idle Sdl2Ap­pli­ca­tion Idle Em­scripte­nAp­pli­ca­tion The new im­ple­men­ta­tion was al­so used for an ex­per­i­ment in how far can Mag­num po­ten­tial­ly get with ex­e­cutable size op­ti­miza­tion. A few of those op­ti­miza­tions al­ready made it to 2019.10 and lots more is in the buf­fer for next re­leas­es — sub­scribe to mosra/mag­num#293 for up­dates. 52.1 kB 36.3 kB 35.7 kB 19.4 kB 14.7 kB 14.7 kB 14.7 kB 14.7 kB 14.7 kB 14.7 kB 226.3 kB 224.5 kB 224.5 kB 224.5 kB 83.6 kB 75.4 kB 69.3 kB 62.6 kB 56.3 kB 44.0 kB 0 50 100 150 200 250 kB Initial state Enabling minimal runtime Additional slimming flags Disabling filesystem Chopping off all C++ stream usage Enabling CORRADE_NO_ASSERT Removing a single use of std::sort() Removing one std::unordered_map Using emmalloc instead of dlmalloc Removing all printf() usage Download size (*.js, *.wasm) Read more: New Ap­pli­ca­tion im­ple­men­ta­tion for Em­scripten »

Im­age API im­prove­ments With Mu­ta­bleIm­ageView2D and friends and new over­loads to GL::Ab­stract­Frame­buf­fer::read(), GL::Tex­ture::im­age() etc. it’s now pos­si­ble to read GPU im­ages in­to ex­ist­ing mem­o­ry, with­out un­want­ed large mem­o­ry al­lo­ca­tions hap­pen­ing in the back­ground. These new APIs are al­so ex­posed to Python, al­low­ing for ef­fi­cient trans­fer of ren­dered im­ages di­rect­ly in­to a mem­o­ry buf­fer man­aged by a ma­chine learn­ing frame­work, for ex­am­ple. Back in 2018.04, Mag­num gained back­end-in­de­pen­dent pix­el for­mats, how­ev­er the Com­pressed­Pix­elFor­mat enum was quite ne­glect­ed un­til now, sup­port­ing just ba­sic S3TC. Now it sup­ports all wide­ly-used com­pres­sion for­mats — sRGB S3TC vari­ants, one/two-chan­nel BC4 and BC5 for­mats, BC6h and BC7, ETC2 and EAC for­mats for mo­bile plat­forms, ASTC (in­clud­ing 3D and HDR) and PVRTC. On the GL side, GL::Com­pressed­Pix­elFor­mat learned PVRTC for­mats as well, ex­posed the (3D) ASTC for­mats for We­bGL, and same was done for the Vk::vk­For­mat() con­ver­sion util­i­ty. Be­sides GL and Vulkan, the Pix­elFor­mat / Com­pressed­Pix­elFor­mat enum doc­u­men­ta­tion now lists al­so cor­re­spond­ing D3D and Met­al val­ues to make it eas­i­er for peo­ple us­ing (or com­ing from) these back­ends. These im­prove­ments are the ini­tial batch of new fea­tures be­ing added, with more fol­low­ing next — im­proved DDS sup­port (see mosra/mag­num-plug­ins#67), a KTX2 im­porter or, for ex­am­ple, mip lev­el se­lec­tion (mosra/mag­num#369).

Ba­sis Uni­ver­sal tex­ture com­pres­sion The main rea­son why all the above-list­ed com­pres­sion for­mats were added is Ba­sis Uni­ver­sal. It’s a suc­ces­sor to Crunch, open-sourced a few months ago thanks to fund­ing from Google. What makes it so rev­o­lu­tion­al is best ex­plained by the fol­low­ing plot. I took the cov­er.jpg used on top of this ar­ti­cle and con­vert­ed it to cov­er.ba­sis and a bunch of raw block com­pres­sion for­mats for com­par­i­son: 215.0 kB 1296.0 kB 269.5 kB 154.9 kB 0.0 kB 0.0 kB 242.1 kB 0.0 kB 0.0 kB 0.0 kB 508.1 kB 0.0 kB 4969.0 kB 0.0 kB 276.3 kB 1141.1 kB 0 1000 2000 3000 4000 5000 kB JPEG -> RGBA8 Uncompressed BC3 DDS -> BC3 Compressed BC3 + ETC2 + PVRTC -> BC3 Basis Universal -> BC3 File size / memory use Be­fore Ba­sis, you had ba­si­cal­ly two ways how to op­ti­mize your as­set size: Ei­ther op­ti­mize stor­age size by us­ing lossy com­pres­sion (such as JPEG), but then hav­ing to ful­ly un­com­press to RG­BA8. With the 1536×864 cov­er im­age it’s a ~200 kB im­age in­flat­ed to over 5 MB of RG­BA da­ta.

Or op­ti­mize GPU mem­o­ry us­age by us­ing var­i­ous block com­pres­sion for­mats (such as BC3 / DX­T5), which is on­ly 1.3 MB of da­ta in mem­o­ry; and with a loss­less com­pres­sion on top you’ll get down to a 270 kB file. How­ev­er, es­pe­cial­ly on mo­bile de­vices, each GPU ven­dor sup­ports a dif­fer­ent for­mat so you need to ship at least a BCn, ETC and PVRTC vari­ant. With Ba­sis Uni­ver­sal, you get the best of both worlds — da­ta is in­ter­nal­ly stored in a sub­set of the ETC1 block com­pres­sion for­mat with ad­di­tion­al com­pres­sion on top, mak­ing it small­er than an equiv­a­lent JPEG, and then you can transcode that sin­gle file to BCn, ETC2, ASTC or PVRTC de­pend­ing on what the GPU needs. Thanks to work done by @Squareys, Mag­num sup­ports both im­port­ing (and transcod­ing to a de­sired GPU for­mat) via the Ba­sisIm­porter plug­in as well as en­cod­ing im­ages in­to the Ba­sis Uni­ver­sal for­mat us­ing the Ba­sisIm­age­Con­vert­er. Com­pared to the of­fi­cial basisu tool, which works on­ly with PNGs, the mag­num-im­age­con­vert­er util­i­ty sup­ports any for­mat that Mag­num can im­port: magnum-imageconverter image.jpg image.basis Of course, all op­tions sup­port­ed by basisu are ex­posed to the plug­in con­fig­u­ra­tion as well: magnum-imageconverter image.jpg --converter BasisImageConverter \ -c flip_y = false,threads = 8 image.basis The TinyGlt­fIm­porter sup­ports Ba­sis files through the un­of­fi­cial GOOGLE_texture_basis ex­ten­sion. There are still some fea­tures we’re wait­ing on to get merged in or­der to have a full sup­port. One of them is an abil­i­ty to Y-flip im­ages dur­ing transcode (in­stead of on­ly in the en­coder, Bi­no­mi­al­LLC/ba­sis_u­ni­ver­sal#79), an­oth­er are buildsys­tem im­prove­ments (Bi­no­mi­al­LLC/ba­sis_u­ni­ver­sal#13) — right now, the soft­ware can’t be built as a li­brary on its own and thus is im­pos­si­ble to pack­age / dis­trib­ute with­out re­quir­ing each project to bun­dle it. Un­til that’s re­solved, Ba­sis won’t be en­abled in any Mag­num pack­ages. The on­ly ex­cep­tion is Vcp­kg, where a Ba­sis fork, based off the above PR, is used.

Mag­num Play­er im­prove­ments The Mag­num Play­er util­i­ty re­ceived quite a few new fea­tures. It can now au­to­mat­i­cal­ly gen­er­ate smooth nor­mals for mod­els that don’t have them and you can in­spect mesh topol­o­gy by se­lect­ing it us­ing a right-click. Apart from mesh­es, the play­er can now al­so open im­ages of all types that Mag­num can im­port. This in­cludes the above-men­tioned Ba­sis Uni­ver­sal — and the web ver­sion knows those, too, and transcodes to BCn, ETC, PVRTC, ASTC or plain RG­BA de­pend­ing on what your brows­er sup­ports. Mag­num Web Play­er drag&drop any glTF, JPEG, PNG or Ba­sis file

Buildsys­tem us­abil­i­ty im­prove­ments The 2019.10 re­lease irons out the re­main­ing pain points in us­ing Mag­num li­braries as CMake sub­pro­jects. All bi­na­ries are now put in­to a com­mon di­rec­to­ry in­side the build dir, which means no has­sle with DLL paths on Win­dows any­more — and to help the com­mon use cas­es even fur­ther, SDL and GLFW DLLs are au­to­mat­i­cal­ly copied there as well. Plug­in us­age with CMake sub­pro­jects is sig­nif­i­cant­ly im­proved too. Dy­nam­ic plug­in bi­na­ries are put in a cen­tral place in the build di­rec­to­ry and the plug­in man­agers now look for them rel­a­tive­ly to lo­ca­tion of giv­en plug­in in­ter­face li­brary, re­mov­ing the need to in­stall ev­ery­thing first. set ( WITH_TINYGLTFIMPORTER ON ) set ( WITH_STBIMAGEIMPORTER ON ) add_subdirectory ( magnum-plugins ) Note that the above bumped the min­i­mal CMake ver­sion re­quire­ment from 3.1 to 3.4, al­though we don’t ex­pect any is­sues as the ver­sions cur­rent­ly in wide­spread use is 3.5. In any case, you can al­ways down­load a pre­built ver­sion for your plat­form. Thanks to ex­ten­sive feed­back from @alan­jfs, the Get­ting Start­ed Guide got rewrit­ten and is now eas­i­er to fol­low by first-time users on Win­dows, not re­quir­ing any­body to fid­dle with %PATH% or in­stalling things to ran­dom places any­more.

Win­dows-spe­cif­ic good­ies Com­pared to 2019.01, there’s an of­fi­cial sup­port for MSVC 2019. The com­pil­er still needs a few work­arounds com­pared to GCC / Clang, but it’s rel­a­tive­ly mi­nor things that should not af­fect us­abil­i­ty. Ex­trap­o­lat­ing fur­ther, we ex­pect the next ver­sion of MSVC to be ful­ly con­form­ing, and thus not need­ing any com­pil­er-spe­cif­ic han­dling. We’re com­mit­ed to ful­ly sup­port­ing all pre­vi­ous ver­sions back to MSVC 2015 for the fore­see­able fu­ture. Cor­rade::Main is a new li­brary that, on Win­dows, adds a shim around your main() func­tion, sets up UTF-8 ter­mi­nal en­cod­ing, en­ables AN­SI col­or es­cape codes and con­verts Uni­code com­mand-line ar­gu­ments to UTF-8 as well, en­abling you to use the same stan­dards-con­form­ing code on all plat­forms. Ad­di­tion­al­ly, it’ll al­so al­low you to hide the ter­mi­nal win­dow lurk­ing in back­ground with­out forc­ing you to im­ple­ment WinMain() . With CMake, this is all you need to do: find_package ( Corrade REQUIRED Main ) add_executable ( my-application WIN32 main.cpp ) # WIN32 turns it into a GUI app target_link_libraries ( my-application PRIVATE Corrade::Main ) An­oth­er thing worth men­tion­ing is that Plat­form::Sdl2Ap­pli­ca­tion and Plat­form::GlfwAp­pli­ca­tion now have ba­sic DPI aware­ness on Win­dows, catch­ing up with oth­er plat­forms.

OpenGL-re­lat­ed im­prove­ments Com­pared to oth­er parts of the li­brary, the OpenGL back­end was kept on the back­burn­er in the last few re­leas­es, not re­ceiv­ing any huge new fea­tures. For 2019.10, it got cleaned up from the old EX­T_­di­rec­t_s­tate_ac­cess ex­ten­sion, keep­ing just the new­er and bet­ter-de­signed AR­B_­di­rec­t_s­tate_ac­cess sim­ply be­cause all cur­rent driv­ers im­ple­ment­ing the EXT vari­ant sup­port the ARB one as well. On the oth­er hand, there’s a ton of driv­er bugs re­lat­ed to the new ex­ten­sion, es­pe­cial­ly on In­tel and AMD driv­ers on Win­dows. To counter that, Mag­num re­ceived about a dozen new driv­er work­arounds to en­sure it be­haves as ex­pect­ed even when the driv­er doesn’t. One of the es­sen­tial new things is the --magnum-gpu-validation com­mand-line op­tion. In­stead of hav­ing to cre­ate a de­bug con­text man­u­al­ly and then fid­dle with GL::De­bu­gOut­put call­backs, this op­tion will do this for you. Very use­ful for quick de­bug­ging of the dread­ed “why it doesn’t ren­der” is­sues. For win­dow­less EGL con­texts on Lin­ux, there’s al­so a new --magnum-device op­tion giv­ing you an abil­i­ty to switch be­tween dif­fer­ent de­vices. It’s known to work with bi­na­ry NVidia driv­ers and Mesa since 19.2 — there it usu­al­ly al­lows you to choose be­tween an in­te­grat­ed GPU, a ded­i­cat­ed card or a soft­ware ren­der­er. The new op­tion names are de­lib­er­ate­ly cho­sen to not have any re­la­tion to the OpenGL API, since the plan is to have the same op­tions af­fect the up­com­ing Vulkan back­end as well.

Test­Suite im­prove­ments, shad­er test­ing If you’re not yet us­ing Test­Suite for tests in your Mag­num-based project (well, or any oth­er), con­sid­er giv­ing it a try. For this re­lease, con­tin­ued ef­fort was put on ren­der out­put test­ing — De­bug­Tools::Com­pareIm­age re­ceived an abil­i­ty to save a di­ag­nos­tic file in case of a com­par­i­son fail­ure, and can com­pare against an ar­bi­trary pix­el view in ad­di­tion to files and Im­ageView in­stances. Tests can be now al­so run with ver­bose out­put, show­ing de­tailed in­fo even in case the com­par­i­son pass­es: ./ShadersPhongGLTest -v --only 38 Starting Magnum::Shaders::Test::PhongGLTest with 1 test cases... INFO [ 38 ] renderShininess( 80 ) at src/Magnum/Shaders/Test/PhongGLTest.cpp on line 966 Images Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()) and Utility::Directory::join({_testDir, "PhongTestFiles", data.expected}) have deltas 1.66667/0.0159375 below threshold 12/0.043. Delta image: | | | | | | | :: : | | Z: ::: | | :Z Z | | :: Z : :Z: | | Z :Z: Z: : | | :: + : : : | | : : : : :: : | | : Z | | +:: : : Z : | | : ++:: ZZ : +Z:: : :: | | +ZZ: +: : : Z: | Top 10 out of 189 pixels above max/mean threshold: [19,23] #f96161, expected #fa6363 (Δ = 1.66667 ) [20,23] #ffa9a9, expected #ffabab (Δ = 1.33333 ) [21,22] #ffd9d9, expected #ffdbdb (Δ = 1.33333 ) [20,22] #ff9090, expected #ff9292 (Δ = 1.33333 ) [13,62] #250707, expected #260808 (Δ = 1 ) [28,56] #2a0808, expected #2b0909 (Δ = 1 ) [11,56] #4d0f0f, expected #4e1010 (Δ = 1 ) [31,54] #2a0808, expected #2b0909 (Δ = 1 ) [19,54] #490f0f, expected #480e0e (Δ = 1 ) [ 8,51] #6b1515, expected #6c1616 (Δ = 1 ) Finished Magnum::Shaders::Test::PhongGLTest with 0 errors out of 2 checks. With these im­prove­ments in place, the whole Shaders li­brary has tests for ren­der­ing out­put. So far, thanks to these, we ironed out a bunch of bugs in dusty cor­ner cas­es, but that’s not all — it makes fur­ther mod­i­fi­ca­tions, op­ti­miza­tions and im­prove­ments eas­i­er to make as re­gres­sions will now be caught through au­to­mat­ic test­ing.

Re­duced over­head, guar­an­teed thread safe­ty and unique­ness of glob­als While glob­als are of­ten a source of im­mense pain, some­times hav­ing a state glob­al is the most prag­mat­ic de­ci­sion of all. Mag­num cur­rent­ly us­es glob­als in these few places: Sin­gle­tons are bad!! — ev­ery­one Util­i­ty::De­bug scoped out­put re­di­rect­ion and col­or­ing Each com­piled-in Util­i­ty::Re­source re­source reg­is­ters it­self in­to a glob­al stor­age Sim­i­lar­ly, stat­ic plug­ins reg­is­ter them­selves in­to Plug­in­Man­ag­er And be­cause OpenGL (and then Ope­nAL, which is mod­elled af­ter it) has a glob­al con­text, it makes sense to have cur­rent GL::Con­text / Au­dio::Con­text glob­al­ly ac­ces­si­ble as well One oth­er us­age of glob­als was in Re­source­M­an­ag­er (and tran­si­tive­ly in De­bug­Tools::Ob­jec­tRen­der­er as well), but those APIs are now dep­re­cat­ed in fa­vor of ex­plic­it­ly passed ref­er­ences. And, for the up­com­ing Vulkan back­end, there’s no plan to have a GL-like “glob­al con­text” at all. For this re­lease, all glob­al state was rewrit­ten to be com­plete­ly al­lo­ca­tion-free (reg­is­tra­tion of re­sources and stat­ic plug­ins is now just build­ing an in­tru­sive linked list), which means there’s no need to run any glob­al de­struc­tors for these. More­over, while al­ready very light­weight, the au­to­mat­ic reg­is­tra­tion can be com­plete­ly opt­ed out of, al­low­ing you to get rid of glob­al con­struc­tors as well. All glob­al state that’s read-write is now made thread_local , mean­ing ev­ery thread will have its own copy of the glob­al da­ta. This makes more sense than hav­ing the glob­al state ac­cess guard­ed by a lock. Be­sides be­ing faster, you might want to re­di­rect your log out­put to a file in one thread but not in the oth­er. Apart from these, Mag­num doesn’t do any­thing about thread­ing on its own — if your app needs to share da­ta across threads, you’re ful­ly re­spon­si­ble for guard­ing against da­ta races. Thread-lo­cal vari­ables of course come with some small over­head, and if you don’t need that, you can turn it off via the COR­RADE_BUILD_­MUL­TI­THREAD­ED op­tion. With the in­tro­duc­tion of Python bind­ings, glob­als posed an­oth­er prob­lem — if Mag­num is built stat­i­cal­ly and then linked in­to two dis­tinct Python mod­ules, the glob­als get du­pli­cat­ed, each mod­ule hav­ing its own copy. On Unix sys­tems this was eas­i­ly solved by mark­ing the few glob­als ex­port­ed weak sym­bols, telling the dy­nam­ic link­er to al­ways pick on­ly one of them. On Win­dows there’s no no­tion of a weak link­ing and ad­di­tion­al­ly __declspec(dllexport) at­tributes can’t be thread_local , so this got solved by a brown mag­ic in­volv­ing Get­P­ro­cAd­dress().

Au­dio-re­lat­ed ad­di­tions @williamjcm, who can be now con­sid­ered our au­dio ex­pert thanks to all his con­tri­bu­tions, im­ple­ment­ed loop points in Au­dio::Buf­fer, buf­fer queu­ing and an MP3 im­porter in DrM­p3Au­dioIm­porter — all MP3-re­lat­ed patents fi­nal­ly ex­pired back in 2017, so there’s shouldn’t be any le­gal pres­sure against us­ing MP3 files for your au­dio tracks any­more. Fol­low­ing GL::Con­text, the Au­dio::Con­text now un­der­stands the --magnum-disable-extensions and --magnum-log op­tions as well.