mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2024-12-03 23:24:17 +08:00
docs/isl: Add detailed documentation about isl formats
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11366>
This commit is contained in:
parent
3894e42590
commit
0f6ebd2b73
228
docs/isl/formats.rst
Normal file
228
docs/isl/formats.rst
Normal file
@ -0,0 +1,228 @@
|
||||
Surface Formats
|
||||
===============
|
||||
|
||||
A surface format describes the encoding of color information into the actual
|
||||
data stored in memory. Surface formats in isl are specified via the
|
||||
:cpp:enum:`isl_format` enum. A complete list of surface formats is included at
|
||||
the end of this chapter.
|
||||
|
||||
In general, a surface format definition consists of two parts: encoding and
|
||||
layout.
|
||||
|
||||
Data Encoding
|
||||
-------------
|
||||
|
||||
There are several different ways that one can encode a number (or vector) into
|
||||
a binary form, and each makes different trade-offs. By default, most color
|
||||
values lie in the range [0, 1], so one of the most common encodings for color
|
||||
data is unsigned normalized where the range of an unsigned integer of a
|
||||
particular size is mapped linearly onto the interval [0, 1]. While normalized
|
||||
is certainly the most common representation for color data, not all data is
|
||||
color data, and not all values are nicely bounded. The possible data encodings
|
||||
are specified by :cpp:enum:`isl_base_type`:
|
||||
|
||||
.. doxygenenum:: isl_base_type
|
||||
|
||||
Data Layout
|
||||
-----------
|
||||
|
||||
The different data layouts fall into two categories: array and packed. When an
|
||||
array layout is used, the components are stored sequentially in an array of the
|
||||
given encoding. For instance, if the data is encoded in an 8-bit RGBA array
|
||||
format the data is stored in an array of type :c:type:`uint8_t` where the blue
|
||||
component of the :c:expr:`i`'th color value is accessed as:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
uint8_t r = ((uint8_t *)data)[i * 4 + 0];
|
||||
uint8_t g = ((uint8_t *)data)[i * 4 + 1];
|
||||
uint8_t b = ((uint8_t *)data)[i * 4 + 2];
|
||||
uint8_t a = ((uint8_t *)data)[i * 4 + 3];
|
||||
|
||||
Array formats are popular because of their simplicity. However, they are
|
||||
limited to formats where all components have the same size and fit in
|
||||
a standard C data type.
|
||||
|
||||
Packed formats, on the other hand, are encoded with the entire color value
|
||||
packed into a single 8, 16, or 32-bit value. The components are specified by
|
||||
which bits they occupy within that value. For instance, with the popular
|
||||
:c:expr:`RGB565` format, each :c:type:`vec3` takes up 16 bits and the
|
||||
:c:expr:`i`'th color value is accessed as:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
uint8_t r = (*(uint16_t *)data >> 0) & 0x1f;
|
||||
uint8_t g = (*(uint16_t *)data >> 5) & 0x3f;
|
||||
uint8_t b = (*(uint16_t *)data >> 11) & 0x1f;
|
||||
|
||||
Packed formats are useful because they allow you to specify formats with uneven
|
||||
component sizes such as :c:expr:`RGBA1010102` or where the components are
|
||||
smaller than 8 bits such as :c:expr:`RGB565` discussed above. It does,
|
||||
however, come with the restriction that the entire vector must fit within 8,
|
||||
16, or 32 bits.
|
||||
|
||||
One has to be careful when reasoning about packed formats because it is easy to
|
||||
get the color order wrong. With array formats, the channel ordering is usually
|
||||
implied directly from the format name with :c:expr:`RGBA8888` storing the
|
||||
formats as in the first example and :c:expr:`BGRA8888` storing them in the BGRA
|
||||
ordering. Packed formats, however, are not as simple because some
|
||||
specifications choose to use a MSB to LSB ordering and others LSB to MSB. One
|
||||
must be careful to pay attention to the enum in question in order to avoid
|
||||
getting them backwards.
|
||||
|
||||
From an API perspective, both types of formats are available. In Vulkan, the
|
||||
formats that are of the form :c:enumerator:`VK_FORMAT_xxx_PACKEDn` are packed
|
||||
formats where the entire color fits in :c:expr:`n` bits and formats without the
|
||||
:c:expr:`_PACKEDn` suffix are array formats. In GL, if you specify one of the
|
||||
base types such as :c:enumerator:`GL_FLOAT` you get an array format but if you
|
||||
specify a packed type such as :c:enumerator:`GL_UNSIGNED_INT_8_8_8_8_REV` you
|
||||
get a packed format.
|
||||
|
||||
The following table provides a summary of the bit orderings of different packed
|
||||
format specifications. The bit ordering is relative to the reading of the enum
|
||||
name from left to right.
|
||||
|
||||
===================== ==============
|
||||
Component Left → Right
|
||||
===================== ==============
|
||||
GL MSB → LSB
|
||||
Vulkan MSB → LSB
|
||||
mesa_format LSB → MSB
|
||||
Intel surface format LSB → MSB
|
||||
===================== ==============
|
||||
|
||||
Understanding sRGB
|
||||
------------------
|
||||
|
||||
The sRGB colorspace is one of the least tractable concepts in the entire world
|
||||
of surfaces and formats. Most texture formats are stored in a linear
|
||||
colorspace where the floating-point value corresponds linearly to intensity
|
||||
values. The sRGB color space, on the other hand, is non-linear and provides
|
||||
greater precision in the lower-intensity (darker) end of the spectrum. The
|
||||
relationship between linear and sRGB is governed by the following continuous
|
||||
bijection:
|
||||
|
||||
.. math::
|
||||
|
||||
c_l =
|
||||
\begin{cases}
|
||||
\frac{c_s}{12.92} &\text{if } c_s \le 0.04045 \\\\
|
||||
\left(\frac{c_s + 0.055}{1.055}\right)^{2.4} &\text{if } c_s > 0.04045
|
||||
\end{cases}
|
||||
|
||||
where :math:`c_l` is the linear color and :math:`c_s` is the color in sRGB.
|
||||
It is important to note that, when an alpha channel is present, the alpha
|
||||
channel is always stored in the linear colorspace.
|
||||
|
||||
The key to understanding sRGB is to think about it starting from the physical
|
||||
display. All displays work natively in sRGB. On older displays, there isn't
|
||||
so much a conversion operation as a fact of how the hardware works. All
|
||||
display hardware has a natural gamma curve required to get from linear to the
|
||||
signal level required to generate the correct color. On older CRT displays,
|
||||
the gamma curve of your average CRT is approximately the sRGB curve. More
|
||||
modern display hardware has support for additional gamma curves to try and get
|
||||
accurate colors but, for the sake of compatibility, everything still operates
|
||||
in sRGB. When an image is sent to the X server, X passes the pixels on to the
|
||||
display verbatim without doing any conversions. (Fun fact: When dealing with
|
||||
translucent windows, X blends in the wrong colorspace.) This means that the
|
||||
image into which you are rendering will always be interpreted as if it were in
|
||||
the sRGB colorspace.
|
||||
|
||||
When sampling from a texture, the value returned to the shader is in the linear
|
||||
colorspace. The conversion from sRGB happens as part of sampling. In OpenGL,
|
||||
thanks mostly to history, there are various knobs for determining when you
|
||||
should or should not encode or decode sRGB. In 2007, GL_EXT_texture_sRGB added
|
||||
support for sRGB texture formats and was included in OpenGL 2.1. In 2010,
|
||||
GL_EXT_texture_sRGB_decode added a flag to allow you to disable texture
|
||||
decoding so that the shader received the data still in the sRGB colorspace.
|
||||
Then, in 2012, GL_ARB_texture_view came along and made
|
||||
GL_EXT_texture_sRGB_decode` simultaneously obsolete and very confusing. Now,
|
||||
thanks to the combination of extensions, you can upload a texture as linear,
|
||||
create an sRGB view of it and ask that sRGB not be decoded. What format is it
|
||||
in again?
|
||||
|
||||
The situation with render targets is a bit different. Historically, you got
|
||||
your render target from the window system (which is always sRGB) and the spec
|
||||
said nothing whatsoever about encoding. All render targets were sRGB because
|
||||
that's how monitors worked and application writers were expected to understand
|
||||
that their final rendering needed to be in sRGB. However, with the advent of
|
||||
EXT_framebuffer_object this was no longer true. Also, sRGB was causing
|
||||
problems with blending because GL was blind to the fact that the output was
|
||||
sRGB and blending was occurring in the wrong colorspace. In 2006, a set of
|
||||
EXT_framebuffer_sRGB extensions added support (on both the GL and window-system
|
||||
sides) for detecting whether a particular framebuffer was in sRGB and
|
||||
instructing GL to do the conversion into the sRGB colorspace as the final step
|
||||
prior to writing out to the render target. Enabling sRGB also implied that
|
||||
blending would occur in the linear colorspace prior to sRGB conversion and
|
||||
would therefore be more accurate. When sRGB was added to the OpenGL ES spec in
|
||||
3.1, they added the query for sRGB but did not add the flag to allow you to
|
||||
turn it on and off.
|
||||
|
||||
In Vulkan, this is all much more straightforward. Your format is sRGB or it
|
||||
isn't. If you have an sRGB image and you don't want sRGB decoding to happen
|
||||
when you sample from it, you simply create a c:struct:`VkImageView` that has
|
||||
the appropriate linear format and the data will be treated as linear and not
|
||||
converted. Similarly for render targets, blending always happens in the same
|
||||
colorspace as the shader output and you determine whether or not you want sRGB
|
||||
conversion by the format of the c:struct:`VkImageView` used as the render
|
||||
target.
|
||||
|
||||
Surface Format Introspection API
|
||||
--------------------------------
|
||||
|
||||
ISL provides an API for introspecting the :cpp:enum:`isl_format` enum and
|
||||
getting various bits of information about a format. ISL provides helpers for
|
||||
introspecting both the data layout of an cpp:enum:`isl_format` and the
|
||||
capabilities of that format for a particular piece of Intel hardware.
|
||||
|
||||
Format Layout Introspection
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To get the layout of a given :cpp:enum:`isl_format`, call
|
||||
:cpp:func:`isl_format_get_layout`:
|
||||
|
||||
.. doxygenfunction:: isl_format_get_layout
|
||||
|
||||
.. doxygenstruct:: isl_format_layout
|
||||
:members:
|
||||
|
||||
.. doxygenstruct:: isl_channel_layout
|
||||
:members:
|
||||
|
||||
There are also quite a few helpers for many of the common cases that allow you
|
||||
to avoid using :cpp:struct:`isl_format_layout` manually. There are a lot of
|
||||
them so we won't include a full list here. Look at isl.h for more details.
|
||||
|
||||
Hardware Format Support Introspection
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is provided by means of a table located in isl_format.c. Looking at the
|
||||
table directly is often useful for understanding HW support for various
|
||||
formats. However, for the purposes of code cleanliness, the table is not
|
||||
exposed directly and, instead, hardware support information is exposed via
|
||||
a set of helper functions:
|
||||
|
||||
.. doxygenfunction:: isl_format_supports_rendering
|
||||
.. doxygenfunction:: isl_format_supports_alpha_blending
|
||||
.. doxygenfunction:: isl_format_supports_sampling
|
||||
.. doxygenfunction:: isl_format_supports_filtering
|
||||
.. doxygenfunction:: isl_format_supports_vertex_fetch
|
||||
.. doxygenfunction:: isl_format_supports_typed_writes
|
||||
.. doxygenfunction:: isl_format_supports_typed_reads
|
||||
.. doxygenfunction:: isl_format_supports_ccs_d
|
||||
.. doxygenfunction:: isl_format_supports_ccs_e
|
||||
.. doxygenfunction:: isl_format_supports_multisampling
|
||||
.. doxygenfunction:: isl_formats_are_ccs_e_compatible
|
||||
|
||||
Surface Format Enums
|
||||
--------------------
|
||||
|
||||
Everything in ISL is done in terms of the :cpp:enum:`isl_format` enum. However,
|
||||
for the sake of interacting with other parts of Mesa, we provide a helper for
|
||||
converting a :cpp:enum:`pipe_format` to an :cpp:enum:`isl_format`:
|
||||
|
||||
.. doxygenfunction:: isl_format_for_pipe_format
|
||||
|
||||
The :cpp:enum:`isl_format` enum is as follows:
|
||||
|
||||
.. doxygenenum:: isl_format
|
@ -10,6 +10,7 @@ Chery.
|
||||
:maxdepth: 2
|
||||
|
||||
units
|
||||
formats
|
||||
|
||||
The core representation of a surface in ISL is :cpp:struct:`isl_surf`.
|
||||
|
||||
|
@ -415,17 +415,113 @@ enum isl_format {
|
||||
* Numerical base type for channels of isl_format.
|
||||
*/
|
||||
enum PACKED isl_base_type {
|
||||
/** Data which takes up space but is ignored */
|
||||
ISL_VOID,
|
||||
|
||||
/** Data in a "raw" form and cannot be easily interpreted */
|
||||
ISL_RAW,
|
||||
|
||||
/**
|
||||
* Unsigned normalized data
|
||||
*
|
||||
* Though stored as an integer, the data is interpreted as a floating-point
|
||||
* number in the range [0, 1] where the conversion from the in-memory
|
||||
* representation to float is given by \f$\frac{x}{2^{bits} - 1}\f$.
|
||||
*/
|
||||
ISL_UNORM,
|
||||
|
||||
/**
|
||||
* Signed normalized data
|
||||
*
|
||||
* Though stored as an integer, the data is interpreted as a floating-point
|
||||
* number in the range [-1, 1] where the conversion from the in-memory
|
||||
* representation to float is given by
|
||||
* \f$max\left(\frac{x}{2^{bits - 1} - 1}, -1\right)\f$.
|
||||
*/
|
||||
ISL_SNORM,
|
||||
|
||||
/**
|
||||
* Unsigned floating-point data
|
||||
*
|
||||
* Unlike the standard IEEE floating-point representation, unsigned
|
||||
* floating-point data has no sign bit. This saves a bit of space which is
|
||||
* important if more than one float is required to represent a color value.
|
||||
* As with IEEE floats, the high bits are the exponent and the low bits are
|
||||
* the mantissa. The available bit sizes for unsigned floats are as
|
||||
* follows:
|
||||
*
|
||||
* \rst
|
||||
* ===== ========= =========
|
||||
* Bits Mantissa Exponent
|
||||
* ===== ========= =========
|
||||
* 11 6 5
|
||||
* 10 5 5
|
||||
* ===== ========= =========
|
||||
* \endrst
|
||||
*
|
||||
* In particular, both unsigned floating-point formats are identical to
|
||||
* IEEE float16 except that the sign bit and the bottom mantissa bits are
|
||||
* removed.
|
||||
*/
|
||||
ISL_UFLOAT,
|
||||
|
||||
/** Signed floating-point data
|
||||
*
|
||||
* Signed floating-point data is represented as standard IEEE floats with
|
||||
* the usual number of mantissa and exponent bits
|
||||
*
|
||||
* \rst
|
||||
* ===== ========= =========
|
||||
* Bits Mantissa Exponent
|
||||
* ===== ========= =========
|
||||
* 64 52 11
|
||||
* 32 23 8
|
||||
* 16 10 5
|
||||
* ===== ========= =========
|
||||
* \endrst
|
||||
*/
|
||||
ISL_SFLOAT,
|
||||
|
||||
/**
|
||||
* Unsigned fixed-point data
|
||||
*
|
||||
* This is a 32-bit unsigned integer that is interpreted as a 16.16
|
||||
* fixed-point value.
|
||||
*/
|
||||
ISL_UFIXED,
|
||||
|
||||
/**
|
||||
* Signed fixed-point data
|
||||
*
|
||||
* This is a 32-bit signed integer that is interpreted as a 16.16
|
||||
* fixed-point value.
|
||||
*/
|
||||
ISL_SFIXED,
|
||||
|
||||
/** Unsigned integer data */
|
||||
ISL_UINT,
|
||||
|
||||
/** Signed integer data */
|
||||
ISL_SINT,
|
||||
|
||||
/**
|
||||
* Unsigned scaled data
|
||||
*
|
||||
* This is data which is stored as an unsigned integer but interpreted as a
|
||||
* floating-point value by the hardware. The re-interpretation is done via
|
||||
* a simple unsigned integer to float cast. This is typically used as a
|
||||
* vertex format.
|
||||
*/
|
||||
ISL_USCALED,
|
||||
|
||||
/**
|
||||
* Signed scaled data
|
||||
*
|
||||
* This is data which is stored as a signed integer but interpreted as a
|
||||
* floating-point value by the hardware. The re-interpretation is done via
|
||||
* a simple signed integer to float cast. This is typically used as a
|
||||
* vertex format.
|
||||
*/
|
||||
ISL_SSCALED,
|
||||
};
|
||||
|
||||
@ -1101,21 +1197,26 @@ struct isl_extent4d {
|
||||
union { uint32_t a, array_len; };
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a single channel of an isl_format
|
||||
*/
|
||||
struct isl_channel_layout {
|
||||
enum isl_base_type type;
|
||||
enum isl_base_type type; /**< Channel data encoding */
|
||||
uint8_t start_bit; /**< Bit at which this channel starts */
|
||||
uint8_t bits; /**< Size in bits */
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the layout of an isl_format
|
||||
*
|
||||
* Each format has 3D block extent (width, height, depth). The block extent of
|
||||
* compressed formats is that of the format's compression block. For example,
|
||||
* the block extent of ISL_FORMAT_ETC2_RGB8 is (w=4, h=4, d=1). The block
|
||||
* extent of uncompressed pixel formats, such as ISL_FORMAT_R8G8B8A8_UNORM, is
|
||||
* is (w=1, h=1, d=1).
|
||||
* the block extent of `ISL_FORMAT_ETC2_RGB8` is `(w=4, h=4, d=1)`. The block
|
||||
* extent of uncompressed pixel formats, such as `ISL_FORMAT_R8G8B8A8_UNORM`,
|
||||
* is `(w=1, h=1, d=1)`.
|
||||
*/
|
||||
struct isl_format_layout {
|
||||
enum isl_format format;
|
||||
enum isl_format format; /**< Format */
|
||||
|
||||
uint16_t bpb; /**< Bits per block */
|
||||
uint8_t bw; /**< Block width, in pixels */
|
||||
@ -1135,7 +1236,7 @@ struct isl_format_layout {
|
||||
struct isl_channel_layout channels_array[7];
|
||||
};
|
||||
|
||||
/** Set if all channels have the same isl_base_type. Otherwise, ISL_BASE_VOID. */
|
||||
/** Set if all channels have the same isl_base_type. Otherwise, ISL_VOID. */
|
||||
enum isl_base_type uniform_channel_type;
|
||||
|
||||
enum isl_colorspace colorspace;
|
||||
@ -1569,6 +1670,9 @@ isl_device_init(struct isl_device *dev,
|
||||
isl_sample_count_mask_t ATTRIBUTE_CONST
|
||||
isl_device_get_sample_counts(struct isl_device *dev);
|
||||
|
||||
/**
|
||||
* \return The isl_format_layout for the given isl_format
|
||||
*/
|
||||
static inline const struct isl_format_layout * ATTRIBUTE_CONST
|
||||
isl_format_get_layout(enum isl_format fmt)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user