JPEG XL

Info

rules 58
github 38692
reddit 688

JPEG XL

jxl-rs 0
tools 4270
website 1813
adoption 22069
image-compression-forum 0
glitch-art 1071

General chat

welcome 3957
introduce-yourself 294
color 1687
photography 3532
other-codecs 25116
on-topic 26652
off-topic 23987

Voice Channels

General 2578

Archived

bot-spam 4577

At this time I have 380 images from

monad
RaveSteel At this time I have 380 images from maybe 10 games on my side, nonidea from how many games the files you sent me previously are from. Unless you have +10000 images I will take all you can send me. I aim at a very large datset to be representative of different types of graphical styles/games this time.
2024-09-14 01:54:10
2024-09-14 01:54:16
<@167354761270525953> round 2 <https://mega.nz/file/adtFwJDI#iIQY0GmfskdnBc_nUJlK1fRDdoO-m6hG6hnNhCwW9vI>
RaveSteel
2024-09-14 01:54:28
Nice!
2024-09-14 02:18:35
Waiter, Waiter! More screnshots please
2024-09-14 02:18:47
https://tenor.com/view/waiter-cat-more-kibble-please-gif-13625215935695637260
2024-09-14 02:19:17
I am at 560 screenshots now
2024-09-15 12:36:13
I am at 1300 screenshots from over 80 games btw and currently testing the encodes
monad
2024-09-15 12:36:35
Sweet.
RaveSteel
2024-09-15 12:36:48
JXL was so much faster that I had to switch to writing to /tmp
2024-09-15 12:36:58
This made no difference at all for AVIF lol
2024-09-15 01:05:53
Here is the current draft, feel free to criticise
2024-09-15 01:06:31
I will post the encoding results in <#803645746661425173> later
monad
2024-09-15 01:32:25
As you are including png decode in your measurement, it is not exactly representative of the use case, but this should just mean the fastest jxl has a bit better ratio. Personally, I would be curious about at least proportion of HDR content, even proportion of different art styles, but I would also suggest it doesn't have to be totally scientific if it can convince Valve to run their own testing. They would be best informed about the requirements of the product.
2024-09-15 06:32:34
I'm going to guess a big reason why JXL wouldn't be a priority for Valve is JXL is much harder to share on the web.
RaveSteel
monad As you are including png decode in your measurement, it is not exactly representative of the use case, but this should just mean the fastest jxl has a bit better ratio. Personally, I would be curious about at least proportion of HDR content, even proportion of different art styles, but I would also suggest it doesn't have to be totally scientific if it can convince Valve to run their own testing. They would be best informed about the requirements of the product.
2024-09-15 10:35:34
> png decode I only measured the encoding from PNG to AVIF/JXL, or was this just a typo? I could split up the images into photographic, pixel art, HDR, but I don't know if that is really needed seeing as the dataset itself has screenshots from so many games that it should be representative. I could add some more pixel art screenshots, but even without them the results speak for themselves I think
monad I'm going to guess a big reason why JXL wouldn't be a priority for Valve is JXL is much harder to share on the web.
2024-09-15 10:38:08
That is true, but this is one of the main reasons I am doing this: to gain more adoption for JXL (besides the general advantages). And another point: the current AVIF screenshots gamescope creates cannot be shared over discord either, the main platform of almost any person playing games
monad
2024-09-15 11:12:18
Normally gamescope would take an image buffer directly from memory and encode it to the target storage format, right? In this test, a png file needs to be decoded first to a buffer in memory, then that is encoded to the target format. But again, If this just gets the format in front of Valve, maybe they will test it properly in their architecture.
RaveSteel
2024-09-15 11:13:04
Agreed, I have cloned gamescope to try and get it to encode JXL directly, but I am not a programmer
2024-09-15 11:14:37
But still, even with that, it is clear that JXL is much faster than encoding AVIF
monad
2024-09-15 11:16:58
Yeah, question is always is it _faster enough_, _denser enough_ ... but I think your presentation is pretty good, you did things much better than most would.
RaveSteel
2024-09-15 11:17:14
Thanks
monad Yeah, question is always is it _faster enough_, _denser enough_ ... but I think your presentation is pretty good, you did things much better than most would.
2024-09-15 11:17:40
That is the question that remains to be answered by Valve indeed
2024-09-15 11:17:47
But we'll see
2024-09-15 11:21:17
Also I would count it as a win if the default remains AVIF but an option to ouput JXL is added
monad
2024-09-15 11:23:00
Yes, that would be the best outcome actually, until JXL has support in Chromium.
AccessViolation_
2024-09-15 03:34:17
Do you have any direct frame buffer captures from console emulators? I imagine screenshots from NES games for example would compress greatly if it's able to utilize the "patches" feature, because those consoles games themselves make heavy use of repeating sprites from a tiny sprite sheet
2024-09-15 03:35:32
This is assuming there are JXL encoders capable of utilizing that, I don't know whether there are actually
2024-09-15 03:37:48
Oh this is about Steam games, I see... Well, many modern games still use sprites in this way to achieve a retro look
RaveSteel
AccessViolation_ Do you have any direct frame buffer captures from console emulators? I imagine screenshots from NES games for example would compress greatly if it's able to utilize the "patches" feature, because those consoles games themselves make heavy use of repeating sprites from a tiny sprite sheet
2024-09-15 03:45:33
I would happily incoporate this into another comparison, but I don't know of any emulator that supports JXL
AccessViolation_ Oh this is about Steam games, I see... Well, many modern games still use sprites in this way to achieve a retro look
2024-09-15 03:46:16
I have included some "pixel art" games into the dataset, these images often get blown up massively if encoded to AVIF, even compared to PNG
AccessViolation_
2024-09-15 03:58:53
Ah nice, I'm guessing that's because both PNG and JXL are utilizing palette compression then, as those pixel art games will often have many in-game pixels that are the same color and in the image can be represented as few-bit indices into a color palette. If `JXL_ENC_FRAME_SETTING_PATCHES` is set to `1`, JXL files should be even smaller as it will create not just palettes of pixels but pallets of groups of pixels as well. For example it would only store one of these tiles that I have highlighted with white lines, and the rest of the tiles would reuse the stored one
2024-09-15 04:02:39
But the patches need to be an exact match, so as soon as the game does funky upscaling with some sort of interpolation that makes that not the case it won't do it at all (in lossless mode)
RaveSteel
2024-09-15 04:03:20
luckily most pixel art games use nearest upscaling, so it still looks proper
AccessViolation_
2024-09-15 04:04:30
Ah, that's good
RaveSteel
2024-09-15 04:05:31
Take a look at this
2024-09-15 04:05:56
Same data, but the avif is almost 10(!) times larger
AccessViolation_
2024-09-15 04:07:19
Quite the difference indeed
2024-09-15 04:14:40
Unrelated, but now I'm thinking that it might be possible to create a custom encoder specifically for an NES emulator that doesn't even use a frame buffer as the screenshot source, but just directly builds a JXL from sprite data and other relevant information loaded in the emulator's memory. I wonder how small you could get the screenshots
RaveSteel
2024-09-15 04:17:26
This possibly(?) sounds like something an encoder could be purpose built for. But I wouldn't know, i'm not a programmer
2024-09-15 04:20:45
https://github.com/ValveSoftware/gamescope/issues/1525
2024-09-15 04:20:58
Here the feature request on gamescope's github
jonnyawsom3
2024-09-15 06:34:51
If it were possible I might've included memory usage too
2024-09-15 06:35:02
(I only just found the thread)
RaveSteel
If it were possible I might've included memory usage too
2024-09-15 06:36:27
Since I ran 16 encodes in parallel CPU usage was more the issue, but both encodes ran at the maximum possible speed. It took very little RAM, even with 16 parallel encodes
jonnyawsom3
AccessViolation_ This is assuming there are JXL encoders capable of utilizing that, I don't know whether there are actually
2024-09-15 06:37:43
Currently the patch detection in libjxl is mostly focused on text. Monad made a version that focuses on tiles such as in games, and it does add improvement, but the ideal solution would be directly referencing the sprite sheet in an emulator and essentially rebuilding the scene inside the JXL file. You can also do 2x, 4x and 8x nearest neighbour upsampling in JXL files, so it could be encoded at native resolution and just scaled in metadata instead
AccessViolation_
2024-09-15 06:38:24
Hehe, that's exactly what I came up with a bit further down
jonnyawsom3
RaveSteel Since I ran 16 encodes in parallel CPU usage was more the issue, but both encodes ran at the maximum possible speed. It took very little RAM, even with 16 parallel encodes
2024-09-15 06:38:26
Actually yeah, fair point. I immedeately forgot it was e1, although I know I'd have OOM'd with 16 4K files at once
AccessViolation_ Hehe, that's exactly what I came up with a bit further down
2024-09-15 06:39:32
Natually in the 2 messages I skipped before getting distracted by the github issue Dx
AccessViolation_
2024-09-15 06:41:18
Does JXL allow you to transform these sprite like flip them? I know the NES has a handful of sprite transformations like these, and I'm wondering how many of them are natively available in JPEG XL. It's not a problem if there's some things JXL can't do, you'd just need to store some redundant data
2024-09-15 06:42:35
It's not something that's likely to appear in real world images *except specifically* screenshots of emulators that can do sprite transformations, so I don't expect much to be possible there
jonnyawsom3
2024-09-15 06:46:09
> We considered it `Rotation` (also flips, etc) but decided to keep it simple since it adds a bit of implementation complications to do patch blitting in arbitrary orientations and it is already hard enough to detect patches without considering orientations...
AccessViolation_
2024-09-15 06:54:20
Back on topic - does this version of the encoder used here in lossless mode suffer from some of the file size regressions that have been there since 0.8.X in lossy mode?
2024-09-15 06:55:24
If so, it might be more representative of JPEG XL as a format to go with the decoder on 0.8 as a bug is causing larger file sizes and worse quality in recent versions
RaveSteel
AccessViolation_ If so, it might be more representative of JPEG XL as a format to go with the decoder on 0.8 as a bug is causing larger file sizes and worse quality in recent versions
2024-09-15 06:57:48
While not comparing across mutiple releases, this graph showed slight improvement since 0.10.1 https://discord.com/channels/794206087879852103/803645746661425173/1284340338143526997
AccessViolation_
2024-09-15 07:01:28
Oh ok. Yeah I read the issue linked there before ([ Regression in lossy nonphotographic image quality #3530 ](<https://github.com/libjxl/libjxl/issues/3530#issuecomment-2132164263>)) which was about versions up to 0.10 being worse than 0.8 but I guess 0.11 resolved that then, nice (but also that was lossy, idk if lossless was affected)
2024-09-15 07:03:05
How do we expect Valve to react to this feature request?
2024-09-15 07:04:37
I'm guessing at best they'd want JXL support in embedded chromium first to actually be able to display it
RaveSteel
2024-09-15 07:07:43
If they chose to implement it I'd hope that that an option to chose JXL as an output format for screenshots would be added. AVIF will very likely remain the default for as long as chromium does not support JXL.
monad
AccessViolation_ Ah nice, I'm guessing that's because both PNG and JXL are utilizing palette compression then, as those pixel art games will often have many in-game pixels that are the same color and in the image can be represented as few-bit indices into a color palette. If `JXL_ENC_FRAME_SETTING_PATCHES` is set to `1`, JXL files should be even smaller as it will create not just palettes of pixels but pallets of groups of pixels as well. For example it would only store one of these tiles that I have highlighted with white lines, and the rest of the tiles would reuse the stored one
2024-09-16 12:10:41
Current libjxl will not detect patches along those white lines. It may detect patches for the tiny decorations on the floor, which would likely make the file larger than without in this case. libjxl often makes bad decisions on non-text content.
2024-09-16 12:26:30
In any case, it does not try to do patch detection at all when fast speeds are required.
jonnyawsom3
2024-09-27 02:44:35
I only just realised, but you did the encodes running 16 jobs at once, right? Wouldn't that have messed with the multithreading of the encodes?
2024-09-27 02:47:26
Was wondering why the 4k set was so much slower than the 1080p, since usually larger images scale near-linearly to cores until a certain extent. Took 10x as long for 4x the pixels
RaveSteel
I only just realised, but you did the encodes running 16 jobs at once, right? Wouldn't that have messed with the multithreading of the encodes?
2024-09-27 06:48:23
It possibly has, the best way to compare would probably have been to encode one image after the other and compare the encoding times for each image. But even though the 4k encode was *slightly* slower than it would have been (~2 seconds * 2 because of dataset size and * 4 because of pixel amount equals to rougly 16 seconds compared to the 22 seconds real "Encode to RAM" encoding time) it still showed the massive speed increase that is possible with adding support for JXL as an alternative to AVIF
username
2024-09-29 06:26:53
wouldn't a benefit of JXL compared to AVIF for game screenshots be that JXL can actually fully store 16-bit data while AVIF only goes up to 12-bit. the reason I bring this up is because (at least with DirectX and Windows) games can only really output as 8-bit, 10-bit, and 16-bit (ignoring older games and APIs which can get below 8-bit)
2024-09-29 06:27:59
only really relevant for native HDR games mostly
2024-09-29 06:40:47
https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
2024-09-29 09:55:15
<@167354761270525953> <@238552565619359744> apologies if the pings are unnecessary but I have no clue if Discord properly notifies people of new messages in threads
RaveSteel
2024-09-29 09:56:10
No worries, I have seen your message. I wanted to answer when I am not on mobile
jonnyawsom3
2024-09-29 09:58:11
Oh I saw it, but with no HDR display I've got limited experience with it. The closest I can get is taking NVIDIA Ansel photos that output float jxr files with imaginary values
2024-09-29 10:02:08
I don't think I know of any games that output 16bit, but that could just be a limitation of whatever library they're using to save the image
username
2024-09-29 10:09:42
I know that I can get this game to output as 8-bit or 10-bit or 16-bit by changing around some stuff in the dgVoodoo2 config and can confirm it provides a noticeable bending reduction when I set the internal color formats to a higher bit depth and display it as 10-bit on my 10-bit monitor with the right presentation mode https://store.steampowered.com/app/1304510/SCP_NineTailed_Fox/
2024-09-29 10:10:25
the bending reduction is really noticeable for all the dark areas
2024-09-29 10:11:28
if uh anyone has the proper software and know how for capturing high bit depth screenshots then this might be a good game to test with
jonnyawsom3
2024-09-29 10:11:40
<:banding:804346788982030337>
RaveSteel
username only really relevant for native HDR games mostly
2024-09-29 12:20:08
This is currently only a theoretical advantage, because gamescope will always output a 10 bit AVIF, even though AVIF does support 12 bit indeed, as you mentioned. I have also found no communication from Valve concerning their choice of AVIF. I strongly assume it is because a speed 10 AVIF is often smaller than a barely compressed PNG. This is pure speculation on my part though, I have not tested whether that is actually the case. If anyone wants to benchmark encoding between PNG and AVIF, please do share it. Mayb I'll get around to testing it, but I am much more interested in a possible inclusion of JXL into gamescope.
username if uh anyone has the proper software and know how for capturing high bit depth screenshots then this might be a good game to test with
2024-09-29 12:21:18
The people over at the "HDR Den" discord seem to often use SKIV, maybe have a look at that. I haven't used it myself and there is no linux release for it, so no idea as to how it works
AccessViolation_
2024-10-07 04:45:17
I found a game that allowed me to choose between JPEG and PNG for the in-game camera mode, which gives me the normal "screenshot saved" popup from Steam. I guess games are allowed to hook into the screenshot system. Also I found that screenshots taken with the Steam Deck in the Steam UI are JPEG, and I also found some Steam discussions about screenshots being in JPEG by default. Any chance you could clarify in what situation Steam will use lossless AVIF? I don't quite understand (RE: this issue: [ [Feature request] Add support for JPEG XL screenshots due to greater speed and compression #1525](<https://github.com/ValveSoftware/gamescope/issues/1525>)) <@167354761270525953>
username
AccessViolation_ I found a game that allowed me to choose between JPEG and PNG for the in-game camera mode, which gives me the normal "screenshot saved" popup from Steam. I guess games are allowed to hook into the screenshot system. Also I found that screenshots taken with the Steam Deck in the Steam UI are JPEG, and I also found some Steam discussions about screenshots being in JPEG by default. Any chance you could clarify in what situation Steam will use lossless AVIF? I don't quite understand (RE: this issue: [ [Feature request] Add support for JPEG XL screenshots due to greater speed and compression #1525](<https://github.com/ValveSoftware/gamescope/issues/1525>)) <@167354761270525953>
2024-10-07 04:48:55
there is a setting in Steam that makes it so when you take a screenshot it will save both a JPEG and PNG and the PNGs have their own dedicated folder, the option for AVIF just makes it be used in HDR games instead of tone-mapped PNGs
AccessViolation_
2024-10-07 04:49:45
Ohh I see, thanks
RaveSteel
username there is a setting in Steam that makes it so when you take a screenshot it will save both a JPEG and PNG and the PNGs have their own dedicated folder, the option for AVIF just makes it be used in HDR games instead of tone-mapped PNGs
2024-10-07 05:02:14
Do note that I could not get the steam deck to save even HDR games as AVIF on the steamdeck. I tested Doom Eternal, but all screenshots were PNG. Just to be sure I even checked /tmp, but no dice.
AccessViolation_
2024-10-07 05:03:01
Does the Deck even support HDR?
RaveSteel
2024-10-07 05:03:07
<@384009621519597581> gamescope on the desktop will always save screenshots as AVIF if it is executed via `gamescope %command%` in game launhc settings
AccessViolation_ Does the Deck even support HDR?
2024-10-07 05:03:10
Yes
2024-10-07 05:03:23
It even has a dedicated icon for showing that it is displaying HDR
2024-10-07 05:03:33
Only for the steam dekc OLED though
AccessViolation_
2024-10-07 05:03:52
Ah
RaveSteel
2024-10-07 05:04:55
To be aboslutely sure that no AVIF HDR screenshots were recorded i even attached a usb keyboard and pressed super+S, the screenshot keykombo, but this had no efect either
jonnyawsom3
AccessViolation_ I found a game that allowed me to choose between JPEG and PNG for the in-game camera mode, which gives me the normal "screenshot saved" popup from Steam. I guess games are allowed to hook into the screenshot system. Also I found that screenshots taken with the Steam Deck in the Steam UI are JPEG, and I also found some Steam discussions about screenshots being in JPEG by default. Any chance you could clarify in what situation Steam will use lossless AVIF? I don't quite understand (RE: this issue: [ [Feature request] Add support for JPEG XL screenshots due to greater speed and compression #1525](<https://github.com/ValveSoftware/gamescope/issues/1525>)) <@167354761270525953>
2024-10-07 05:12:21
Yeah, you can do quite a bit with the hook https://github.com/BOLL7708/SuperScreenShotterVR
AccessViolation_
2024-10-07 05:14:24
We love integration
jonnyawsom3
RaveSteel https://github.com/ValveSoftware/gamescope/issues/1525
2025-11-13 10:13:56
I ended up reading your issue again just now, and thought about our findings from this regression https://github.com/libjxl/libjxl/issues/4447
2025-11-13 10:14:02
I'm assuming you used Clang, so the main regression shouldn't have been present, but the standalone speed could've given it a slight boost too depending on how many threads you were using
RaveSteel
2025-11-13 10:19:13
No, it was a default cmake build from the arch repos
2025-11-13 10:19:27
and still it was that much faster
jonnyawsom3
2025-11-13 10:20:08
That means you could've got between 2-12x the performance then
RaveSteel
2025-11-13 10:20:35
Since I opened that issue I have tested more with 4k and HDR on steam
2025-11-13 10:20:46
HDR AVIFs take seconds to save
2025-11-13 10:21:16
I have vibecoded a very bad implementation of libjxl into gamescope and it was many times faster
2025-11-13 10:21:41
of course nothing I would bring into this discussion on a serious basis, since it was just vibecoded
jonnyawsom3
2025-11-13 10:22:15
<@274048677851430913> took a shot at integrating simple-lossless JXL into ReShade, works great and thought about making a PR
RaveSteel
2025-11-13 10:22:53
sounds great!
2025-11-13 10:23:18
another pain point of steams HDR AVIFs is that the PNGs derived from them are not identical to the AVIF
2025-11-13 10:23:40
ssimulacra2 shows around 96 score, whioch is pretty similar, but still not identical
Kampidh
<@274048677851430913> took a shot at integrating simple-lossless JXL into ReShade, works great and thought about making a PR
2025-11-13 11:24:37
Already tested HDR on both 10 bit and 16 bit (float) surface format and seems to work well, about time now :p Though I just found out by default reshade HDR PNG implementation always converts that F16 linear sRGB surface into r2020 PQ, while my jxl implementation preserve it (which I do prefer personally)
username
Kampidh Already tested HDR on both 10 bit and 16 bit (float) surface format and seems to work well, about time now :p Though I just found out by default reshade HDR PNG implementation always converts that F16 linear sRGB surface into r2020 PQ, while my jxl implementation preserve it (which I do prefer personally)
2025-11-13 11:37:10
preserving the original space seems preferable IMO especially since JXL is fully color managed. Also something else but I know that the simple lossless encoder for JXL sadly doesn't support this but it would be cool if the ReShade implementation could save the depth information to the dedicated depth channel that the JXL format supports. <@794205442175402004> how hard would it be to have the [simple lossless encoder](https://github.com/libjxl/simple-lossless-encoder) support writing to other channels besides RGBA?
2025-11-13 11:38:22
quickly skimming the code it doesn't seem to be setup in a way that would allow writing to other channels easily but idk maybe I'm wrong
jonnyawsom3
2025-11-13 11:45:21
Well Simple is a cut down version of Fast, which says it supports 4 channels <https://github.com/libjxl/libjxl/tree/main/tools/fast_lossless> but is hardcoded for Alpha <https://github.com/libjxl/libjxl/blob/main/lib/jxl/enc_fast_lossless.cc>
Kampidh Already tested HDR on both 10 bit and 16 bit (float) surface format and seems to work well, about time now :p Though I just found out by default reshade HDR PNG implementation always converts that F16 linear sRGB surface into r2020 PQ, while my jxl implementation preserve it (which I do prefer personally)
2025-11-13 11:49:16
And that's something I never considered, with practically all screenshot formats being 10-bit or int, JXL might be the only way to get a truly lossless image from some games (Ignoring JXR because it's support is awful)
Kampidh
2025-11-14 12:41:21
one test with 16 bit float
jonnyawsom3
2025-11-14 01:10:26
Ah yes, thank you Irfanview for not doing any color management
2025-11-14 01:11:10
Good photo though, and I might actually use ReShade to get around an FSR bug with NMS' photo mode
Kampidh
2025-11-14 01:13:02
I usually use tev for viewing HDR images
_wb_
username preserving the original space seems preferable IMO especially since JXL is fully color managed. Also something else but I know that the simple lossless encoder for JXL sadly doesn't support this but it would be cool if the ReShade implementation could save the depth information to the dedicated depth channel that the JXL format supports. <@794205442175402004> how hard would it be to have the [simple lossless encoder](https://github.com/libjxl/simple-lossless-encoder) support writing to other channels besides RGBA?
2025-11-14 08:07:06
in principle that shouldn't be hard, but part of what makes it fast is that it is hardcoded for taking 1-4 interleaved channels (in uint8 or uint16) as input, so it would need to be extended.
jonnyawsom3
_wb_ in principle that shouldn't be hard, but part of what makes it fast is that it is hardcoded for taking 1-4 interleaved channels (in uint8 or uint16) as input, so it would need to be extended.
2025-11-14 08:10:36
Games generally don't use the Alpha channel, so in this instance Depth could likely be replace it
username
2025-11-14 08:21:50
hmm yeah I forgot that alpha isn't relevent for this use-case so would doing that be as simple as just changing what the final channel type is marked as for alpha or no? (non-upstreamable change)
_wb_
2025-11-14 08:26:35
Yes, actually in general we should probably upstream something like that so it can be used not just for RGBA but also CMYK or RGBD
username
_wb_ Yes, actually in general we should probably upstream something like that so it can be used not just for RGBA but also CMYK or RGBD
2025-11-16 07:46:16
for the time being does this seem like a correct hack for making the simple lossless encoder use it's input of RGBA to output as a proper RGBD file? ```diff --- simple_lossless.cc +++ simple_lossless_depth-hack.cc @@ -637,32 +637,27 @@ if (frame->bitdepth <= 14) { output->Write(1, 1); // 16-bit-buffer sufficient } else { output->Write(1, 0); // 16-bit-buffer NOT sufficient } if (have_alpha) { output->Write(2, 0b01); // One extra channel - if (frame->bitdepth == 8) { - output->Write(1, 1); // ... all_default (ie. 8-bit alpha) - } else { output->Write(1, 0); // not d_alpha - output->Write(2, 0); // type = kAlpha + output->Write(2, 1); // type = kDepth output->Write(1, 0); // not float if (frame->bitdepth == 10) { output->Write(2, 0b01); // bit_depth.bits_per_sample = 10 } else if (frame->bitdepth == 12) { output->Write(2, 0b10); // bit_depth.bits_per_sample = 12 } else { output->Write(2, 0b11); // 1 + u(6) output->Write(6, frame->bitdepth - 1); } output->Write(2, 0); // dim_shift = 0 output->Write(2, 0); // name_len = 0 - output->Write(1, 0); // alpha_associated = 0 - } } else { output->Write(2, 0b00); // No extra channel } output->Write(1, 0); // Not XYB if (frame->nb_chans > 2) { output->Write(1, 1); // color_encoding.all_default (sRGB) } else { ```
_wb_
2025-11-16 08:49:40
Looks ok. You can use jxlinfo to check if it's correctly tagged.
RaveSteel
2026-01-09 03:55:14
https://github.com/0fbcb238c0/gamescope-jxl/tree/jxl-screenshots-avif-removed
2026-01-09 03:55:48
I did only some rough benchmarking, since it is hard to make direct comparisons as I have to switch the gamescope binaries around
2026-01-09 03:56:22
But, as expected, the JXL capture will always be smaller than the AVIF screenshots
2026-01-09 03:57:29
I tested 4 games, Holocure, Doom Eternal, Deep Rock Galactic and Death Must Die
2026-01-09 03:58:19
Regarding Deep Rock Galactic, which I have used this adapted gamescope in, I already captured 102 images
2026-01-09 04:01:23
The screenshots will on average be around 0.5-1MB smaller than the comparable AVIF image (in Deep Rock Galactic) This varies per game of course
2026-01-09 04:08:20
For Death Must Die (Pixel art) the difference is more significant. ``` 704K gamescope_2026-01-09_16-40-37.jxl - Main Menu 636K gamescope_2026-01-09_16-40-59.jxl - Hub 2,1M gamescope_2026-01-09_16-41-41.avif - Main Menu 2,8M gamescope_2026-01-09_16-41-53.avif - Hub ``` Captured were the main menu and the character in the main hub
2026-01-09 04:10:59
I have two branches in this fork: The one I posted, where I replaced AVIF entirely and a second one, where I plan to make both AVIF and JXL work
AccessViolation_
2026-01-09 04:13:23
btw, do you normalize them by visual quality? in terms of file size difference
RaveSteel
2026-01-09 04:14:08
Are you refering to the file size comparison I posted? This can be done with `du -sh`
2026-01-09 04:14:25
In terms of capture all images are lossless
AccessViolation_
2026-01-09 04:14:40
ahhh gotcha
2026-01-09 04:14:58
I thought they were lossy, that's why I asked
RaveSteel
2026-01-09 04:14:59
As can be read on my profile: "Give me lossless or give me death" XD
AccessViolation_
2026-01-09 04:15:13
based
jonnyawsom3
RaveSteel I tested 4 games, Holocure, Doom Eternal, Deep Rock Galactic and Death Must Die
2026-01-09 04:16:28
Holocure and Deep Rock my beloved. The latter I was using to test simple-lossless JXL in ReShade
RaveSteel
2026-01-09 04:17:08
I first thought of trying to include simple lossless, but found normal libjxl to be easier, novice coder and all that
2026-01-09 04:18:04
Also I had some help from ChaGPT, but as far as I can tell by comparing to other projects, nothing too unusual slipped in
2026-01-09 04:18:18
the most complex part was the bitshifting that has to be done for JXL but not for AVIF
2026-01-09 04:18:45
since gamescope uses DRM_FORMAT_XRGB2101010
2026-01-09 04:20:41
Specifically refering to [these lines of code](https://github.com/0fbcb238c0/gamescope-jxl/commit/737a541218557a7f45aa131b3af9fb01d7d76f71#diff-452db373032f99b8c4bd2ed8b40bbd2bab7bed63bf8c51714acc025ea9b6200fR2969)
jonnyawsom3
RaveSteel the most complex part was the bitshifting that has to be done for JXL but not for AVIF
2026-01-09 05:44:49
You mean this? And what's it doing, 10-bit to 16-bit?
RaveSteel
2026-01-09 06:28:08
Yes, via padding as far as I underrstand. I could have likely changed the DRM format to full 16 bit instead, but the filesize would have bloated enormously with little to no benefit
2026-01-09 06:28:50
This is how it looks like if this code is used with AVIF
2026-01-09 06:29:43
If the bits are not padded as shown here, the JXL will appear very dark
2026-01-09 06:30:30
I would show an example, but have deleted the improper JXLs a few weeks before I got it working properly
2026-01-09 06:44:55
I considered adding the JXL parallel runner for threaded encoding, but even single threaded JXL is faster than AVIF
2026-01-09 06:45:44
at least from testing with cjxl, 4 threads could lead to a significant improvement
2026-01-09 06:45:54
after that it drops off a lot
RaveSteel This is how it looks like if this code is used with AVIF
2026-01-09 06:49:44
Ah, for those who are not on linux, this is how it's supposed to look like
jonnyawsom3
RaveSteel If the bits are not padded as shown here, the JXL will appear very dark
2026-01-09 07:02:04
I could be wrong, but I think you can either zero-pad the LSB or use this to directly set it to the 10-bit range https://github.com/libjxl/libjxl/blob/53042ec537712e0df08709524f4df097d42174bc/lib/include/jxl/types.h#L124>
2026-01-09 07:02:35
Based on info in this https://github.com/libjxl/libjxl/issues/1763
RaveSteel
2026-01-09 07:04:05
Good to know, thanks Will try this out later
2026-01-09 07:06:38
Wait, IIRC the "always float" part of JXL only applies to lossy, right? Since the capture here is lossless, the bitdepth is more "rigid", no?
jonnyawsom3
2026-01-09 07:08:39
Yeah, you'd have to add a separate path if you wanted to allow SDR captures too, but HDR games will only output 10bit usually, so it should be fine
RaveSteel
2026-01-09 07:08:57
There's a catch here, gamescope always captures in 10 bit, even for SDR
2026-01-09 07:10:47
PNG with DRM_FORMAT_XRGB8888 are in the code, but never used when actually capturing screenshots
2026-01-09 07:11:12
Probably used for the steam gamescope session I'd assume
jonnyawsom3
2026-01-09 07:14:21
Huh, weird but the JXL can still be set as 10-bit. Decoders can just request 8-bit from libjxl ~~Once it stops outputting Linear for sRGB~~
RaveSteel
2026-01-09 07:17:11
I thought about this during testing, but decided to try and keep as close as possible to the original AVIF code
2026-01-09 07:18:09
I did set the bitdepth of the JXL encoder to 8 during that, which worked fine
2026-01-09 07:18:15
so maybe it would be possible
2026-01-09 07:18:37
but for 8 bit capture there is a whole block for PNG capture
2026-01-09 07:19:10
the previous AVIF, now JXL encoder, are located entirely within the HDR screenshot function
jonnyawsom3
RaveSteel I did set the bitdepth of the JXL encoder to 8 during that, which worked fine
2026-01-09 07:22:24
Wait, so you're padding 10-bit, to 16-bit, then saving to 8-bit?
RaveSteel
2026-01-09 07:22:49
nono, that was just in testing
2026-01-09 07:22:55
To see if it would work
jonnyawsom3
2026-01-09 07:22:56
Riight
RaveSteel I considered adding the JXL parallel runner for threaded encoding, but even single threaded JXL is faster than AVIF
2026-01-09 07:24:56
Effort 1/Fast Lossless lives up to the name. Since you already added full libjxl instead of the cut down encoders, maybe effort 2 with a few threads would be a good fit. Depends on content and resolution though thanks to chunked encoding
RaveSteel
2026-01-09 07:30:34
I would consider this, but gamescope is kinda problematic with recording screenshots in general. You basically have to mash the keyboard shortcut for screenshotting, because it may not capture one otherwise
2026-01-09 07:31:34
and since that may either result in many screenshots, or only one or two screenshots, it is IMO better to stay with effort 1
2026-01-09 07:31:44
you can simply reencode losslessly afterwards, after all
2026-01-09 07:32:58
Having said that, I will still try it out
2026-01-09 07:33:12
it may just work better
2026-01-09 08:41:49
Alright, that was much better than expected
2026-01-09 08:42:18
Encoding time of a glxgears 1080p window went from around 100ms at effort 1 to 60-70ms
2026-01-09 08:43:19
Before: JXL encode took 102 ms After: JXL encode took 65 ms
jonnyawsom3
2026-01-09 08:44:20
Effort 2 on 4 threads?
RaveSteel
2026-01-09 08:44:49
I used JxlThreadParallelRunnerDefaultNumWorkerThreads, no idea what it defaults too
2026-01-09 08:45:02
effort 2 with threading is as fast as unthreaded effort 1
jonnyawsom3
2026-01-09 08:45:32
That'll be all threads on the CPU
RaveSteel
2026-01-09 08:45:53
alright
2026-01-09 08:46:13
well, at least with glxgears, which is mostly a black screen, there is pretty much no improvement in density
2026-01-09 08:46:23
i'll need to test a game
2026-01-09 09:19:44
effort 1 single-threaded ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_21-57-44.jxl JXL encode took 162 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_21-57-52.jxl JXL encode took 147 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_21-58-10.jxl JXL encode took 147 ms ``` #### from here on with all cpu threads effort 1 threaded ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-00-40.jxl JXL encode took 96 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-00-44.jxl JXL encode took 85 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-00-44.jxl JXL encode took 83 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-00-56.jxl JXL encode took 89 ms ``` effort 2 threaded ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-17-47.jxl JXL encode took 105 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-17-51.jxl JXL encode took 82 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-17-51.jxl JXL encode took 94 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-18-04.jxl JXL encode took 82 ms ``` effort 3 threaded ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-05-47.jxl JXL encode took 101 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-05-51.jxl JXL encode took 91 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-06-42.jxl JXL encode took 93 ms ``` effort 7 threaded (lol) ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-04-08.jxl JXL encode took 4519 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-04-09.jxl JXL encode took 4395 ms ```
2026-01-09 09:20:12
effort 1 and 2 were pretty much the same in terms of density
2026-01-09 09:20:48
it only starts to make a difference at effort 3
2026-01-09 09:27:31
at 4 cpu threads: effort 1 and 2 threaded were the same with 4 threads effort 3 threaded was a bit slower than previously ``` [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-26-32.jxl JXL encode took 137 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-26-44.jxl JXL encode took 122 ms [gamescope] [Info] xwm: Screenshot saved to /tmp/gamescope_2026-01-09_22-26-44.jxl JXL encode took 123 ms ```
2026-01-09 09:28:13
so at least for my hardware effort 3 is very much viable, but for low power devices effort 1 may be preferable
2026-01-09 09:28:47
especially considering the insignificant difference in density from effort 1 to effort 2
2026-01-09 09:40:32
I tried something rather simple and it worked
2026-01-09 09:41:20
The original AVIF code uses the equivalent of the current JXL code which looks like this ``` color_encoding.primaries = bHDRScreenshot ? JXL_PRIMARIES_2100 : JXL_PRIMARIES_SRGB; ```
2026-01-09 09:41:34
So I tried setting this same thing for the bitdepth
2026-01-09 09:41:42
``` basic_info.bits_per_sample = bHDRScreenshot ? 10 : 8;```
2026-01-09 09:41:55
and it seems to work
2026-01-09 09:42:04
SDR gets saved as 8 bit and HDR as 10 bit
2026-01-09 09:43:26
Is there anything I am missing which could cause problems with this?
username
2026-01-09 09:58:55
can't games output as 16 bit?
2026-01-09 09:59:21
actually don't most if not all HDR games output as 16 bit and not 10 bit technically?
RaveSteel
2026-01-09 10:00:49
Saving as 16 bit would bloat filesizes, and 8 bit screenshots are the default for SDR after all
2026-01-09 10:01:07
But actually no idea about if games output 16 bit
username
2026-01-09 10:02:20
actually you bringing up SDR being done as 8 bit reminds me that some SDR games actually output as 10 bit
RaveSteel
2026-01-09 10:02:36
Which would be the default gamescope captures in
2026-01-09 10:02:55
I wonder if I should keep the 10 bit at all times or do 8 bit for SDR capture
2026-01-09 10:03:20
always 10 bit is the default behaviour in gamescope after all
jonnyawsom3
username actually don't most if not all HDR games output as 16 bit and not 10 bit technically?
2026-01-09 10:04:07
The display buffer is 10bit, RGBA1010102 to make 32 bits
RaveSteel
2026-01-09 10:04:38
and gamescope discards alpha, so 30 bits
jonnyawsom3
2026-01-09 10:04:38
Depends how the game itself handles it
username
The display buffer is 10bit, RGBA1010102 to make 32 bits
2026-01-09 10:05:22
I don't know how many games actually use this format for HDR. the cases I've seen of this format being used is for 10 bit SDR with RGBA16161616 being used for HDR
RaveSteel
2026-01-09 10:05:51
can you give an example?
username
2026-01-09 10:06:27
I think this game maybe? https://store.steampowered.com/app/870780/Control_Ultimate_Edition/
RaveSteel
2026-01-09 10:06:53
can you test it if you own it?
2026-01-09 10:07:17
does it have an inbuilt screenshot function?
username
2026-01-09 10:07:54
I don't know if it does. Ill install it for testing but it's gonna take a few hours for me
2026-01-09 10:08:48
also something else that might be an example of https://discord.com/channels/794206087879852103/1284444511375855667/1459307086671511756 is I think modern Unreal engine does the same or a similar thing
2026-01-09 10:09:30
I need to install some tooling/tools to check display swapchains to confirm though
RaveSteel
2026-01-09 10:10:56
sounds good
2026-01-09 10:12:31
Ah, another point in favor of 10 bit HDR — many viewers, such as mpv, expect HDR content to have 10 bits. the metadata may still be there at higher bitdepths, but it doesn't (always?) recognise it
2026-01-09 10:13:30
The entirety of HDR is weird and everyone is doing their own standards smh
username
RaveSteel Ah, another point in favor of 10 bit HDR — many viewers, such as mpv, expect HDR content to have 10 bits. the metadata may still be there at higher bitdepths, but it doesn't (always?) recognise it
2026-01-09 10:15:34
I mean isn't that kinda a problem on the program/viewer if It can't handle different bitdepths? either way with JXL this shouldn't be a problem since libjxl lets you request a desired output bitdepth when decoding so everything should still work fine
2026-01-09 10:16:22
higher bitdepths will get dithered down
RaveSteel
2026-01-09 10:16:41
true to the first part. the second part only applies to lossy JXLs IIRC? do correct me if I am wrong here
2026-01-09 10:17:12
I pushed a commit with multithreading
2026-01-09 10:17:39
And I included chrono for measuring encoding time
2026-01-09 10:17:57
Will have to do the same for AVIF later, to properly compare encoding times
username
2026-01-09 10:18:40
2026-01-09 10:19:15
dithering is still done for lossless because the process of viewing a high bitdepth image in a lower bitdepth is a lossy process
RaveSteel
username
2026-01-09 10:20:00
I think the 16 bit depth is due to this image being captured with reshade. or does reshade always capture the native bitdepth?
username
RaveSteel I think the 16 bit depth is due to this image being captured with reshade. or does reshade always capture the native bitdepth?
2026-01-09 10:21:31
ReShade is able to know the format of the output from the game but idk if that is actually being fully taken advantage of or if it's just doing 16 bit if it's not 8 bit
RaveSteel
2026-01-09 10:21:55
maybe <@274048677851430913> can explain how reshade works here
username
2026-01-09 10:27:22
is gamescope able to see/know the output swapchain of a game or no? because if not and if what I said about SDR being 10 bit and HDR being 16 bit is true then what would the solution here be? treat SDR as 10 bit and treat HDR as 12 bit or 16 bit?
2026-01-09 10:27:59
might be worth checking to see how much larger 16 bit JXLs actually are
RaveSteel
2026-01-09 10:28:16
gamescope only defines the output colour format for screenshots by file extension. like so ``` if ( path.extension() == ".jxl" ) drmCaptureFormat = DRM_FORMAT_XRGB2101010; else if ( path.extension() == ".png" ) drmCaptureFormat = DRM_FORMAT_XRGB8888; else if ( path.extension() == ".nv12.bin" ) drmCaptureFormat = DRM_FORMAT_NV12; ```
jonnyawsom3
RaveSteel I think the 16 bit depth is due to this image being captured with reshade. or does reshade always capture the native bitdepth?
2026-01-09 10:28:32
It's 10-bit expanded to 16-bit IIRC
RaveSteel
2026-01-09 10:29:59
great
2026-01-09 10:30:37
if this is true then I will likely stay with always 10 bit for screenshots
2026-01-09 10:30:50
but we'll see
username
2026-01-09 10:32:55
Control is going to take a loooong time to download for me. in the meantime ill try and see if I have any HDR Unreal engine 5 games I can check with
RaveSteel
2026-01-09 10:33:10
nice
jonnyawsom3
RaveSteel I think the 16 bit depth is due to this image being captured with reshade. or does reshade always capture the native bitdepth?
2026-01-09 10:43:46
https://saklistudio.com/miniblog/reshade-jpegxl-saga/ > It seems like in HDR PNG they do some color conversion that contributes to that heavy processing, while in JXL I only did buffer conversion (R10G10B10A2 to R16G16B16) without scaling, preserving 10 bit values 0-1023.
RaveSteel
2026-01-09 10:44:24
alright, so 10 bit it is
2026-01-09 10:47:20
since the PNG on the blog is 8 bit I think I will go with reshade's approach and do 8 bit for SDR and 10 bit for HDR
Kampidh
2026-01-09 10:47:22
My initial PR was native 10 bit, but then it seems to be changed to 16 bit scaled recently
RaveSteel
2026-01-09 10:47:36
oh? interesting
Kampidh
2026-01-09 10:48:32
https://github.com/crosire/reshade/blob/dc4e47ac2dde03f892f0be5ff1d4a241847c71ff/source/runtime.cpp#L5231
RaveSteel
2026-01-09 10:49:20
any idea why this was changed?
2026-01-09 10:49:49
it seems better to retain the original 10 bits in my opinion
Kampidh
2026-01-09 10:54:09
Mmm I'm not really sure since it got changed quite a bit since the last time I checked
jonnyawsom3
Kampidh Mmm I'm not really sure since it got changed quite a bit since the last time I checked
2026-01-09 10:55:43
Looks like it was this commit https://github.com/crosire/reshade/commit/7303208e58163e58ef59857995abe985bf958fc2
Kampidh
2026-01-09 10:56:39
Shouldn't be a massive performance hit though since it's just a scaling without color conversion, but I do also prefer native bitdepth x)
RaveSteel
2026-01-09 10:56:52
same
username
2026-01-09 10:56:59
yeah having the bitdepth be native is nice
Kampidh
2026-01-09 10:57:08
Ah yes you beat me to it for sharing the commit link :p
RaveSteel
2026-01-09 11:20:28
Setting the bitdepth to 8 for SDR doesn't seem to conflict with the bit expansion
2026-01-09 11:21:04
I took a PNG screenshot via steam and a JXL screenshot via gamescope in a game with static elements and both have the same checksum
2026-01-09 11:21:33
So it seems I can simply save SDR screenshots as 8 bit JXLs
username
2026-01-09 11:22:04
hmm. do you have any Unreal 5 (4 might work as well idk) games to test?
RaveSteel
2026-01-09 11:22:33
let me check the list of unreal engine games on wikipedia
2026-01-09 11:23:20
Yeah I have one
2026-01-09 11:23:23
let me test
2026-01-09 11:32:12
well, the problem I am encountering is that steam, not gamescope, produces glitchart instead of screenshots
2026-01-09 11:32:14
2026-01-09 11:34:20
it even happens with vanilla gamescope lol
2026-01-09 11:37:53
Seems to be an issue of the steam client with gamescope
2026-01-09 11:38:24
happens in deep rock galactic as well
2026-01-09 11:39:09
the gamescope screenshots work and look as epxtected though
2026-01-09 11:56:33
Testing some more, 8 bit capture for SDR works better than expected. Capturing SDR JXLs with the same checksum as PNG screenshots is repeatable in multiple games, so 8 bit capture is actually bitexact compared to the classic steam screenshotting
2026-01-10 07:59:38
<@184373105588699137> it would theoretically possible to encode scRGB, but doing so would include modifying other parts of gamescope too and I am out of my depth here honestly. gamescope only offers EOTF_PQ and EOTF_Gamma22 so I would have to hack linear in as well. I could use the code i added to encode 16 bit float, but only with gamma it seems So I will, at this time, keep it simple and stay with 10 bit for PQ and 8 bit for SDR, which is already a departure from the default always 10 bit
2026-01-10 08:06:37
Is there even a game which solely features scRGB as its HDR option? scRGB is rare as it is already
2026-01-10 11:43:15
Did some benchmarking to compare vanilla gamescope with AVIF to the JXL encoder Do keep in mind that the JXLs will be even smaller than expected because they are 8 bit while the AVIFs are 10 bit. But even if both were equal, JXL would still win handily Effort 1 for all JXLs Factorio: ``` AVIF: 489 ms 495 ms 482 ms 493 ms 489 ms Total file size: 28MiB JXL: 85 ms 88 ms 90 ms 86 ms 84 ms Total file size: 16MiB ``` Deep Rock Galactic: ``` AVIF: 351 ms 349 ms 306 ms 292 ms 334 ms Total file size: 19.8MiB JXL: 90 ms 81 ms 63 ms 63 ms 60 ms Total file size: 7.9MiB ``` Holocure: ``` AVIF: 277 ms 210 ms 214 ms 199 ms 183 ms Total file size: 8.6MiB JXL: 49 ms 45 ms 64 ms 65 ms 61 ms Total file size: 1.9MiB ``` Doom Eternal ``` AVIF: 267 ms 399 ms 384 ms 309 ms 390 ms Total file size: 21.5MiB JXL: 66 ms 80 ms 89 ms 75 ms 87 ms Total file size: 9.8MiB ```
username
2026-01-11 02:44:22
always using 8 bit for SDR still feels wrong to me. I remember I was a bit annoyed a few months back because Steam would only capture 8 bit for SDR meanwhile the game itself was outputting 10 bit to my 10 bit monitor
RaveSteel
2026-01-11 02:56:31
Changing SDR capture to 8 bit was a choice I made. Could always switch back to 10 bit
2026-01-11 02:57:28
I simply assumed that 8 bit would be "better" because it does not really matter for SDR to my understanding
2026-01-11 02:58:03
How did you know that the game put out 10 bit btw?
2026-01-11 02:58:13
And which game was it
username
2026-01-11 03:21:37
it was this game https://store.steampowered.com/app/1304510/SCP_NineTailed_Fox/ which I went and modified the dgVoodoo2 config for it to set the default texture creation/allocation format to 10 bit and the output framebuffer/swapchain to 10 bit which resulted in noticeably less bending when my monitor was set to 10 bit. the Steam screenshots however where all 8 bit and had noticeable banding
2026-01-11 03:24:45
oh also I only got an hour or so left on the download for [Control](https://store.steampowered.com/app/870780/Control_Ultimate_Edition/) however I don't have SteamOS or anything so all ill be able to do is confirm the output format however I'm pretty sure it's 10 bit since the changelog says so: https://store.steampowered.com/news/app/870780/view/530966340601644728
2026-01-11 03:24:58
> Updated SDR to 10bit (from 8bit), which reduces visible color banding
RaveSteel
2026-01-11 09:59:05
HM, I Start to See why valve went with simply always capturing 10 bit
2026-01-11 09:59:35
Else it getw complicated fast with all the different bitdepths and formats that are used by games
username oh also I only got an hour or so left on the download for [Control](https://store.steampowered.com/app/870780/Control_Ultimate_Edition/) however I don't have SteamOS or anything so all ill be able to do is confirm the output format however I'm pretty sure it's 10 bit since the changelog says so: https://store.steampowered.com/news/app/870780/view/530966340601644728
2026-01-11 11:28:36
You mean you are on windows? I would have liked steam+gamescopes output too see which format the game uses
2026-01-11 11:29:58
sadly I don't own the game and can take a look myself
AccessViolation_
username > Updated SDR to 10bit (from 8bit), which reduces visible color banding
2026-01-11 12:59:59
this is so based
2026-01-11 01:00:35
this is the first time I personally see something deliberately choosing 10 bit SDR to void color banding
2026-01-11 01:01:10
I finally feel validated in having that discord profile description that complains about color banding
RaveSteel
2026-01-11 02:02:58
I tested some games 8 Bit SDR - Holocure - Doom Eternal - Factorio (native game, doesn't use vulkan but SDL_PIXELFORMAT_RGB888) - Deep Rock Galactic Survivor - Warhammer 40k Spacemarine - Death Must Die 10 bit SDR - Deep Rock Galactic -> actually uses the same color format for both SDR and HDR - VK_FORMAT_A2B10G10R10_UNORM_PACK32 With Terraria it doesn't even show the color format So there is no uniform way to query color format especially if it isn't even reported in the first place
Quackdoc
RaveSteel Is there even a game which solely features scRGB as its HDR option? scRGB is rare as it is already
2026-01-11 02:05:36
its fairly common, but I dunno if there are any scrgb only games, I know windows does push it hard
RaveSteel
2026-01-11 02:07:51
I did manage to capture scRGB I think, but the problem remains with the transfer functions offered by gamescope
2026-01-11 02:08:40
looks very wrong because of this of course
Quackdoc
2026-01-11 02:08:55
interesting
RaveSteel
2026-01-11 02:09:07
this is the jxl
Quackdoc
2026-01-11 02:09:27
can libjxl even recognise scrgb input?
RaveSteel
2026-01-11 02:09:49
seems so
2026-01-11 02:09:50
2026-01-11 02:09:54
I created this for testing
2026-01-11 02:10:22
16 bit float linear sRGB with red values exceeding 1
Quackdoc
2026-01-11 02:10:46
I'm at the er today so I can't look at code. but I might be able to take a gander next week
RaveSteel
2026-01-11 02:11:10
I haven't commited the code because it can't be used without the proper transfer function anyway
2026-01-11 02:11:41
If you want to take a go at hacking in linear EOTF that would be welcome
jonnyawsom3
RaveSteel I tested some games 8 Bit SDR - Holocure - Doom Eternal - Factorio (native game, doesn't use vulkan but SDL_PIXELFORMAT_RGB888) - Deep Rock Galactic Survivor - Warhammer 40k Spacemarine - Death Must Die 10 bit SDR - Deep Rock Galactic -> actually uses the same color format for both SDR and HDR - VK_FORMAT_A2B10G10R10_UNORM_PACK32 With Terraria it doesn't even show the color format So there is no uniform way to query color format especially if it isn't even reported in the first place
2026-01-11 02:11:45
I was just looking into Deep Rock, since I remembered it using a 10bit swapchain in Reshade. Not sure if it actually uses 10bit values in SDR though
RaveSteel
2026-01-11 02:12:14
I tried to keep it simple and do the original AVIF encoder pretty much 1:1 with JXL, but there are a lot of edge cases I wasn't aware of
2026-01-11 02:12:36
again, I can now very much understand why Valve went with their approach
I was just looking into Deep Rock, since I remembered it using a 10bit swapchain in Reshade. Not sure if it actually uses 10bit values in SDR though
2026-01-11 02:13:51
Good question, maybe something that should be tested properly if anyone finds the time
jonnyawsom3
2026-01-11 06:13:56
Couldn't find a way to dump the swapchain in ReShade and see if it's using 8bit values in the 10bit swapchain I did dump the depth buffer though and then tried recompressing the EXR... Interesting... ```cjxl -d 0 "FSD-Win64-Shipping.exe 2026-01-11 16-08-44 191 DepthBuffer.jxl" 12.jxl JPEG XL encoder v0.12.0 029cec42 [_AVX2_] {Clang 20.1.8} Encoding [Modular, lossless, effort: 7] Compressed to 3311.6 kB (28.622 bpp). 1282 x 722, 0.698 MP/s, 16 threads. cjxl -d 0 "FSD-Win64-Shipping.exe 2026-01-11 16-08-44 191 DepthBuffer.jxl" 12.jxl -e 8 JPEG XL encoder v0.12.0 029cec42 [_AVX2_] {Clang 20.1.8} Encoding [Modular, lossless, effort: 8] Compressed to 1041.6 kB (9.003 bpp). 1282 x 722, 0.222 MP/s, 16 threads.```
RaveSteel
2026-01-11 08:19:45
Using the python lubrary PIL and numpy to check for image data with a 10 bit JXL screenshot from DRG tells me it does only use 8 bits depsite the requested 10 bit color format ``` uint8 0 255 ``` This test may not be accurate, I am checking if I can find another method
2026-01-11 08:21:43
Ok, this test is 100% *not* accurate lol
Couldn't find a way to dump the swapchain in ReShade and see if it's using 8bit values in the 10bit swapchain I did dump the depth buffer though and then tried recompressing the EXR... Interesting... ```cjxl -d 0 "FSD-Win64-Shipping.exe 2026-01-11 16-08-44 191 DepthBuffer.jxl" 12.jxl JPEG XL encoder v0.12.0 029cec42 [_AVX2_] {Clang 20.1.8} Encoding [Modular, lossless, effort: 7] Compressed to 3311.6 kB (28.622 bpp). 1282 x 722, 0.698 MP/s, 16 threads. cjxl -d 0 "FSD-Win64-Shipping.exe 2026-01-11 16-08-44 191 DepthBuffer.jxl" 12.jxl -e 8 JPEG XL encoder v0.12.0 029cec42 [_AVX2_] {Clang 20.1.8} Encoding [Modular, lossless, effort: 8] Compressed to 1041.6 kB (9.003 bpp). 1282 x 722, 0.222 MP/s, 16 threads.```
2026-01-11 08:37:58
Short question since you encoded JXL->JXL Do you know if it is intentional that libjxl attaches an ICC profile to a JXL in JXL->JXL conversions, even for lossless?
jonnyawsom3
RaveSteel Short question since you encoded JXL->JXL Do you know if it is intentional that libjxl attaches an ICC profile to a JXL in JXL->JXL conversions, even for lossless?
2026-01-11 08:48:26
There's this https://github.com/libjxl/libjxl/pull/2635
2026-01-11 08:49:12
There's a commit at the bottom saying it attaches an ICC for legacy compatibility. libjxl ignores it, png encoding for output uses it (I think) https://github.com/sboukortt/libjxl/commit/945f7c5a8238fccb2bdb20d734639475fcaae3f1
RaveSteel
2026-01-11 08:49:21
Hm ,alright
2026-01-11 08:49:23
thanks
2026-01-11 08:49:46
But wait, why not directly create an ICC profile upon first encode?
2026-01-11 08:49:53
if it is for legacy purposes
2026-01-11 08:50:05
No need to wait until a transcode happens
jonnyawsom3
RaveSteel Short question since you encoded JXL->JXL Do you know if it is intentional that libjxl attaches an ICC profile to a JXL in JXL->JXL conversions, even for lossless?
2026-01-11 08:50:34
I've only got 1 build with EXR support due to it not being part of the releases, so I use that one to then get a JXL the other versions can ingest (v0.10 vs 0.11 for floats)
RaveSteel
2026-01-11 08:51:42
EXR support is pretty useful to have sometimes and since it is enabled by default when building I just keep it
jonnyawsom3
2026-01-11 08:54:11
~~One day <https://github.com/libjxl/libjxl/issues/3511>~~
RaveSteel
2026-01-11 08:55:18
EXR support is almost flawless since a while ago, hopefully the remaining roadblocks can be cleared in the future
2026-01-11 08:55:25
After jxl-rs is complete I suppose
2026-01-11 08:55:53
But well, some more work to be done there, jxl-rs is often 10-20 times slower than libjxl on my machine
jonnyawsom3
2026-01-11 08:59:14
Well, it is singlethreaded
RaveSteel
2026-01-11 09:03:49
fair point
2026-01-11 09:39:17
I am still not sure if this test is accurate, but using cv2 with python to print values indeed shows a difference between 8 bit screenshots of DRG and 10 bit screenshots
2026-01-11 09:39:44
and while that may be the case, I cannot find any real difference evben with pixel peeping
2026-01-11 09:40:04
but I will trust this test for now, as it put out values that fit the expecation
2026-01-11 09:42:52
Hm, could it be that DRG uses the constantly appearing very subtle noise as a sort of dithering? it is so subtle that it is only really noticable for me if I zoom massively
jonnyawsom3
2026-01-11 10:04:11
I think it does
RaveSteel
2026-01-12 01:15:34
I think it may be prudent to always capture 10 bit except for when games declare they are 8 bit the 16 bit scrgb thingy is much more tedious than expected, so will either be only much later, or not at all
2026-01-12 01:15:44
This is also because the gamescope logic isn't great
2026-01-12 01:16:29
currently the extension of the to-be-written screenshot file is used to decide which DRM format should be used
2026-01-12 01:17:37
it even makes sense, kinda - because some games just do not report a color format. no idea if this could be queried, but this would also be additional work elsewhere
2026-01-12 01:19:39
for now I've started a windows VM and will experiment with how reshade handles jxl screenshots
username
Kampidh Shouldn't be a massive performance hit though since it's just a scaling without color conversion, but I do also prefer native bitdepth x)
2026-01-12 09:57:34
might be worth submitting a PR (if you have the time) to bring it back to outputting native bitdepth. because the more I think about it the less desirable it sounds to have games that output at 10 bit (swapchain/framebuffer formats like RGBA1010102) be saved to 16 bit JXLs. It's very possible that the behavior change was unintentional since I wouldn't be surprised if the dev isn't checking the exact bitdepth(s) getting defined in the output files as long as they contain the right data
2026-01-12 09:59:07
I wanted to try reporting it to the ReShade dev but the GitHub repo has the issues section disabled
RaveSteel
I was just looking into Deep Rock, since I remembered it using a 10bit swapchain in Reshade. Not sure if it actually uses 10bit values in SDR though
2026-01-12 10:13:50
Any specific way you observed This? For me DRG shows "Format 24 (10bpc)" in the reshade statistics
2026-01-12 10:16:07
DRG reshade JXL Screenshots are also 8 bit
2026-01-12 10:35:58
I wonder if reshade properly detects that the game only sends 8 bit data with a 10 bit swapchain? While gamescope only shows the requested color format, which is 10 bit
jonnyawsom3
RaveSteel Any specific way you observed This? For me DRG shows "Format 24 (10bpc)" in the reshade statistics
2026-01-12 02:30:10
Yeahh? That is the 10bit swapchain
RaveSteel I wonder if reshade properly detects that the game only sends 8 bit data with a 10 bit swapchain? While gamescope only shows the requested color format, which is 10 bit
2026-01-12 02:32:40
https://discord.com/channels/794206087879852103/805007255061790730/1435011063354757251
RaveSteel
2026-01-12 03:12:45
I meant that while the swapchain may be 10 bit, reshade shows the content(?) as 8 bit. So a 10 bit swapchain, but only 8 bits are actually used I guess
https://discord.com/channels/794206087879852103/805007255061790730/1435011063354757251
2026-01-12 03:14:25
There is bug report regarding this on the [steam-for-linux github repo](https://github.com/ValveSoftware/steam-for-linux/issues/11165), but it likely affects the windows version as well, shared codebase and all that
RaveSteel I meant that while the swapchain may be 10 bit, reshade shows the content(?) as 8 bit. So a 10 bit swapchain, but only 8 bits are actually used I guess
2026-01-12 03:35:10
And I have no idea how to get the true bitdepth of imagedata to decide the bitdepth of the to-be-encoded JXL
Kampidh
username might be worth submitting a PR (if you have the time) to bring it back to outputting native bitdepth. because the more I think about it the less desirable it sounds to have games that output at 10 bit (swapchain/framebuffer formats like RGBA1010102) be saved to 16 bit JXLs. It's very possible that the behavior change was unintentional since I wouldn't be surprised if the dev isn't checking the exact bitdepth(s) getting defined in the output files as long as they contain the right data
2026-01-12 05:57:04
Urghh right, I keep thinking how unideal that simple multiplication scaling is.. Seems like the author wants to simplify buffer conversion from 32bpp (rgba1010102) to 48bpp (rgb161616) so that it can be directly used for both PNG (stb) and libjxl input
jonnyawsom3
2026-01-12 09:27:44
Couldn't they just pad the LSB, then both JXL and PNG can signal 10bit (Via encoder setting and sBIT)
username
2026-01-12 11:32:23
oh huh yeah I forgot about the `sBIT` PNG chunk
RaveSteel
2026-01-14 06:45:57
gamescope measures maxCLLNits for HDR, would it be a good idea to set the intensity target of the JXL to that value? There should be no downsides, right?
2026-01-14 06:46:51
So instead of the 10000 nits for PQ it would show i.e. 346 nits
jonnyawsom3
RaveSteel There is bug report regarding this on the [steam-for-linux github repo](https://github.com/ValveSoftware/steam-for-linux/issues/11165), but it likely affects the windows version as well, shared codebase and all that
2026-01-14 06:55:00
I left a comment on that with some suggestions
RaveSteel
2026-01-14 07:00:02
I saw your comments and had a look at the reshade file you linked. would certainly be a good solution for gamescope, but lots of work to implement
2026-01-17 11:07:46
I pixel peeped some comparison screenshots I took of DRG with gamescope and reshade. reshade's a 8bit JXL has visibly more banding than the 10 bit JXLs gamescope produces
2026-01-17 11:08:10
So safe to say that DRG indeed uses 10 bits with SDR for reduced banding
jonnyawsom3
2026-01-17 11:09:11
Huh, really?
RaveSteel
2026-01-17 11:09:58
It isn't a massive difference, but visible at high magnification nonetheless
jonnyawsom3
2026-01-17 11:10:28
I tried to do some comparisons, but without a HDR monitor nothing captures the 10bit swapchain
RaveSteel
2026-01-17 11:10:59
gamescope now does :)
2026-01-17 11:11:09
Just need to do some polishing and then I can open a PR
2026-01-17 11:11:48
But to be fair, gamescope captured 10 bit even before my changes, just as AVIF, not JXL
jonnyawsom3
2026-01-17 11:15:30
I know before ReShade was capturing 10-bit, but it displayed as corrupted. Maybe that was just from running a HDR transform on SDR content, and not the bitdepth being incorrect like we thought? <@274048677851430913> would be curious to hear your thoughts, since you fixed the corruped output before
Kampidh
2026-01-17 11:27:31
Kinda forget already, but yep IIRC it was an output buffer mismatch. And I think the author wants to keep SDR to 8 bit (to have a unified buffer for multiple formats like I said above) Maybe I can do further experiment, but I don't have DRG to test yet (and I don't think I have other 10bit SDR games on my library)
RaveSteel
2026-01-17 11:28:06
Have a look at other UE4/UE5 games you own, maybe they behave the same
Kampidh
2026-01-18 12:12:25
wait I think I found one~
2026-01-18 01:05:26
a quick bodge before bed, seems to be working though :p
jonnyawsom3
2026-01-18 01:25:21
Did some brightening to compare on my SDR screen, very nice... Now how to get that into ReShade and GameScope...
2026-01-18 01:26:33
Kampidh
2026-01-18 09:12:59
maybe soon™ alongside reverts for those 'bad' HDR scaling (if I'm not too lazy xD) *and maybe ideally should bring that for PNG using `sBIT` chunk as well...*
2026-01-18 04:48:33
more examples that show quite a prominent banding on 8bit
jonnyawsom3
2026-01-18 05:04:29
I did a little test 10-bit JXL is half the size of optimised PNG at effort 1, effort 2 didn't have much effect 8-bit is a tiny bit larger than Oxipng with effort 1, but effort 2 is another 35% smaller (and 5x slower), so might be worth checking for GameScope
RaveSteel
2026-01-18 05:07:36
I had considered querying the number of CPU cores and switch between efforts depending on that
2026-01-18 05:07:57
For my CPU I can effortlessly encode at effort 3 and still be faster than AVIF
2026-01-18 05:08:38
I have left in effort 1 for now, but maybe something that could be added at a later time
2026-01-18 05:09:08
The binary I compiled for my own usage is effort 3
2026-01-18 05:10:07
But more important IMO would be trying to adapt gamescope to handle colorformats similarly to reshade so that screenshots are saved according the the swapchain bitdepth
2026-01-18 05:10:59
The 10 bit default for JXL isn't bad, since it's still much smaller than AVIF, but I would like to "properly" capture nonetheless
jonnyawsom3
2026-01-18 05:22:57
We'd need to check both bitdepth and colorspace, then set each independently if we want 'full' captures
RaveSteel For my CPU I can effortlessly encode at effort 3 and still be faster than AVIF
2026-01-18 05:24:13
3 is weighted only, good for photographic images but by far the slowest to decode
RaveSteel
2026-01-18 05:53:28
Which effort level do you suggest?
2026-01-18 06:02:14
effort 4 is 7 times slower in encode than effort 3, and not really slower in decode for me
2026-01-18 06:02:20
maybe 5ms difference
2026-01-18 06:02:29
effort 5 is even slower
2026-01-18 06:02:41
which is of course expected
2026-01-18 06:03:31
Effort 5 decodes pretty much as fast as effort 3 on my system
2026-01-18 06:06:01
effort 2 decodes slightly faster, ~5-10ms
jonnyawsom3
2026-01-18 06:19:22
Everything 3+ uses weighted, 2 is fastest and 1 fastish. Mostly noticeable with 4K images or when scrolling though a folder
RaveSteel
2026-01-18 06:21:35
At least for me effort 1-3 encode in pretty much the same time, so I can afford the higher effort
2026-01-18 06:21:42
Only a few milliseconds difference
2026-01-18 06:21:43
But since my machine is very fast I chose effort 1 as the equivalent to the encode defined in the AVIF encoder for gamescope
2026-01-18 06:23:11
So it it'll be effort 1 in the PR I will open
2026-01-18 06:25:50
wait
2026-01-18 06:26:09
No idea if it'd make sense
2026-01-18 06:26:25
But I could set the used predictor via JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR
jonnyawsom3
2026-01-18 06:26:51
That only works effort 4+
RaveSteel
2026-01-18 06:26:57
Ah darn
jonnyawsom3
RaveSteel effort 2 decodes slightly faster, ~5-10ms
2026-01-18 06:29:22
You did get me to re-test, not sure what changed but now e1 is the fastest to decode, barely `3840 x 2160, geomean: 24.568 MP/s [23.012, 26.921], 10 reps, 0 threads.` `3840 x 2160, geomean: 23.507 MP/s [21.416, 24.075], 10 reps, 0 threads.` `3840 x 2160, geomean: 7.928 MP/s [7.494, 8.058], 10 reps, 0 threads.` Weighted still drags it's feet though
2026-01-18 06:36:35
But anyway, e1 will still beat the old AVIF, e2 makes sure it beats PNG too
Kampidh more examples that show quite a prominent banding on 8bit
2026-01-18 06:40:05
I'm trying to think, AVIF doesn't have dithering does it? So even if they did capture 10-bit SDR, it would probably have even more banding on the 8-bit display
RaveSteel
2026-01-18 06:52:43
For me it looks like this. e3 of course slower, but since I have many cores the decode is barely noticable in comparison ``` 1920 x 1080, geomean: 30.429 MP/s [29.40, 31.34], , 10 reps, 0 threads. 1920 x 1080, geomean: 30.547 MP/s [28.98, 31.50], , 10 reps, 0 threads. 1920 x 1080, geomean: 8.038 MP/s [7.13, 8.26], , 10 reps, 0 threads. ``` With all cores ``` 1920 x 1080, geomean: 286.227 MP/s [203.60, 314.30], , 10 reps, 32 threads. 1920 x 1080, geomean: 289.143 MP/s [185.43, 308.57], , 10 reps, 32 threads. 1920 x 1080, geomean: 99.722 MP/s [71.61, 107.81], , 10 reps, 32 threads. ``` Of the games I've tested e2 is often only barely smaller than e1
jonnyawsom3
2026-01-18 06:53:36
10bit or 8bit?
RaveSteel
2026-01-18 06:53:40
10 bit
2026-01-18 06:54:30
I would love to chose bitdepth depending on the colorspace and colorformat, but that will have to come later
jonnyawsom3
2026-01-18 06:55:03
Ah right, so you're always encoding as 10bit to cover all cases for now?
RaveSteel
2026-01-18 06:55:31
yep, same as the already included AVIF encoder
2026-01-18 06:55:46
I tried to be 1:1 wherever possible
jonnyawsom3
RaveSteel There is bug report regarding this on the [steam-for-linux github repo](https://github.com/ValveSoftware/steam-for-linux/issues/11165), but it likely affects the windows version as well, shared codebase and all that
2026-01-18 06:56:22
I wonder if that'll happen again given JXL is a bit more forgiving in how it handles bitdepth
RaveSteel
2026-01-18 06:56:47
We'll see if/when JXL is included directly in steam
2026-01-18 06:56:54
soon™, surely
2026-01-18 06:57:23
I mentioned this before, but capturing AVIF screenshots via steam is incredibly slow, much slower than via gamescope
2026-01-18 06:57:58
Steam and gamescope in general have many issues that haven't been resolved even after months or years of open bug reports
2026-01-18 06:59:42
But we have to work with what we've got
monad
RaveSteel The binary I compiled for my own usage is effort 3
2026-01-18 08:04:58
e3 is not usable if you are encoding images characteristic of flat UI or pixel art. the storage size will be massive compared to e1/e2.
2026-01-18 08:07:26
for most games maybe it is not a problem, but for edge cases you'll have poor performance
RaveSteel
2026-01-18 08:19:48
hm, true
2026-01-18 08:20:12
A test screenshot went from 366KB to 1.7MB with e3
monad
2026-01-18 08:23:10
imo, if you can't afford e4, but can afford more compute than e1, then e2 is safer for general content
RaveSteel
2026-01-18 08:24:23
I think e4 would still be fast enough, but slower than AVIF
2026-01-18 08:24:34
And I want to at least beat AVIF in terms of speed
2026-01-18 08:24:50
Density will pretty much always be better, but we know that already
2026-01-18 08:25:02
Alright, e2 it is then
jonnyawsom3
2026-01-20 10:35:52
Random discovery, print screen on Windows captures the 10bit game buffer, but pasting it most places converts it to 8bit. Making a 16bit Linear Krita canvas and *then* pasting it keeps the full data
2026-01-20 10:50:04
Theoretically, this is a 10bit Linear screenshot, though the values seem a bit weird... (Click/tap to zoom and load the original with color management)
RaveSteel
2026-01-20 03:40:49
Interesting
2026-01-20 03:42:38
some heavy posterisation it looks like here
Quackdoc
Theoretically, this is a 10bit Linear screenshot, though the values seem a bit weird... (Click/tap to zoom and load the original with color management)
2026-01-20 03:50:19
10bit linear? rough
jonnyawsom3
2026-01-20 04:24:25
I tried comparing it to an 8bit capture and they seemed identical, but the '10bit' was definitely a lot darker when pasted into an 8bit canvas, so I'm not sure what was going on
RaveSteel
2026-01-21 05:11:59
<@794205442175402004> two questions regarding the encoding of JXLs in gamescope 1. gamescope determines the maxCLL of a given frame and wrote that into the HDR AVIF. Is there any negative to taking that determined maximum in nits and setting it as the intensity target of a lossless to-be-encoded JXL? 2. I am capturing 8 bit but also 10 bit data with JXL_TYPE_UINT16. Are JXL_BIT_DEPTH_FROM_PIXEL_FORMAT and JXL_BIT_DEPTH_FROM_CODESTREAM relevant for encode here? Or do they only/mostly affect decoding of the image? Thanks in advance
_wb_
2026-01-21 06:18:14
<@604964375924834314> can probably answer this better than me. 1. I think intensity_target defines the luminance corresponding to maxval (1.0), not the actually-reached-peak. So if you set intensity_target to the maxCLL value, you'll end up making the image darker (e.g. if the image was 1.0 signaled = 10000 nits, and the highest value reached is 3800 nits, then if you set intensity_target to 3800, you get 1.0 signaled = 3800 nits and the whole image becomes darker). 2. I don't remember, is this part of the API not documented?
username
2026-01-21 06:19:59
it's documented here but a little bit unclear: https://libjxl.readthedocs.io/en/latest/api_common.html#_CPPv415JxlBitDepthType
spider-mario
2026-01-21 07:33:39
`intensity_target` is supposed to be an upper bound on the actually reached peak (ideally as tight as possible, but that’s not mandatory), and for non-PQ only, doubles as the luminance of 1.0
2026-01-21 07:35:36
(PQ is always interpreted as specified in SMPTE ST 2084)
2026-01-21 07:40:42
(that is,)
RaveSteel
2026-01-21 07:41:29
Good to know, thank you. In that case it doesn't apply here, because gamescope only determines maxCLL for HDR, which, in gamescope's case, is always PQ About my second question, because the documentation is indeed a bit unclear: Does it make a difference if I set it, or is it optional? I am mainly asking, because I tried both and it didn't seem to make a difference, but maybe I am missing something? If I were to use it, it would probably have to be JXL_BIT_DEPTH_FROM_CODESTREAM, because the input data is 8 or 10 bit with JXL_TYPE_UINT16
jonnyawsom3
RaveSteel Good to know, thank you. In that case it doesn't apply here, because gamescope only determines maxCLL for HDR, which, in gamescope's case, is always PQ About my second question, because the documentation is indeed a bit unclear: Does it make a difference if I set it, or is it optional? I am mainly asking, because I tried both and it didn't seem to make a difference, but maybe I am missing something? If I were to use it, it would probably have to be JXL_BIT_DEPTH_FROM_CODESTREAM, because the input data is 8 or 10 bit with JXL_TYPE_UINT16
2026-01-21 10:51:01
Maybe this helps https://github.com/libjxl/libjxl/pull/1812
RaveSteel
2026-01-22 12:08:56
Hm, doesn't seem that setting the depth type like this even has an effect, at least in this case. I get bitexact identical files if all circumstances match
2026-01-22 12:11:02
The documentation says that JXL_BIT_DEPTH_FROM_PIXEL_FORMAT is the default, but maybe the encoder is smart enough to switch if it doesn't match?
jonnyawsom3
2026-01-22 12:13:04
Or the buffer is already scaled/padded correctly?
RaveSteel
2026-01-22 12:13:56
I am shifting the data so it fits into the 16 bits of JXL_TYPE_UINT16, but if I don't do that the image is just black or otherwise incorrect
2026-01-22 12:14:27
Of course it is possible, that I am doing something wrong here
_wb_
2026-01-22 06:16:17
Maybe check how cjxl/djxl are using this, iirc it does have something to do with supporting both msb padding and lsb padding.
RaveSteel
2026-01-22 11:33:14
If I understood it correctly it is mostly on how a JXL decoder interprets the data. But, seeing as all software that I tried used the default JXL_BIT_DEPTH_FROM_PIXEL_FORMAT (apparently), I will not set the bit depth type and simply retain the bitshift I am currently doing. That seems to be the best solution at this time
2026-01-22 06:56:26
I tested out JXL vs AVIF encoding on an old AMD Ryzen 2200G from 2018. Just starting DRG was hard, with 30fps and dips in just the main menu with all low settings and FSR set to ultra performance. JXL absolutely slaughtered AVIF here
2026-01-22 06:56:42
AVIF encode took 2973 ms AVIF encode took 3516 ms AVIF encode took 3217 ms AVIF encode took 2879 ms AVIF encode took 5015 ms AVIF encode took 4640 ms JXL encode took 730 ms JXL encode took 791 ms JXL encode took 832 ms JXL encode took 727 ms JXL encode took 1105 ms JXL encode took 995 ms
jonnyawsom3
2026-01-22 06:59:42
I'm on a Ryzen 1700... I'm guessing you were running with no dedicated GPU?
RaveSteel
2026-01-22 06:59:49
yep
2026-01-22 07:01:38
this was with effort 2 btw
2026-01-22 07:01:45
could have been even faster at effort 1
username
2026-01-22 07:06:35
would maybe be good to include these results with the PR. Actually speaking of, I'm wondering if it would make sense to try and set JXL as the default right off the bat since it does so much better on all metrics. Microsoft is able to get away with making Windows output JXR files for screenshots despite the only web browsers supporting them being unavailable now
RaveSteel
2026-01-22 07:07:31
I would prefer that, but setting JXL as the secondary option is the safe bet for now I reckon
2026-01-22 07:07:39
Maybe it can be brought up in the PR discussion
2026-01-22 07:08:03
And yes, I will include some of these results
2026-01-22 07:09:24
Also, while not quite an accurate measurement but rather by sight — capturing a JXL screenshot takes about 200MB of RAM. I'll take a look later at roughly how much AVIF uses
2026-01-22 07:16:46
AVIF varies much more than JXL in memory usage. I took around 20 screenshots with some pauses between each. Sometimes it was around 200MB, same as JXL
2026-01-22 07:16:56
But other times it went up to 500MB
2026-01-22 07:17:11
But it mostly stayed between 200-400MB of memory usage
2026-01-22 07:18:24
I wonder how much these will differ when I will have included 8 bit capture at a later date
2026-01-22 07:19:16
But JXL should have no problem replacing AVIF as well as PNG in gamescope, while also making scRGB capture possible, which AVIF just cannot do
2026-01-22 07:19:27
no idea about nv12.bin though
2026-01-23 02:43:02
I opened the PR https://github.com/ValveSoftware/gamescope/pull/2068
Quackdoc
2026-01-23 02:50:45
is it saving lossless or lossy?
RaveSteel
2026-01-23 02:51:02
lossless
2026-01-23 02:51:29
I didn't even add an option for lossy
Quackdoc
2026-01-23 02:52:00
perfect
RaveSteel
2026-01-23 02:52:35
lossless or nothing!
2026-01-23 02:53:11
Sadly the commits aren't squashed, that may be a problem. I did enable the option, so no idea why
Quackdoc
2026-01-23 02:53:29
commits get squashed when the maintainer merges
2026-01-23 02:53:37
or you manually squash
RaveSteel
2026-01-23 02:53:40
ah
2026-01-23 02:53:47
my first PR, so no idea
2026-01-23 02:54:34
I wonder how it'll go
2026-01-23 02:54:38
I wrote a wall of text
Quackdoc
2026-01-23 02:54:39
I usually don't squash myself unless it's asked, or I have a lot of nonsense commits I need to unfuck, but typically it's better to leave it in commits as it's easier to review
RaveSteel
2026-01-23 02:54:48
Good to know, thanks