From 0f6ebd2b73207845be0f01d49c22b0a48c7ff367 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 14 Jun 2021 23:44:05 -0500 Subject: [PATCH] docs/isl: Add detailed documentation about isl formats Part-of: --- docs/isl/formats.rst | 228 +++++++++++++++++++++++++++++++++++++++++++ docs/isl/index.rst | 1 + src/intel/isl/isl.h | 116 ++++++++++++++++++++-- 3 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 docs/isl/formats.rst diff --git a/docs/isl/formats.rst b/docs/isl/formats.rst new file mode 100644 index 00000000000..71a32158d8c --- /dev/null +++ b/docs/isl/formats.rst @@ -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 diff --git a/docs/isl/index.rst b/docs/isl/index.rst index e3dbd694a19..74dbcda171d 100644 --- a/docs/isl/index.rst +++ b/docs/isl/index.rst @@ -10,6 +10,7 @@ Chery. :maxdepth: 2 units + formats The core representation of a surface in ISL is :cpp:struct:`isl_surf`. diff --git a/src/intel/isl/isl.h b/src/intel/isl/isl.h index c2fb791c23d..2a03bc847dc 100644 --- a/src/intel/isl/isl.h +++ b/src/intel/isl/isl.h @@ -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) {