fix(hdr): Native HDR scaled in 10 bits and shown as the game made it at 100% contrast #1942

Closed
j64esatxzdrhfg wants to merge 2 commits from j64esatxzdrhfg/fix/hdr-native-format into main
j64esatxzdrhfg commented 2026-11-10 21:23:31 +00:00 (Migrated from github.com)

With HDR on Native and Sharpness, the Window scaling filter or ultrawide's menu sides on, the game's HDR10 picture goes through an RGBA8 image. Its PQ codes lose two bits, which shows as banding in the dark, and #1941's contrast row would grade that 8-bit picture.

Builds on #1941: this PR is the last commit. #1941 and this PR are meant to land together.

Why, after the rebase: this PR first fixed Native never being taken: the texture cache shared one image between RGBA8 and RGB10A2 (format_class), so tex->format never said RGB10A2. Main's 0a28ff2 (#2038, moosky73's fix) has since given RGB10A2 a class of its own, so Native is taken on main now. This PR keeps telling Native by the format the game presents, which no longer depends on the cache's classes, and keeps the picture in 10 bits.

What changed (the last commit)

  • do_present: Native is told by vk_format(t->d.format) alone, the format the view is made in. The image's format is no longer asked: it would take an SDR picture for PQ in an image first made as RGB10A2. One log line names the presented format, its image's format and the HDR path when any of them changes.
  • present_image: Native always goes through the HDR pass, which reads the picture through that view.
    • At 100% contrast (and exposure 1) the pass writes the game's PQ codes as they are, with no decode and encode in between. Highlights above the HDR peak row reach the display as the game made them. sling86 saw the game's grade ignore the peak it is told, so the display's own tone mapping takes whatever is above it.
    • Off 100%, #1941's grade is fitted under the peak the game is told and does not limit light above it, so it moves on smoothly from this picture.
    • If the pass can't be made, Native shows its scaled picture, else black (logged once). It never falls back to a blit of the image, which converts from the image's format.
  • framegen_scale(pq): with the scaling filter, Sharpness or ultrawide's sides, the game's picture is scaled into RGB10A2 images instead of RGBA8. Its crop is copied as bits, not blitted. The RX_VK_SHOT_FX shot passes present_native_pq too.
  • shaders/fg_scale.comp: FG_SCALE_PQ makes the destination rgb10_a2 and the dither a 10-bit step. fg_scale_pq_comp_spv.h comes from test_fg_scale_sharpen.py --write (glslang 16.6.0); the RGBA8 header's code is unchanged. hdr_encode_comp_spv.h is regenerated with glslang 16.6.0.
  • Tests:
    • test_fg_scale_sharpen.py covers both headers.
    • upscale_independent_test.cpp runs the 10-bit path: pipeline, images, crop copy, the switch back, and a failed pipeline.
    • test_hdr_encode.py checks the format test (the presented format alone), the pass-through at 100%, that present_image never blits Native's image, and the shot's pq.
    • test_present_aspect.py covers Native without the pass: its scaled picture, else black.
    • present_aspect_vulkan.cpp's stubs now follow present_image, so the real-Vulkan check compiles again. It passes here; on main it does not compile.

Testing: RTX 5080, 3840x2160 HDR10, 800 nits peak, 300 nits paper white. With this PR's earlier version, before main's class fix, Native showed the game's own HDR picture instead of a flat, grey one. That ran in a build of main (8c92fe3) with this series and HDR contrast at 150% (screenshots), and earlier on v2.0.13 at 100%. Rebased on main d7a7306: it compiles with the build's clang. tests/run_all.py with hdr_encode, fg_scale, dlss_actor, dlss_checks, upscale_independent, framegen_resources, present_, upscaler_rows and menu: 28 pass, including present_aspect_vulkan on the RTX 5080. present_semaphores fails the same way on main, because this PC has no g++.

In game after the review changes (a build of main d7a7306 with this series): the present log says the game's PQ picture (Native) (format 64, its image's 64); at 100% contrast the game's own picture shows untouched, topping out at about 700 nits under the 800-nit peak row, and graded at 150% at 784 nits. Not triggered in game: the black fallback.

Not tested: AMD/Intel, Linux.

