Page 6 of 11

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 13:54
by Argh
Guess I have to do this the hard way. People can test the entire set of optimizations vs. your code.

This is the very last time I will help you, though, I don't help people who don't want to learn, and your attitude sucks.

Code: Select all

function widget:GetInfo()
  return {
    name      = "Enemy Spotter",
    desc      = "Shows a team-colored circle where bad guys' units are",
    author    = "Argh",
    date      = "December 16th, 2009",
    license   = "(C) Wolfe Games, released under GPL v. 2",
    layer     = 0,
    enabled   = true -- loaded by default?
  }
end

------------------------------------------------------------------------------LOCAL STUFF
local myUnitID, myDefID, myTeamID
local frame, oldframe = 1,0
local texture = "bitmaps/StaticCircle.tga"
local LocalTeam = Spring.GetLocalTeamID()
local teamColorTable = {}
local visibleTable = {}
local radiusTable = {}
------------------------------------------------------------------------------OPENGL LOCALS
local glColor = gl.Color
local glTexture = gl.Texture
local glDrawListAtUnit = gl.DrawListAtUnit
local glDeleteList = gl.DeleteList
local glBlending = gl.Blending
local glDepthMask = gl.DepthMask
local glTexCoord = gl.TexCoord
local glVertex = gl.Vertex
local glCreateList = gl.CreateList
local glBeginEnd = gl.BeginEnd
local GL_QUADS = GL.QUADS
local glDepthTest = gl.DepthTest
local GL_SRC_ALPHA = GL.SRC_ALPHA
local GL_ONE = GL.ONE

------------------------------------------------------------------------------SPRING LOCALS
local GetVisibleUnits = Spring.GetVisibleUnits
local GetUnitTeam = Spring.GetUnitTeam
local GetUnitDefID = Spring.GetUnitDefID
local GetTeamColor = Spring.GetTeamColor
local IsUnitVisible = Spring.IsUnitVisible

local GetDrawFrame = Spring.GetDrawFrame
local GetVisibleUnits = Spring.GetVisibleUnits
local ALL_UNITS       = Spring.ALL_UNITS


function widget:Initialize()
	--A simple quad
	SimpleCircle = glCreateList(function()
		glBeginEnd(GL_QUADS, function()
			glTexCoord(0.04,0.04)
			glVertex(-1,10,-1)
			glTexCoord(0.96,0.04)
			glVertex(1,10,-1)
			glTexCoord(0.96,0.96)
			glVertex(1,10,1)
			glTexCoord(0.04,0.96)
			glVertex(-1,10,1)
		end)
	end)
	--Little speedup, so we don't have to constantly look that up from Spring
	for i = 0,64 do
		r,g,b = GetTeamColor(i)
		if i ~= nil then
			table.insert(teamColorTable,i,{r = r,g = g,b =b})
		end
	end
	for i,k in ipairs (UnitDefs) do
		table.insert(radiusTable,{radius = k.xsize * 6})
	end
end

function widget:Shutdown()
	glDeleteList(SimpleCircle)
end



-- Drawing:
function widget:DrawWorldPreUnit()
	--Speedup of GetVisibleUnits
	frame = GetDrawFrame()
	if frame > oldframe then
		visibleTable = GetVisibleUnits(ALL_UNITS,LocalTeam,false)
		oldframe = frame + 5		
	end

	--Main drawing setup
	glBlending(GL_SRC_ALPHA, GL_ONE)
	glDepthTest(true)
	glDepthMask(false)
	for i=1,#visibleTable do
		myUnitID = visibleTable[i]
		myDefID = GetUnitDefID(myUnitID)
		myTeamID = GetUnitTeam(myUnitID)
		if (myTeamID) and myTeamID ~= LocalTeam then
			radius = radiusTable[myDefID].radius
			glColor(teamColorTable[myTeamID].r,teamColorTable[myTeamID].g,teamColorTable[myTeamID].b,1.0)
			glTexture(texture)
			glDrawListAtUnit(myUnitID, SimpleCircle, false, radius, 1.0, radius, 0, 0, 0, 0)
		end
	end
end
Put the attached file into /bitmaps.

For advanced folks, there is at least one speedup left (and, ofc, if you don't like the circles clipping the ground, feel free to change that glDepthTest to false, it's that way because of other things in P.U.R.E.).

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:13
by TradeMark
I didnt ask for help actually, im happy with my widget i made

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:15
by Argh
Well, you needed some. So, you're out of excuses. Test it and let us all know how much faster yours is.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:17
by TradeMark
i dont have the code needed to compare the speeds in a scientifical way

besides i never claimt my code is faster, i just said drawing textures will be slower than drawing single color

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:18
by Argh
It's here, in this Forum. Try the Search function, use "profiler".

