blob: 28b514b66cdf29171670a3c10bd4848577684cff [file]
/*
* Copyright 2021 Alyssa Rosenzweig
* Copyright (C) 2019-2021 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef AGX_STATE_H
#define AGX_STATE_H
#include "gallium/include/pipe/p_context.h"
#include "gallium/include/pipe/p_state.h"
#include "gallium/include/pipe/p_screen.h"
#include "gallium/auxiliary/util/u_blitter.h"
#include "asahi/lib/agx_pack.h"
#include "asahi/lib/agx_bo.h"
#include "asahi/lib/agx_device.h"
#include "asahi/lib/pool.h"
#include "asahi/compiler/agx_compile.h"
#include "asahi/layout/layout.h"
#include "compiler/nir/nir_lower_blend.h"
#include "util/hash_table.h"
#include "util/bitset.h"
struct agx_streamout_target {
struct pipe_stream_output_target base;
uint32_t offset;
};
struct agx_streamout {
struct pipe_stream_output_target *targets[PIPE_MAX_SO_BUFFERS];
unsigned num_targets;
};
static inline struct agx_streamout_target *
agx_so_target(struct pipe_stream_output_target *target)
{
return (struct agx_streamout_target *)target;
}
struct agx_compiled_shader {
/* Mapped executable memory */
struct agx_bo *bo;
/* Metadata returned from the compiler */
struct agx_shader_info info;
};
struct agx_uncompiled_shader {
struct pipe_shader_state base;
struct nir_shader *nir;
struct hash_table *variants;
/* Set on VS, passed to FS for linkage */
unsigned base_varying;
};
struct agx_stage {
struct agx_uncompiled_shader *shader;
uint32_t dirty;
struct pipe_constant_buffer cb[PIPE_MAX_CONSTANT_BUFFERS];
uint32_t cb_mask;
/* Need full CSOs for u_blitter */
struct agx_sampler_state *samplers[PIPE_MAX_SAMPLERS];
struct agx_sampler_view *textures[PIPE_MAX_SHADER_SAMPLER_VIEWS];
unsigned sampler_count, texture_count;
};
/* Uploaded scissor or depth bias descriptors */
struct agx_array {
struct agx_bo *bo;
unsigned count;
};
struct agx_batch {
unsigned width, height, nr_cbufs;
struct pipe_surface *cbufs[8];
struct pipe_surface *zsbuf;
/* PIPE_CLEAR_* bitmask */
uint32_t clear, draw, load;
/* Base of uploaded texture descriptors */
uint64_t textures;
float clear_color[4];
double clear_depth;
unsigned clear_stencil;
/* Whether we're drawing points, lines, or triangles */
enum pipe_prim_type reduced_prim;
/* Current varyings linkage structures */
uint32_t varyings;
/* Resource list requirements, represented as a bit set indexed by BO
* handles (GEM handles on Linux, or IOGPU's equivalent on macOS)
*/
struct {
BITSET_WORD *set;
unsigned word_count;
} bo_list;
struct agx_pool pool, pipeline_pool;
struct agx_bo *encoder;
uint8_t *encoder_current;
uint8_t *encoder_end;
struct agx_array scissor, depth_bias;
};
struct agx_zsa {
struct pipe_depth_stencil_alpha_state base;
struct agx_fragment_face_packed depth;
struct agx_fragment_stencil_packed front_stencil, back_stencil;
};
struct agx_blend {
bool logicop_enable, blend_enable;
union {
nir_lower_blend_rt rt[8];
unsigned logicop_func;
};
};
struct asahi_shader_key {
struct agx_shader_key base;
struct agx_blend blend;
unsigned nr_cbufs;
/* From rasterizer state, to lower point sprites */
uint16_t sprite_coord_enable;
uint8_t clip_plane_enable;
enum pipe_format rt_formats[PIPE_MAX_COLOR_BUFS];
};
enum agx_dirty {
AGX_DIRTY_VERTEX = BITFIELD_BIT(0),
AGX_DIRTY_VIEWPORT = BITFIELD_BIT(1),
AGX_DIRTY_SCISSOR_ZBIAS = BITFIELD_BIT(2),
AGX_DIRTY_ZS = BITFIELD_BIT(3),
AGX_DIRTY_STENCIL_REF = BITFIELD_BIT(4),
AGX_DIRTY_RS = BITFIELD_BIT(5),
AGX_DIRTY_SPRITE_COORD_MODE = BITFIELD_BIT(6),
AGX_DIRTY_PRIM = BITFIELD_BIT(7),
/* Vertex/fragment pipelines, including uniforms and textures */
AGX_DIRTY_VS = BITFIELD_BIT(8),
AGX_DIRTY_FS = BITFIELD_BIT(9),
/* Just the progs themselves */
AGX_DIRTY_VS_PROG = BITFIELD_BIT(10),
AGX_DIRTY_FS_PROG = BITFIELD_BIT(11),
};
struct agx_context {
struct pipe_context base;
struct agx_compiled_shader *vs, *fs;
uint32_t dirty;
struct agx_batch *batch;
struct pipe_vertex_buffer vertex_buffers[PIPE_MAX_ATTRIBS];
uint32_t vb_mask;
struct agx_stage stage[PIPE_SHADER_TYPES];
struct agx_attribute *attributes;
struct agx_rasterizer *rast;
struct agx_zsa *zs;
struct agx_blend *blend;
struct pipe_blend_color blend_color;
struct pipe_viewport_state viewport;
struct pipe_scissor_state scissor;
struct pipe_stencil_ref stencil_ref;
struct agx_streamout streamout;
uint16_t sample_mask;
struct pipe_framebuffer_state framebuffer;
struct pipe_query *cond_query;
bool cond_cond;
enum pipe_render_cond_flag cond_mode;
struct util_debug_callback debug;
bool is_noop;
uint8_t render_target[8][AGX_RENDER_TARGET_LENGTH];
struct blitter_context *blitter;
};
static inline struct agx_context *
agx_context(struct pipe_context *pctx)
{
return (struct agx_context *) pctx;
}
static inline void
agx_dirty_all(struct agx_context *ctx)
{
ctx->dirty = ~0;
for (unsigned i = 0; i < ARRAY_SIZE(ctx->stage); ++i)
ctx->stage[i].dirty = ~0;
}
struct agx_rasterizer {
struct pipe_rasterizer_state base;
uint8_t cull[AGX_CULL_LENGTH];
uint8_t line_width;
};
struct agx_query {
unsigned query;
};
struct agx_sampler_state {
struct pipe_sampler_state base;
/* Prepared descriptor */
struct agx_sampler_packed desc;
};
struct agx_sampler_view {
struct pipe_sampler_view base;
/* Prepared descriptor */
struct agx_texture_packed desc;
};
struct agx_screen {
struct pipe_screen pscreen;
struct agx_device dev;
struct sw_winsys *winsys;
};
static inline struct agx_screen *
agx_screen(struct pipe_screen *p)
{
return (struct agx_screen *)p;
}
static inline struct agx_device *
agx_device(struct pipe_screen *p)
{
return &(agx_screen(p)->dev);
}
/* TODO: UABI, fake for macOS */
#ifndef DRM_FORMAT_MOD_LINEAR
#define DRM_FORMAT_MOD_LINEAR 1
#endif
#define DRM_FORMAT_MOD_APPLE_TWIDDLED (2)
struct agx_resource {
struct pipe_resource base;
uint64_t modifier;
/* Should probably be part of the modifier. Affects the tiling algorithm, or
* something like that.
*/
bool mipmapped;
/* Hardware backing */
struct agx_bo *bo;
/* Software backing (XXX) */
struct sw_displaytarget *dt;
unsigned dt_stride;
BITSET_DECLARE(data_valid, PIPE_MAX_TEXTURE_LEVELS);
struct ail_layout layout;
/* Metal does not support packed depth/stencil formats; presumably AGX does
* not either. Instead, we create separate depth and stencil resources,
* managed by u_transfer_helper. We provide the illusion of packed
* resources.
*/
struct agx_resource *separate_stencil;
};
static inline struct agx_resource *
agx_resource(struct pipe_resource *pctx)
{
return (struct agx_resource *) pctx;
}
static inline void *
agx_map_texture_cpu(struct agx_resource *rsrc, unsigned level, unsigned z)
{
return ((uint8_t *) rsrc->bo->ptr.cpu) +
ail_get_layer_level_B(&rsrc->layout, z, level);
}
static inline uint64_t
agx_map_texture_gpu(struct agx_resource *rsrc, unsigned z)
{
return rsrc->bo->ptr.gpu +
(uint64_t) ail_get_layer_offset_B(&rsrc->layout, z);
}
struct agx_transfer {
struct pipe_transfer base;
void *map;
struct {
struct pipe_resource *rsrc;
struct pipe_box box;
} staging;
};
static inline struct agx_transfer *
agx_transfer(struct pipe_transfer *p)
{
return (struct agx_transfer *)p;
}
uint64_t
agx_push_location(struct agx_context *ctx, struct agx_push push,
enum pipe_shader_type stage);
uint64_t
agx_build_clear_pipeline(struct agx_context *ctx, uint32_t code, uint64_t clear_buf);
uint64_t
agx_build_store_pipeline(struct agx_context *ctx, uint32_t code,
uint64_t render_target);
uint64_t
agx_build_reload_pipeline(struct agx_context *ctx, uint32_t code, struct pipe_surface *surf);
/* Add a BO to a batch. This needs to be amortized O(1) since it's called in
* hot paths. To achieve this we model BO lists by bit sets */
static unsigned
agx_batch_bo_list_bits(struct agx_batch *batch)
{
return batch->bo_list.word_count * sizeof(BITSET_WORD) * 8;
}
static inline void
agx_batch_add_bo(struct agx_batch *batch, struct agx_bo *bo)
{
/* Double the size of the BO list if we run out, this is amortized O(1) */
if (unlikely(bo->handle > agx_batch_bo_list_bits(batch))) {
batch->bo_list.set = rerzalloc(batch, batch->bo_list.set, BITSET_WORD,
batch->bo_list.word_count,
batch->bo_list.word_count * 2);
batch->bo_list.word_count *= 2;
}
/* The batch holds a single reference to each BO in the batch, released when
* the batch finishes execution.
*/
if (!BITSET_TEST(batch->bo_list.set, bo->handle))
agx_bo_reference(bo);
BITSET_SET(batch->bo_list.set, bo->handle);
}
static unsigned
agx_batch_num_bo(struct agx_batch *batch)
{
return __bitset_count(batch->bo_list.set, batch->bo_list.word_count);
}
#define AGX_BATCH_FOREACH_BO_HANDLE(batch, handle) \
BITSET_FOREACH_SET(handle, (batch)->bo_list.set, agx_batch_bo_list_bits(batch))
/* Blit shaders */
void
agx_blitter_save(struct agx_context *ctx, struct blitter_context *blitter,
bool render_cond);
void agx_blit(struct pipe_context *pipe,
const struct pipe_blit_info *info);
void agx_internal_shaders(struct agx_device *dev);
/* Batch logic */
static void
agx_flush_all(struct agx_context *ctx, const char *reason)
{
//printf("Flushing due to: %s\n", reason);
ctx->base.flush(&ctx->base, NULL, 0);
}
void
agx_batch_init_state(struct agx_batch *batch);
#endif