Screenshots (Native, in a build of main d7a7306 with this series)

Native 100%: the game's picture as it made it HDR contrast 150% on Native
Deku Tree, Native at 100% The rows: Native, 150%

HDR10 captures, tone-mapped to SDR for this page (paper white to white, highlights rolled off up to 800 nits).

🤖 Generated with Claude Code

With HDR on Native and Sharpness, the Window scaling filter or ultrawide's menu sides on, the game's HDR10 picture goes through an RGBA8 image. Its PQ codes lose two bits, which shows as banding in the dark, and #1941's contrast row would grade that 8-bit picture. > Builds on #1941: this PR is the last commit. #1941 and this PR are meant to land together. **Why, after the rebase:** this PR first fixed Native never being taken: the texture cache shared one image between RGBA8 and RGB10A2 (`format_class`), so `tex->format` never said RGB10A2. Main's 0a28ff2 (#2038, moosky73's fix) has since given RGB10A2 a class of its own, so Native is taken on main now. This PR keeps telling Native by the format the game presents, which no longer depends on the cache's classes, and keeps the picture in 10 bits. **What changed** (the last commit) - `do_present`: Native is told by `vk_format(t->d.format)` alone, the format the view is made in. The image's format is no longer asked: it would take an SDR picture for PQ in an image first made as RGB10A2. One log line names the presented format, its image's format and the HDR path when any of them changes. - `present_image`: Native always goes through the HDR pass, which reads the picture through that view. - At 100% contrast (and exposure 1) the pass writes the game's PQ codes as they are, with no decode and encode in between. Highlights above the HDR peak row reach the display as the game made them. sling86 saw the game's grade ignore the peak it is told, so the display's own tone mapping takes whatever is above it. - Off 100%, #1941's grade is fitted under the peak the game is told and does not limit light above it, so it moves on smoothly from this picture. - If the pass can't be made, Native shows its scaled picture, else black (logged once). It never falls back to a blit of the image, which converts from the image's format. - `framegen_scale(pq)`: with the scaling filter, Sharpness or ultrawide's sides, the game's picture is scaled into RGB10A2 images instead of RGBA8. Its crop is copied as bits, not blitted. The `RX_VK_SHOT_FX` shot passes `present_native_pq` too. - `shaders/fg_scale.comp`: `FG_SCALE_PQ` makes the destination `rgb10_a2` and the dither a 10-bit step. `fg_scale_pq_comp_spv.h` comes from `test_fg_scale_sharpen.py --write` (glslang 16.6.0); the RGBA8 header's code is unchanged. `hdr_encode_comp_spv.h` is regenerated with glslang 16.6.0. - Tests: - `test_fg_scale_sharpen.py` covers both headers. - `upscale_independent_test.cpp` runs the 10-bit path: pipeline, images, crop copy, the switch back, and a failed pipeline. - `test_hdr_encode.py` checks the format test (the presented format alone), the pass-through at 100%, that `present_image` never blits Native's image, and the shot's `pq`. - `test_present_aspect.py` covers Native without the pass: its scaled picture, else black. - `present_aspect_vulkan.cpp`'s stubs now follow `present_image`, so the real-Vulkan check compiles again. It passes here; on main it does not compile. **Testing:** RTX 5080, 3840x2160 HDR10, 800 nits peak, 300 nits paper white. With this PR's earlier version, before main's class fix, Native showed the game's own HDR picture instead of a flat, grey one. That ran in a build of main (8c92fe3) with this series and HDR contrast at 150% (screenshots), and earlier on v2.0.13 at 100%. Rebased on main d7a7306: it compiles with the build's clang. `tests/run_all.py` with hdr_encode, fg_scale, dlss_actor, dlss_checks, upscale_independent, framegen_resources, present_, upscaler_rows and menu: 28 pass, including `present_aspect_vulkan` on the RTX 5080. `present_semaphores` fails the same way on main, because this PC has no g++. **In game after the review changes** (a build of main d7a7306 with this series): the present log says `the game's PQ picture (Native)` (format 64, its image's 64); at 100% contrast the game's own picture shows untouched, topping out at about 700 nits under the 800-nit peak row, and graded at 150% at 784 nits. Not triggered in game: the black fallback. **Not tested:** AMD/Intel, Linux. **Screenshots** (Native, in a build of main d7a7306 with this series) | Native 100%: the game's picture as it made it | HDR contrast 150% on Native | |---|---| | ![Deku Tree, Native at 100%](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/hdr-native/native-100.jpg) | ![The rows: Native, 150%](https://raw.githubusercontent.com/j64esatxzdrhfg/Hookshot/pr-screenshots/hdr-native/menu-native-150.jpg) | _HDR10 captures, tone-mapped to SDR for this page (paper white to white, highlights rolled off up to 800 nits)._ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hckrman101 (Migrated from github.com) reviewed 2026-11-11 02:20:58 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is the piece #1941 was missing, and it answers both Native points from my review there: Native is now actually taken, and with Sharpness, the scaling filter or ultrawide's sides the game's PQ codes stay in 10 bits instead of going through RGBA8.

