1-- Parse cmdstream dump and analyse blits and batches
2
3--local posix = require "posix"
4
5function printf(fmt, ...)
6	return io.write(string.format(fmt, ...))
7end
8
9function dbg(fmt, ...)
10	--printf(fmt, ...)
11end
12
13printf("Analyzing Data...\n")
14
15local r = rnn.init("a630")
16
17-- Each submit, all draws will target the same N MRTs:
18local mrts = {}
19local allmrts = {}  -- includes historical render targets
20function push_mrt(fmt, w, h, samples, base, flag, gmem)
21	dbg("MRT: %s %ux%u 0x%x\n", fmt, w, h, base)
22
23	local mrt = {}
24	mrt.format = fmt
25	mrt.w = w
26	mrt.h = h
27	mrt.samples = samples
28	mrt.base = base
29	mrt.flag = flag
30	mrt.gmem = gmem
31
32	mrts[base] = mrt
33	allmrts[base] = mrt
34end
35
36-- And each each draw will read from M sources/textures:
37local sources = {}
38function push_source(fmt, w, h, samples, base, flag)
39	dbg("SRC: %s %ux%u 0x%x\n", fmt, w, h, base)
40
41	local source = {}
42	source.format = fmt
43	source.w = w
44	source.h = h
45	source.samples = samples
46	source.base = base
47	source.flag = flag
48
49	sources[base] = source
50end
51
52local binw
53local binh
54local nbins
55local blits = 0
56local draws = 0
57local drawmode
58local cleared
59local restored
60local resolved
61local nullbatch
62local depthtest
63local depthwrite
64local stenciltest
65local stencilwrite
66
67function reset()
68	dbg("reset\n")
69	mrts = {}
70	sources = {}
71	draws = 0
72	blits = 0
73	cleared = {}
74	restored = {}
75	resolved = {}
76	depthtest = false
77	depthwrite = false
78	stenciltest = false
79	stencilwrite = false
80	drawmode = Nil
81end
82
83function start_submit()
84	dbg("start_submit\n")
85	reset()
86	nullbatch = true
87end
88
89function finish()
90	dbg("finish\n")
91
92	printf("\n")
93
94	-- TODO we get false-positives for 'NULL BATCH!' because we don't have
95	-- a really good way to differentiate between submits and cmds.  Ie.
96	-- with growable cmdstream, and a large # of tiles, IB1 can get split
97	-- across multiple buffers.  Since we ignore GMEM draws for window-
98	-- offset != 0,0, the later cmds will appear as null batches
99	if draws == 0 and blits == 0 then
100		if nullbatch then
101			printf("NULL BATCH!\n");
102		end
103		return
104	end
105
106	if draws > 0 then
107		printf("Batch:\n")
108		printf("-------\n")
109		printf("  # of draws: %u\n", draws)
110		printf("  mode: %s\n", drawmode)
111		if drawmode == "RM6_GMEM" then
112			printf("  bin size: %ux%u (%u bins)\n", binw, binh, nbins)
113		end
114		if depthtest or depthwrite then
115			printf("  ")
116			if depthtest then
117				printf("DEPTHTEST ")
118			end
119			if depthwrite then
120				printf("DEPTHWRITE")
121			end
122			printf("\n")
123		end
124		if stenciltest or stencilwrite then
125			printf("  ")
126			if stenciltest then
127				printf("STENCILTEST ")
128			end
129			if stencilwrite then
130				printf("STENCILWRITE")
131			end
132			printf("\n")
133		end
134	else
135		printf("Blit:\n")
136		printf("-----\n")
137	end
138
139	for base,mrt in pairs(mrts) do
140		printf("  MRT[0x%x:0x%x]:\t%ux%u\t\t%s (%s)", base, mrt.flag, mrt.w, mrt.h, mrt.format, mrt.samples)
141		if drawmode == "RM6_GMEM" then
142			if cleared[mrt.gmem] then
143				printf("\tCLEARED")
144			end
145			if restored[mrt.gmem] then
146				printf("\tRESTORED")
147			end
148			if resolved[mrt.gmem] then
149				printf("\tRESOLVED")
150			end
151		else
152			if cleared[mrt.base] then
153				printf("\tCLEARED")
154			end
155		end
156		printf("\n")
157	end
158
159	function print_source(source)
160		printf("  SRC[0x%x:0x%x]:\t%ux%u\t\t%s (%s)\n", source.base, source.flag, source.w, source.h, source.format, source.samples)
161	end
162
163	for base,source in pairs(sources) do
164		-- only show sources that have been previously rendered to, other
165		-- textures are less interesting.  Possibly this should be an
166		-- option somehow
167		if draws < 10 then
168			print_source(source)
169		elseif allmrts[base] or draws == 0 then
170			print_source(source)
171		elseif source.flag and allmrts[source.flag] then
172			print_source(source)
173		end
174	end
175	reset()
176end
177
178function end_submit()
179	dbg("end_submit\n")
180	finish()
181end
182
183-- Track the current mode:
184local mode = ""
185function CP_SET_MARKER(pkt, size)
186	mode = pkt[0].MARKER
187	dbg("mode: %s\n", mode)
188end
189
190function CP_EVENT_WRITE(pkt, size)
191	if tostring(pkt[0].EVENT) ~= "BLIT" then
192		return
193	end
194	nullbatch = false
195	local m = tostring(mode)
196	if m == "RM6_GMEM" then
197		-- either clear or restore:
198		if r.RB_BLIT_INFO.CLEAR_MASK == 0 then
199			restored[r.RB_BLIT_BASE_GMEM] = 1
200		else
201			cleared[r.RB_BLIT_BASE_GMEM] = 1
202		end
203		-- push_mrt() because we could have GMEM
204		-- passes with only a clear and no draws:
205		local flag = 0
206		local sysmem = 0;
207		-- try to match up the GMEM addr with the MRT/DEPTH state,
208		-- to avoid relying on RB_BLIT_DST also getting written:
209		for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
210			if r.RB_MRT[n].BASE_GMEM == r.RB_BLIT_BASE_GMEM then
211				sysmem = r.RB_MRT[n].BASE
212				flag = r.RB_MRT_FLAG_BUFFER[n].ADDR
213				break
214			end
215		end
216		if sysmem == 0 and r.RB_BLIT_BASE_GMEM == r.RB_DEPTH_BUFFER_BASE_GMEM then
217			sysmem = r.RB_DEPTH_BUFFER_BASE
218			flag = r.RB_DEPTH_FLAG_BUFFER_BASE
219
220		end
221		--NOTE this can get confused by previous blits:
222		--if sysmem == 0 then
223		--	-- fallback:
224		--	sysmem = r.RB_BLIT_DST
225		--	flag = r.RB_BLIT_FLAG_DST
226		--end
227		if not r.RB_BLIT_DST_INFO.FLAGS then
228			flag = 0
229		end
230		-- TODO maybe just emit RB_BLIT_DST/HI for clears.. otherwise
231		-- we get confused by stale values in registers.. not sure
232		-- if this is a problem w/ blob
233		push_mrt(r.RB_BLIT_DST_INFO.COLOR_FORMAT,
234			r.RB_BLIT_SCISSOR_BR.X + 1,
235			r.RB_BLIT_SCISSOR_BR.Y + 1,
236			r.RB_BLIT_DST_INFO.SAMPLES,
237			sysmem,
238			flag,
239			r.RB_BLIT_BASE_GMEM)
240	elseif m == "RM6_RESOLVE" then
241		resolved[r.RB_BLIT_BASE_GMEM] = 1
242	else
243		printf("I am confused!!!\n")
244	end
245end
246
247function A6XX_TEX_CONST(pkt, size)
248	push_source(pkt[0].FMT,
249		pkt[1].WIDTH, pkt[1].HEIGHT,
250		pkt[0].SAMPLES,
251		pkt[4].BASE_LO | (pkt[5].BASE_HI << 32),
252		pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32))
253end
254
255function handle_blit()
256	-- blob sometimes uses CP_BLIT for resolves, so filter those out:
257	-- TODO it would be nice to not hard-code GMEM addr:
258	-- TODO I guess the src can be an offset from GMEM addr..
259	if r.SP_PS_2D_SRC == 0x100000 and not r.RB_2D_BLIT_CNTL.SOLID_COLOR then
260		resolved[0] = 1
261		return
262	end
263	if draws > 0 then
264		finish()
265	end
266	reset()
267	drawmode = "BLIT"
268	-- This kinda assumes that we are doing full img blits, which is maybe
269	-- Not completely legit.  We could perhaps instead just track pitch and
270	-- size/pitch??  Or maybe the size doesn't matter much
271	push_mrt(r.RB_2D_DST_INFO.COLOR_FORMAT,
272		r.GRAS_2D_DST_BR.X + 1,
273		r.GRAS_2D_DST_BR.Y + 1,
274		"MSAA_ONE",
275		r.RB_2D_DST,
276		r.RB_2D_DST_FLAGS,
277		-1)
278	if r.RB_2D_BLIT_CNTL.SOLID_COLOR then
279		dbg("CLEAR=%x\n", r.RB_2D_DST)
280		cleared[r.RB_2D_DST] = 1
281	else
282		push_source(r.SP_2D_SRC_FORMAT.COLOR_FORMAT,
283			r.GRAS_2D_SRC_BR_X.X + 1,
284			r.GRAS_2D_SRC_BR_Y.Y + 1,
285			"MSAA_ONE",
286			r.SP_PS_2D_SRC,
287			r.SP_PS_2D_SRC_FLAGS)
288	end
289	blits = blits + 1
290	finish()
291end
292
293function valid_transition(curmode, newmode)
294	if curmode == "RM6_BINNING" and newmode == "RM6_GMEM" then
295		return true
296	end
297	if curmode == "RM6_GMEM" and newmode == "RM6_RESOLVE" then
298		return true
299	end
300	return false
301end
302
303function draw(primtype, nindx)
304	dbg("draw: %s (%s)\n", primtype, mode)
305	nullbatch = false
306	if primtype == "BLIT_OP_SCALE" then
307		handle_blit()
308		return
309	elseif primtype == "EVENT:BLIT" then
310		return
311	end
312
313	local m = tostring(mode)
314
315	-- detect changes in drawmode which indicate a different
316	-- pass..  BINNING->GMEM means same pass, but other
317	-- transitions mean different pass:
318	if drawmode and m ~= drawmode then
319		dbg("%s -> %s transition\n", drawmode, m)
320		if not valid_transition(drawmode, m) then
321			dbg("invalid transition, new render pass!\n")
322			finish()
323			reset()
324		end
325	end
326
327	if m ~= "RM6_GMEM" and m ~= "RM6_BYPASS" then
328		if m == "RM6_BINNING" then
329			drawmode = m
330			return
331		end
332		if m == "RM6_RESOLVE" and primtype == "EVENT:BLIT" then
333			return
334		end
335		printf("unknown MODE %s for primtype %s\n", m, primtype)
336		return
337	end
338
339	-- Only count the first tile for GMEM mode to avoid counting
340	-- each draw for each tile
341	if m == "RM6_GMEM" then
342		if r.RB_WINDOW_OFFSET.X ~= 0 or r.RB_WINDOW_OFFSET.Y ~= 0 then
343			return
344		end
345	end
346
347	drawmode = m
348	local render_components = {}
349	render_components[0] = r.RB_RENDER_COMPONENTS.RT0;
350	render_components[1] = r.RB_RENDER_COMPONENTS.RT1;
351	render_components[2] = r.RB_RENDER_COMPONENTS.RT2;
352	render_components[3] = r.RB_RENDER_COMPONENTS.RT3;
353	render_components[4] = r.RB_RENDER_COMPONENTS.RT4;
354	render_components[5] = r.RB_RENDER_COMPONENTS.RT5;
355	render_components[6] = r.RB_RENDER_COMPONENTS.RT6;
356	render_components[7] = r.RB_RENDER_COMPONENTS.RT7;
357	for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
358		if render_components[n] ~= 0 then
359			push_mrt(r.RB_MRT[n].BUF_INFO.COLOR_FORMAT,
360				r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
361				r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
362				r.RB_MSAA_CNTL.SAMPLES,
363				r.RB_MRT[n].BASE,
364				r.RB_MRT_FLAG_BUFFER[n].ADDR,
365				r.RB_MRT[n].BASE_GMEM)
366		end
367	end
368
369	local depthbase = r.RB_DEPTH_BUFFER_BASE
370
371	if depthbase ~= 0 then
372		push_mrt(r.RB_DEPTH_BUFFER_INFO.DEPTH_FORMAT,
373			r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
374			r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
375			r.RB_MSAA_CNTL.SAMPLES,
376			depthbase,
377			r.RB_DEPTH_FLAG_BUFFER_BASE,
378			r.RB_DEPTH_BUFFER_BASE_GMEM)
379	end
380
381	if r.RB_DEPTH_CNTL.Z_WRITE_ENABLE then
382		depthwrite = true
383	end
384
385	if r.RB_DEPTH_CNTL.Z_TEST_ENABLE then
386		depthtest = true
387	end
388
389	-- clearly 0 != false.. :-/
390	if r.RB_STENCILWRMASK.WRMASK ~= 0 then
391		stencilwrite = true
392	end
393
394	if r.RB_STENCIL_CONTROL.STENCIL_ENABLE then
395		stenciltest = true
396	end
397
398	-- TODO should also check for stencil buffer for z32+s8 case
399
400	if m == "RM6_GMEM" then
401		binw = r.VSC_BIN_SIZE.WIDTH
402		binh = r.VSC_BIN_SIZE.HEIGHT
403		nbins = r.VSC_BIN_COUNT.NX * r.VSC_BIN_COUNT.NY
404	end
405
406	draws = draws + 1
407end
408
409