I am still very new to PICO-8 and saw a few games rendered with polygons.
This lead me to the Trifill Thunderdome benchmark by Musurca.
One thing this benchmark didn't show was the number of tokens for each method which is an important detail considering the limitations of the platform.
Also, I wrote two triangle rasterizers in 163 and 335 tokens respectively which perform pretty decently. They might be an acceptable alternative to ElectricGryphon's super fast triangle rasterizer if you need an extra 2-400 tokens. If you don't, by all mean use his. It is the fastest one I found, and by a good margin!
Update 20180706: Added the possibility to switch rasterizer and turn the test triangle with the arrow keys. This allows to compare rasterizers at a normal pace. Updated EG's rasterizer to the latest he posted in the previous thread and added the one from Gryphon 3D Engine Library v2 only modified to use normal color() and fillp().
Update 20180708: Added a fill pattern and a toggle for the results and control wireframe.
Hope this helps,
Looks like some of the new rasterizers have severe artifacts on edges.
@p01 are these yours?
That would be the one by @creamdog. It uses memset(screenAddress, col * 17, scanlineWidth) which can only set an even number of pixels. Hence the half resolution horizontally and the artifacts.
The triangle you see spinning after the benchmark uses my 335 tokens rasterizer.
Nice work! I haven't looked at the code yet, but that's a mighty optimization effort right there. (Twice!)
edit: I added some code to switch between renderers on the result screen, there seems to be something not quite right with at least one of the algorithms. (Pretty sure I set things up right, maybe not?)
z changes the renderer, x pauses the rotation, up changes the colour.
You might want to extend this to modify the vertices as well if you're hunting for artefacts.
THANKS!
Excellent idea to try the different rasterizers after the benchmark.
I tried this last night and saw some have wild bugs. I updated EG's rasterizer to the last one he posted in the previous thread, and the one from the Gryphon 3D librarry v2 and now my "big" rasterizer comes ahead :U
Another nice addition would be an overlay of the demo triangle using plain lines (dunno, switch on/off using button).
Not all rasterizers are drawing the triangle at the same place!!
EDIT: just did the test, and @p01 are fast and accurate. Nice job!
... tests[method_index].fn(1) -- "native" pico triangle/lines if btn(4) then for i=0,2 do local p0,p1=2*i+1,(2*(i+1))%6+1 line(f[p0],f[p0+1],f[p1],f[p1+1],8) end end |
Thanks. Great idea. I also added a fill pattern to make sure the different rasterizers can draw shaded triangles.
Hi, @p01
I'm developing a 3D modeling tool with a small specification for PICO-8.
I would like to post on the BBS using your awesome trifill code.
That's the idea ;)
Go right ahead an use this code if it helps your project.
This modeler looks neat!
Thank you!
I've posted a production!
I created a trifill() for my project.
I'd like to share it because it has a different loop than the other code.
Thank you so much for posting this place!
[UPDATE:20230908(1)]
Taking a cue from @Spaz48's idea, we sped up the trifill() code, reduced the cost of tokens, and drew the last 1px!
"_HV", "_TCLIP", "_LOW".
Each code was even more extreme in its results, but more selective.
[UPDATE:20230908(2)]
The last 1px was not to be drawn in a near-horizontal triangle.( _HV )
[UPDATE:20230909(3)]
Return to the beginning how to sort vertices,
and Changed the process to one that does not use the API.( _HVB )
"_HVB" 272token(top clipping & H-V processing branch & Minimize API & Sort well used at the Beginning.)
"_TCLIP" 140 token(Enable top clipping only)
"_LOW" 113 token(minfy-code! Non top clipping)
Previous Code 1
Previous Code 2
Previous Code 3
The random result is fixed by srand(4).
Here's my 143 token take on a triangle fill.
--@Domino_Marama function domino_tri(x0,y0,x1,y1,x2,y2,col) color(col) if(y1<y0)x0,x1,y0,y1=x1,x0,y1,y0 if(y2<y0)x0,x2,y0,y2=x2,x0,y2,y0 if(y2<y1)x1,x2,y1,y2=x2,x1,y2,y1 if(y0!=y1) x0,col=trapa(y0,y1,x0,x0,(x1-x0)/(y1-y0),(x2-x0)/(y2-y0)) if(y1!=y2) trapa(y1,y2,x1,x0,(x2-x1)/(y2-y1),col) end function trapa(y1,y2,lx,rx,dl,dr) for y=y1,min(y2,128) do rectfill(lx,y,rx,y) lx+=dl rx+=dr end return rx-dr,dr end |
i made my own tri function : https://www.lexaloffle.com/bbs/?tid=49930
Felt like throwing my hat into the ring. Here's my trifill func, and a revised test cart with mine and a couple others from the thread added.
EDIT: Realized the a var was technically redundant, shaved a few more tokens, now 122 and 134! Slightly less legible, though..
EDIT 2: Further optimizations! Avg tris/sec is up by somewhere in the range of 400-500, all for no extra tokens in the faster ver (found a small token optim to make up for it) and for only 3 extra tokens in the lower token ver! Now it's punching pretty close to the big leagues, managing nearly 5000 tris/sec, at the same tiny size it's always been!
Faster ver. (134 tokens)
Token saving ver. (125 tokens) (Ver in "old vers" is slower but slightly smaller, if you need 3 more tokens)
The trick ended up being to abuse a really nuanced little feature of p8's cycle counter. Functions, being variables under the hood like any other, take 2 cycles to look up (on top of their other costs) same as looking up global variables. This, of course, applies to the built in global funcs too. Local variables however, don't charge anything to look up.
..So if you're using a func a ton for something, what's to stop you from redefining it as a local so that you can avoid those 2 look up cycles per call? Nothing, not even a special exception for the builtins!
This little trick could optimize a ton of things outside of triangles, and it'd make pretty much every other tri algo in the cart faster at the cost of those 3 extra tokens to redefine it. If I feel like it later, I might try to apply it to all of the others just for the sake of fair comparison.
Moreover.. as a trick I think it's balanced. You have to always eat those extra tokens if you want the speedup. That is to say, zep please don't patch this lmao
Old vers:
Old old vers:
They may not be the fastest, but I've made the lowest token count func with performance around above! average compared to the others. Using the kinda silly statistic of tokens per tri per sec, the lower token ver is the best bang for buck out of all of them! The actual test cart has commented vers, if you feel like picking them apart.
(Feel free to use these for whatever, just credit me somewhere as Azure48, preferably leave "azu" in the func name.)
well done! - can be further optimized:
local a,b,da,db=x1,x2,… … for … do rectfill(…) a+=da b+=db end |
Fascinating, I never even thought about using ... like that, I'll have to toy with that some tomorrow.
However, the choice to use a=a+da over a+=da was intentional. It's faster.. sometimes, including this. Assignment operators used to be a macro p8 did and thus was a free token save, but recent versions of p8 have replaced it with a real implementation. In my testing, it seems that the new "real" ones are more expensive for some reason, unfortunately.. but only sometimes? You should be able to edit the cart and see for yourself that it preforms worse with +=. Perhaps it's a bug, I'm really not sure.
Yeah, makes sense. I keep fairly on top of p8 changelogs, so I'll just edit the post whenever that's fixed. As for the other thing, now that I'm not so sleepy i realize you were just using ... to skip over bits you didn't want to type out, my tired brain read that as you using it as an iterator and etc.
The variable defs not being all in one line was intentional too, that's also faster, though the difference is a lot more negligible there. It is 3 tokens less.. I guess I ought to use it in the token reduced ver, fair enough.
@Spaz48
I was particularly interested in this swap code.
for i = 0, 1 do if(y1>y2)x1,y1,x2,y2 = x2,y2,x1,y1 if(y2>y3)x2,y2,x3,y3 = x3,y3,x2,y2 end |
It reduces one line that was set in stone in exchange for taking an extra step.
Also, the pset()
fills in the missing edge at the bottom of the triangle. It is truly the finishing touch.
(These are things I had given up on and seem to have more potential!)
(Bump, I have made major progress with my func that I believe is worth seeing. I did so by editing my earlier post, bc I thought that'd bump since I updated the cart, but I guess that's only how it works for the OP.)
@shiftalow 6500 tris/sec! Incredible, I didn't think that'd even be possible, and still so competitively small.. I had already started thinking about ways to get that last ~500 tris to catch up to p01.. but then you blow me out of the water by managing +~1000-1500. Insane. I'm going to have to try to beat that, of course.. :3
@Spaz48 I could not believe it myself!
So I re-tested it, reflected it on the modeling cart, and carefully looked at the stability.
And I want to apologize for that. In my post edit, I mistakenly compared it with your old code...!
I have now resubmitted my cart.
P.S. We may need to note the characteristics of art that arise when pressure is applied.
@shiftalow Ah, that bug popped up pretty early but I was pretty sure i squashed it.. i'm pretty sure it's caused by p8's fixed point numbers lacking precision.. I have some ideas on how to speed things up that might also get rid of that, but I'm not entirely sure.
https://www.lexaloffle.com/bbs/?pid=75530#p
I have updated the fast type trifill!
pelogen_tri_hvb()
[272token, 7200 tris/sec].
[Please log in to post a comment]