summaryrefslogtreecommitdiff
path: root/src/intel/isl/docs/isl-intro.rst
diff options
context:
space:
mode:
Diffstat (limited to 'src/intel/isl/docs/isl-intro.rst')
-rw-r--r--src/intel/isl/docs/isl-intro.rst256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/intel/isl/docs/isl-intro.rst b/src/intel/isl/docs/isl-intro.rst
new file mode 100644
index 0000000000..bb0f91c8c6
--- /dev/null
+++ b/src/intel/isl/docs/isl-intro.rst
@@ -0,0 +1,256 @@
+Introduction to ISL
+===================
+
+All of the documentation here focuses around ISL, the **I**\ ntel
+**S**\ urface **L**\ ayout library that was originally written by Chad
+for doing surface layout in the Vulkan driver. When writing the Vulkan
+driver we decided not to port the old code from ``intel_mipmap_tree``.
+Instead, we did a complete rewrite of the surface layout code and the
+result of that rewrite is ISL.
+
+The best place to start with ISL is the ``isl_surf`` data structure:
+
+.. code:: c
+
+ struct isl_surf {
+ enum isl_surf_dim dim;
+ enum isl_dim_layout dim_layout;
+ enum isl_msaa_layout msaa_layout;
+ enum isl_tiling tiling;
+ enum isl_format format;
+
+ /**
+ * Alignment of the upper-left sample of each subimage, in units of surface
+ * elements.
+ */
+ struct isl_extent3d image_alignment_el;
+
+ /**
+ * Logical extent of the surface's base level, in units of pixels. This is
+ * identical to the extent defined in isl_surf_init_info.
+ */
+ struct isl_extent4d logical_level0_px;
+
+ /**
+ * Physical extent of the surface's base level, in units of physical
+ * surface samples and aligned to the format's compression block.
+ *
+ * Consider isl_dim_layout as an operator that transforms a logical surface
+ * layout to a physical surface layout. Then
+ *
+ * logical_layout := (isl_surf::dim, isl_surf::logical_level0_px)
+ * isl_surf::phys_level0_sa := isl_surf::dim_layout * logical_layout
+ */
+ struct isl_extent4d phys_level0_sa;
+
+ uint32_t levels;
+ uint32_t samples;
+
+ /** Total size of the surface, in bytes. */
+ uint32_t size;
+
+ /** Required alignment for the surface's base address. */
+ uint32_t alignment;
+
+ /**
+ * Pitch between vertically adjacent surface elements, in bytes.
+ */
+ uint32_t row_pitch;
+
+ /**
+ * Pitch between physical array slices, in rows of surface elements.
+ */
+ uint32_t array_pitch_el_rows;
+
+ enum isl_array_pitch_span array_pitch_span;
+
+ /** Copy of isl_surf_init_info::usage. */
+ isl_surf_usage_flags_t usage;
+ };
+
+This data structure describes everything you need to know about a
+surface in a canonical way. Everything in ISL has well-defined units and
+things don't change meanings based on hardware generation. Units are
+usually denoted by a suffix such as "``_el``" for elements or "``_sa``"
+for samples. Understanding ISL's units is key to understanding how ISL
+performs surface calculations; so important, in fact, that they are
+given `their own section <#units#>`__.
+
+Units
+-----
+
+Before we go any further, we should discuss the different units that ISL
+uses. There are four of them:
+
+- Pixels (px)
+- Samples (sa)
+- Elements (el)
+- Tiles (tl)
+
+**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
+``isl_surf::samples``. The exact layout of the samples depends on the
+value of ``isl_surf::msaa_layout``. If the layout is
+``ISL_MSAA_LAYOUT_ARRAY`` then each logical array in the surface
+corresponds to ``isl_surf::samples`` actual slices in the resulting
+surface, one per array slice. If the layout is
+``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 ``isl_surf::phys_level0_sa``
+which gives the dimensions of the base miplevel of the surface in
+samples. The type of ``isl_surf::phys_level0_sa`` is ``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 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. This means that you cannot convert directly from
+pixels to elements or vice versa; any conversion between pixels and
+elements *must* go through samples.
+
+Finally, we have **tiles**. A tile is a large rectangular block of
+surface data that all fits in a single contiguous block of memory
+(usually a 4K page). 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.
+
+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: "``_el``" for elements, "``_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 ``RENDER_SURFACE_STATE``.
+This is one of the primary differences in ideology between ISL and the
+old miptree code which tried to keep everything in the same units as the
+hardware expects. One example of this difference is QPitch which
+specifies the distance between array slices. For compressed textures,
+the QPitch field in ``RENDER_SURFACE_STATE`` was in compression blocks
+on Broadwell but it changed to pixels on Sky Lake. Since the old surface
+state code tries to store things in hardware units, everyone who ever
+reads ``intel_mipmap_tree::qpitch`` has to change their interpretation
+based on hardware generation. In ISL, we have ``array_pitch_el_rows``
+which, as the name says, is in rows of elements. On Sky Lake and later,
+we have to multiply by the block size of the texture when we finally
+fill out the hardware packet, but it makes any other users of the field
+much simpler because they know that it's always in elements.
+
+Creating Surfaces
+-----------------
+
+Creating an ``isl_surf`` is done via the ``isl_surf_init_s`` function
+which takes an ``isl_surf_init_info`` structure. There is also an
+``isl_surf_init`` macro which uses a C99 designated initializer to
+provide a function-like interface with named parameters.
+
+.. code:: c
+
+ struct isl_surf_init_info {
+ enum isl_surf_dim dim;
+ enum isl_format format;
+
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+ uint32_t levels;
+ uint32_t array_len;
+ uint32_t samples;
+
+ /** Lower bound for isl_surf::alignment, in bytes. */
+ uint32_t min_alignment;
+
+ /** Lower bound for isl_surf::pitch, in bytes. */
+ uint32_t min_pitch;
+
+ isl_surf_usage_flags_t usage;
+
+ /** Flags that alter how ISL selects isl_surf::tiling. */
+ isl_tiling_flags_t tiling_flags;
+ };
+
+ #define isl_surf_init(dev, surf, ...) \
+ isl_surf_init_s((dev), (surf), \
+ &(struct isl_surf_init_info) { __VA_ARGS__ });
+
+ bool
+ isl_surf_init_s(const struct isl_device *dev,
+ struct isl_surf *surf,
+ const struct isl_surf_init_info *restrict info);
+
+The dimensionality of the surface is given by the ``isl_surf_dim`` enum:
+
+.. code:: c
+
+ enum isl_surf_dim {
+ ISL_SURF_DIM_1D,
+ ISL_SURF_DIM_2D,
+ ISL_SURF_DIM_3D,
+ };
+
+Not that ISL has no inherent concept of cube or array surfaces. All 1-D
+or 2-D surfaces are potentially arrays. Cube surfaces are simply 2-D
+surfaces with 6 array layers that have the ``ISL_SURF_USAGE_CUBE_BIT``
+set (more on usage bits later).
+
+Next we have an ``isl_format`` which specifies the nominal format of the
+surface. The values in the ``isl_format`` enum are exactly the same
+integer values as the hardware surface format enumerations. This allows
+for zero-cost translations between ISL and the hardware. The format
+specified in the ``isl_surf`` is used for surface layout calculations
+but it is not necessarily the format that will be packed into the
+``RENDER_SURFACE_STATE`` structure. When emitting a surface state, you
+also provide an ``isl_view`` structure that provides array layer and
+miplevel ranges as well as the final format.
+
+Next we have 6 unsigned integer values that provide the size of the
+surface in all possible dimensions. The ``width``, ``height``,
+``depth``, and ``array_len`` fields are all in terms of surface
+*pixels*. The ``array_len`` field is expected to be 6 for cubemap
+surfaces and is specified in number of faces (not number of cubes) for
+cube array surfaces. The ``levels`` and ``samples`` fields are fairly
+self-explanatory.
+
+The ``min_alignment`` and ``min_pitch`` fields allow some control over
+the way the surface is laid out in memory. While the final alignment and
+pitch are calculated by ISL in ``isl_init_surf_s``, these allow the
+caller to specify a lower bound. For linear surfaces, these fields are
+more-or-less respected with the exception that ISL may round up to the
+size of an element.
+
+The ``usage`` field is a bitwise OR of ``ISL_SURF_USAGE_*`` flags that
+specify all of the possible ways the surface may be used. Correctly
+specifying these flags is crucial to getting the correct results.
+Because the hardware has no surface formats for depth of stencil
+textures, the only way that ISL can know that a texture is expected to
+be used for depth or stencil is by the usage flags. For instance, a
+stencil texture should always have a format of ``ISL_FORMAT_R8_UINT``
+and specify ``ISL_SURF_USAGE_STENCIL_BIT``. It is illegal to combine
+depth or stencil bits with ``ISL_SURF_USAGE_RENDER_TARGET_BIT`` because
+they have different layout requirements which may or may not be
+renderable. The usage flags are also where you specify that a given
+surface may be used as a cube map.
+
+Finally, we have tiling flags. These specify the allowed tiling modes
+for the given surface. Usually, this will be one of
+``ISL_TILING_LINEAR_BIT``, ``ISL_TILING_NON_LINEAR_MASK`` or
+``ISL_TILING_ANY_MASK``. Inside of ``isl_surf_init_s``, isl will
+automatically filter the set of possible tilings based on hardware
+generation, usage flags, etc. and produce choose the tiling format that
+it thinks is the most appropriate. If, however, the calling code knows
+exactly what tiling format it wants, then it can specify a single bit
+and it will get that tiling format assuming it's supported.