I went through the last commit (9aeb580) and it reads right:

  • shown_format comes from vk_format(t->d.format), and direct_view is made by texture_view(tex, t, ...), which also picks the view format from t. So the scale pass and the HDR pass both read the RGB10A2 picture through an RGB10A2 view, even when the cached image is RGBA8 (format_class aliasing, MUTABLE_FORMAT images).
  • The crop goes through vkCmdCopyImage when pq is set. That's a raw bit copy between two 4-byte formats, so RGB10A2 bits held in an RGBA8 image come through intact, where a blit would have converted them.
  • sling86's warning on #1940 (copying frame generation's RGBA8 made frames as RGB10A2 posterises them) doesn't apply here. The display thread calls present_image with crop == nullptr for the made frames, so the copy branch never runs for them. They're sampled as RGBA8 holding PQ values, which the pq scale and HDR passes handle fine.
  • rgb10_a2 storage writes aren't a new requirement, since hdr_encode.comp already writes that format. If fg_scale_pq can't be built, the code logs once and falls back to the unscaled HDR pass, and the test covers that.
  • Switching pq resets the cached sizes, so the images get made again (with the device wait) in the other format. That only happens when the mode changes.
  • The SPIR-V variant check in test_fg_scale_sharpen.py and the string checks in test_hdr_encode.py / test_present_aspect.py match the code.

Open points:

  1. The || tex->format == A2B10G10R10 half of the Native test (inline). Is there a reason to keep it now that shown_format exists?
  2. At 100% contrast Native is no longer passed through untouched. It now goes through decode, then the peak limit, then encode, so anything the game puts above the HDR peak row gets scaled down to the peak. sling86 saw the game's grade ignore the peak it's given, so this can flatten highlights that used to reach the display. That's the same point as my inline on #1941, and it now applies at 100% too. If the pass is only needed at 100% to read through the right view, hdr_encode.comp could skip the peak limit for mode 1 when contrast and exposure are both 1. That would keep it a pure re-encode. This is a judgment call, so I'd at least note it in the description.
  3. Nit: if framegen_hdr returns null under Native, present_image still falls back to blit_crop_to_rect(src, ...) on the image. That's exactly the format-converting blit the comment says garbles the colours. It only happens on a failure path, but it could take scaled or skip the blit.
  4. Nit: the RX_VK_SHOT_FX call to framegen_scale (vk_replay.cpp ~3078) still uses the default pq = false. Under Native, every shot flips scale.pq, so the images are made twice with a device wait each time, and the shot goes through the RGBA8 path. It's debug-only, but passing present_native_pq there is a one-word fix.
  5. Nit, and not this PR's fault: the framegen_scale stub in tests/present_aspect_vulkan.cpp still has 8 parameters. That fixture already doesn't compile against main's present_image (no dev.hdr / framegen_hdr / present_native_pq in it), and run_all skips it without a Vulkan device. It's worth fixing while the signature is changing anyway.

Stacking: this branch carries #1940 (eea8875) and #1941 (69f7d3e) as its first two commits, so it can only land after them or together with them. My earlier suggestion to land #1941 and #1942 together still stands. #1943 to #1946 don't touch these hunks. #1944 adds lines near the end of present_image, well away from this change. No CI has run on this branch (fork workflows are waiting for approval), so nothing is green or red yet. Whether the DLSS-for-Native part from #1940 goes in as is is still @NotRexed's call, so I'm leaving this as a comment and not an approval.

