freedreno: Add helpers to mark dirty state
Doesn't change anything yet, but this will let us more easily add mapping from dirty gallium state to dirty gen-specific state-groups. Note that the mapping from shader-state to global state in fd_context_dirty_shader() optimizes out for release builds. This is kind of important, in the next patch we'll want ffs(SOME_CONST) to optimize away even more. Signed-off-by: Rob Clark <robdclark@chromium.org> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9581>
This commit is contained in:
parent
9aef029635
commit
0cb989d71f
|
@ -135,7 +135,7 @@ fixup_draw_state(struct fd_context *ctx, struct fd6_emit *emit)
|
|||
if (ctx->last.dirty ||
|
||||
(ctx->last.primitive_restart != emit->primitive_restart)) {
|
||||
/* rasterizer state is effected by primitive-restart: */
|
||||
ctx->dirty |= FD_DIRTY_RASTERIZER;
|
||||
fd_context_dirty(ctx, FD_DIRTY_RASTERIZER);
|
||||
ctx->last.primitive_restart = emit->primitive_restart;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -496,6 +496,42 @@ fd_stream_output_target(struct pipe_stream_output_target *target)
|
|||
return (struct fd_stream_output_target *)target;
|
||||
}
|
||||
|
||||
/* Mark specified non-shader-stage related state as dirty: */
|
||||
static inline void
|
||||
fd_context_dirty(struct fd_context *ctx, enum fd_dirty_3d_state dirty)
|
||||
assert_dt
|
||||
{
|
||||
assert(util_is_power_of_two_nonzero(dirty));
|
||||
ctx->dirty |= dirty;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fd_context_dirty_shader(struct fd_context *ctx, enum pipe_shader_type shader,
|
||||
enum fd_dirty_shader_state dirty)
|
||||
assert_dt
|
||||
{
|
||||
const enum fd_dirty_3d_state map[] = {
|
||||
FD_DIRTY_PROG,
|
||||
FD_DIRTY_CONST,
|
||||
FD_DIRTY_TEX,
|
||||
FD_DIRTY_SSBO,
|
||||
FD_DIRTY_IMAGE,
|
||||
};
|
||||
|
||||
/* Need to update the table above if these shift: */
|
||||
STATIC_ASSERT(FD_DIRTY_SHADER_PROG == BIT(0));
|
||||
STATIC_ASSERT(FD_DIRTY_SHADER_CONST == BIT(1));
|
||||
STATIC_ASSERT(FD_DIRTY_SHADER_TEX == BIT(2));
|
||||
STATIC_ASSERT(FD_DIRTY_SHADER_SSBO == BIT(3));
|
||||
STATIC_ASSERT(FD_DIRTY_SHADER_IMAGE == BIT(4));
|
||||
|
||||
assert(util_is_power_of_two_nonzero(dirty));
|
||||
assert(ffs(dirty) <= ARRAY_SIZE(map));
|
||||
|
||||
ctx->dirty_shader[shader] |= dirty;
|
||||
fd_context_dirty(ctx, map[ffs(dirty) - 1]);
|
||||
}
|
||||
|
||||
/* mark all state dirty: */
|
||||
static inline void
|
||||
fd_context_all_dirty(struct fd_context *ctx)
|
||||
|
|
|
@ -38,8 +38,7 @@ fd_vs_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->prog.vs = hwcso;
|
||||
ctx->dirty_shader[PIPE_SHADER_VERTEX] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_VERTEX, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -48,8 +47,7 @@ fd_tcs_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->prog.hs = hwcso;
|
||||
ctx->dirty_shader[PIPE_SHADER_TESS_CTRL] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_TESS_CTRL, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -58,8 +56,7 @@ fd_tes_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->prog.ds = hwcso;
|
||||
ctx->dirty_shader[PIPE_SHADER_TESS_EVAL] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_TESS_EVAL, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -68,8 +65,7 @@ fd_gs_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->prog.gs = hwcso;
|
||||
ctx->dirty_shader[PIPE_SHADER_GEOMETRY] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_GEOMETRY, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -78,8 +74,7 @@ fd_fs_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->prog.fs = hwcso;
|
||||
ctx->dirty_shader[PIPE_SHADER_FRAGMENT] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_FRAGMENT, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
static const char *solid_fs =
|
||||
|
|
|
@ -80,7 +80,7 @@ rebind_resource_in_ctx(struct fd_context *ctx, struct fd_resource *rsc)
|
|||
struct fd_vertexbuf_stateobj *vb = &ctx->vtx.vertexbuf;
|
||||
for (unsigned i = 0; i < vb->count && !(ctx->dirty & FD_DIRTY_VTXBUF); i++) {
|
||||
if (vb->vb[i].buffer.resource == prsc)
|
||||
ctx->dirty |= FD_DIRTY_VTXBUF;
|
||||
fd_context_dirty(ctx, FD_DIRTY_VTXBUF);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,7 @@ rebind_resource_in_ctx(struct fd_context *ctx, struct fd_resource *rsc)
|
|||
const unsigned num_ubos = util_last_bit(cb->enabled_mask);
|
||||
for (unsigned i = 0; i < num_ubos; i++) {
|
||||
if (cb->cb[i].buffer == prsc) {
|
||||
ctx->dirty_shader[stage] |= FD_DIRTY_SHADER_CONST;
|
||||
ctx->dirty |= FD_DIRTY_CONST;
|
||||
fd_context_dirty_shader(ctx, stage, FD_DIRTY_SHADER_CONST);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +113,7 @@ rebind_resource_in_ctx(struct fd_context *ctx, struct fd_resource *rsc)
|
|||
struct fd_texture_stateobj *tex = &ctx->tex[stage];
|
||||
for (unsigned i = 0; i < tex->num_textures; i++) {
|
||||
if (tex->textures[i] && (tex->textures[i]->texture == prsc)) {
|
||||
ctx->dirty_shader[stage] |= FD_DIRTY_SHADER_TEX;
|
||||
ctx->dirty |= FD_DIRTY_TEX;
|
||||
fd_context_dirty_shader(ctx, stage, FD_DIRTY_SHADER_TEX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -128,8 +126,7 @@ rebind_resource_in_ctx(struct fd_context *ctx, struct fd_resource *rsc)
|
|||
const unsigned num_images = util_last_bit(si->enabled_mask);
|
||||
for (unsigned i = 0; i < num_images; i++) {
|
||||
if (si->si[i].resource == prsc) {
|
||||
ctx->dirty_shader[stage] |= FD_DIRTY_SHADER_IMAGE;
|
||||
ctx->dirty |= FD_DIRTY_IMAGE;
|
||||
fd_context_dirty_shader(ctx, stage, FD_DIRTY_SHADER_IMAGE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +139,7 @@ rebind_resource_in_ctx(struct fd_context *ctx, struct fd_resource *rsc)
|
|||
const unsigned num_ssbos = util_last_bit(sb->enabled_mask);
|
||||
for (unsigned i = 0; i < num_ssbos; i++) {
|
||||
if (sb->sb[i].buffer == prsc) {
|
||||
ctx->dirty_shader[stage] |= FD_DIRTY_SHADER_SSBO;
|
||||
ctx->dirty |= FD_DIRTY_SSBO;
|
||||
fd_context_dirty_shader(ctx, stage, FD_DIRTY_SHADER_SSBO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1393,13 +1389,13 @@ fd_invalidate_resource(struct pipe_context *pctx, struct pipe_resource *prsc)
|
|||
|
||||
if (pfb->zsbuf && pfb->zsbuf->texture == prsc) {
|
||||
batch->resolve &= ~(FD_BUFFER_DEPTH | FD_BUFFER_STENCIL);
|
||||
ctx->dirty |= FD_DIRTY_ZSA;
|
||||
fd_context_dirty(ctx, FD_DIRTY_ZSA);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < pfb->nr_cbufs; i++) {
|
||||
if (pfb->cbufs[i] && pfb->cbufs[i]->texture == prsc) {
|
||||
batch->resolve &= ~(PIPE_CLEAR_COLOR0 << i);
|
||||
ctx->dirty |= FD_DIRTY_FRAMEBUFFER;
|
||||
fd_context_dirty(ctx, FD_DIRTY_FRAMEBUFFER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ fd_set_blend_color(struct pipe_context *pctx,
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->blend_color = *blend_color;
|
||||
ctx->dirty |= FD_DIRTY_BLEND_COLOR;
|
||||
fd_context_dirty(ctx, FD_DIRTY_BLEND_COLOR);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -60,7 +60,7 @@ fd_set_stencil_ref(struct pipe_context *pctx,
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->stencil_ref = stencil_ref;
|
||||
ctx->dirty |= FD_DIRTY_STENCIL_REF;
|
||||
fd_context_dirty(ctx, FD_DIRTY_STENCIL_REF);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -70,7 +70,7 @@ fd_set_clip_state(struct pipe_context *pctx,
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->ucp = *clip;
|
||||
ctx->dirty |= FD_DIRTY_UCP;
|
||||
fd_context_dirty(ctx, FD_DIRTY_UCP);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -79,7 +79,7 @@ fd_set_sample_mask(struct pipe_context *pctx, unsigned sample_mask)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->sample_mask = (uint16_t)sample_mask;
|
||||
ctx->dirty |= FD_DIRTY_SAMPLE_MASK;
|
||||
fd_context_dirty(ctx, FD_DIRTY_SAMPLE_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -88,7 +88,7 @@ fd_set_min_samples(struct pipe_context *pctx, unsigned min_samples)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->min_samples = min_samples;
|
||||
ctx->dirty |= FD_DIRTY_MIN_SAMPLES;
|
||||
fd_context_dirty(ctx, FD_DIRTY_MIN_SAMPLES);
|
||||
}
|
||||
|
||||
/* notes from calim on #dri-devel:
|
||||
|
@ -120,9 +120,8 @@ fd_set_constant_buffer(struct pipe_context *pctx,
|
|||
}
|
||||
|
||||
so->enabled_mask |= 1 << index;
|
||||
ctx->dirty_shader[shader] |= FD_DIRTY_SHADER_CONST;
|
||||
ctx->dirty |= FD_DIRTY_CONST;
|
||||
|
||||
fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_CONST);
|
||||
fd_resource_set_usage(cb->buffer, FD_DIRTY_CONST);
|
||||
}
|
||||
|
||||
|
@ -164,8 +163,7 @@ fd_set_shader_buffers(struct pipe_context *pctx,
|
|||
}
|
||||
}
|
||||
|
||||
ctx->dirty_shader[shader] |= FD_DIRTY_SHADER_SSBO;
|
||||
ctx->dirty |= FD_DIRTY_SSBO;
|
||||
fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_SSBO);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -220,8 +218,7 @@ fd_set_shader_images(struct pipe_context *pctx,
|
|||
|
||||
so->enabled_mask &= ~(BITFIELD_MASK(unbind_num_trailing_slots) << (start + count));
|
||||
|
||||
ctx->dirty_shader[shader] |= FD_DIRTY_SHADER_IMAGE;
|
||||
ctx->dirty |= FD_DIRTY_IMAGE;
|
||||
fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_IMAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -282,14 +279,14 @@ fd_set_framebuffer_state(struct pipe_context *pctx,
|
|||
fd_batch_flush(ctx->batch);
|
||||
}
|
||||
|
||||
ctx->dirty |= FD_DIRTY_FRAMEBUFFER;
|
||||
fd_context_dirty(ctx, FD_DIRTY_FRAMEBUFFER);
|
||||
|
||||
ctx->disabled_scissor.minx = 0;
|
||||
ctx->disabled_scissor.miny = 0;
|
||||
ctx->disabled_scissor.maxx = cso->width;
|
||||
ctx->disabled_scissor.maxy = cso->height;
|
||||
|
||||
ctx->dirty |= FD_DIRTY_SCISSOR;
|
||||
fd_context_dirty(ctx, FD_DIRTY_SCISSOR);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -299,7 +296,7 @@ fd_set_polygon_stipple(struct pipe_context *pctx,
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->stipple = *stipple;
|
||||
ctx->dirty |= FD_DIRTY_STIPPLE;
|
||||
fd_context_dirty(ctx, FD_DIRTY_STIPPLE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -312,7 +309,7 @@ fd_set_scissor_states(struct pipe_context *pctx,
|
|||
struct fd_context *ctx = fd_context(pctx);
|
||||
|
||||
ctx->scissor = *scissor;
|
||||
ctx->dirty |= FD_DIRTY_SCISSOR;
|
||||
fd_context_dirty(ctx, FD_DIRTY_SCISSOR);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -352,7 +349,7 @@ fd_set_viewport_states(struct pipe_context *pctx,
|
|||
scissor->maxx = CLAMP(ceilf(maxx), 0.f, max_dims);
|
||||
scissor->maxy = CLAMP(ceilf(maxy), 0.f, max_dims);
|
||||
|
||||
ctx->dirty |= FD_DIRTY_VIEWPORT;
|
||||
fd_context_dirty(ctx, FD_DIRTY_VIEWPORT);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -378,7 +375,7 @@ fd_set_vertex_buffers(struct pipe_context *pctx,
|
|||
uint32_t new_stride = vb ? vb[i].stride : 0;
|
||||
uint32_t old_stride = so->vb[i].stride;
|
||||
if ((new_enabled != old_enabled) || (new_stride != old_stride)) {
|
||||
ctx->dirty |= FD_DIRTY_VTXSTATE;
|
||||
fd_context_dirty(ctx, FD_DIRTY_VTXSTATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -392,7 +389,7 @@ fd_set_vertex_buffers(struct pipe_context *pctx,
|
|||
if (!vb)
|
||||
return;
|
||||
|
||||
ctx->dirty |= FD_DIRTY_VTXBUF;
|
||||
fd_context_dirty(ctx, FD_DIRTY_VTXBUF);
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
assert(!vb[i].is_user_buffer);
|
||||
|
@ -413,9 +410,9 @@ fd_blend_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
cso->rt[0].blend_enable && util_blend_state_is_dual(cso, 0) :
|
||||
false;
|
||||
ctx->blend = hwcso;
|
||||
ctx->dirty |= FD_DIRTY_BLEND;
|
||||
fd_context_dirty(ctx, FD_DIRTY_BLEND);
|
||||
if (old_is_dual != new_is_dual)
|
||||
ctx->dirty |= FD_DIRTY_BLEND_DUAL;
|
||||
fd_context_dirty(ctx, FD_DIRTY_BLEND_DUAL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -434,7 +431,7 @@ fd_rasterizer_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
bool discard = ctx->rasterizer && ctx->rasterizer->rasterizer_discard;
|
||||
|
||||
ctx->rasterizer = hwcso;
|
||||
ctx->dirty |= FD_DIRTY_RASTERIZER;
|
||||
fd_context_dirty(ctx, FD_DIRTY_RASTERIZER);
|
||||
|
||||
if (ctx->rasterizer && ctx->rasterizer->scissor) {
|
||||
ctx->current_scissor = &ctx->scissor;
|
||||
|
@ -448,10 +445,10 @@ fd_rasterizer_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
* if it changed to/from &ctx->disable_scissor
|
||||
*/
|
||||
if (old_scissor != fd_context_get_scissor(ctx))
|
||||
ctx->dirty |= FD_DIRTY_SCISSOR;
|
||||
fd_context_dirty(ctx, FD_DIRTY_SCISSOR);
|
||||
|
||||
if (ctx->rasterizer && (discard != ctx->rasterizer->rasterizer_discard))
|
||||
ctx->dirty |= FD_DIRTY_RASTERIZER_DISCARD;
|
||||
fd_context_dirty(ctx, FD_DIRTY_RASTERIZER_DISCARD);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -467,7 +464,7 @@ fd_zsa_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->zsa = hwcso;
|
||||
ctx->dirty |= FD_DIRTY_ZSA;
|
||||
fd_context_dirty(ctx, FD_DIRTY_ZSA);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -505,7 +502,7 @@ fd_vertex_state_bind(struct pipe_context *pctx, void *hwcso)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->vtx.vtx = hwcso;
|
||||
ctx->dirty |= FD_DIRTY_VTXSTATE;
|
||||
fd_context_dirty(ctx, FD_DIRTY_VTXSTATE);
|
||||
}
|
||||
|
||||
static struct pipe_stream_output_target *
|
||||
|
@ -582,7 +579,7 @@ fd_set_stream_output_targets(struct pipe_context *pctx,
|
|||
|
||||
so->num_targets = num_targets;
|
||||
|
||||
ctx->dirty |= FD_DIRTY_STREAMOUT;
|
||||
fd_context_dirty(ctx, FD_DIRTY_STREAMOUT);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -591,6 +588,7 @@ fd_bind_compute_state(struct pipe_context *pctx, void *state)
|
|||
{
|
||||
struct fd_context *ctx = fd_context(pctx);
|
||||
ctx->compute = state;
|
||||
/* NOTE: Don't mark FD_DIRTY_PROG for compute specific state */
|
||||
ctx->dirty_shader[PIPE_SHADER_COMPUTE] |= FD_DIRTY_SHADER_PROG;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,8 +109,7 @@ fd_sampler_states_bind(struct pipe_context *pctx,
|
|||
struct fd_context *ctx = fd_context(pctx);
|
||||
|
||||
bind_sampler_states(&ctx->tex[shader], start, nr, hwcso);
|
||||
ctx->dirty_shader[shader] |= FD_DIRTY_SHADER_TEX;
|
||||
ctx->dirty |= FD_DIRTY_TEX;
|
||||
fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_TEX);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -122,8 +121,7 @@ fd_set_sampler_views(struct pipe_context *pctx, enum pipe_shader_type shader,
|
|||
struct fd_context *ctx = fd_context(pctx);
|
||||
|
||||
set_sampler_views(&ctx->tex[shader], start, nr, unbind_num_trailing_slots, views);
|
||||
ctx->dirty_shader[shader] |= FD_DIRTY_SHADER_TEX;
|
||||
ctx->dirty |= FD_DIRTY_TEX;
|
||||
fd_context_dirty_shader(ctx, shader, FD_DIRTY_SHADER_TEX);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -454,13 +454,11 @@ ir3_fixup_shader_state(struct pipe_context *pctx, struct ir3_shader_key *key)
|
|||
|
||||
if (!ir3_shader_key_equal(ctx->last.key, key)) {
|
||||
if (ir3_shader_key_changes_fs(ctx->last.key, key)) {
|
||||
ctx->dirty_shader[PIPE_SHADER_FRAGMENT] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_FRAGMENT, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
if (ir3_shader_key_changes_vs(ctx->last.key, key)) {
|
||||
ctx->dirty_shader[PIPE_SHADER_VERTEX] |= FD_DIRTY_SHADER_PROG;
|
||||
ctx->dirty |= FD_DIRTY_PROG;
|
||||
fd_context_dirty_shader(ctx, PIPE_SHADER_VERTEX, FD_DIRTY_SHADER_PROG);
|
||||
}
|
||||
|
||||
/* NOTE: currently only a6xx has gs/tess, but needs no
|
||||
|
|
Loading…
Reference in New Issue