1 | Surface Formats
|
---|
2 | ===============
|
---|
3 |
|
---|
4 | A surface format describes the encoding of color information into the actual
|
---|
5 | data stored in memory. Surface formats in isl are specified via the
|
---|
6 | :c:enum:`isl_format` enum. A complete list of surface formats is included at
|
---|
7 | the end of this chapter.
|
---|
8 |
|
---|
9 | In general, a surface format definition consists of two parts: encoding and
|
---|
10 | layout.
|
---|
11 |
|
---|
12 | Data Encoding
|
---|
13 | -------------
|
---|
14 |
|
---|
15 | There are several different ways that one can encode a number (or vector) into
|
---|
16 | a binary form, and each makes different trade-offs. By default, most color
|
---|
17 | values lie in the range [0, 1], so one of the most common encodings for color
|
---|
18 | data is unsigned normalized where the range of an unsigned integer of a
|
---|
19 | particular size is mapped linearly onto the interval [0, 1]. While normalized
|
---|
20 | is certainly the most common representation for color data, not all data is
|
---|
21 | color data, and not all values are nicely bounded. The possible data encodings
|
---|
22 | are specified by :c:enum:`isl_base_type`:
|
---|
23 |
|
---|
24 | .. c:autoenum:: isl_base_type
|
---|
25 | :file: src/intel/isl/isl.h
|
---|
26 | :members:
|
---|
27 |
|
---|
28 | Data Layout
|
---|
29 | -----------
|
---|
30 |
|
---|
31 | The different data layouts fall into two categories: array and packed. When an
|
---|
32 | array layout is used, the components are stored sequentially in an array of the
|
---|
33 | given encoding. For instance, if the data is encoded in an 8-bit RGBA array
|
---|
34 | format the data is stored in an array of type :c:type:`uint8_t` where the blue
|
---|
35 | component of the `i`'th color value is accessed as:
|
---|
36 |
|
---|
37 | .. code-block:: C
|
---|
38 |
|
---|
39 | uint8_t r = ((uint8_t *)data)[i * 4 + 0];
|
---|
40 | uint8_t g = ((uint8_t *)data)[i * 4 + 1];
|
---|
41 | uint8_t b = ((uint8_t *)data)[i * 4 + 2];
|
---|
42 | uint8_t a = ((uint8_t *)data)[i * 4 + 3];
|
---|
43 |
|
---|
44 | Array formats are popular because of their simplicity. However, they are
|
---|
45 | limited to formats where all components have the same size and fit in
|
---|
46 | a standard C data type.
|
---|
47 |
|
---|
48 | Packed formats, on the other hand, are encoded with the entire color value
|
---|
49 | packed into a single 8, 16, or 32-bit value. The components are specified by
|
---|
50 | which bits they occupy within that value. For instance, with the popular
|
---|
51 | `RGB565` format, each :c:type:`vec3` takes up 16 bits and the
|
---|
52 | `i`'th color value is accessed as:
|
---|
53 |
|
---|
54 | .. code-block:: C
|
---|
55 |
|
---|
56 | uint8_t r = (*(uint16_t *)data >> 0) & 0x1f;
|
---|
57 | uint8_t g = (*(uint16_t *)data >> 5) & 0x3f;
|
---|
58 | uint8_t b = (*(uint16_t *)data >> 11) & 0x1f;
|
---|
59 |
|
---|
60 | Packed formats are useful because they allow you to specify formats with uneven
|
---|
61 | component sizes such as `RGBA1010102` or where the components are
|
---|
62 | smaller than 8 bits such as `RGB565` discussed above. It does,
|
---|
63 | however, come with the restriction that the entire vector must fit within 8,
|
---|
64 | 16, or 32 bits.
|
---|
65 |
|
---|
66 | One has to be careful when reasoning about packed formats because it is easy to
|
---|
67 | get the color order wrong. With array formats, the channel ordering is usually
|
---|
68 | implied directly from the format name with `RGBA8888` storing the
|
---|
69 | formats as in the first example and `BGRA8888` storing them in the BGRA
|
---|
70 | ordering. Packed formats, however, are not as simple because some
|
---|
71 | specifications choose to use a MSB to LSB ordering and others LSB to MSB. One
|
---|
72 | must be careful to pay attention to the enum in question in order to avoid
|
---|
73 | getting them backwards.
|
---|
74 |
|
---|
75 | From an API perspective, both types of formats are available. In Vulkan, the
|
---|
76 | formats that are of the form :c:enumerator:`VK_FORMAT_xxx_PACKEDn` are packed
|
---|
77 | formats where the entire color fits in `n` bits and formats without the
|
---|
78 | `_PACKEDn` suffix are array formats. In GL, if you specify one of the
|
---|
79 | base types such as :c:enumerator:`GL_FLOAT` you get an array format but if you
|
---|
80 | specify a packed type such as :c:enumerator:`GL_UNSIGNED_INT_8_8_8_8_REV` you
|
---|
81 | get a packed format.
|
---|
82 |
|
---|
83 | The following table provides a summary of the bit orderings of different packed
|
---|
84 | format specifications. The bit ordering is relative to the reading of the enum
|
---|
85 | name from left to right.
|
---|
86 |
|
---|
87 | ===================== ==============
|
---|
88 | Component Left → Right
|
---|
89 | ===================== ==============
|
---|
90 | GL MSB → LSB
|
---|
91 | Vulkan MSB → LSB
|
---|
92 | mesa_format LSB → MSB
|
---|
93 | Intel surface format LSB → MSB
|
---|
94 | ===================== ==============
|
---|
95 |
|
---|
96 | Understanding sRGB
|
---|
97 | ------------------
|
---|
98 |
|
---|
99 | The sRGB colorspace is one of the least tractable concepts in the entire world
|
---|
100 | of surfaces and formats. Most texture formats are stored in a linear
|
---|
101 | colorspace where the floating-point value corresponds linearly to intensity
|
---|
102 | values. The sRGB color space, on the other hand, is non-linear and provides
|
---|
103 | greater precision in the lower-intensity (darker) end of the spectrum. The
|
---|
104 | relationship between linear and sRGB is governed by the following continuous
|
---|
105 | bijection:
|
---|
106 |
|
---|
107 | .. math::
|
---|
108 |
|
---|
109 | c_l =
|
---|
110 | \begin{cases}
|
---|
111 | \frac{c_s}{12.92} &\text{if } c_s \le 0.04045 \\\\
|
---|
112 | \left(\frac{c_s + 0.055}{1.055}\right)^{2.4} &\text{if } c_s > 0.04045
|
---|
113 | \end{cases}
|
---|
114 |
|
---|
115 | where :math:`c_l` is the linear color and :math:`c_s` is the color in sRGB.
|
---|
116 | It is important to note that, when an alpha channel is present, the alpha
|
---|
117 | channel is always stored in the linear colorspace.
|
---|
118 |
|
---|
119 | The key to understanding sRGB is to think about it starting from the physical
|
---|
120 | display. All displays work natively in sRGB. On older displays, there isn't
|
---|
121 | so much a conversion operation as a fact of how the hardware works. All
|
---|
122 | display hardware has a natural gamma curve required to get from linear to the
|
---|
123 | signal level required to generate the correct color. On older CRT displays,
|
---|
124 | the gamma curve of your average CRT is approximately the sRGB curve. More
|
---|
125 | modern display hardware has support for additional gamma curves to try and get
|
---|
126 | accurate colors but, for the sake of compatibility, everything still operates
|
---|
127 | in sRGB. When an image is sent to the X server, X passes the pixels on to the
|
---|
128 | display verbatim without doing any conversions. (Fun fact: When dealing with
|
---|
129 | translucent windows, X blends in the wrong colorspace.) This means that the
|
---|
130 | image into which you are rendering will always be interpreted as if it were in
|
---|
131 | the sRGB colorspace.
|
---|
132 |
|
---|
133 | When sampling from a texture, the value returned to the shader is in the linear
|
---|
134 | colorspace. The conversion from sRGB happens as part of sampling. In OpenGL,
|
---|
135 | thanks mostly to history, there are various knobs for determining when you
|
---|
136 | should or should not encode or decode sRGB. In 2007, :ext:`GL_EXT_texture_sRGB`
|
---|
137 | added support for sRGB texture formats and was included in OpenGL 2.1. In
|
---|
138 | 2010, :ext:`GL_EXT_texture_sRGB_decode` added a flag to allow you to disable
|
---|
139 | texture decoding so that the shader received the data still in the sRGB
|
---|
140 | colorspace. Then, in 2012, :ext:`GL_ARB_texture_view` came along and made
|
---|
141 | :ext:`GL_EXT_texture_sRGB_decode` simultaneously obsolete and very confusing.
|
---|
142 | Now, thanks to the combination of extensions, you can upload a texture as
|
---|
143 | linear, create an sRGB view of it and ask that sRGB not be decoded. What
|
---|
144 | format is it in again?
|
---|
145 |
|
---|
146 | The situation with render targets is a bit different. Historically, you got
|
---|
147 | your render target from the window system (which is always sRGB) and the spec
|
---|
148 | said nothing whatsoever about encoding. All render targets were sRGB because
|
---|
149 | that's how monitors worked and application writers were expected to understand
|
---|
150 | that their final rendering needed to be in sRGB. However, with the advent of
|
---|
151 | :ext:`GL_EXT_framebuffer_object` this was no longer true. Also, sRGB was causing
|
---|
152 | problems with blending because GL was blind to the fact that the output was
|
---|
153 | sRGB and blending was occurring in the wrong colorspace. In 2006, a set of
|
---|
154 | :ext:`GL_EXT_framebuffer_sRGB` extensions added support (on both the GL and
|
---|
155 | window-system sides) for detecting whether a particular framebuffer was in sRGB
|
---|
156 | and instructing GL to do the conversion into the sRGB colorspace as the final
|
---|
157 | step prior to writing out to the render target. Enabling sRGB also implied
|
---|
158 | that blending would occur in the linear colorspace prior to sRGB conversion and
|
---|
159 | would therefore be more accurate. When sRGB was added to the OpenGL ES spec in
|
---|
160 | 3.1, they added the query for sRGB but did not add the flag to allow you to
|
---|
161 | turn it on and off.
|
---|
162 |
|
---|
163 | In Vulkan, this is all much more straightforward. Your format is sRGB or it
|
---|
164 | isn't. If you have an sRGB image and you don't want sRGB decoding to happen
|
---|
165 | when you sample from it, you simply create a :c:struct:`VkImageView` that has
|
---|
166 | the appropriate linear format and the data will be treated as linear and not
|
---|
167 | converted. Similarly for render targets, blending always happens in the same
|
---|
168 | colorspace as the shader output and you determine whether or not you want sRGB
|
---|
169 | conversion by the format of the :c:struct:`VkImageView` used as the render
|
---|
170 | target.
|
---|
171 |
|
---|
172 | Surface Format Introspection API
|
---|
173 | --------------------------------
|
---|
174 |
|
---|
175 | ISL provides an API for introspecting the :c:enum:`isl_format` enum and
|
---|
176 | getting various bits of information about a format. ISL provides helpers for
|
---|
177 | introspecting both the data layout of an :c:enum:`isl_format` and the
|
---|
178 | capabilities of that format for a particular piece of Intel hardware.
|
---|
179 |
|
---|
180 | Format Layout Introspection
|
---|
181 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
---|
182 |
|
---|
183 | To get the layout of a given :c:enum:`isl_format`, call
|
---|
184 | :c:func:`isl_format_get_layout`:
|
---|
185 |
|
---|
186 | .. c:autofunction:: isl_format_get_layout
|
---|
187 |
|
---|
188 | .. c:autostruct:: isl_format_layout
|
---|
189 | :members:
|
---|
190 |
|
---|
191 | .. c:autostruct:: isl_channel_layout
|
---|
192 | :members:
|
---|
193 |
|
---|
194 | There are also quite a few helpers for many of the common cases that allow you
|
---|
195 | to avoid using :c:struct:`isl_format_layout` manually. There are a lot of
|
---|
196 | them so we won't include a full list here. Look at isl.h for more details.
|
---|
197 |
|
---|
198 | Hardware Format Support Introspection
|
---|
199 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
---|
200 |
|
---|
201 | This is provided by means of a table located in isl_format.c. Looking at the
|
---|
202 | table directly is often useful for understanding HW support for various
|
---|
203 | formats. However, for the purposes of code cleanliness, the table is not
|
---|
204 | exposed directly and, instead, hardware support information is exposed via
|
---|
205 | a set of helper functions:
|
---|
206 |
|
---|
207 | .. c:autofunction:: isl_format_supports_rendering
|
---|
208 |
|
---|
209 | .. c:autofunction:: isl_format_supports_alpha_blending
|
---|
210 |
|
---|
211 | .. c:autofunction:: isl_format_supports_sampling
|
---|
212 |
|
---|
213 | .. c:autofunction:: isl_format_supports_filtering
|
---|
214 |
|
---|
215 | .. c:autofunction:: isl_format_supports_vertex_fetch
|
---|
216 |
|
---|
217 | .. c:autofunction:: isl_format_supports_typed_writes
|
---|
218 | :file: src/intel/isl/isl_format.c
|
---|
219 |
|
---|
220 | .. c:autofunction:: isl_format_supports_typed_reads
|
---|
221 |
|
---|
222 | .. c:autofunction:: isl_format_supports_ccs_d
|
---|
223 |
|
---|
224 | .. c:autofunction:: isl_format_supports_ccs_e
|
---|
225 |
|
---|
226 | .. c:autofunction:: isl_format_supports_multisampling
|
---|
227 |
|
---|
228 | .. c:autofunction:: isl_formats_are_ccs_e_compatible
|
---|
229 |
|
---|
230 | Surface Format Enums
|
---|
231 | --------------------
|
---|
232 |
|
---|
233 | Everything in ISL is done in terms of the :c:enum:`isl_format` enum. However,
|
---|
234 | for the sake of interacting with other parts of Mesa, we provide a helper for
|
---|
235 | converting a :c:enum:`pipe_format` to an :c:enum:`isl_format`:
|
---|
236 |
|
---|
237 | .. c:autofunction:: isl_format_for_pipe_format
|
---|
238 |
|
---|
239 | The :c:enum:`isl_format` enum is as follows:
|
---|
240 |
|
---|
241 | .. c:autoenum:: isl_format
|
---|
242 | :members:
|
---|