Units
=====

Almost every variable, function parameter, or struct field in ISL that carries
a numeric value has explicit units associated with it.  The units used in ISL
are as follows:

 * Pixels (px)
 * Samples (sa)
 * Elements (el)
 * Tiles (tl)
 * Bytes (B)
 * Rows of some other unit size (<unit>_rows)

These units are fundamental to ISL because they allow us to specify information
about a surface in a canonical way that isn't dependent on hardware generation.
Each field in an ISL data structure that stores any sort of dimension has a
suffix that declares the units for that particular value: ":c:expr:`_el`" for
elements, ":c:expr:`_sa`" for samples, etc.  If the units of the particular
field aren't quite what is wanted by the hardware, we do the conversion when we
emit :c:expr:`RENDER_SURFACE_STATE`.

This is one of the primary differences between ISL and the old miptree code and
one of the core design principles of ISL.  In the old miptree code, we tried to
keep everything in the same units as the hardware expects but this lead to
unnecessary complications as the hardware evolved.  One example of this
difference is QPitch which specifies the distance between array slices.  On
Broadwell and earlier, QPitch field in :c:expr:`RENDER_SURFACE_STATE` was in
rows of samples.  For block-compressed images, this meant it had to be
a multiple of the block height.  On Skylake, it changed to always being in rows
of elements so you have to divide the pitch in samples by the compresssion
block height.  Since the old surface state code tries to store things in
hardware units, everyone who ever reads :cpp:expr:`brw_mipmap_tree::qpitch` has
to change their interpretation based on hardware generation and whether or not
the surface was block-compressed.  In ISL, we have
:cpp:member:`isl_surf::array_pitch_el_rows` which, as the name says, is in rows
of elements.  On Broadwell and earlier, we have to multiply by the block size
of the texture when we finally fill out the hardware packet.  However, the
consistency of always being in rows of elements makes any other users of the
field much simpler because they never have to look at hardware generation or
whether or not the image is block-compressed.

**Pixels** are the most straightforward unit and are where everything starts. A
pixel simply corresponds to a single pixel (or texel if you prefer) in the
surface.  For multisampled surfaces, a pixel may contain one or more samples.
For compressed textures, a compression block may contain one or more pixels.
When initially creating a surface, everything passed to isl_surf_init is
implicitly in terms of pixels because this is what all of the APIs use.

The next unit in ISL's repertoire is **samples**.  In a multisampled surface,
each pixel corresponds to some number of samples given by
:cpp:member:`isl_surf::samples`.  The exact layout of the samples depends on
the value of :cpp:member:`isl_surf::msaa_layout`.  If the layout is
:cpp:enumerator:`ISL_MSAA_LAYOUT_ARRAY` then each logical array in the surface
corresponds to :cpp:member:`isl_surf::samples` actual slices
in the resulting surface, one per array slice.  If the layout is
:cpp:enumerator:`ISL_MSAA_LAYOUT_INTERLEAVED` then each pixel corresponds to a
2x1, 2x2, 4x2, or 4x4 grid of samples.  In order to aid in calculations, one of
the first things ISL does is to compute :cpp:member:`isl_surf::phys_level0_sa`
which gives the dimensions of the base miplevel of the surface in samples.  The
type of :cpp:member:`isl_surf::phys_level0_sa` is :cpp:struct:`isl_extent4d`
which allows us to express both the array and interleaved cases. Most of the
calculations of how the different miplevels and array slices are laid out is
done in terms of samples.

Next, we have surface **elements**.  An element is the basic unit of actual
surface memory. For multisampled textures, an element is equal to a single
sample. For block compressed textures, an element corresponds to an entire
compression block. The conversion from samples to elements is given by dividing
by the block width and block height of the surface format. This is true
regardless of whether or not the surface is multisampled; for multisampled
compressed textures (these exist for certain auxiliary formats), the block
width and block height are expressed in samples. Pixels cannot be converted
directly to elements or vice versa; any conversion between pixels and elements
*must* go through samples.

The final surface unit is **tiles**. A tile is a large rectangular block of
surface data that all fits in a single contiguous block of memory (usually a 4K
or 64K page, depending on tile format). Tiles are used to provide an
arrangement of the data in memory that yields better cache performance. The
size of a tile is always specified in surface elements.
