mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-10-31 19:08:03 +03:00
801 lines
27 KiB
C
801 lines
27 KiB
C
/*
|
|
* Copyright (C) 2026 Collabora, Ltd.
|
|
* Copyright (C) 2026 Arm Ltd.
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include "pan_fb.h"
|
|
|
|
#include "pan_afbc.h"
|
|
#include "pan_afrc.h"
|
|
#include "pan_desc.h"
|
|
#include "pan_encoder.h"
|
|
#include "pan_format.h"
|
|
#include "pan_image.h"
|
|
#include "pan_props.h"
|
|
#include "pan_util.h"
|
|
|
|
static unsigned
|
|
pan_bytes_per_pixel_tib(enum pipe_format format)
|
|
{
|
|
const struct pan_blendable_format *bf =
|
|
GENX(pan_blendable_format_from_pipe_format)(format);
|
|
return pan_format_tib_size(format, bf->internal);
|
|
}
|
|
|
|
void
|
|
GENX(pan_select_fb_tile_size)(struct pan_fb_layout *fb)
|
|
{
|
|
uint32_t rt_B_per_sa = 0;
|
|
for (unsigned rt = 0; rt < fb->rt_count; rt++) {
|
|
enum pipe_format format = fb->rt_formats[rt];
|
|
if (format == PIPE_FORMAT_NONE)
|
|
continue;
|
|
|
|
const struct pan_blendable_format *bf =
|
|
GENX(pan_blendable_format_from_pipe_format)(format);
|
|
rt_B_per_sa += pan_format_tib_size(format, bf->internal);
|
|
}
|
|
|
|
/* The PLS area overlaps with the color targets */
|
|
rt_B_per_sa = MAX2(rt_B_per_sa, fb->pls_size_B);
|
|
|
|
const uint32_t rt_B_per_px = rt_B_per_sa * fb->sample_count;
|
|
|
|
/* We always have depth and it's is always stored in a 32-bit float.
|
|
* Stencil requires depth to be allocated, but doesn't have it's own
|
|
* budget; it's tied to the depth buffer.
|
|
*/
|
|
const uint32_t z_B_per_px = sizeof(float) * fb->sample_count;
|
|
|
|
fb->tile_size_px =
|
|
MIN2(fb->tile_rt_budget_B >> util_logbase2_ceil(rt_B_per_px),
|
|
fb->tile_z_budget_B >> util_logbase2_ceil(z_B_per_px));
|
|
|
|
/* Check if we're using too much tile-memory; if we are, try disabling
|
|
* pipelining. This works because we're starting with an optimistic half
|
|
* of the tile-budget, so we actually have another half that can be used.
|
|
*
|
|
* On v6 GPUs, doing this is not allowed; they *have* to pipeline.
|
|
*/
|
|
if (PAN_ARCH != 6 && fb->tile_size_px < 4 * 4)
|
|
fb->tile_size_px *= 2;
|
|
|
|
/* Clamp tile size to hardware limits */
|
|
fb->tile_size_px =
|
|
MIN2(fb->tile_size_px, pan_max_effective_tile_size(PAN_ARCH));
|
|
assert(util_is_power_of_two_nonzero(fb->tile_size_px));
|
|
assert(fb->tile_size_px >= 4 * 4);
|
|
|
|
/* Colour buffer allocations must be 1K aligned. */
|
|
fb->tile_rt_alloc_B = ALIGN_POT(rt_B_per_px * fb->tile_size_px, 1024);
|
|
|
|
#if PAN_ARCH == 6
|
|
assert(fb->tile_rt_alloc_B <= fb->tile_rt_budget_B && "tile too big");
|
|
#else
|
|
assert(fb->tile_rt_alloc_B <= fb->tile_rt_budget_B * 2 && "tile too big");
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
align_fb_tiling_area_for_image_plane(struct pan_fb_layout *fb,
|
|
struct pan_image_plane_ref pref)
|
|
{
|
|
if (!pref.image)
|
|
return;
|
|
|
|
struct pan_image_block_size block_size;
|
|
if (drm_is_afbc(pref.image->props.modifier)) {
|
|
/* For AFBC render targets, the hardware always writes full superblocks.
|
|
* In order to ensure we don't write garbage, we need to expand the
|
|
* render area accordingly and load the border pixels.
|
|
*/
|
|
block_size = pan_afbc_renderblock_size(pref.image->props.modifier);
|
|
assert(block_size.width >= 16 && block_size.height >= 16);
|
|
} else if (drm_is_afrc(pref.image->props.modifier)) {
|
|
/* For AFRC render targets, the hardware always writes full clumps. In
|
|
* order to ensure we don't write garbage, we need to expand the render
|
|
* area accordingly and load the border pixels.
|
|
*/
|
|
bool scan = pan_afrc_is_scan(pref.image->props.modifier);
|
|
block_size = pan_afrc_clump_size(pref.image->props.format, scan);
|
|
} else {
|
|
/* No alignment requirements */
|
|
return;
|
|
}
|
|
|
|
fb->tiling_area_px = pan_fb_bbox_align(fb->tiling_area_px,
|
|
block_size.width,
|
|
block_size.height);
|
|
}
|
|
|
|
void
|
|
GENX(pan_align_fb_tiling_area)(struct pan_fb_layout *fb,
|
|
const struct pan_fb_store *store)
|
|
{
|
|
if (store == NULL)
|
|
return;
|
|
|
|
for (unsigned rt = 0; rt < fb->rt_count; rt++) {
|
|
if (store->rts[rt].store) {
|
|
align_fb_tiling_area_for_image_plane(fb,
|
|
pan_image_view_get_color_plane(store->rts[rt].iview));
|
|
}
|
|
}
|
|
|
|
if (store->zs.store) {
|
|
align_fb_tiling_area_for_image_plane(fb,
|
|
pan_image_view_get_zs_plane(store->zs.iview));
|
|
}
|
|
|
|
if (store->s.store) {
|
|
align_fb_tiling_area_for_image_plane(fb,
|
|
pan_image_view_get_s_plane(store->s.iview));
|
|
}
|
|
}
|
|
|
|
static void
|
|
fold_sample_0_image_load(struct pan_fb_resolve_op_msaa *rm,
|
|
const struct pan_image_view *iview,
|
|
uint8_t fb_sample_count)
|
|
{
|
|
if (rm->resolve != PAN_FB_RESOLVE_IMAGE)
|
|
return;
|
|
|
|
if (rm->msaa != PAN_FB_MSAA_COPY_ALL)
|
|
return;
|
|
|
|
assert(pan_image_view_get_nr_samples(iview) == fb_sample_count);
|
|
|
|
/* If we're loading all samples of the image but only storing SAMPLE_0,
|
|
* then we don't actually need all samples in the image. Drop it to a
|
|
* SAMPLE_0 load to save bandwidth.
|
|
*/
|
|
rm->msaa = PAN_FB_MSAA_COPY_SAMPLE_0;
|
|
}
|
|
|
|
static bool
|
|
resolve_msaa_can_fold(struct pan_fb_resolve_op_msaa rm,
|
|
enum pan_fb_resolve_op self_op,
|
|
enum pipe_format format)
|
|
{
|
|
if (rm.resolve == PAN_FB_RESOLVE_IMAGE) {
|
|
/* We already folded away COPY_ALL and all the other MSAA modes produce
|
|
* a single per-pixel result across all the samples, i.e. COPY_IDENTICAL.
|
|
* We can fold that with any resolve mode.
|
|
*/
|
|
assert(rm.msaa != PAN_FB_MSAA_COPY_ALL);
|
|
return true;
|
|
}
|
|
|
|
if (rm.resolve != self_op)
|
|
return false;
|
|
|
|
/* PAN_FB_MSAA_COPY_IDENTICAL is always foldable */
|
|
if (rm.msaa == PAN_FB_MSAA_COPY_IDENTICAL)
|
|
return true;
|
|
|
|
if (self_op == PAN_FB_RESOLVE_Z || self_op == PAN_FB_RESOLVE_S) {
|
|
/* Z/S only supports SAMPLE_0 */
|
|
return rm.msaa == PAN_FB_MSAA_COPY_SAMPLE_0;
|
|
} else {
|
|
if (rm.msaa == PAN_FB_MSAA_COPY_AVERAGE &&
|
|
!GENX(pan_format_supports_msaa_average)(format))
|
|
return false;
|
|
|
|
/* The color hardware supports SAMPLE_0 and AVERAGE */
|
|
return rm.msaa == PAN_FB_MSAA_COPY_SAMPLE_0 ||
|
|
rm.msaa == PAN_FB_MSAA_COPY_AVERAGE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fold_resolve_into_store_target(enum pipe_format format,
|
|
uint8_t fb_sample_count,
|
|
enum pan_fb_resolve_op self_op,
|
|
struct pan_fb_resolve_target *resolve,
|
|
struct pan_fb_store_target *store)
|
|
{
|
|
if (format == PIPE_FORMAT_NONE)
|
|
return;
|
|
|
|
/* We need SAMPLE_0 if we're going to fold in the resolve */
|
|
if (store->msaa != PAN_FB_MSAA_COPY_SAMPLE_0)
|
|
return;
|
|
|
|
fold_sample_0_image_load(&resolve->in_bounds, resolve->iview,
|
|
fb_sample_count);
|
|
fold_sample_0_image_load(&resolve->in_bounds, resolve->iview,
|
|
fb_sample_count);
|
|
|
|
if (!resolve_msaa_can_fold(resolve->in_bounds, self_op, format) ||
|
|
!resolve_msaa_can_fold(resolve->border, self_op, format))
|
|
return;
|
|
|
|
enum pan_fb_msaa_copy_op msaa;
|
|
if (resolve->in_bounds.resolve == PAN_FB_RESOLVE_IMAGE) {
|
|
/* If they're both IMAGE, there's nothing to fold */
|
|
if (resolve->border.resolve == PAN_FB_RESOLVE_IMAGE)
|
|
return;
|
|
msaa = resolve->border.msaa;
|
|
} else if (resolve->border.resolve == PAN_FB_RESOLVE_IMAGE) {
|
|
msaa = resolve->in_bounds.msaa;
|
|
} else {
|
|
/* If neither is an image resolve, the MSAA copy op has to match or one
|
|
* of them has to be PAN_FB_MSAA_COPY_IDENTICAL.
|
|
*/
|
|
if (resolve->in_bounds.msaa != PAN_FB_MSAA_COPY_IDENTICAL) {
|
|
if (resolve->border.msaa != PAN_FB_MSAA_COPY_IDENTICAL &&
|
|
resolve->border.msaa != resolve->in_bounds.msaa)
|
|
return;
|
|
msaa = resolve->in_bounds.msaa;
|
|
} else {
|
|
msaa = resolve->border.msaa;
|
|
}
|
|
}
|
|
|
|
/* PAN_FB_MSAA_COPY_IDENTICAL isn't real. Use SAMPLE_0 */
|
|
if (msaa == PAN_FB_MSAA_COPY_IDENTICAL)
|
|
msaa = PAN_FB_MSAA_COPY_SAMPLE_0;
|
|
|
|
/* At this point, we know we can fold the resolve */
|
|
store->msaa = msaa;
|
|
|
|
if (resolve->in_bounds.resolve != PAN_FB_RESOLVE_IMAGE) {
|
|
resolve->in_bounds.resolve = PAN_FB_RESOLVE_NONE;
|
|
resolve->in_bounds.msaa = PAN_FB_MSAA_COPY_ALL;
|
|
}
|
|
|
|
if (resolve->border.resolve != PAN_FB_RESOLVE_IMAGE) {
|
|
resolve->border.resolve = PAN_FB_RESOLVE_NONE;
|
|
resolve->border.msaa = PAN_FB_MSAA_COPY_ALL;
|
|
}
|
|
}
|
|
|
|
void
|
|
GENX(pan_fb_fold_resolve_into_store)(const struct pan_fb_layout *fb,
|
|
struct pan_fb_resolve *resolve,
|
|
struct pan_fb_store *store)
|
|
{
|
|
if (fb->sample_count == 1)
|
|
return;
|
|
|
|
fold_resolve_into_store_target(fb->z_format, fb->sample_count,
|
|
PAN_FB_RESOLVE_Z, &resolve->z, &store->zs);
|
|
fold_resolve_into_store_target(fb->s_format, fb->sample_count,
|
|
PAN_FB_RESOLVE_S, &resolve->s, &store->s);
|
|
for (unsigned rt = 0; rt < fb->rt_count; rt++) {
|
|
fold_resolve_into_store_target(fb->rt_formats[rt], fb->sample_count,
|
|
PAN_FB_RESOLVE_RT(rt),
|
|
&resolve->rts[rt], &store->rts[rt]);
|
|
}
|
|
}
|
|
|
|
void
|
|
GENX(pan_fill_fb_info)(const struct pan_fb_desc_info *info,
|
|
struct pan_fb_info *fbinfo)
|
|
{
|
|
struct pan_bbox bbox = { 0, 0, 0, 0 };
|
|
if (info->fb->width_px > 0 && info->fb->width_px > 0) {
|
|
const struct pan_fb_bbox fb_area_px =
|
|
pan_fb_bbox_from_xywh(0, 0, info->fb->width_px, info->fb->height_px);
|
|
|
|
assert(pan_fb_bbox_is_valid(info->fb->tiling_area_px));
|
|
const struct pan_fb_bbox bbox_px =
|
|
pan_fb_bbox_clamp(info->fb->tiling_area_px, fb_area_px);
|
|
|
|
bbox = (struct pan_bbox) {
|
|
.minx = bbox_px.min_x,
|
|
.miny = bbox_px.min_y,
|
|
.maxx = bbox_px.max_x,
|
|
.maxy = bbox_px.max_y,
|
|
};
|
|
}
|
|
|
|
*fbinfo = (struct pan_fb_info) {
|
|
.width = info->fb->width_px,
|
|
.height = info->fb->height_px,
|
|
.draw_extent = bbox,
|
|
.frame_bounding_box = bbox,
|
|
.nr_samples = info->fb->sample_count,
|
|
.rt_count = info->fb->rt_count,
|
|
.pls_enabled = info->fb->pls_size_B > 0,
|
|
.bifrost.pre_post = {
|
|
.dcds.gpu = info->frame_shaders.dcd_pointer,
|
|
.modes = {
|
|
info->frame_shaders.modes[0],
|
|
info->frame_shaders.modes[1],
|
|
info->frame_shaders.modes[2],
|
|
},
|
|
},
|
|
|
|
.tile_buf_budget = info->fb->tile_rt_budget_B,
|
|
.z_tile_buf_budget = info->fb->tile_z_budget_B,
|
|
.tile_size = info->fb->tile_size_px,
|
|
.cbuf_allocation = info->fb->tile_rt_alloc_B,
|
|
|
|
.sample_positions = info->sample_pos_array_pointer,
|
|
.sprite_coord_origin = info->sprite_coord_origin_max_y,
|
|
.first_provoking_vertex = info->provoking_vertex_first,
|
|
.allow_hsr_prepass = info->provoking_vertex_first,
|
|
};
|
|
|
|
/* There are cases where we only want to fill out a partial fb_info */
|
|
if (info->load == NULL && info->store == NULL)
|
|
return;
|
|
|
|
for (unsigned rt = 0; rt < info->fb->rt_count; rt++) {
|
|
fbinfo->rts[rt] = (struct pan_fb_color_attachment) {
|
|
.view = info->store->rts[rt].iview,
|
|
.clear = info->load->rts[rt].border_load == PAN_FB_LOAD_CLEAR ||
|
|
info->load->rts[rt].in_bounds_load == PAN_FB_LOAD_CLEAR,
|
|
.preload = info->load->rts[rt].in_bounds_load == PAN_FB_LOAD_IMAGE,
|
|
.discard = !info->store->rts[rt].store,
|
|
};
|
|
|
|
if (fbinfo->rts[rt].clear) {
|
|
pan_pack_color(GENX(pan_blendable_formats),
|
|
fbinfo->rts[rt].clear_value,
|
|
&info->load->rts[rt].clear.color,
|
|
info->fb->rt_formats[rt],
|
|
false /* dithered */);
|
|
}
|
|
}
|
|
|
|
if (info->fb->z_format != PIPE_FORMAT_NONE) {
|
|
fbinfo->zs.view.zs = info->store->zs.iview;
|
|
fbinfo->zs.clear.z = info->load->z.border_load == PAN_FB_LOAD_CLEAR ||
|
|
info->load->z.in_bounds_load == PAN_FB_LOAD_CLEAR;
|
|
fbinfo->zs.preload.z = info->load->z.in_bounds_load == PAN_FB_LOAD_IMAGE;
|
|
fbinfo->zs.discard.z = !info->store->zs.store;
|
|
if (fbinfo->zs.clear.z)
|
|
fbinfo->zs.clear_value.depth = info->load->z.clear.depth;
|
|
}
|
|
|
|
if (info->fb->s_format != PIPE_FORMAT_NONE) {
|
|
fbinfo->zs.view.s = info->store->s.iview;
|
|
fbinfo->zs.clear.s = info->load->s.border_load == PAN_FB_LOAD_CLEAR ||
|
|
info->load->s.in_bounds_load == PAN_FB_LOAD_CLEAR;
|
|
fbinfo->zs.preload.s = info->load->s.in_bounds_load == PAN_FB_LOAD_IMAGE;
|
|
fbinfo->zs.discard.s = !info->store->s.store;
|
|
if (fbinfo->zs.clear.s)
|
|
fbinfo->zs.clear_value.stencil = info->load->s.clear.stencil;
|
|
}
|
|
}
|
|
|
|
#if PAN_ARCH >= 5
|
|
static enum mali_msaa
|
|
translate_msaa_copy_op(const struct pan_fb_layout *fb,
|
|
const struct pan_image_view *iview,
|
|
enum pan_fb_msaa_copy_op msaa)
|
|
{
|
|
switch (msaa) {
|
|
case PAN_FB_MSAA_COPY_ALL:
|
|
assert(pan_image_view_get_nr_samples(iview) == fb->sample_count);
|
|
if (fb->sample_count > 1)
|
|
return MALI_MSAA_LAYERED;
|
|
else
|
|
return MALI_MSAA_SINGLE;
|
|
|
|
case PAN_FB_MSAA_COPY_SINGLE:
|
|
case PAN_FB_MSAA_COPY_SAMPLE_0:
|
|
return MALI_MSAA_SINGLE;
|
|
|
|
case PAN_FB_MSAA_COPY_AVERAGE:
|
|
/* It must be a blendable format or the hardware can't average */
|
|
assert(GENX(pan_blendable_format_from_pipe_format)(iview->format)->internal);
|
|
if (fb->sample_count > 1)
|
|
return MALI_MSAA_AVERAGE;
|
|
else
|
|
return MALI_MSAA_SINGLE;
|
|
|
|
default:
|
|
UNREACHABLE("Unsupported MSAA copy op");
|
|
}
|
|
}
|
|
|
|
static bool
|
|
pan_fb_load_target_always(const struct pan_fb_load_target *target)
|
|
{
|
|
if (target->in_bounds_load == PAN_FB_LOAD_NONE &&
|
|
target->border_load == PAN_FB_LOAD_NONE)
|
|
return false;
|
|
|
|
return target->always;
|
|
}
|
|
|
|
static bool
|
|
pan_fb_store_target_always(const struct pan_fb_store_target *target)
|
|
{
|
|
return target->store && target->always;
|
|
}
|
|
|
|
struct pan_fb_clean_tile
|
|
GENX(pan_fb_get_clean_tile)(const struct pan_fb_desc_info *info)
|
|
{
|
|
const struct pan_fb_layout *fb = info->fb;
|
|
const struct pan_fb_load *load = info->load;
|
|
const struct pan_fb_store *store = info->store;
|
|
|
|
struct pan_fb_clean_tile ct = { };
|
|
|
|
for (unsigned rt = 0; rt < fb->rt_count; rt++) {
|
|
if (fb->rt_formats[rt] == PIPE_FORMAT_NONE)
|
|
continue;
|
|
|
|
if ((load && pan_fb_load_target_always(&load->rts[rt])) ||
|
|
(store && pan_fb_store_target_always(&store->rts[rt])))
|
|
ct.rts |= BITFIELD_BIT(rt);
|
|
|
|
if (store && store->rts[rt].store) {
|
|
const struct pan_image *img =
|
|
pan_image_view_get_color_plane(store->rts[rt].iview).image;
|
|
if (GENX(pan_force_clean_write_on)(img, fb->tile_size_px))
|
|
ct.rts |= BITFIELD_BIT(rt);
|
|
}
|
|
}
|
|
|
|
const bool z_always_load = load && pan_fb_load_target_always(&load->z);
|
|
const bool s_always_load = load && pan_fb_load_target_always(&load->s);
|
|
const bool zs_always_store = store && pan_fb_store_target_always(&store->zs);
|
|
const bool s_always_store = store && pan_fb_store_target_always(&store->s);
|
|
|
|
if (fb->z_format != PIPE_FORMAT_NONE) {
|
|
ct.zs = z_always_load || zs_always_store;
|
|
|
|
if (store && store->zs.store) {
|
|
const struct pan_image *img =
|
|
pan_image_view_get_zs_plane(store->zs.iview).image;
|
|
assert(util_format_get_depth_bits(img->props.format) ==
|
|
util_format_get_depth_bits(fb->z_format));
|
|
assert(util_format_get_depth_bits(store->zs.iview->format) ==
|
|
util_format_get_depth_bits(fb->z_format));
|
|
const struct util_format_description *zs_fmt_desc =
|
|
util_format_description(img->props.format);
|
|
|
|
/* If ZS writes stencil, we have to also include stencil loads */
|
|
if (util_format_has_stencil(zs_fmt_desc) && s_always_load)
|
|
ct.zs = true;
|
|
|
|
if (GENX(pan_force_clean_write_on)(img, fb->tile_size_px))
|
|
ct.zs = true;
|
|
}
|
|
}
|
|
|
|
if (fb->s_format != PIPE_FORMAT_NONE) {
|
|
ct.s = s_always_load || s_always_store;
|
|
|
|
if (store && store->s.store) {
|
|
const struct pan_image *img =
|
|
pan_image_view_get_s_plane(store->s.iview).image;
|
|
if (GENX(pan_force_clean_write_on)(img, fb->tile_size_px))
|
|
ct.s = true;
|
|
}
|
|
}
|
|
|
|
return ct;
|
|
}
|
|
|
|
static void
|
|
emit_zs_crc_desc(const struct pan_fb_desc_info *info,
|
|
const struct pan_fb_clean_tile ct,
|
|
struct mali_zs_crc_extension_packed *zs_crc)
|
|
{
|
|
const struct pan_fb_layout *fb = info->fb;
|
|
const struct pan_fb_store *store = info->store;
|
|
|
|
pan_pack(zs_crc, ZS_CRC_EXTENSION, cfg) {
|
|
if (store && store->zs.store) {
|
|
cfg.zs.msaa = translate_msaa_copy_op(fb, store->zs.iview,
|
|
store->zs.msaa);
|
|
|
|
#if PAN_ARCH >= 6
|
|
cfg.zs.clean_tile_write_enable = ct.zs;
|
|
#else
|
|
cfg.zs.clean_pixel_write_enable = ct.zs;
|
|
#endif
|
|
|
|
}
|
|
|
|
if (store && store->s.store) {
|
|
cfg.s.msaa = translate_msaa_copy_op(fb, store->s.iview,
|
|
store->s.msaa);
|
|
|
|
#if PAN_ARCH >= 6
|
|
cfg.s.clean_tile_write_enable = ct.s;
|
|
#else
|
|
cfg.s.clean_pixel_write_enable = ct.s;
|
|
#endif
|
|
}
|
|
|
|
/* TODO CRC */
|
|
}
|
|
|
|
if (store && store->zs.store) {
|
|
const struct pan_image_view *iview = store->zs.iview;
|
|
const struct pan_mod_handler *mod_handler =
|
|
pan_image_view_get_zs_plane(iview).image->mod_handler;
|
|
|
|
assert(info->layer < pan_image_view_get_layer_count(iview));
|
|
const struct pan_attachment_info att = {
|
|
.iview = iview,
|
|
.layer_or_z_slice = iview->first_layer + info->layer,
|
|
.fb_tile_size_px = fb->tile_size_px,
|
|
};
|
|
|
|
struct mali_zs_crc_extension_packed zs_part;
|
|
mod_handler->emit_zs_attachment(&att, &zs_part);
|
|
pan_merge(zs_crc, &zs_part, ZS_CRC_EXTENSION);
|
|
}
|
|
|
|
if (store && store->s.store) {
|
|
const struct pan_image_view *iview = store->s.iview;
|
|
const struct pan_mod_handler *mod_handler =
|
|
pan_image_view_get_s_plane(iview).image->mod_handler;
|
|
|
|
assert(info->layer < pan_image_view_get_layer_count(iview));
|
|
const struct pan_attachment_info att = {
|
|
.iview = iview,
|
|
.layer_or_z_slice = iview->first_layer + info->layer,
|
|
.fb_tile_size_px = fb->tile_size_px,
|
|
};
|
|
|
|
struct mali_zs_crc_extension_packed s_part;
|
|
mod_handler->emit_s_attachment(&att, &s_part);
|
|
pan_merge(zs_crc, &s_part, ZS_CRC_EXTENSION);
|
|
}
|
|
|
|
/* TODO: CRC */
|
|
}
|
|
|
|
static void
|
|
emit_rgb_rt_desc(const struct pan_fb_desc_info *info,
|
|
const struct pan_fb_clean_tile ct,
|
|
unsigned rt, uint32_t tile_rt_offset_B,
|
|
struct mali_rgb_render_target_packed *rgb_rt)
|
|
{
|
|
const struct pan_fb_layout *fb = info->fb;
|
|
const struct pan_fb_load *load = info->load;
|
|
const struct pan_fb_store *store = info->store;
|
|
|
|
if (fb->rt_formats[rt] == PIPE_FORMAT_NONE) {
|
|
pan_pack(rgb_rt, RGB_RENDER_TARGET, cfg) {
|
|
/* Place non-existent targets at the end. Hardware bounds
|
|
* checking should just discard writes and zero reads.
|
|
*/
|
|
cfg.internal_buffer_offset = fb->tile_rt_alloc_B;
|
|
cfg.internal_format = MALI_COLOR_BUFFER_INTERNAL_FORMAT_R8G8B8A8;
|
|
cfg.write_enable = false;
|
|
#if PAN_ARCH >= 7
|
|
cfg.writeback_block_format = MALI_BLOCK_FORMAT_TILED_U_INTERLEAVED;
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
pan_pack(rgb_rt, RGB_RENDER_TARGET, cfg) {
|
|
cfg.internal_buffer_offset = tile_rt_offset_B;
|
|
cfg.dithering_enable = true;
|
|
|
|
if (store && store->rts[rt].store) {
|
|
cfg.writeback_msaa =
|
|
translate_msaa_copy_op(fb, store->rts[rt].iview,
|
|
store->rts[rt].msaa);
|
|
}
|
|
|
|
#if PAN_ARCH >= 6
|
|
cfg.clean_tile_write_enable = !!(ct.rts & BITFIELD_BIT(rt));
|
|
#else
|
|
cfg.clean_pixel_write_enable = !!(ct.rts & BITFIELD_BIT(rt));
|
|
#endif
|
|
|
|
if (load && pan_target_has_clear(&load->rts[rt])) {
|
|
uint32_t packed[4] = {};
|
|
pan_pack_color(GENX(pan_blendable_formats), packed,
|
|
&load->rts[rt].clear.color, fb->rt_formats[rt],
|
|
false /* dithered */);
|
|
|
|
cfg.clear = (struct MALI_RT_CLEAR) {
|
|
.color_0 = packed[0],
|
|
.color_1 = packed[1],
|
|
.color_2 = packed[2],
|
|
.color_3 = packed[3],
|
|
};
|
|
}
|
|
}
|
|
|
|
struct mali_render_target_packed desc;
|
|
if (store && store->rts[rt].store) {
|
|
const struct pan_image_view *iview = store->rts[rt].iview;
|
|
const struct pan_mod_handler *mod_handler =
|
|
pan_image_view_get_color_plane(iview).image->mod_handler;
|
|
|
|
assert(info->layer < pan_image_view_get_layer_count(iview));
|
|
const struct pan_attachment_info att = {
|
|
.iview = iview,
|
|
.layer_or_z_slice = iview->first_layer + info->layer,
|
|
.fb_tile_size_px = fb->tile_size_px,
|
|
};
|
|
|
|
mod_handler->emit_color_attachment(&att, &desc);
|
|
} else {
|
|
GENX(pan_emit_default_color_attachment)(fb->rt_formats[rt], &desc);
|
|
}
|
|
pan_merge(rgb_rt, &desc, RGB_RENDER_TARGET);
|
|
}
|
|
|
|
static void
|
|
emit_rts(const struct pan_fb_desc_info *info,
|
|
struct mali_rgb_render_target_packed *rts)
|
|
{
|
|
const struct pan_fb_layout *fb = info->fb;
|
|
const struct pan_fb_clean_tile ct = GENX(pan_fb_get_clean_tile)(info);
|
|
|
|
uint32_t tile_rt_offset_B = 0;
|
|
for (unsigned rt = 0; rt < fb->rt_count; rt++) {
|
|
emit_rgb_rt_desc(info, ct, rt, tile_rt_offset_B, rts);
|
|
rts++;
|
|
|
|
if (fb->rt_formats[rt] != PIPE_FORMAT_NONE) {
|
|
tile_rt_offset_B += pan_bytes_per_pixel_tib(fb->rt_formats[rt]) *
|
|
fb->tile_size_px * fb->sample_count;
|
|
}
|
|
}
|
|
assert(tile_rt_offset_B <= fb->tile_rt_alloc_B);
|
|
}
|
|
|
|
#if PAN_ARCH >= 14
|
|
uint32_t
|
|
GENX(pan_emit_fb_desc)(const struct pan_fb_desc_info *info,
|
|
const struct pan_fb_descs *out)
|
|
{
|
|
if (pan_fb_has_zs(info->fb)) {
|
|
emit_zs_crc_desc(info, GENX(pan_fb_get_clean_tile)(info), out->zs_crc);
|
|
}
|
|
|
|
emit_rts(info, out->rts);
|
|
|
|
return 0;
|
|
}
|
|
#else /* PAN_ARCH < 14 */
|
|
uint32_t
|
|
GENX(pan_emit_fb_desc)(const struct pan_fb_desc_info *info,
|
|
const struct pan_fb_descs *out)
|
|
{
|
|
const struct pan_fb_layout *fb = info->fb;
|
|
const struct pan_fb_load *load = info->load;
|
|
const struct pan_fb_store *store = info->store;
|
|
const struct pan_fb_clean_tile ct = GENX(pan_fb_get_clean_tile)(info);
|
|
|
|
const bool has_zs_crc_ext = pan_fb_has_zs(fb);
|
|
|
|
struct mali_framebuffer_packed fbd = {};
|
|
|
|
#if PAN_ARCH <= 5
|
|
GENX(pan_emit_tls)(info->tls,
|
|
pan_section_ptr(&fbd, FRAMEBUFFER, LOCAL_STORAGE));
|
|
#endif
|
|
|
|
pan_section_pack(&fbd, FRAMEBUFFER, PARAMETERS, cfg) {
|
|
#if PAN_ARCH >= 6
|
|
cfg.pre_frame_0 = pan_fix_frame_shader_mode(info->frame_shaders.modes[0],
|
|
ct.rts || ct.zs || ct.s);
|
|
cfg.pre_frame_1 = pan_fix_frame_shader_mode(info->frame_shaders.modes[1],
|
|
ct.rts || ct.zs || ct.s);
|
|
cfg.post_frame = info->frame_shaders.modes[2];
|
|
cfg.frame_shader_dcds = info->frame_shaders.dcd_pointer;
|
|
|
|
cfg.sample_locations = info->sample_pos_array_pointer;
|
|
#endif
|
|
|
|
#if PAN_ARCH >= 13
|
|
/* Enabling prepass without pipelineing is generally not good for
|
|
* performance, so disable HSR in that case.
|
|
*/
|
|
cfg.hsr_prepass_enable = info->allow_hsr_prepass &&
|
|
pan_fb_can_pipeline_zs(fb);
|
|
cfg.hsr_prepass_interleaving_enable = pan_fb_can_pipeline_zs(fb);
|
|
cfg.hsr_prepass_filter_enable = true;
|
|
cfg.hsr_hierarchical_optimizations_enable = true;
|
|
#endif
|
|
|
|
#if PAN_ARCH >= 9
|
|
/* internal_layer_index is used to select the right primitive list in
|
|
* the tiler context, and frame_arg is the value that's passed to the
|
|
* fragment shader through r62-r63, which we use to pass gl_Layer. Since
|
|
* the layer_idx only takes 8-bits, we might use the extra 56-bits we
|
|
* have in frame_argument to pass other information to the fragment
|
|
* shader at some point.
|
|
*/
|
|
assert(info->layer >= info->tiler_ctx->valhall.layer_offset);
|
|
cfg.internal_layer_index =
|
|
info->layer - info->tiler_ctx->valhall.layer_offset;
|
|
cfg.frame_argument = info->layer;
|
|
#endif
|
|
|
|
cfg.width = fb->width_px;
|
|
cfg.height = fb->height_px;
|
|
|
|
const struct pan_fb_bbox fb_area_px =
|
|
pan_fb_bbox_from_xywh(0, 0, info->fb->width_px, info->fb->height_px);
|
|
assert(pan_fb_bbox_is_valid(info->fb->tiling_area_px));
|
|
const struct pan_fb_bbox bbox_px =
|
|
pan_fb_bbox_clamp(info->fb->tiling_area_px, fb_area_px);
|
|
cfg.bound_min_x = bbox_px.min_x;
|
|
cfg.bound_min_y = bbox_px.min_y;
|
|
cfg.bound_max_x = bbox_px.max_x;
|
|
cfg.bound_max_y = bbox_px.max_y;
|
|
|
|
cfg.sample_count = fb->sample_count;
|
|
cfg.sample_pattern = pan_sample_pattern(fb->sample_count);
|
|
|
|
/* Ensure we cover the samples on the edge for 16x MSAA */
|
|
cfg.tie_break_rule = fb->sample_count == 16 ?
|
|
MALI_TIE_BREAK_RULE_MINUS_180_OUT_0_IN :
|
|
MALI_TIE_BREAK_RULE_MINUS_180_IN_0_OUT;
|
|
|
|
cfg.effective_tile_size = fb->tile_size_px;
|
|
|
|
#if PAN_ARCH >= 9
|
|
cfg.point_sprite_coord_origin_max_y = info->sprite_coord_origin_max_y;
|
|
cfg.first_provoking_vertex = info->provoking_vertex_first;
|
|
#endif
|
|
|
|
assert(fb->rt_count > 0);
|
|
cfg.render_target_count = fb->rt_count;
|
|
cfg.color_buffer_allocation = fb->tile_rt_alloc_B;
|
|
|
|
if (fb->s_format != PIPE_FORMAT_NONE) {
|
|
cfg.s_clear =
|
|
load && pan_target_has_clear(&load->s) ? load->s.clear.stencil : 0;
|
|
cfg.s_write_enable = store && store->s.store;
|
|
}
|
|
|
|
if (fb->z_format != PIPE_FORMAT_NONE) {
|
|
cfg.z_internal_format = pan_get_z_internal_format(fb->z_format);
|
|
cfg.z_clear =
|
|
load && pan_target_has_clear(&load->z) ? load->z.clear.depth : 0;
|
|
cfg.z_write_enable = store && store->zs.store;
|
|
} else {
|
|
/* Default to 24 bit depth if there's no surface. */
|
|
cfg.z_internal_format = MALI_Z_INTERNAL_FORMAT_D24;
|
|
|
|
/* If we want a stencil store and we don't have depth, that should
|
|
* happen as a stencil store, not combined depth/stencil.
|
|
*/
|
|
assert(!store || !store->zs.store);
|
|
}
|
|
|
|
cfg.has_zs_crc_extension = has_zs_crc_ext;
|
|
|
|
#if PAN_ARCH >= 6
|
|
cfg.tiler = PAN_ARCH >= 9 ? info->tiler_ctx->valhall.desc
|
|
: info->tiler_ctx->bifrost.desc;
|
|
#endif
|
|
}
|
|
|
|
#if PAN_ARCH <= 5
|
|
/* TODO: Midgard tiler */
|
|
assert(info->tiler_ctx == NULL);
|
|
|
|
/* All weights set to 0, nothing to do here */
|
|
pan_section_pack(&fbd, FRAMEBUFFER, TILER_WEIGHTS, w);
|
|
#endif
|
|
|
|
memcpy(out->fbd, &fbd, sizeof(fbd));
|
|
|
|
if (has_zs_crc_ext) {
|
|
emit_zs_crc_desc(info, ct, out->zs_crc);
|
|
}
|
|
|
|
emit_rts(info, out->rts);
|
|
|
|
struct mali_framebuffer_pointer_packed tag;
|
|
pan_pack(&tag, FRAMEBUFFER_POINTER, cfg) {
|
|
cfg.zs_crc_extension_present = has_zs_crc_ext;
|
|
cfg.render_target_count = fb->rt_count;
|
|
}
|
|
return tag.opaque[0];
|
|
}
|
|
#endif /* PAN_ARCH >= 14 */
|
|
#endif /* PAN_ARCH >= 5 */
|