From 3894e42590a79551541819185fbf0161990f7953 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 14 Jun 2021 21:33:35 -0500 Subject: [PATCH] docs/isl: Document ISL's units Part-of: --- docs/isl/index.rst | 5 +++ docs/isl/units.rst | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 docs/isl/units.rst diff --git a/docs/isl/index.rst b/docs/isl/index.rst index a8d2a517b6c..e3dbd694a19 100644 --- a/docs/isl/index.rst +++ b/docs/isl/index.rst @@ -6,6 +6,11 @@ surface layout calculations for Intel graphics drivers. It was originally written by Chad Versace and is now maintained by Jason Ekstrand and Nanley Chery. +.. toctree:: + :maxdepth: 2 + + units + The core representation of a surface in ISL is :cpp:struct:`isl_surf`. .. doxygenstruct:: isl_surf diff --git a/docs/isl/units.rst b/docs/isl/units.rst new file mode 100644 index 00000000000..5b0db004413 --- /dev/null +++ b/docs/isl/units.rst @@ -0,0 +1,81 @@ +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 (_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.