diff options
Diffstat (limited to 'src/intel/isl/docs/isl-intro.rst')
-rw-r--r-- | src/intel/isl/docs/isl-intro.rst | 256 |
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. |