const uint32_t bs = format_bpb / 8;
struct isl_extent2d logical_el, phys_B;
- assert(tiling == ISL_TILING_LINEAR || isl_is_pow2(format_bpb));
+ if (tiling != ISL_TILING_LINEAR && !isl_is_pow2(format_bpb)) {
+ /* It is possible to have non-power-of-two formats in a tiled buffer.
+ * The easiest way to handle this is to treat the tile as if it is three
+ * times as wide. This way no pixel will ever cross a tile boundary.
+ * This really only works on legacy X and Y tiling formats.
+ */
+ assert(tiling == ISL_TILING_X || tiling == ISL_TILING_Y0);
+ assert(bs % 3 == 0 && isl_is_pow2(format_bpb / 3));
+ return isl_tiling_get_info(dev, tiling, format_bpb / 3, tile_info);
+ }
switch (tiling) {
case ISL_TILING_LINEAR:
*tile_info = (struct isl_tile_info) {
.tiling = tiling,
+ .format_bpb = format_bpb,
.logical_extent_el = logical_el,
.phys_extent_B = phys_B,
};
}
base_alignment = isl_round_up_to_power_of_two(base_alignment);
} else {
+ assert(fmtl->bpb % tile_info.format_bpb == 0);
+ const uint32_t tile_el_scale = fmtl->bpb / tile_info.format_bpb;
+
assert(phys_slice0_sa.w % fmtl->bw == 0);
const uint32_t total_w_el = phys_slice0_sa.width / fmtl->bw;
const uint32_t total_w_tl =
- isl_align_div(total_w_el, tile_info.logical_extent_el.width);
+ isl_align_div(total_w_el * tile_el_scale,
+ tile_info.logical_extent_el.width);
row_pitch = total_w_tl * tile_info.phys_extent_B.width;
if (row_pitch < info->min_pitch) {
- row_pitch = isl_align(info->min_pitch, tile_info.phys_extent_B.width);
+ row_pitch = isl_align_npot(info->min_pitch,
+ tile_info.phys_extent_B.width);
}
total_h_el += isl_align_div_npot(pad_bytes, row_pitch);
uint32_t *x_offset_el,
uint32_t *y_offset_el)
{
- /* This function only really works for power-of-two surfaces. In
- * theory, we could make it work for non-power-of-two surfaces by going
- * to the left until we find a block that is bs-aligned. The Vulkan
- * driver doesn't use non-power-of-two tiled surfaces so we'll leave
- * this unimplemented for now.
- */
- assert(tiling == ISL_TILING_LINEAR || isl_is_pow2(bs));
-
if (tiling == ISL_TILING_LINEAR) {
*base_address_offset = total_y_offset_el * row_pitch +
total_x_offset_el * bs;
return;
}
+ const uint32_t bpb = bs * 8;
+
struct isl_tile_info tile_info;
- isl_tiling_get_info(dev, tiling, bs * 8, &tile_info);
+ isl_tiling_get_info(dev, tiling, bpb, &tile_info);
+
+ assert(row_pitch % tile_info.phys_extent_B.width == 0);
+
+ /* For non-power-of-two formats, we need the address to be both tile and
+ * element-aligned. The easiest way to achieve this is to work with a tile
+ * that is three times as wide as the regular tile.
+ *
+ * The tile info returned by get_tile_info has a logical size that is an
+ * integer number of tile_info.format_bpb size elements. To scale the
+ * tile, we scale up the physical width and then treat the logical tile
+ * size as if it has bpb size elements.
+ */
+ const uint32_t tile_el_scale = bpb / tile_info.format_bpb;
+ tile_info.phys_extent_B.width *= tile_el_scale;
/* Compute the offset into the tile */
*x_offset_el = total_x_offset_el % tile_info.logical_extent_el.w;
uint32_t x_offset_tl = total_x_offset_el / tile_info.logical_extent_el.w;
uint32_t y_offset_tl = total_y_offset_el / tile_info.logical_extent_el.h;
- assert(row_pitch % tile_info.phys_extent_B.width == 0);
*base_address_offset =
y_offset_tl * tile_info.phys_extent_B.h * row_pitch +
x_offset_tl * tile_info.phys_extent_B.h * tile_info.phys_extent_B.w;
struct isl_tile_info {
enum isl_tiling tiling;
- /** The logical size of the tile in units of surface elements
+ /* The size (in bits per block) of a single surface element
+ *
+ * For surfaces with power-of-two formats, this is the same as
+ * isl_format_layout::bpb. For non-power-of-two formats it may be smaller.
+ * The logical_extent_el field is in terms of elements of this size.
+ *
+ * For example, consider ISL_FORMAT_R32G32B32_FLOAT for which
+ * isl_format_layout::bpb is 96 (a non-power-of-two). In this case, none
+ * of the tiling formats can actually hold an integer number of 96-bit
+ * surface elements so isl_tiling_get_info returns an isl_tile_info for a
+ * 32-bit element size. It is the responsibility of the caller to
+ * recognize that 32 != 96 ad adjust accordingly. For instance, to compute
+ * the width of a surface in tiles, you would do:
+ *
+ * width_tl = DIV_ROUND_UP(width_el * (format_bpb / tile_info.format_bpb),
+ * tile_info.logical_extent_el.width);
+ */
+ uint32_t format_bpb;
+
+ /** The logical size of the tile in units of format_bpb size elements
*
* This field determines how a given surface is cut up into tiles. It is
* used to compute the size of a surface in tiles and can be used to