-- Parse logs from https://github.com/freedreno/freedreno/
-- test-texturator.c to generate a src/freedreno/fdl/fd6_layout_test.c
-- block.  We figure out the offsets from blits, but there may be some
-- unrelated blits.  So just save all of them until we find the
-- texture state.  This gives us the base address, and the miplevel #0
-- width/height/depth.  Then work backwards from there finding the
-- blits to the same dst buffer and deducing the miplevel from the
-- minified dimensions

local posix = require "posix"

io.write("Analyzing Data...\n")

local r = rnn.init("a630")
local found_tex = 0

local allblits = {}
local nallblits = 0

function get_first_blit(base, width, height)
  local first_blit = nil

  for n = 0,nallblits-1 do
    local blit = allblits[n]
    if blit.base == base and blit.width == width and blit.height == height then
      if not first_blit or blit.addr < first_blit.addr then
        first_blit = blit
      end
    end
  end

  return first_blit
end

function minify(val, lvls)
  val = val >> lvls
  if val < 1 then
    return 1
  end
  return val
end

function printf(fmt, ...)
  return io.write(string.format(fmt, ...))
end

function start_cmdstream(name)
  io.write("Parsing " .. name .. "\n")
  allblits = {}
  nallblits = 0
end

function draw(primtype, nindx)
  local blit = {}

  local type = "???";
  if primtype == "BLIT_OP_SCALE" then
    -- Just in case, filter out anything that isn't starting
    -- at 0,0
    if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
      return
    end

    blit.width   = r.GRAS_2D_DST_BR.X + 1
    blit.height  = r.GRAS_2D_DST_BR.Y + 1
    blit.pitch   = r.RB_2D_DST_PITCH
    blit.addr    = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
    blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
    blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH
    type="blit";
  else
    blit.width   = r.GRAS_SC_WINDOW_SCISSOR_BR.X + 1
    blit.height  = r.GRAS_SC_WINDOW_SCISSOR_BR.Y + 1
    blit.pitch = r.RB_MRT[0].PITCH
    blit.addr = r.RB_MRT[0].BASE_LO | (r.RB_MRT[0].BASE_HI << 32);
    blit.ubwc_addr = r.RB_MRT_FLAG_BUFFER[0].ADDR_LO | (r.RB_MRT_FLAG_BUFFER[0].ADDR_HI << 32)
    blit.ubwc_pitch = r.RB_MRT_FLAG_BUFFER[0].PITCH.PITCH
    type="draw"
  end
  blit.base    = bos.base(blit.addr)
  blit.ubwc_base = bos.base(blit.uwbc_addr)
  blit.endaddr = 0  -- filled in later

  printf("Found %s: 0x%x/%d (0x%x) %dx%d UBWC 0x%x/%d (0x%x)\n",
         type, blit.addr, blit.pitch, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_pitch, blit.ubwc_base)

  allblits[nallblits] = blit
  nallblits = nallblits + 1
end

function A6XX_TEX_CONST(pkt, size)
  -- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
  -- are looking for

  local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
  local ubwc_base = pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32)
  local width0  = pkt[1].WIDTH
  local height0 = pkt[1].HEIGHT
  local depth0  = pkt[5].DEPTH

  if (found_tex ~= 0) then
    return
  end
  found_tex = 1

  printf("Found texture state:\n  %ux%ux%u (%s, %s, MIN_LAYERSZ=0x%x, TILE_ALL=%s, UBWC=%s FLAG_LOG2=%ux%u %s)\n",
         width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, pkt[3].MIN_LAYERSZ, tostring(pkt[3].TILE_ALL), tostring(pkt[3].FLAG), pkt[10].FLAG_BUFFER_LOGW, pkt[10].FLAG_BUFFER_LOGH, tostring(pkt[0].SAMPLES))

  -- Note that in some case the texture has some extra page or so
  -- at the beginning:
  local basebase = bos.base(base)
  printf("base: 0x%x (0x%x)\n", base, basebase)
  printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))

  -- see if we can find the associated blits..  The blob always seems to
  -- start from the lower (larger) mipmap levels and layers, so we don't
  -- need to sort by dst address.  Also, while we are at it, fill in the
  -- end-addr (at least for everything but the last blit)
  local blits = {}
  local nblits = 0
  local lastblit = nil
  for n = 0,nallblits-1 do
    local blit = allblits[n]
    --printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
    if blit.base == basebase and blit.addr >= base then
      blits[nblits] = blit
      nblits = nblits + 1
      if lastblit then
        lastblit.endaddr = blit.addr
      end
      lastblit = blit
    end
  end

  printf("	{\n")
  printf("		.format = %s,\n", pkt[0].FMT)
  if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
    printf("		.is_3d = true,\n")
  end

  printf("		.layout = {\n")
  printf("			.tile_mode = %s,\n", pkt[0].TILE_MODE)
  printf("			.ubwc = %s,\n", tostring(pkt[3].FLAG))

  if (tostring(pkt[0].SAMPLES) == "MSAA_ONE") then
    -- Ignore it, 1 is the default
  elseif (tostring(pkt[0].SAMPLES) == "MSAA_TWO") then
    printf("			.nr_samples = 2,\n")
  elseif (tostring(pkt[0].SAMPLES) == "MSAA_FOUR") then
    printf("			.nr_samples = 4,\n")
  else
    printf("			.nr_samples = XXX,\n")
  end

  if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
    printf("			.width0 = %d, .height0 = %d, .depth = %d,\n", width0, height0, depth0)
  else
    printf("			.width0 = %d, .height0 = %d,\n", width0, height0)
  end

  printf("			.slices = {\n")
  local w = 0
  local h = 0
  local level = 0
  repeat
    local w = minify(width0, level)
    local h = minify(height0, level)
    local blit = get_first_blit(basebase, w, h)
    if blit then
      printf("				{ .offset = %d, .pitch = %u },\n",
          blit.addr - base,
          blit.pitch);
    end
    level = level + 1
  until w == 1 and h == 1
  printf("			},\n")

  if pkt[3].FLAG then
    printf("			.ubwc_slices = {\n")
    level = 0
    repeat
      local w = minify(width0, level)
      local h = minify(height0, level)
      local blit = get_first_blit(basebase, w, h)
      if blit then
        printf("				{ .offset = %d, .pitch = %u },\n",
            blit.ubwc_addr - ubwc_base,
            blit.ubwc_pitch);
      end
      level = level + 1
    until w == 1 and h == 1
    printf("			},\n")
  end

  printf("		},\n")
  printf("	},\n")
  printf("\n\n")
end