<p dir="ltr"><br>
On Jul 24, 2015 4:00 AM, "Francisco Jerez" <<a href="mailto:currojerez@riseup.net">currojerez@riseup.net</a>> wrote:<br>
><br>
> Jason Ekstrand <<a href="mailto:jason@jlekstrand.net">jason@jlekstrand.net</a>> writes:<br>
><br>
> > Ok, I've looked through this again and convinced myself that it's<br>
> > *mostly* correct.  I am a bit skeptical of the address swizzling for<br>
> > Y-major tiling.<br>
> ><br>
> > I've also included some comments that I'd like to see added (assuming<br>
> > they're correct).  Sometimes it's easier to write helpful comments if<br>
> > the person who is confused does the writing. :-)<br>
> ><br>
><br>
> Thanks!  This will save me quite some guesswork, I'll add them to the<br>
> patch.<br>
><br>
> > Also, I'd just like to say that I'm terribly impressed after reading<br>
> > through this.  You managed to handle an amazing number of cases<br>
> > without so much as a single if statement or predicated instruction.<br>
> > My hat's off to you sir.<br>
> ><br>
> Hah, I feel flattered. :)<br>
><br>
> > Modulo Y-tiling and comments,<br>
> ><br>
> > Reviewed-by: Jason Ekstrand <<a href="mailto:jason.ekstrand@intel.com">jason.ekstrand@intel.com</a>><br>
> ><br>
> > On Tue, Jul 21, 2015 at 9:38 AM, Francisco Jerez <<a href="mailto:currojerez@riseup.net">currojerez@riseup.net</a>> wrote:<br>
> >> Define a function to calculate the memory address of the image<br>
> >> location given by a vector of coordinates.  This is required in cases<br>
> >> where we need to fall back to untyped surface access, which take a raw<br>
> >> memory offset and know nothing about surface coordinates, type<br>
> >> conversion or memory tiling and swizzling.  They are still useful<br>
> >> because typed surface reads don't support any 64 or 128-bit formats on<br>
> >> IVB, and they don't support any 128-bit formats on HSW and BDW.<br>
> >><br>
> >> The tiling algorithm is implemented based on a number of parameters<br>
> >> which are passed in as uniforms and determine whether the surface<br>
> >> layout is X-tiled, Y-tiled or untiled.  This allows binding surfaces<br>
> >> of different tiling layouts to the pipeline without recompiling the<br>
> >> program.<br>
> >><br>
> >> v2: Drop VEC4 suport.<br>
> >> v3: Rebase.<br>
> >> ---<br>
> >>  .../drivers/dri/i965/brw_fs_surface_builder.cpp    | 108 +++++++++++++++++++++<br>
> >>  1 file changed, 108 insertions(+)<br>
> >><br>
> >> diff --git a/src/mesa/drivers/dri/i965/brw_fs_surface_builder.cpp b/src/mesa/drivers/dri/i965/brw_fs_surface_builder.cpp<br>
> >> index 5ee04de..0c879db 100644<br>
> >> --- a/src/mesa/drivers/dri/i965/brw_fs_surface_builder.cpp<br>
> >> +++ b/src/mesa/drivers/dri/i965/brw_fs_surface_builder.cpp<br>
> >> @@ -215,4 +215,112 @@ namespace {<br>
> >>           return BRW_PREDICATE_NORMAL;<br>
> >>        }<br>
> >>     }<br>
> >> +<br>
> >> +   namespace image_coordinates {<br>
> >> +      /**<br>
> >> +       * Calculate the offset in memory of the texel given by \p coord.<br>
> >> +       *<br>
> >> +       * This is meant to be used with untyped surface messages to access a<br>
> >> +       * tiled surface, what involves taking into account the tiling and<br>
> >> +       * swizzling modes of the surface manually so it will hopefully not<br>
> >> +       * happen very often.<br>
> >> +       */<br>
> >> +      fs_reg<br>
> >> +      emit_address_calculation(const fs_builder &bld, const fs_reg &image,<br>
> >> +                               const fs_reg &coord, unsigned dims)<br>
> >> +      {<br>
> >> +         const brw_device_info *devinfo = bld.shader->devinfo;<br>
> >> +         const fs_reg off = offset(image, bld, BRW_IMAGE_PARAM_OFFSET_OFFSET);<br>
> >> +         const fs_reg stride = offset(image, bld, BRW_IMAGE_PARAM_STRIDE_OFFSET);<br>
> >> +         const fs_reg tile = offset(image, bld, BRW_IMAGE_PARAM_TILING_OFFSET);<br>
> >> +         const fs_reg swz = offset(image, bld, BRW_IMAGE_PARAM_SWIZZLING_OFFSET);<br>
> >> +         const fs_reg addr = bld.vgrf(BRW_REGISTER_TYPE_UD, 2);<br>
> >> +         const fs_reg tmp = bld.vgrf(BRW_REGISTER_TYPE_UD, 2);<br>
> >> +         const fs_reg minor = bld.vgrf(BRW_REGISTER_TYPE_UD, 2);<br>
> >> +         const fs_reg major = bld.vgrf(BRW_REGISTER_TYPE_UD, 2);<br>
> >> +         const fs_reg dst = bld.vgrf(BRW_REGISTER_TYPE_UD);<br>
> >> +<br>
> >> +         /* Shift the coordinates by the fixed surface offset. */<br>
> >> +         for (unsigned c = 0; c < 2; ++c)<br>
> >> +            bld.ADD(offset(addr, bld, c), offset(off, bld, c),<br>
> >> +                    (c < dims ?<br>
> >> +                     offset(retype(coord, BRW_REGISTER_TYPE_UD), bld, c) :<br>
> >> +                     fs_reg(0)));<br>
> >> +<br>
> >> +         if (dims > 2) {<br>
> >> +            /* Decompose z into a major (tmp.y) and a minor (tmp.x)<br>
> >> +             * index.<br>
> ><br>
> > Please add a comment:<br>
> > /*<br>
> > The layout of 3-D textures in memory is sort-of like a tiling format.<br>
> > At each miplevel, the slices are arranged in rows of 2^level slices<br>
> > per row.  The slice row is stored in tmp.y and the slice within the<br>
> > row is stored in tmp.x.  The layout of 2-D array textures is much<br>
> > simpler.  Each slice of the texture is storred as a 2-D texture with<br>
> > all its miplevels and then one qpitch later, the next slice is stored<br>
> > with its miplevels.  This code handles both by passing in the miplevel<br>
> > as tile[2] for 3-D textures and 0 in tile[2] for 2-D array textures.<br>
> > */<br>
><br>
> Note that depending on the ARYSPC_LOD0 surface state flag array textures<br>
> may also be interleaved in the opposite way -- All slices for LOD0, then<br>
> all slices for LOD1, and so on.  We probably don't hit this case<br>
> currently because MS images are not exposed but it's handled in the same<br>
> way by setting stride[3] to the spacing between slices.  I'll add that<br>
> to your comment.<br>
><br>
> >> +             */<br>
> >> +            bld.BFE(offset(tmp, bld, 0), offset(tile, bld, 2), fs_reg(0),<br>
> >> +                    offset(retype(coord, BRW_REGISTER_TYPE_UD), bld, 2));<br>
> >> +            bld.SHR(offset(tmp, bld, 1),<br>
> >> +                    offset(retype(coord, BRW_REGISTER_TYPE_UD), bld, 2),<br>
> >> +                    offset(tile, bld, 2));<br>
> >> +<br>
> >> +            /* Take into account the horizontal (tmp.x) and vertical (tmp.y)<br>
> >> +             * slice offset.<br>
> >> +             */<br>
> >> +            for (unsigned c = 0; c < 2; ++c) {<br>
> >> +               bld.MUL(offset(tmp, bld, c),<br>
> >> +                       offset(stride, bld, 2 + c), offset(tmp, bld, c));<br>
> >> +               bld.ADD(offset(addr, bld, c),<br>
> >> +                       offset(addr, bld, c), offset(tmp, bld, c));<br>
> >> +            }<br>
> >> +         }<br>
> >> +<br>
> >> +         if (dims > 1) {<br>
> ><br>
> > Please add as a comment:<br>
> > /*<br>
> > Calculate the major/minor x and y indices.  In order to accommodate<br>
> > both X and Y tiling, the Y-major tiling format is treated as being a<br>
> > bunch of narrow X-tiles placed next to each other.   This means that<br>
> > the tile width for Y-tiling is actually the width of one sub-column of<br>
> > the Y-major tile where each 4K tile has 8 512B sub-columns.<br>
> ><br>
> > The major Y value is the row of tiles in which the pixel lives.  The<br>
> > major X value is the tile sub-column in which the pixel lives; for X<br>
> > tiling, this is the same as the tile column, for Y tiling, each tile<br>
> > has 8 sub-columns.  The minor X and Y indices are the position within<br>
> > the sub-column.<br>
> > */<br>
> >> +            for (unsigned c = 0; c < 2; ++c) {<br>
> >> +               /* Calculate the minor x and y indices. */<br>
> >> +               bld.BFE(offset(minor, bld, c), offset(tile, bld, c),<br>
> >> +                       fs_reg(0), offset(addr, bld, c));<br>
> >> +<br>
> >> +               /* Calculate the major x and y indices. */<br>
> >> +               bld.SHR(offset(major, bld, c),<br>
> >> +                       offset(addr, bld, c), offset(tile, bld, c));<br>
> >> +            }<br>
> >> +<br>
> >> +            /* Calculate the texel index from the start of the tile row and<br>
> >> +             * the vertical coordinate of the row.<br>
> >> +             * Equivalent to:<br>
> >> +             *   tmp.x = (major.x << tile.y << tile.x) +<br>
> >> +             *           (minor.y << tile.x) + minor.x<br>
> >> +             *   tmp.y = major.y << tile.y<br>
> >> +             */<br>
> >> +            bld.SHL(tmp, major, offset(tile, bld, 1));<br>
> >> +            bld.ADD(tmp, tmp, offset(minor, bld, 1));<br>
> >> +            bld.SHL(tmp, tmp, offset(tile, bld, 0));<br>
> >> +            bld.ADD(tmp, tmp, minor);<br>
> >> +            bld.SHL(offset(tmp, bld, 1),<br>
> >> +                    offset(major, bld, 1), offset(tile, bld, 1));<br>
> >> +<br>
> >> +            /* Add it to the start of the tile row. */<br>
> >> +            bld.MUL(offset(tmp, bld, 1),<br>
> >> +                    offset(tmp, bld, 1), offset(stride, bld, 1));<br>
> >> +            bld.ADD(tmp, tmp, offset(tmp, bld, 1));<br>
> >> +<br>
> >> +            /* Multiply by the Bpp value. */<br>
> >> +            bld.MUL(dst, tmp, stride);<br>
> >> +<br>
> >> +            if (devinfo->gen < 8 && !devinfo->is_baytrail) {<br>
> ><br>
> > Talking to Ken a bit about this. It seems as if this is actually the<br>
> > check we want.  It's going to be a nasty bug if someone has a shader<br>
> > cache on a HSW machine, runs some compute shaders, pulls out a stick<br>
> > of ram (going from dual to single-channel), and starts it up again.<br>
> > Probably best to compile the shader the same regardless of how much<br>
> > ram you have.<br>
> ><br>
> Hmm, I guess we could also invalidate the cache if the flag changes?<br>
> (or in case anything else changes from the brw_device_info structure)<br>
><br>
> >> +               /* Take into account the two dynamically specified shifts. */<br>
> >> +               for (unsigned c = 0; c < 2; ++c)<br>
> >> +                  bld.SHR(offset(tmp, bld, c), dst, offset(swz, bld, c));<br>
> >> +<br>
> >> +               /* XOR tmp.x and tmp.y with bit 6 of the memory address. */<br>
> >> +               bld.XOR(tmp, tmp, offset(tmp, bld, 1));<br>
> >> +               bld.AND(tmp, tmp, fs_reg(1 << 6));<br>
> >> +               bld.XOR(dst, dst, tmp);<br>
> ><br>
> > I don't think this is correct for Y-tiled surfaces.  In Y-tiling, bit<br>
> > 6 of the address is given by bit6 XOR bit9.  You have one extra XOR<br>
> > which means you just get just bit9.<br>
> ><br>
> The extra XOR is made a no-op for the Y-tiled case in the same way that<br>
> they are both made a no-op in the no-swizzling case.  I'll add another<br>
> comment clarifying that.</p>
<p dir="ltr">I don't think that actually works.  In the zero-shift case, the first XOR produces an all-zero value, you mask to bit 6 (still all-zero) and XOR with the original value and get the original back.  In the Y-tiled case, you compute a ^ (a >> 3) and mask off bit 6.  This gives you bit6 ^ bit9 in the sixth bit and zero everywhere else.  When you then XOR that with the original, the two copies of bit6 cancel out and you are left with bit9 only.</p>
<p dir="ltr">> >> +            }<br>
> >> +<br>
> >> +         } else {<br>
> >> +            /* Multiply by the Bpp/stride value. */<br>
> ><br>
> > I'm still confused as to how addr can have 2 things while dims == 1.<br>
> > Does dims specify the per-slice dimension or total dimension?  That<br>
> > should probably be documented at the top of the function.<br>
> ><br>
> dims specifies the dimension of the coord argument.  addr may still have<br>
> a non-zero y coordinate because it's the sum of coord and the<br>
> two-dimensional offset value specified by surface set-up.  1D texture<br>
> slices and levels are laid out in a 2D layout just like the rest, the<br>
> offset value specifies where in the 2D miptree the requested 1D image<br>
> starts.  You may wonder why we pass a 2D offset to the shader instead of<br>
> just fixing up the surface base memory address to point at the right<br>
> slice at offset (0, 0), the answer is that in the general case (i.e. not<br>
> necessarily 1D) it wouldn't work, because a level may start mid-tile and<br>
> the result of shifting a tiled surface by less than the tile size is in<br>
> general not a well-formed tiled surface -- In the 1D case it could<br>
> probably be done though because I deliberately disabled tiling for those<br>
> (36a17f0f9913), but it would require special-casing them in the surface<br>
> state set-up code and another manual calculation of the base memory<br>
> address of the right slice-level of the miptree.<br>
><br>
> >> +            bld.MUL(offset(addr, bld, 1),<br>
> >> +                    offset(addr, bld, 1), offset(stride, bld, 1));<br>
> >> +            bld.ADD(addr, addr, offset(addr, bld, 1));<br>
> >> +            bld.MUL(dst, addr, stride);<br>
> >> +         }<br>
> >> +<br>
> >> +         return dst;<br>
> >> +      }<br>
> >> +   }<br>
> >>  }<br>
> >> --<br>
> >> 2.4.3<br>
> >><br>
> >> _______________________________________________<br>
> >> mesa-dev mailing list<br>
> >> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
> >> <a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</p>