And, gee, you could just spawn 1000 guys for team 1, show us a screenshot of your FPS.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:19
by TradeMark
the FPS is not a good way to measure things, most of the FPS is used by Spring engine other stuff, like 0.01% goes for that widget thing

and i dont actually need to test that speed via spring widget... i can just write openGL app to do the same with just the texture/no-texture speed diff

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:20
by Argh
No, that's absolutely wrong. Bad Widgets can and do eat FPS. Moreover, if the CPU is busy because of poorly-optimized code, the simulation will slow, and so will FPS.

And no, you can't test the texture / no-texture. You aren't using quads, you're building circles with polygons. And that's not what this is mainly about, anyhow.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:21
by TradeMark
i can notice barely 1 fps difference when the game has 1000 units around... so i dont think its a problem

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:22
by Argh
Show us some pictures, or it didn't happen.

For those just joining us, I released optimized source on the previous page, demonstrating everything I've been nattering on about. I need to take a nap now.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:27
by TradeMark
actually i cant see any FPS difference.

ill post a video soon? since screenshots wont prove anything...

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:28
by Argh
Just show us the screenshots already. Videos aren't exactly good science, the CPU's very busy making the video at that point.

I don't believe you've tested anything at all yet. 1000 Units makes an appreciable dent in anybody's FPS, even if you're one of those wankers who doesn't turn shadows / reflectivity on.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 14:33
by TradeMark
Hold on, here, 1800 units on screen:

Image


Image

I noticed the FPS is 15-16 when not using my widget, and when using it was 14-15

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 16:02
by very_bad_soldier
doublepost...

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 16:02
by very_bad_soldier
Well, I came back to make some tests for myself, cause I am really curious whats fast now and whats not.

I compared Trademark's widget to the one Argh posted. They were tested in the exact same game with all the exact same settings and 1000 flashers in BA 7.04. Using Meltrax' Profiler ofc. Looking at fps ingame isnt a really good metric imo.

Results:
Time hanging around in DrawWorldPreUnit:
Argh: 4.71ms
Tradermark: 3.20ms

And the winner is Tradermark. Arghs version seems to be about 50% slower than Tradermarks.

Look at the screenies for full Profiler output.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 16:48
by AF
Thank you trademark for beign stubborn and wasting your time arguing a pointless point instead of working on a competing project.

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 17:25
by TradeMark
Nice, i was right after all... gg no re

Seems like Argh doesnt know a jack about how GPU's work

Writing long messages with meaningless crap doesnt make you any more right...

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 18:29
by AF
Writing long messages with meaningless crap doesnt make you any more right...
erm.... lol

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 19:59
by TradeMark
loolloloolloo XD

Re: New widget: Enemy Spotter

Posted: 16 Dec 2009, 21:17
by lurker
Argh, as much as you're right about getting rid of extra lines of code, you're dead wrong about local variables. Nothing is allocated for them in normal use, they are part of the stack.

Code: Select all

local a = tellmeA()
if a == 5 then end

0+ params, 2 slots, 0 upvalues, 1 local, 2 constants, 0 functions
        1       [1]     GETGLOBAL       0 -1    ; tellmeA
        2       [1]     CALL            0 1 2
        3       [2]     EQ              0 0 -2  ; - 5
        4       [2]     JMP             0       ; to 5
        5       [2]     RETURN          0 1

Code: Select all

if tellmeA() == 5 then end

0+ params, 2 slots, 0 upvalues, 0 locals, 2 constants, 0 functions
        1       [1]     GETGLOBAL       0 -1    ; tellmeA
        2       [1]     CALL            0 1 2
        3       [1]     EQ              0 0 -2  ; - 5
        4       [1]     JMP             0       ; to 5
        5       [1]     RETURN          0 1
You can see that whether you store it in a 'local' or not you get the same exact code. 'local's are just names you stick on the registers lua stores values in. Occasionally you can end up with an extra MOVE instruction, but those are really cheap.


And beyond, you suggested something that would actually make performance (slightly) worse. If you have a 'local' at the top of a widget, then inside a function it's not 'local' any more, it's what's called an 'upvalue'. You spend an extra (really cheap) lua instruction every time you get or set them.

Re: New widget: Enemy Spotter

Posted: 17 Dec 2009, 03:23
by Argh
And the winner is Tradermark. Arghs version seems to be about 50% slower than Tradermarks.
Well, mine also shows the real TeamColors, instead of just blue, and uses textures so that it can be very visually flexible, amongst other things. I guess I padded out the features too much.

I also forgot to move the glTexture callout to the start of the main draw function. That should help a bit.



@Lurker: Eh, really? My understanding about this was that if we localized like this:

local GetUnitPosition = Spring.GetUnitPosition

...then we're saving an upvalue call on a regular basis, because that memory address was localized within the application at runtime. What you're saying implies that it's being re-localized every pass, and therefore the cost savings are zero.