Thanks, this is the piece #1941 was missing, and it answers both Native points from my review there: Native is now actually taken, and with Sharpness, the scaling filter or ultrawide's sides the game's PQ codes stay in 10 bits instead of going through RGBA8. I went through the last commit (9aeb580) and it reads right: - `shown_format` comes from `vk_format(t->d.format)`, and `direct_view` is made by `texture_view(tex, t, ...)`, which also picks the view format from `t`. So the scale pass and the HDR pass both read the RGB10A2 picture through an RGB10A2 view, even when the cached image is RGBA8 (`format_class` aliasing, MUTABLE_FORMAT images). - The crop goes through `vkCmdCopyImage` when `pq` is set. That's a raw bit copy between two 4-byte formats, so RGB10A2 bits held in an RGBA8 image come through intact, where a blit would have converted them. - sling86's warning on #1940 (copying frame generation's RGBA8 made frames as RGB10A2 posterises them) doesn't apply here. The display thread calls `present_image` with `crop == nullptr` for the made frames, so the copy branch never runs for them. They're sampled as RGBA8 holding PQ values, which the pq scale and HDR passes handle fine. - `rgb10_a2` storage writes aren't a new requirement, since `hdr_encode.comp` already writes that format. If `fg_scale_pq` can't be built, the code logs once and falls back to the unscaled HDR pass, and the test covers that. - Switching `pq` resets the cached sizes, so the images get made again (with the device wait) in the other format. That only happens when the mode changes. - The SPIR-V variant check in `test_fg_scale_sharpen.py` and the string checks in `test_hdr_encode.py` / `test_present_aspect.py` match the code. Open points: 1. **The `|| tex->format == A2B10G10R10` half of the Native test** (inline). Is there a reason to keep it now that `shown_format` exists? 2. **At 100% contrast Native is no longer passed through untouched.** It now goes through decode, then the peak limit, then encode, so anything the game puts above the HDR peak row gets scaled down to the peak. sling86 saw the game's grade ignore the peak it's given, so this can flatten highlights that used to reach the display. That's the same point as my inline on #1941, and it now applies at 100% too. If the pass is only needed at 100% to read through the right view, `hdr_encode.comp` could skip the peak limit for mode 1 when contrast and exposure are both 1. That would keep it a pure re-encode. This is a judgment call, so I'd at least note it in the description. 3. Nit: if `framegen_hdr` returns null under Native, `present_image` still falls back to `blit_crop_to_rect(src, ...)` on the image. That's exactly the format-converting blit the comment says garbles the colours. It only happens on a failure path, but it could take `scaled` or skip the blit. 4. Nit: the `RX_VK_SHOT_FX` call to `framegen_scale` (vk_replay.cpp ~3078) still uses the default `pq = false`. Under Native, every shot flips `scale.pq`, so the images are made twice with a device wait each time, and the shot goes through the RGBA8 path. It's debug-only, but passing `present_native_pq` there is a one-word fix. 5. Nit, and not this PR's fault: the `framegen_scale` stub in `tests/present_aspect_vulkan.cpp` still has 8 parameters. That fixture already doesn't compile against main's `present_image` (no `dev.hdr` / `framegen_hdr` / `present_native_pq` in it), and run_all skips it without a Vulkan device. It's worth fixing while the signature is changing anyway. Stacking: this branch carries #1940 (eea8875) and #1941 (69f7d3e) as its first two commits, so it can only land after them or together with them. My earlier suggestion to land #1941 and #1942 together still stands. #1943 to #1946 don't touch these hunks. #1944 adds lines near the end of `present_image`, well away from this change. No CI has run on this branch (fork workflows are waiting for approval), so nothing is green or red yet. Whether the DLSS-for-Native part from #1940 goes in as is is still @NotRexed's call, so I'm leaving this as a comment and not an approval.
hckrman101 (Migrated from github.com) commented 2026-11-11 02:20:58 +00:00

