1// Copyright 2022 The Khronos Group Inc. 2// 3// SPDX-License-Identifier: CC-BY-4.0 4 5= VK_EXT_legacy_dithering 6:toc: left 7:refpage: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/ 8:sectnums: 9 10This proposal regards layering OpenGL over Vulkan, and provides an efficient 11path to implement OpenGL dithering on Vulkan. 12 13== Problem Statement 14 15Dithering is an important technique necessary to remove color banding, which in 16itself is an artifact of low-frequency sampling (by our eyes) of a step 17function (jumps from one color value to another due to quantization). 18This technique is particularly useful to improve the visuals of gradients 19quantized over a low-precision format (for example 20`VK_FORMAT_R5G6B5_UNORM_PACK16`), or even the highest-precision formats when 21the two ends of the gradient are close in value (for example sky at late dusk). 22 23**Dithering is best done by the Vulkan application.** 24For example, an application rendering to a swapchain with `R5G6B5` format can 25render instead to an `R8G8B8A8` (or even `B10G11R11`) lazily-allocated image in 26one subpass, then apply dithering in a second subpass along with other 27necessary work such as color space transformations. 28 29In OpenGL and OpenGL ES, the `GL_DITHER` state is enabled by default and 30dithering is made the responsibility of the driver. 31However, the dithering algorithm itself is undefined, and explicitly allows "no 32dithering" as a possible algorithm. 33As such, some vendors do not provide dithering in OpenGL at all, while others 34do, especially on some tile-based hardware. 35Those that do provide dithering implement it differently, including using 36different dithering matrix sizes, or performing dithering at different times 37(e.g. fragment output vs render pass store operation). 38Most importantly, devices that apply dithering in OpenGL have specialized 39hardware to perform this efficiently. 40 41As far as OpenGL layering is concerned, no dithering is technically an 42acceptable algorithm. 43However, in practice, due to this state being on by default and implemented by 44a number of vendors, numerous Android applications have come to (accidentally 45or otherwise) rely on the visual improvements dithering brings. 46It is therefore necessary for an OpenGL layer over Vulkan to provide a 47dithering implementation. 48 49== Solution Space 50 51=== Emulation Through Per-Draw Dithering 52 53Dithering could be emulated by applying the Bayer matrix at the end of the 54fragment shader. 55This can potentially be constrained to low-bit formats to limit its effect on 56performance and shader size. 57 58Pros: 59 60- Easy to implement by adding code to the fragment shader during shader 61 compilation. 62 63Cons: 64 65- When enabled, the dithering cost is paid extra for each pixel that is 66 overdrawn. 67- As dithering is non-temporal per the OpenGL spec, overdraw with additive 68 blending can accentuate the dithering that is applied, making the image look 69 grainy. 70- Dithering alpha can create visual artifacts as the background is leaked into 71 an otherwise completely opaque shape when blending is enabled. 72 * Quantizing alpha in the shader itself can alleviate this. 73- The generated shader size is increased, with the usual caveats regarding 74 performance. 75 76=== Emulation Through Per-Subpass Dithering 77 78Dithering can also be applied at the end of the subpass, closely emulating what 79some hardware do. 80For this to work, a post-process subpass needs to be added that applies 81dithering. 82The original subpass needs to render to a lazily-allocated image with higher 83precision. 84 85Pros: 86 87- Performant when possible. 88- No issues with blending. 89 90Cons: 91 92- Costly on non-tiled-based hardware both in memory and performance. 93- Relatively complicated to code. 94- Currently, Vulkan provides no feedback to ensure this is done efficiently 95 (e.g. whether the tile-based Vulkan driver could successfully merge and 96 execute the two subpasses on the tile, without implicit splits such as due 97 to memory constraints). 98- The high and low precision versions of the attachment live on the tile memory 99 at the same time for the final subpass, increasing tile memory usage and 100 potentially degrading performance (if the tile size has to change because of 101 that). 102 103=== Exposing Dithering Hardware Through a Vulkan Extension 104 105For vendors that support dithering in hardware, exposing it in a Vulkan 106extension would enable the OpenGL layer to match the vendors' current OpenGL 107driver in behavior. 108 109Pros: 110 111- Trivial to use. 112- Efficient. 113 114Cons: 115 116- Hardware vendors may be planning on removing this feature. 117- The dithering algorithm is grossly underspecified in OpenGL, and vendors are 118 known to apply it incompletely or incorrectly in some situations. 119 A Vulkan extension would necessarily be almost as vague as the OpenGL spec, 120 which is not in the spirit of Vulkan. 121 122== Proposal 123 124Despite the caveats, a Vulkan extension represents the most efficient way 125currently to provide equivalent visuals in an OpenGL layer compared to the 126vendors' OpenGL implementations. 127Once the OpenGL layer replaces vendors' implementations, and once the hardware 128features are removed, an emulation path can be chosen for dithering. 129By that time, it is hoped that applications requiring implicit dithering are a 130relic of the past, and paying the extra cost on newer more efficient hardware 131would be negligible, or at least acceptable. 132 133=== Features 134 135[source,c] 136---- 137typedef struct VkPhysicalDeviceLegacyDitheringFeaturesEXT { 138 VkStructureType sType; 139 void* pNext; 140 VkBool32 legacyDithering; 141} VkPhysicalDeviceLegacyDitheringFeaturesEXT; 142---- 143 144- `legacyDithering` specifies that OpenGL dithering is available. 145 146=== Enabling Dithering 147 148Extending `VkSubpassDescriptionFlagBits`, 149`VK_SUBPASS_DESCRIPTION_ENABLE_LEGACY_DITHERING_BIT_EXT` will enable dithering 150for the subpass. 151Extending `VkRenderingFlagBits`, `VK_RENDERING_ENABLE_LEGACY_DITHERING_BIT_EXT` 152does the same when using dynamic rendering. 153 154The Vulkan implementation is expected to apply dithering equivalently to the 155vendor's OpenGL driver. 156 157=== Limitations 158 159The dithering applied through the use of this extension is unspecified, and 160it is possible that the implementation performs no dithering at all for some 161formats. 162However, it will be equivalent to the vendor's OpenGL driver given equivalent 163OpenGL API calls. 164The following limitations thus apply to OpenGL as well. 165 166- Only certain formats may actually be dithered. 167- The details of the dithering algorithm are unknown. 168- Correctness of the dithering algorithm with respect to sRGB are not 169 guaranteed. 170 171**It is strongly recommended that Vulkan applications implement dithering on 172their own if needed.** 173This extension is intended only for use by OpenGL emulation layers. 174