/*
 * Copyright 2012 Advanced Micro Devices, Inc.
 *
 * SPDX-License-Identifier: MIT
 */

#include "si_pm4.h"
#include "si_pipe.h"
#include "si_build_pm4.h"
#include "sid.h"
#include "util/u_memory.h"
#include "ac_debug.h"

void si_pm4_clear_state(struct si_pm4_state *state, struct si_screen *sscreen,
                        bool is_compute_queue)
{
   const bool debug_sqtt = !!(sscreen->debug_flags & DBG(SQTT));

   ac_pm4_clear_state(&state->base, &sscreen->info, debug_sqtt, is_compute_queue);
}

void si_pm4_free_state(struct si_context *sctx, struct si_pm4_state *state, unsigned idx)
{
   if (!state)
      return;

   if (idx != ~0) {
      if (sctx->emitted.array[idx] == state)
         sctx->emitted.array[idx] = NULL;

      if (sctx->queued.array[idx] == state) {
         sctx->queued.array[idx] = NULL;
         sctx->dirty_atoms &= ~BITFIELD64_BIT(idx);
      }
   }

   FREE(state);
}

void si_pm4_emit_commands(struct si_context *sctx, struct ac_pm4_state *state)
{
   struct radeon_cmdbuf *cs = &sctx->gfx_cs;

   ac_pm4_emit_commands(&cs->current, state);
}

void si_pm4_emit_state(struct si_context *sctx, unsigned index)
{
   struct si_pm4_state *state = sctx->queued.array[index];
   struct radeon_cmdbuf *cs = &sctx->gfx_cs;

   /* All places should unset dirty_states if this doesn't pass. */
   assert(state && state != sctx->emitted.array[index]);

   radeon_begin(cs);
   radeon_emit_array(state->base.pm4, state->base.ndw);
   radeon_end();

   sctx->emitted.array[index] = state;
}

void si_pm4_emit_shader(struct si_context *sctx, unsigned index)
{
   struct si_pm4_state *state = sctx->queued.array[index];

   si_pm4_emit_state(sctx, index);

   radeon_add_to_buffer_list(sctx, &sctx->gfx_cs, ((struct si_shader*)state)->bo,
                             RADEON_USAGE_READ | RADEON_PRIO_SHADER_BINARY);
   if (state->atom.emit)
      state->atom.emit(sctx, -1);
}

void si_pm4_reset_emitted(struct si_context *sctx)
{
   memset(&sctx->emitted, 0, sizeof(sctx->emitted));

   for (unsigned i = 0; i < SI_NUM_STATES; i++) {
      if (sctx->queued.array[i])
         sctx->dirty_atoms |= BITFIELD64_BIT(i);
   }
}

struct si_pm4_state *si_pm4_create_sized(struct si_screen *sscreen, unsigned max_dw,
                                         bool is_compute_queue)
{
   struct si_pm4_state *pm4;
   unsigned size = sizeof(*pm4) + 4 * (max_dw - ARRAY_SIZE(pm4->base.pm4));

   pm4 = (struct si_pm4_state *)calloc(1, size);
   if (pm4) {
      pm4->base.max_dw = max_dw;
      si_pm4_clear_state(pm4, sscreen, is_compute_queue);
   }
   return pm4;
}

struct si_pm4_state *si_pm4_clone(struct si_screen *sscreen, struct si_pm4_state *orig)
{
   struct si_pm4_state *pm4 = si_pm4_create_sized(sscreen, orig->base.max_dw,
                                                  orig->base.is_compute_queue);
   if (pm4)
      memcpy(pm4, orig, sizeof(*pm4) + 4 * (pm4->base.max_dw - ARRAY_SIZE(pm4->base.pm4)));
   return pm4;
}