With the format the game presents now known, why keep this half? It's the reverse of the bug this PR fixes. If the window's image was first made as RGB10A2 (the game started in HDR) and the game later presents an RGBA8 picture through it, this still says Native. The SDR picture then gets decoded as PQ, and with a crop the new vkCmdCopyImage copies RGBA8 bits into an RGB10A2 image, so you get garbage, not just a wrong tone. shown_format alone (it falls back to VK_FORMAT_UNDEFINED only when t is null, and tex needs t anyway) looks like the accurate test. If there's a case where vk_format(t->d.format) doesn't say RGB10A2 but the picture is PQ, a comment naming it would help.

With the format the game presents now known, why keep this half? It's the reverse of the bug this PR fixes. If the window's image was first made as RGB10A2 (the game started in HDR) and the game later presents an RGBA8 picture through it, this still says Native. The SDR picture then gets decoded as PQ, and with a crop the new `vkCmdCopyImage` copies RGBA8 bits into an RGB10A2 image, so you get garbage, not just a wrong tone. `shown_format` alone (it falls back to `VK_FORMAT_UNDEFINED` only when `t` is null, and `tex` needs `t` anyway) looks like the accurate test. If there's a case where `vk_format(t->d.format)` doesn't say RGB10A2 but the picture is PQ, a comment naming it would help.
j64esatxzdrhfg commented 2026-11-11 10:39:26 +00:00 (Migrated from github.com)

Thanks. This now builds on #1941 alone, rebased on main d7a7306, which includes 0a28ff2 (#2038). That fix took care of the cache aliasing this PR first described, so the description now says what's left: the presented-format test and the 10-bit path. On your points:

  1. The tex->format half is dropped (inline).
  2. At 100% contrast and exposure 1, hdr_encode.comp now writes the sampled PQ code as it is for mode 1, with no decode and encode in between. Native still goes through the pass, because the pass reads the right view. Off 100%, #1941's grade is fitted under the peak the game is told and doesn't limit light above it, so the two meet at 100%. The test checks that the pass-through comes before any decode.
  3. When framegen_hdr returns null under Native, present_image takes scaled if there is one, else clears to black and logs once. It never blits src. test_present_aspect.py covers both, with and without the scaling filter.
  4. The RX_VK_SHOT_FX call passes present_native_pq.load(...).
  5. present_aspect_vulkan.cpp's stubs now follow present_image (framegen_scale's pq, framegen_hdr, framegen_scaled_view, dev.hdr, present_native_pq, logf). It compiles and passes here at this head on an RTX 5080. At #1941's head it still fails, as on main, because that head's present block reads opts.hdr_contrast.

In game (a build of main d7a7306 with this series): the log says the game's PQ picture (Native), the game's picture shows untouched at 100% (about 700 nits at most under the 800-nit row) and graded at 150% stays under the row (784 nits). The black fallback wasn't triggered.

Thanks. This now builds on #1941 alone, rebased on main d7a7306, which includes 0a28ff2 (#2038). That fix took care of the cache aliasing this PR first described, so the description now says what's left: the presented-format test and the 10-bit path. On your points: 1. The `tex->format` half is dropped (inline). 2. At 100% contrast and exposure 1, `hdr_encode.comp` now writes the sampled PQ code as it is for mode 1, with no decode and encode in between. Native still goes through the pass, because the pass reads the right view. Off 100%, #1941's grade is fitted under the peak the game is told and doesn't limit light above it, so the two meet at 100%. The test checks that the pass-through comes before any decode. 3. When `framegen_hdr` returns null under Native, `present_image` takes `scaled` if there is one, else clears to black and logs once. It never blits `src`. `test_present_aspect.py` covers both, with and without the scaling filter. 4. The `RX_VK_SHOT_FX` call passes `present_native_pq.load(...)`. 5. `present_aspect_vulkan.cpp`'s stubs now follow `present_image` (`framegen_scale`'s `pq`, `framegen_hdr`, `framegen_scaled_view`, `dev.hdr`, `present_native_pq`, `logf`). It compiles and passes here at this head on an RTX 5080. At #1941's head it still fails, as on main, because that head's present block reads `opts.hdr_contrast`. In game (a build of main d7a7306 with this series): the log says `the game's PQ picture (Native)`, the game's picture shows untouched at 100% (about 700 nits at most under the 800-nit row) and graded at 150% stays under the row (784 nits). The black fallback wasn't triggered.
j64esatxzdrhfg (Migrated from github.com) reviewed 2026-11-11 10:39:27 +00:00
j64esatxzdrhfg (Migrated from github.com) commented 2026-11-11 10:39:27 +00:00

