Files
intel-hard-graphics/src/panfrost/lib/pan_fb.h
T
2026-06-06 13:25:15 +03:00

666 lines
19 KiB
C

/*
* Copyright (C) 2026 Collabora, Ltd.
* Copyright (C) 2026 Arm Ltd.
* SPDX-License-Identifier: MIT
*/
#ifndef __PAN_FB_H
#define __PAN_FB_H
#include "genxml/gen_macros.h"
#include "util/format/u_formats.h"
#include "compiler/shader_enums.h"
struct nir_shader;
struct nir_shader_compiler_options;
struct pan_fb_info;
struct pan_image_view;
#define PAN_MAX_RTS 8
/** Bounding box used by the framebuffer abstraction */
struct pan_fb_bbox {
/** Minimum x/y value */
uint16_t min_x, min_y;
/** Maximum x/y value
*
* Like the hardware, the maximums here are inclusive. A pixel is
* in-bounds if min_x <= x <= max_x and min_y <= y <= max_y.
*/
uint16_t max_x, max_y;
};
static inline bool
pan_fb_bbox_is_valid(struct pan_fb_bbox b)
{
return b.min_x <= b.max_x && b.min_y <= b.max_y;
}
static inline bool
pan_fb_bbox_contains_bbox(struct pan_fb_bbox a, struct pan_fb_bbox b)
{
return a.min_x <= b.min_x && b.max_x <= a.max_x &&
a.min_y <= b.min_y && b.max_y <= a.max_y;
}
static inline bool
pan_fb_bbox_equal(struct pan_fb_bbox a, struct pan_fb_bbox b)
{
return a.min_x == b.min_x && b.max_x == a.max_x &&
a.min_y == b.min_y && b.max_y == a.max_y;
}
static inline struct pan_fb_bbox
pan_fb_bbox_clamp(struct pan_fb_bbox a, struct pan_fb_bbox b)
{
assert(pan_fb_bbox_is_valid(a) && pan_fb_bbox_is_valid(a));
return (struct pan_fb_bbox) {
.min_x = CLAMP(a.min_x, b.min_x, b.max_x),
.min_y = CLAMP(a.min_y, b.min_y, b.max_y),
.max_x = CLAMP(a.max_x, b.min_x, b.max_x),
.max_y = CLAMP(a.max_y, b.min_y, b.max_y),
};
}
static inline struct pan_fb_bbox
pan_fb_bbox_align(struct pan_fb_bbox b, uint32_t align_x, uint32_t align_y)
{
assert(util_is_power_of_two_nonzero(align_x));
assert(util_is_power_of_two_nonzero(align_y));
assert(align_x <= UINT16_MAX && align_y <= UINT16_MAX);
b.min_x = ROUND_DOWN_TO(b.min_x, align_x);
b.min_y = ROUND_DOWN_TO(b.min_y, align_y);
b.max_x = align((uint32_t)b.max_x + 1, align_x) - 1;
b.max_y = align((uint32_t)b.max_y + 1, align_y) - 1;
return b;
}
static inline struct pan_fb_bbox
pan_fb_bbox_from_xywh(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
assert(x + w > x && x + w - 1 <= UINT16_MAX);
assert(y + h > y && y + h - 1 <= UINT16_MAX);
return (struct pan_fb_bbox) {
.min_x = x,
.min_y = y,
.max_x = x + w - 1,
.max_y = y + h - 1,
};
}
/** Framebuffer layout
*
* This describes the layout of framebuffer in tile memory as well as various
* properties that are properties of the framebuffer itself, nit any of the
* image views attached to it.
*/
struct pan_fb_layout {
/** Dimensions of the framebuffer itself, in pixels */
uint32_t width_px, height_px;
/** The render area
*
* This is the API render area (as defined by Vulkan) and may be smaller
* than the actual bounding box of the framebuffer. The in_bounds_load
* and border_load members of pan_fb_load_target are relative to the
* render area.
*/
struct pan_fb_bbox render_area_px;
/** The tiling area
*
* This is based on the render area but may be larger to accomodate tiling
* restrictions.
*/
struct pan_fb_bbox tiling_area_px;
/** Sample count for this framebuffer */
uint8_t sample_count;
/** Color render target formats
*
* If a render target is unused, set it to PIPE_FORMAT_NONE.
*/
uint8_t rt_count;
enum pipe_format rt_formats[PAN_MAX_RTS];
/** Depth format for this framebuffer, or PIPE_FORMAT_NONE */
enum pipe_format z_format;
/** Stencil format for this framebuffer, or PIPE_FORMAT_NONE */
enum pipe_format s_format;
/** Amount of space reserved for PLS, in bytes.
*
* This may overlap the color render targets in tile memory.
*/
uint16_t pls_size_B;
uint32_t tile_rt_budget_B;
uint32_t tile_z_budget_B;
/** Optimal tile buffer size, in pixels
*
* This must be a power of two.
*/
unsigned tile_size_px;
/** Amount of tile memory allocated for color render targets */
unsigned tile_rt_alloc_B;
};
static inline bool
pan_fb_has_zs(const struct pan_fb_layout *fb)
{
/* An intentional choice is made here to base has_zs on the formats in
* the pan_fb_layout and not the stores being done in the end. PanVK in
* particular relies on the size of the final framebuffer descriptor being
* the same for all the different variants with incremental rendering.
*/
return fb->z_format != PIPE_FORMAT_NONE ||
fb->s_format != PIPE_FORMAT_NONE;
}
static inline bool
pan_fb_is_fully_covered(const struct pan_fb_layout *fb)
{
const struct pan_fb_bbox fb_area_px =
pan_fb_bbox_from_xywh(0, 0, fb->width_px, fb->height_px);
return pan_fb_bbox_equal(fb->render_area_px, fb_area_px);
}
static inline bool
pan_fb_has_partial_tiles(const struct pan_fb_layout *fb)
{
assert(pan_fb_bbox_contains_bbox(fb->tiling_area_px, fb->render_area_px));
return !pan_fb_bbox_equal(fb->tiling_area_px, fb->render_area_px);
}
#ifdef PAN_ARCH
void GENX(pan_select_fb_tile_size)(struct pan_fb_layout *fb);
#endif
enum ENUM_PACKED pan_fb_load_op {
PAN_FB_LOAD_NONE = 0,
PAN_FB_LOAD_CLEAR,
PAN_FB_LOAD_IMAGE,
PAN_FB_LOAD_OP_COUNT,
};
/** Describes how MSAA is handled when copying
*
* The source or destination may or may not be multisampled independently and
* this controls how that is handled in the copy.
*/
enum ENUM_PACKED pan_fb_msaa_copy_op {
/** Copies all samples of the source to the destination
*
* This requires that the source and destination sample counts be the same.
* If the source and destination are both single-sampled, this is equivalent
* as PAN_FB_MSAA_COPY_SINGLE or PAN_FB_MSAA_COPY_SAMPLE_0.
*/
PAN_FB_MSAA_COPY_ALL = 0,
/** Copies the single sample from the source to the destination
*
* This requires that the source be single-sampled. Semantically, this is
* the same as PAN_FB_MSAA_COPY_SAMPLE_0, but it's useful to make this
* distinction in shader keys so single and multi-sampled sources can be
* differentiated.
*/
PAN_FB_MSAA_COPY_SINGLE,
/** Copies assuming all source samples are identical
*
* The actual copy can use any resolve mode because we know a priori that
* all samples are identical. All resolve modes yield the same result.
*/
PAN_FB_MSAA_COPY_IDENTICAL,
/** Copies sample 0 from the source to all samples in the destination */
PAN_FB_MSAA_COPY_SAMPLE_0,
/** Copies the average of the samples in the source to the destination */
PAN_FB_MSAA_COPY_AVERAGE,
/** Copies the minimum of the samples in the source to the destination */
PAN_FB_MSAA_COPY_MIN,
/** Copies the maximum of the samples in the source to the destination */
PAN_FB_MSAA_COPY_MAX,
PAN_FB_MSAA_COPY_OP_COUNT,
};
struct pan_fb_load_target {
/** Load op for pixels inside the bounding box */
enum pan_fb_load_op in_bounds_load;
/** Load op for border (out of bounds) pixels
*
* This should almost always be LOAD_OP_LOAD, unless you have a very good
* reason to make a different choice.
*/
enum pan_fb_load_op border_load;
/** Always load, even if no primitives intersect the tile
*
* This should usually be set for clears.
*/
bool always;
/** MSAA mode used when load op is IMAGE */
enum pan_fb_msaa_copy_op msaa;
/* Image view to load from, or NULL if neither load op is IMAGE */
const struct pan_image_view *iview;
/** Clear value, if either load op is CLEAR */
union {
union pipe_color_union color;
float depth;
uint8_t stencil;
} clear;
};
static inline struct pan_fb_load_target
pan_fb_load_iview(const struct pan_image_view *iview)
{
return (struct pan_fb_load_target) {
.in_bounds_load = PAN_FB_LOAD_IMAGE,
.border_load = PAN_FB_LOAD_IMAGE,
.msaa = PAN_FB_MSAA_COPY_ALL,
.iview = iview,
};
}
struct pan_fb_load {
/** Color render target loads */
struct pan_fb_load_target rts[PAN_MAX_RTS];
/** Depth target load */
struct pan_fb_load_target z;
/** Stencil target load */
struct pan_fb_load_target s;
};
static inline bool
pan_fb_has_image_load(const struct pan_fb_load *load, bool include_border)
{
if (load->z.in_bounds_load == PAN_FB_LOAD_IMAGE ||
load->s.in_bounds_load == PAN_FB_LOAD_IMAGE)
return true;
if (include_border &&
(load->z.border_load == PAN_FB_LOAD_IMAGE ||
load->s.border_load == PAN_FB_LOAD_IMAGE))
return true;
for (unsigned rt = 0; rt < PAN_MAX_RTS; rt++) {
if (load->rts[rt].in_bounds_load == PAN_FB_LOAD_IMAGE)
return true;
if (include_border && load->rts[rt].border_load == PAN_FB_LOAD_IMAGE)
return true;
}
return false;
}
/** Describes a resolve operation
*
* This describes the source data for the resolve operation. The way in which
* MSAA is handled in the source data is defined by the pan_fb_msaa_copy_op.
*/
enum ENUM_PACKED pan_fb_resolve_op {
/** Do nothing
*
* In this mode, the framebuffer is left unaffected by the resolve. If
* necessary (sometimes required for Z/S), the old value will be read and
* written back to ensure the final value is the same.
*/
PAN_FB_RESOLVE_NONE = 0,
/** Load from the specified image view */
PAN_FB_RESOLVE_IMAGE,
/** Load from color render target 0 */
PAN_FB_RESOLVE_RT_0,
PAN_FB_RESOLVE_RT_1,
PAN_FB_RESOLVE_RT_2,
PAN_FB_RESOLVE_RT_3,
PAN_FB_RESOLVE_RT_4,
PAN_FB_RESOLVE_RT_5,
PAN_FB_RESOLVE_RT_6,
PAN_FB_RESOLVE_RT_7,
/** Load from the depth target
*
* From the perspective of resolve ops, Z/S are always separate
*/
PAN_FB_RESOLVE_Z,
/** Load from the stencil target
*
* From the perspective of resolve ops, Z/S are always separate
*/
PAN_FB_RESOLVE_S,
PAN_FB_RESOLVE_OP_COUNT,
};
static_assert(PAN_FB_RESOLVE_Z == PAN_FB_RESOLVE_RT_0 + PAN_MAX_RTS,
"There are PAN_MAX_RTS many RTs");
#define PAN_FB_RESOLVE_RT(rt) \
(assert(0 <= (rt) && (rt) < PAN_MAX_RTS), (PAN_FB_RESOLVE_RT_0 + (rt)))
/** Describes a resovle operation on a given target
*
* For each side of the render area (in-bounds or border), this defines both
* a source to copy from (the resolve op) and a MSAA copy op to apply as part
* of the copy. In the common case, a resolve op will read from itself but
* that is not strictly a requirement. Any resolve op can read from any
* render target.
*/
struct pan_fb_resolve_target {
struct pan_fb_resolve_op_msaa {
enum pan_fb_resolve_op resolve;
enum pan_fb_msaa_copy_op msaa;
} in_bounds, border;
/** For PAN_FB_RESOLVE_IMAGE the image view to load from */
const struct pan_image_view *iview;
};
/** Describes a resolve operation
*
* A resolve operation is implemented as a post-frame shader (see
* pan_fb_resolve_shader_key_fill) and does a copy from render targets to
* render targets, applying MSAA copy ops along the way.
*/
struct pan_fb_resolve {
struct pan_fb_resolve_target rts[PAN_MAX_RTS];
struct pan_fb_resolve_target z, s;
};
struct pan_fb_store_target {
/** Whether or not to do a store */
bool store;
/** Always store, even if no primitives intersect the tile
*
* clean_tile_write_enable will be set if either store.always is set or
* load.always is set for the corresponding load[s].
*/
bool always;
/** MSAA mode to use when copying to the destination image
*
* This can only be set to AVERAGE for blendable color formats.
*/
enum pan_fb_msaa_copy_op msaa;
/** Image view to store to */
const struct pan_image_view *iview;
};
static inline struct pan_fb_store_target
pan_fb_store_iview(const struct pan_image_view *iview)
{
return (struct pan_fb_store_target) {
.store = true,
.msaa = PAN_FB_MSAA_COPY_ALL,
.iview = iview,
};
}
static inline struct pan_fb_store_target
pan_fb_always_store_iview_s0(const struct pan_image_view *iview)
{
return (struct pan_fb_store_target) {
.store = true,
.always = true,
.msaa = PAN_FB_MSAA_COPY_SAMPLE_0,
.iview = iview,
};
}
struct pan_fb_store {
/* Color render target stores */
struct pan_fb_store_target rts[PAN_MAX_RTS];
/* Depth/stencil target store
*
* If the attached image view has a combined depth/stencil format, both
* depth and stencil will be written.
*/
struct pan_fb_store_target zs;
/* Stencil target store */
struct pan_fb_store_target s;
};
#ifdef PAN_ARCH
void GENX(pan_align_fb_tiling_area)(struct pan_fb_layout *fb,
const struct pan_fb_store *store);
void GENX(pan_fb_fold_resolve_into_store)(const struct pan_fb_layout *fb,
struct pan_fb_resolve *resolve,
struct pan_fb_store *store);
#endif
struct pan_fb_frame_shaders {
uint64_t dcd_pointer;
uint8_t modes[3];
};
struct pan_fb_desc_info {
const struct pan_fb_layout *fb;
const struct pan_fb_load *load;
const struct pan_fb_store *store;
struct pan_fb_frame_shaders frame_shaders;
uint64_t sample_pos_array_pointer;
/* Only used on Valhal */
bool sprite_coord_origin_max_y;
bool provoking_vertex_first;
bool allow_hsr_prepass;
uint16_t layer;
const struct pan_tls_info *tls;
const struct pan_tiler_context *tiler_ctx;
};
#ifdef PAN_ARCH
void GENX(pan_fill_fb_info)(const struct pan_fb_desc_info *info,
struct pan_fb_info *fbinfo);
struct pan_fb_descs {
#if PAN_ARCH <= 13
struct mali_framebuffer_packed *fbd;
#endif
struct mali_zs_crc_extension_packed *zs_crc;
struct mali_rgb_render_target_packed *rts;
};
uint32_t GENX(pan_emit_fb_desc)(const struct pan_fb_desc_info *info,
const struct pan_fb_descs *out);
#endif
enum ENUM_PACKED pan_fb_shader_op {
/** Preserves the current fragment contents
*
* The MSAA mode is ignored and, if a load does need to occur to preserve
* the current fragment contents, PAN_FB_MSAA_COPY_ALL is implied.
*
* This is only allowed in resolve shaders so this can always be replaced
* with a discard.
*/
PAN_FB_SHADER_PRESERVE = 0,
/** Do whatever is the fastest (lowest bandwidth)
*
* This could be a CLEAR or PRESERVE or just write zeros. It will never
* write actual random data but the exact behavior may depend on other bits
* in the shader key.
*/
PAN_FB_SHADER_DONT_CARE,
/** Loads the clear color
*
* This is only allowed in preload shaders and the clear will also be done
* in hardware so it can always be replaced with a discard.
*/
PAN_FB_SHADER_LOAD_CLEAR,
/** Loads from the image bound at the corresponding gl_frag_result */
PAN_FB_SHADER_LOAD_IMAGE,
PAN_FB_SHADER_COPY_RT_0,
PAN_FB_SHADER_COPY_RT_1,
PAN_FB_SHADER_COPY_RT_2,
PAN_FB_SHADER_COPY_RT_3,
PAN_FB_SHADER_COPY_RT_4,
PAN_FB_SHADER_COPY_RT_5,
PAN_FB_SHADER_COPY_RT_6,
PAN_FB_SHADER_COPY_RT_7,
PAN_FB_SHADER_COPY_Z,
PAN_FB_SHADER_COPY_S,
PAN_FB_SHADER_OP_COUNT,
};
static_assert(PAN_FB_SHADER_COPY_Z == PAN_FB_SHADER_COPY_RT_0 + PAN_MAX_RTS,
"We should have one PAN_FB_SHADER_COPY_RT_N per RT");
#define PAN_FB_SHADER_COPY_RT(rt) \
(assert(0 <= (rt) && (rt) < PAN_MAX_RTS), (PAN_FB_SHADER_COPY_RT_0 + (rt)))
static inline bool
pan_fb_shader_op_can_discard(enum pan_fb_shader_op op)
{
return op == PAN_FB_SHADER_PRESERVE ||
op == PAN_FB_SHADER_DONT_CARE ||
op == PAN_FB_SHADER_LOAD_CLEAR;
}
enum ENUM_PACKED pan_fb_shader_data_type {
PAN_FB_SHADER_DATA_TYPE_F32 = 0,
PAN_FB_SHADER_DATA_TYPE_I32,
PAN_FB_SHADER_DATA_TYPE_U32,
PAN_FB_SHADER_DATA_TYPE_COUNT,
};
static_assert(PAN_FB_SHADER_OP_COUNT <= (1 << 4),
"pan_fb_shader_op fits in 4 bits");
static_assert(PAN_FB_MSAA_COPY_OP_COUNT <= (1 << 3),
"pan_fb_msaa_copy_op fits in 3 bits");
static_assert(PAN_FB_SHADER_DATA_TYPE_COUNT <= (1 << 2),
"pan_fb_shader_data_type fits in 2 bits");
/* Asserts for glsl_sampler_dim glsl_base_type have to be runtime because
* there is no MAX value we can use.
*/
PRAGMA_DIAGNOSTIC_PUSH
PRAGMA_DIAGNOSTIC_ERROR(-Wpadded)
struct pan_fb_shader_key_target {
uint32_t in_bounds_op : 4;
uint32_t border_op : 4;
uint32_t in_bounds_msaa : 3;
uint32_t border_msaa : 3;
uint32_t sample0_only : 1;
uint32_t image_dim : 2;
uint32_t image_is_array : 1;
uint32_t image_samples_log2 : 3;
uint32_t data_type : 2;
uint32_t pad : 9;
};
PRAGMA_DIAGNOSTIC_POP
static_assert(sizeof(struct pan_fb_shader_key_target) == 4,
"This struct has no holes");
/** Whether or not the given target is written by the FB shader
*
* GENX(pan_get_fb_shader)() guarantees that if both in_bounds_op and
* border_op can be discarded, that it does nothing with that target. This
* way the full outputs_written can be known from just the shader key.
*/
static inline bool
pan_fb_shader_key_target_written(const struct pan_fb_shader_key_target *target)
{
return !pan_fb_shader_op_can_discard(target->in_bounds_op) ||
!pan_fb_shader_op_can_discard(target->border_op);
}
PRAGMA_DIAGNOSTIC_PUSH
PRAGMA_DIAGNOSTIC_ERROR(-Wpadded)
struct pan_fb_shader_key {
struct pan_fb_shader_key_target rts[PAN_MAX_RTS];
struct pan_fb_shader_key_target z, s;
uint16_t z_format;
uint8_t fb_sample_count;
uint8_t _pad;
};
PRAGMA_DIAGNOSTIC_POP
static_assert(sizeof(struct pan_fb_shader_key) == 4 * (PAN_MAX_RTS + 3),
"This struct has no holes");
#ifdef PAN_ARCH
bool GENX(pan_fb_load_shader_key_fill)(struct pan_fb_shader_key *key,
const struct pan_fb_layout *fb,
const struct pan_fb_load *load,
bool zs_prepass);
#if PAN_ARCH >= 5
struct pan_fb_clean_tile {
uint8_t rts;
bool zs, s;
};
struct pan_fb_clean_tile
GENX(pan_fb_get_clean_tile)(const struct pan_fb_desc_info *info);
static inline bool
pan_target_has_clear(const struct pan_fb_load_target *target)
{
return target->in_bounds_load == PAN_FB_LOAD_CLEAR ||
target->border_load == PAN_FB_LOAD_CLEAR;
}
#endif /* PAN_ARCH >= 5 */
#if PAN_ARCH >= 6
bool GENX(pan_fb_resolve_shader_key_fill)(struct pan_fb_shader_key *key,
const struct pan_fb_layout *fb,
const struct pan_fb_resolve *resolve);
#endif /* PAN_ARCH >= 6 */
struct nir_shader *
GENX(pan_get_fb_shader)(const struct pan_fb_shader_key *key,
const struct nir_shader_compiler_options *nir_options);
#if PAN_ARCH >= 13
/**
* Returns true if there's enough space in the tile buffer for at least two
* Z/S tiles.
*/
static inline bool
pan_fb_can_pipeline_zs(const struct pan_fb_layout *fb)
{
const uint32_t z_B_per_px = sizeof(float) * fb->sample_count;
const uint32_t z_B_per_tile = z_B_per_px * fb->tile_size_px;
/* The budget is already half the available Z space */
return z_B_per_tile < fb->tile_z_budget_B;
}
#endif
#endif /* PAN_ARCH */
#endif /* __PAN_FB_H */