And, at least in the case of anywhere where we need to call a value in multiple places, it seems counterintuitive that we'd actually improve speed by doing this:

Code: Select all

subfunction(value)
  ... do stuff to value
  return value
end

function blah()
  local value
  ... do stuff to value
  subfunction(value)
  value = value
end
I mean, if that's actually better, fine, it just seems counterintuitive, I thought variable initialization wasn't something we should waste cycles on, though.

Anyhow, here's a fix with the glTexture callout moved to the right place. Should help a bit, in terms of raw speed:

Code: Select all

function widget:GetInfo()
  return {
    name      = "Enemy Spotter",
    desc      = "Shows a team-colored circle where bad guys' units are",
    author    = "Argh",
    date      = "December 16th, 2009",
    license   = "(C) Wolfe Games, released under GPL v. 2",
    layer     = 0,
    enabled   = true -- loaded by default?
  }
end

------------------------------------------------------------------------------LOCAL STUFF
local myUnitID, myDefID, myTeamID
local frame, oldframe = 1,0
local texture = "bitmaps/StaticCircle.tga"
local LocalTeam = Spring.GetLocalTeamID()
local teamColorTable = {}
local visibleTable = {}
local radiusTable = {}
------------------------------------------------------------------------------OPENGL LOCALS
local glColor = gl.Color
local glTexture = gl.Texture
local glDrawListAtUnit = gl.DrawListAtUnit
local glDeleteList = gl.DeleteList
local glBlending = gl.Blending
local glDepthMask = gl.DepthMask
local glTexCoord = gl.TexCoord
local glVertex = gl.Vertex
local glCreateList = gl.CreateList
local glBeginEnd = gl.BeginEnd
local GL_QUADS = GL.QUADS
local glDepthTest = gl.DepthTest
local GL_SRC_ALPHA = GL.SRC_ALPHA
local GL_ONE = GL.ONE

------------------------------------------------------------------------------SPRING LOCALS
local GetVisibleUnits = Spring.GetVisibleUnits
local GetUnitTeam = Spring.GetUnitTeam
local GetUnitDefID = Spring.GetUnitDefID
local GetTeamColor = Spring.GetTeamColor
local IsUnitVisible = Spring.IsUnitVisible

local GetDrawFrame = Spring.GetDrawFrame
local GetVisibleUnits = Spring.GetVisibleUnits
local ALL_UNITS       = Spring.ALL_UNITS


function widget:Initialize()
	--A simple quad
	SimpleCircle = glCreateList(function()
		glBeginEnd(GL_QUADS, function()
			glTexCoord(0.04,0.04)
			glVertex(-1,10,-1)
			glTexCoord(0.96,0.04)
			glVertex(1,10,-1)
			glTexCoord(0.96,0.96)
			glVertex(1,10,1)
			glTexCoord(0.04,0.96)
			glVertex(-1,10,1)
		end)
	end)
	--Little speedup, so we don't have to constantly look that up from Spring
	for i = 0,64 do
		r,g,b = GetTeamColor(i)
		if i ~= nil then
			table.insert(teamColorTable,i,{r = r,g = g,b =b})
		end
	end
	for i,k in ipairs (UnitDefs) do
		table.insert(radiusTable,{radius = k.xsize * 6})
	end
end

function widget:Shutdown()
	glDeleteList(SimpleCircle)
end



-- Drawing:
function widget:DrawWorldPreUnit()
	visibleTable = GetVisibleUnits(ALL_UNITS,LocalTeam,false)

	--Main drawing setup
	glBlending(GL_SRC_ALPHA, GL_ONE)
	glDepthTest(true)
	glDepthMask(false)
	glTexture(texture)
	for i=1,#visibleTable do
		myUnitID = visibleTable[i]
		myDefID = GetUnitDefID(myUnitID)
		myTeamID = GetUnitTeam(myUnitID)
		if (myTeamID) and myTeamID ~= LocalTeam then
			radius = radiusTable[myDefID].radius
			glColor(teamColorTable[myTeamID].r,teamColorTable[myTeamID].g,teamColorTable[myTeamID].b,1.0)
			glDrawListAtUnit(myUnitID, SimpleCircle, false, radius, 1.0, radius, 0, 0, 0, 0)
		end
	end
end
If that doesn't push it over the top, CPU-wise, there are a couple more things I can do, while keeping the extra features, although it's going to get a little harder to squeeze it at that point. In testing over here, mine runs fast enough that I really just don't see much of a difference in total load while it's operating, so I think it's fair to say it's "fast".

However, that said... now that I've tested TradeMark's a bit... where are all these complaints about speed coming from?

I mean, seriously, I am guilty of just reading the complaints and looking for speedups here... which I found, but they aren't earth-shattering.

Are people playing games with Pentium II's here? Neither one of these Widgets are a real load, ever.