No good reason; it's dropped. Native is now shown_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 alone, and the comment says why the image's format isn't asked. test_hdr_encode.py fails if tex->format comes back into that store. With main's 0a28ff2 the two can't disagree for the window's textures any more, but the presented format is the one the view is made in, so it's the one to ask.

No good reason; it's dropped. Native is now `shown_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32` alone, and the comment says why the image's format isn't asked. `test_hdr_encode.py` fails if `tex->format` comes back into that store. With main's 0a28ff2 the two can't disagree for the window's textures any more, but the presented format is the one the view is made in, so it's the one to ask.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:46:19 +00:00
Joey-Magnus (Migrated from github.com) left a comment

This is on main through #2342's squash merge (a0dfd28), which carried #1941, #1942 and #2243. I checked each file of this PR against origin/main:

  • On main exactly: hdr_encode.comp and hdr_encode_comp_spv.h, fg_scale.comp, fg_scale_comp_spv.h and fg_scale_pq_comp_spv.h, vk_framegen.h, vk_menu, and every test except test_hdr_encode.py.
  • The three files left differ only by later changes on main. In vk_framegen.cpp it's #2243's DLSS lines next to the fg_scale_pq include (line 36). In vk_replay.cpp, the RX_VK_SHOT_FX read moved to env_on (#2042) and the framegen_scale(..., present_native_pq) call is there at :3247. test_hdr_encode.py's docstring gained the DLSS paragraph.

Nothing is left to merge, so I'd close it. #2243 is still open even though its commit also landed through #2342, so it's probably worth checking too.

This is on main through #2342's squash merge (a0dfd28), which carried #1941, #1942 and #2243. I checked each file of this PR against origin/main: - On main exactly: hdr_encode.comp and hdr_encode_comp_spv.h, fg_scale.comp, fg_scale_comp_spv.h and fg_scale_pq_comp_spv.h, vk_framegen.h, vk_menu, and every test except test_hdr_encode.py. - The three files left differ only by later changes on main. In vk_framegen.cpp it's #2243's DLSS lines next to the `fg_scale_pq` include (line 36). In vk_replay.cpp, the `RX_VK_SHOT_FX` read moved to `env_on` (#2042) and the `framegen_scale(..., present_native_pq)` call is there at :3247. test_hdr_encode.py's docstring gained the DLSS paragraph. Nothing is left to merge, so I'd close it. #2243 is still open even though its commit also landed through #2342, so it's probably worth checking too.
hckrman101 commented 2026-11-11 22:17:22 +00:00 (Migrated from github.com)

This now conflicts with main in gpu/vk/vk_menu.cpp, gpu/vk/vk_menu.h, gpu/vk/vk_replay.cpp, tests/test_hdr_encode.py. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.

This now conflicts with main in `gpu/vk/vk_menu.cpp`, `gpu/vk/vk_menu.h`, `gpu/vk/vk_replay.cpp`, `tests/test_hdr_encode.py`. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.
github-sync closed this pull request 2026-11-12 09:32:07 +00:00
Owner

@j64esatxzdrhfg commented on GitHub · 2026-11-12 08:33 UTC · original

Closing as landed through #2342's squash merge (a0dfd28): the 10-bit scaling, the presented-format test and the 100% pass-through are all on main. Thanks @Joey-Magnus for the file-by-file check and @hckrman101 for the review.

> [@j64esatxzdrhfg](https://github.com/j64esatxzdrhfg) commented on GitHub · 2026-11-12 08:33 UTC · [original](https://github.com/NotRexed/Hookshot/pull/1942#issuecomment-6818775214) Closing as landed through #2342's squash merge (a0dfd28): the 10-bit scaling, the presented-format test and the 100% pass-through are all on main. Thanks @Joey-Magnus for the file-by-file check and @hckrman101 for the review. <!-- github-sync:c:6818775214 -->
Commenting is not possible because the repository is archived.
No description provided.