Is single surface 2D really faster than normal 2D?

Blitz3D Forums/Blitz3D Programming/Is single surface 2D really faster than normal 2D?

I ask because I did a quick test. I tested my code using my single surface system, and I tested it using normal 2D drawing commands and the 2D commands resulted in a faster framerate.

Would it be my single surface code, or is there a certain point you can reach when adding vertices that can make the system run much slower?

I have done similar tests and 3d accelerated always comes out top - by a very long way.

So the problem seems to stem from my single surface code?

Which confuses me greatly as I don't see where I can optimise it. But then I'm not fantastic at this sort of thing.

;********************************************************
;*														*
;*	EngineTwo											*
;*	2D in 3D engine										*
;*	Initialisation and Routines							*
;*														*
;********************************************************

;NOTES
;the pixel 0,0 on the texture must be coloured white, for rectangle drawing
;the rectangle covering 0,1 to 256,1024 is reserved for fonts only.

;constants
Const SYMBOL_TYPE = 0
Const NUMERAL_TYPE = 1
Const UC_TYPE = 2
Const LC_TYPE = 3
Const UCS_TYPE = 4
Const LCS_TYPE = 5

Const TEXTURE_WIDTH# = 1024.0
Const TEXTURE_HEIGHT# = 1024.0
Const TILE_SIZE = 256

;sprite type
Type sprite
Field width			;width of sprite in pixels
Field height		;height of sprite in pixels
Field u1#,v1#		;top left texture coords
Field u2#,v2#		;bottom right texture coords
Field tile			;tile number of sprite (for use in deleting tiles)
Field id			;sprite id, using handle()
End Type

Type font
Field ttFont		;the truetype font handle
Field flags			;character flags
Field height		;height of the font
Field stored[222]	;tells if character is used
Field x#[222]		;x position
Field y#[222]		;y position
Field u1#[222]		;uv coords for top left, and bottom right
Field u2#[222]
Field v1#[222]
Field v2#[222]
Field width[222]	;width in pixels
End Type


Global mainTexture, mainSurface, mainBrush, mainCamera, mainLight, mainMesh
Global screenWidth, screenHeight, normalMeshX#, normalMeshY#, normalMeshZ#
Global fontBottom, nextFreeTile
Global tilesAcross, totalTiles

tilesAcross = (TEXTURE_WIDTH - 256) / TILE_SIZE
totalTiles = tilesAcross * (TEXTURE_HEIGHT / TILE_SIZE)

Dim tileUsed(111)	;111 tiles
fontBottom = 0

;Engine Initialisation
;Sets up EngineTwo for use
;x = width of screen
;y = height of screen
Function E2_initialiseEngine(x, y,z)
	
	Graphics3D x,y,32,z
	ClearTextureFilters
	
	screenWidth = x
	screenHeight = y
	
	normalMeshX = ((screenWidth*-1)/2)-0.5
	normalMeshY = ((screenHeight*-1)/2)+0.5
	normalMeshZ = screenWidth/2
	
	;load in texture
	mainTexture = CreateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT, 1+2+16+32)
	E2_initialiseTexture(mainTexture)
	
	;create brush
	mainBrush = CreateBrush()
	BrushTexture mainBrush, mainTexture
	
	;create camera
	mainCamera = CreateCamera()
	CameraZoom mainCamera, 1
	CameraViewport mainCamera, 0, 0, screenWidth, screenHeight
	
	;create light
	AmbientLight 255,255,255
	
	;create mesh
	mainMesh = CreateMesh()
	
	;create surface
	mainSurface = CreateSurface(mainMesh)
	
	;vertex colours and alpha
	EntityFX mainMesh, 2+32
	
	;position mesh so it can be seen
	PositionEntity mainMesh, normalMeshX, normalMeshY, normalMeshZ
	PositionEntity mainCamera, 0, 0, 0
	
	;paint surface
	PaintSurface mainSurface, mainBrush
End Function

;Texture Intialisation
;Turns a texture completely black, transparent
;texture = texture handle
Function E2_initialiseTexture(texture)
	width = TextureWidth(texture)
	height = TextureHeight(texture)
	
	LockBuffer TextureBuffer(texture)
	For a = 0 To height-1
		For b = 0 To width-1
			WritePixelFast b, a, $00000000, TextureBuffer(texture)
		Next
	Next
	
	;place white, non-transparent pixel
	WritePixelFast 0,0,$FFFFFFFF, TextureBuffer(texture)
	UnlockBuffer TextureBuffer(texture)
End Function

;Texture Editing
;draws images to texture without disrupting alpha channels
;image = image handle of image to draw
;texture = texture handle
;x = x location to draw image
;y = y location to draw image
Function E2_editTexture(image, texture, x, y)
	LockBuffer TextureBuffer(texture)
	LockBuffer ImageBuffer(image)
	width = ImageWidth(image)
	height = ImageHeight(image)
	
	For a = 0 To height-1
		For b = 0 To width-1
			colour = ReadPixelFast (b, a, ImageBuffer(image))
			If (colour And $00FFFFFF) = $00000000 Then
				WritePixelFast x+b, y+a, $00000000, TextureBuffer(texture)
			Else
				colour = (colour Or $FF000000)
				WritePixelFast x+b, y+a, colour, TextureBuffer(texture)
			End If
		Next
	Next

	UnlockBuffer TextureBuffer(texture)
	UnlockBuffer ImageBuffer(image)
End Function

;Adding a Tile
;Adds a graphics tile to the texture
;tilename$ = filename of the tile
Function E2_addTile(tilename$)
	If nextFreeTile < totalTiles Then
	
		image = LoadImage(tilename)
		x = ((nextFreeTile Mod tilesAcross) * TILE_SIZE) + 256
		y = (nextFreeTile / tilesAcross) * TILE_SIZE
		E2_editTexture(image,mainTexture,x,y)
			
				
		;update next free tile
		tileUsed(nextFreeTile) = True
		tile = nextFreeTile
		a = 0
		While (a < totalTiles)
			If tileUsed(a) Then
				a = a + 1
			Else
				nextFreeTile = a
				a = totalTiles
			End If
		Wend
		If nextFreeTile = tile Then nextFreeTile = totalTiles
		Return tile
	Else
		Return -1
	End If
End Function

;Removing a Tile
;Removes a previously added tile from the system
;tileNumber = number of tile to remove
Function E2_removeTile(tileNumber)

	If tileNumber < totalTiles Then
	
		tileUsed(tileNumber) = False
		nextFreeTile = tileNumber
		
		image = CreateImage(TILE_SIZE, TILE_SIZE)
		SetBuffer ImageBuffer(image)
		ClsColor 0,0,1
		Cls
		SetBuffer BackBuffer()
		x = ((tileNumber Mod tilesAcross) * TILE_SIZE) + 256
		y = (tileNumber / tilesAcross) * TILE_SIZE
		E2_editTexture(image,mainTexture,x,y)
		
		For this.sprite = Each sprite
			If this\tile = tileNumber : Delete this : End If
		Next
	
		Return True
	Else
		Return False
	End If
End Function

;Capturing a Sprite
;Captures a sprite from a tile and returns it
;tile = tile to capture sprite from
;x = x location of sprite in tile
;y = y location of sprite
;width = width of sprite
;height = height of sprite
Function E2_captureSprite.sprite(tile, x, y, width, height)
	newSprite.sprite = New sprite
	newSprite\width = width
	newSprite\height = height
	newSprite\tile = tile
	x = x + 1 + ((tile Mod tilesAcross) * TILE_SIZE) + 256
	y = y + 1 + ((tile / tilesAcross) * TILE_SIZE)
	
	newSprite\u1 = (x) / TEXTURE_WIDTH
	newSprite\v1 = (y) / TEXTURE_HEIGHT
	newSprite\u2 = (x+width-1) / TEXTURE_WIDTH 
	newSprite\v2 = (y+height-1) / TEXTURE_HEIGHT
	
	newSprite\id = Handle(newSprite)

	Return newSprite
End Function

;Draw Sprite
;Draws a sprite to the screen
;this = sprite to be drawn
;xpos = x location to draw sprite
;ypos = y location
;xscale = multiplier to scale image
;yscale = multiplier
;angle = angle to rotate clockwise
;alpha = transparency
Function E2_drawSprite(this.sprite, xpos#, ypos#, xscale#, yscale#, angle#, alpha#)
	
	ypos = -ypos
	;get x and y distances from centre
	angle = 360-angle
	xdist# = (((this\width))/2.0)*xscale
	ydist# = (((this\height))/2.0)*yscale
	
	;rotate each vertex in turn
	x1# = (xdist*-1) * Cos(angle) - ydist * Sin(angle) 
	y1# = (xdist*-1) * Sin(angle) + ydist * Cos(angle)
	
	x2# = xdist * Cos(angle) - ydist * Sin(angle) 
	y2# = xdist * Sin(angle) + ydist * Cos(angle)
	
	x3# = xdist * Cos(angle) + ydist * Sin(angle) 
	y3# = xdist * Sin(angle) - ydist * Cos(angle)
	
	x4# = (xdist*-1) * Cos(angle) + ydist * Sin(angle)
	y4# = (xdist*-1) * Sin(angle) - ydist * Cos(angle)

	;create the four vertices as part of the quad.
	v1 = AddVertex(mainSurface, xpos+x1, ypos+y1, 0, this\u1, this\v1)
	v2 = AddVertex(mainSurface, xpos+x2, ypos+y2, 0, this\u2, this\v1)
	v3 = AddVertex(mainSurface, xpos+x3, ypos+y3, 0, this\u2, this\v2)
	v4 = AddVertex(mainSurface, xpos+x4, ypos+y4, 0, this\u1, this\v2)
	
	VertexColor mainSurface, v1, 255, 255, 255, alpha
	VertexColor mainSurface, v2, 255, 255, 255, alpha
	VertexColor mainSurface, v3, 255, 255, 255, alpha
	VertexColor mainSurface, v4, 255, 255, 255, alpha
	
	;add two triangles
	t1 = AddTriangle(mainSurface, v1, v2, v4)
	t2 = AddTriangle(mainSurface, v2, v3, v4)

End Function

;Draw Rectangle
;Draws a rectangle to the screen
;x = x location
;y = y location
;width = width of rectangle
;height = height of rectangle
;fill = filled
;rgb = colour values
;alpha = transparency
Function E2_Rect(x#, y#, width#, height#, fill = True, red, green, blue, alpha#)
	y = -y
	
	If fill Then
		v1 = AddVertex(mainSurface, x, y, 0, 0.0, 0.0)
		v2 = AddVertex(mainSurface, x+width-1, y, 0, 0.0, 0.0)
		v3 = AddVertex(mainSurface, x+width-1, y-height+1, 0, 0.0, 0.0)
		v4 = AddVertex(mainSurface, x, y-height+1, 0, 0.0, 0.0)
	
		t1 = AddTriangle(mainSurface, v1, v2, v3)
		t2 = AddTriangle(mainSurface, v1, v3, v4)
	
		VertexColor mainSurface, v1, red, green, blue, alpha
		VertexColor mainSurface, v2, red, green, blue, alpha
		VertexColor mainSurface, v3, red, green, blue, alpha
		VertexColor mainSurface, v4, red, green, blue, alpha
	Else
	
		T = 1
		;top
		v1 = AddVertex(mainSurface, x, y, 0, 0.0, 0.0)
		v2 = AddVertex(mainSurface, x+width-1, y, 0, 0.0, 0.0)
		v3 = AddVertex(mainSurface, x+width-1, y-T, 0, 0.0, 0.0)
		v4 = AddVertex(mainSurface, x, y-T, 0, 0.0, 0.0)
		
		;right
		v5 = AddVertex(mainSurface, x+width-1, y, 0, 0.0, 0.0)
		v6 = AddVertex(mainSurface, x+width-1, y-height+1, 0, 0.0, 0.0)
		v7 = AddVertex(mainSurface, x+width-(T+1), y-height+1, 0, 0.0, 0.0)
		v8 = AddVertex(mainSurface, x+width-(T+1), y, 0, 0.0, 0.0)
		
		;bottom
		v9 = AddVertex(mainSurface, x, y-height+1, 0, 0.0, 0.0)
		v10 = AddVertex(mainSurface, x+width-1, y-height+1, 0, 0.0, 0.0)
		v11 = AddVertex(mainSurface, x+width-1, y-height+(1-T), 0, 0.0, 0.0)
		v12 = AddVertex(mainSurface, x, y-height+(1-T), 0, 0.0, 0.0)
		
		;left
		v13 = AddVertex(mainSurface, x, y, 0, 0.0, 0.0)
		v14 = AddVertex(mainSurface, x+T, y, 0, 0.0, 0.0)
		v15 = AddVertex(mainSurface, x+T, y-height+1, 0, 0.0, 0.0)
		v16 = AddVertex(mainSurface, x, y-height+1, 0, 0.0, 0.0)
		
		;triangles
		t1 = AddTriangle(mainSurface, v1, v2, v3)
		t2 = AddTriangle(mainSurface, v1, v3, v4)
		t3 = AddTriangle(mainSurface, v5, v6, v7)
		t4 = AddTriangle(mainSurface, v5, v7, v8)
		t5 = AddTriangle(mainSurface, v9, v10, v11)
		t6 = AddTriangle(mainSurface, v9, v11, v12)
		t7 = AddTriangle(mainSurface, v13, v14, v15)
		t8 = AddTriangle(mainSurface, v13, v15, v16)
		
		;colouring
		VertexColor mainSurface, v1, red, green, blue, alpha
		VertexColor mainSurface, v2, red, green, blue, alpha
		VertexColor mainSurface, v3, red, green, blue, alpha
		VertexColor mainSurface, v4, red, green, blue, alpha
		VertexColor mainSurface, v5, red, green, blue, alpha
		VertexColor mainSurface, v6, red, green, blue, alpha
		VertexColor mainSurface, v7, red, green, blue, alpha
		VertexColor mainSurface, v8, red, green, blue, alpha
		VertexColor mainSurface, v9, red, green, blue, alpha
		VertexColor mainSurface, v10, red, green, blue, alpha
		VertexColor mainSurface, v11, red, green, blue, alpha
		VertexColor mainSurface, v12, red, green, blue, alpha
		VertexColor mainSurface, v13, red, green, blue, alpha
		VertexColor mainSurface, v14, red, green, blue, alpha
		VertexColor mainSurface, v15, red, green, blue, alpha
		VertexColor mainSurface, v16, red, green, blue, alpha
	End If		
		
End Function

;Load Font
;Loads a font into the system
;fontname$ = font filename
;size = size of font
;bold = enable bold
;italic = enable italics
;flags = characters used
Function E2_loadFont.font(fontname$, size, bold, italic, flags, smooth)

	;flags guide
	;1 : symbols
	;2 : numerals
	;4 : upper case
	;8 : lower case
	;16: upper case special
	;32: lower case special
	
	;0 is default
	If flags = 0 Then flags = 63
	
	this.font = New font

	;load in font from disc as normal
	theFont = LoadFont(fontname$, size, bold, italic)
	this\flags = flags
	SetFont theFont
	
	;set buffer, clear screen
	fontImage = CreateImage(256,1024)
	SetBuffer ImageBuffer(fontImage)
	ClsColor 0,0,0
	Cls
	Color 255,255,255

	;start at 0,0
	x = 0
	y = 0
	
	;get character height
	height = FontHeight()
	this\height = height
	
	;go through every character in order
	charType = SYMBOL_TYPE		;stores type of character
	For character = 33 To 255
	
		;skip the unnecessary
		If character = 48 Then charType = NUMERAL_TYPE
		If character = 58 Then charType = SYMBOL_TYPE
		If character = 65 Then charType = UC_TYPE
		If character = 91 Then charType = SYMBOL_TYPE
		If character = 97 Then charType = LC_TYPE
		If character = 123 Then 
			character = 128
			charType = SYMBOL_TYPE
		End If
		If character = 129 Then character = 163
		If character = 164 Then character = 169
		If character = 170 Then
			character = 192
			charType = UCS_TYPE
		End If
		If character = 198 Then character = 199
		If character = 215 Then character = 216
		If character = 221 Then character = 222
		If character = 224 Then charType = LCS_TYPE
		If character = 230 Then character = 231
		If character = 247 Then character = 248
		If character = 253 Then character = 254
		
		If Mid(Bin(flags), 32-chartype, 1) = "1" Then
			
			;check for horizontal fit
			this\width[character-33] = StringWidth(Chr$(character))
			If (x + this\width[character-33] -1 ) > 256 Then
				y = y + height
				x = 0
			End If
			
			;write character to image
			Text x,y, Chr$(character)
			
			;store x and y values
			this\x[character-33] = x
			this\y[character-33] = y
			this\stored[character-33] = True
			x = x + this\width[character-33]
		Else
			this\stored[character-33] = False
		End If
		
	Next
	
	SetBuffer BackBuffer()	
	;check for fit
	y = y + height
	If (fontBottom + y) < 1024 Then
		;success - write section to texture
		imageTwo = CreateImage(256,y)
		SetBuffer ImageBuffer(imageTwo)
		DrawImage fontImage, 0, 0
		E2_editTexture(imageTwo, mainTexture, 0, fontBottom+1)
		SetBuffer BackBuffer()
		
		;sort out u,v coors For letters
		For a = 0 To 221
			If this\stored[a] = True Then
				this\y[a] = this\y[a] + fontBottom + 1
				this\u1[a] = (this\x[a]) / (TEXTURE_WIDTH+smooth)
				this\v1[a] = (this\y[a]) / (TEXTURE_HEIGHT+smooth)
				this\u2[a] = (this\x[a] + this\width[a]) / (TEXTURE_WIDTH+smooth)
				this\v2[a] = (this\y[a] + height) / (TEXTURE_HEIGHT+smooth)
			End If
		Next			
		FreeImage fontImage
		FreeImage imageTwo
		fontbottom = fontbottom + y
		this\ttFont = theFont
		Return this
	Else
		Delete this
		FreeImage fontImage
		Return Null
	End If
	
End Function

;Text
;Writes text on screen
;theText$ = text to be written
;textx = coordinate
;texty = coordinate
;xcent = centre horizontally
;bitmapFont = font to write in
;rgb = colour values
Function E2_text(theText$, textx#, texty#, xcent, bitmapFont.font, red, green, blue)

	SetFont bitmapFont\ttFont
	
	;centre vertically (automatic)
	texty = -texty + (bitmapFont\height * 0.5)
		
	;centre horizontally
	If xcent Then
		textx = textx - (StringWidth(thetext) * 0.5)
	End If
	
	;go through each character in turn, drawing to screen
	For a = 1 To Len(theText)
	
		char = Asc(Mid(theText, a, 1)) - 33
		
		;ensure that spaces are accounted for
		If char = -1 Then char = 94
		
		;check if character in font
		If bitmapFont\stored[char] Then
			
			;create the four vertices as part of the quad.
			v1 = AddVertex(mainSurface, textx, texty, 0, bitmapFont\u1[char], bitmapFont\v1[char])
			v2 = AddVertex(mainSurface, textx+bitmapFont\width[char], texty, 0, bitmapFont\u2[char], bitmapFont\v1[char])
			v3 = AddVertex(mainSurface, textx+bitmapFont\width[char], texty-bitmapFont\height, 0, bitmapFont\u2[char], bitmapFont\v2[char])
			v4 = AddVertex(mainSurface, textx, texty-bitmapFont\height, 0, bitmapFont\u1[char], bitmapFont\v2[char])
		
			;color the vertices
			VertexColor mainSurface, v1, red, green, blue
			VertexColor mainSurface, v2, red, green, blue
			VertexColor mainSurface, v3, red, green, blue
			VertexColor mainSurface, v4, red, green, blue
	
			;add two triangles
			t1 = AddTriangle(mainSurface, v1, v2, v4)
			t2 = AddTriangle(mainSurface, v2, v3, v4)
			
			;move cursor
			textx = textx + bitmapFont\width[char]
		
		Else
			textx = textx + (bitmapFont\height * 0.25)
		End If
	Next
End Function

Function E2_clearSurface()
	ClearSurface mainSurface
End Function

Function E2_updateWorld()


	UpdateNormals mainMesh
	RenderWorld
	Flip
		
		
End Function

;Display Texture
;Debug routine to ensure texture is working
Function E2_displayTexture()
	v1 = AddVertex(mainSurface, 0, TEXTURE_HEIGHT, 0, 0.0, 0.0)
	v2 = AddVertex(mainSurface, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, 1.0, 0.0)
	v3 = AddVertex(mainSurface, TEXTURE_WIDTH, 0, 0, 1.0, 1.0)
	v4 = AddVertex(mainSurface, 0, 0, 0, 0.0, 1.0)
	
	t1 = AddTriangle(mainSurface, v1, v2, v3)
	t2 = AddTriangle(mainSurface, v1, v3, v4)
End Function


Use a {codebox} mate!!

I'm pretty sure that for single surface when you get to around 3000 polys per surface it is more efficient to create another surface. For particle effects this is where emitters come in handy .. for me at least.

Ah, that'll be it then. I'll have to see if I end up creating that amount at some point (my tests were based on high numbers of objects on screen - more than I'd ever need).

Didn't know about codeboxes...

Are you rebuilding the surface every frame? it seems like it. It's faster to just move the vertices offscreen when deleting an object and move them back when creating a new one. Most of the time building the surface every frame is slower than just rendering the maximum amount of particles (where most of them are offscreen and not wasting fillrate anyway) every frame.

Well, the current project I'm working on is a GUI, so everything's always on-screen. However, I do think it could use optimisation in that I don't have to rebuild it unless something happens, because it always looks the same.

Can you remove triangles from a surface entirely?

The ClearSurface command allows you to remove either verts, tris or both.

i wouldn't bother removing tris from your surface, unless your sure they ain't getting used, of you've added a large amount and don't need them again for a while. As bouncer says, just set their alpha to 0 so they aren't rendered.

Comparing the speed of pseudo 2D and real 2d doesn't make much sense because speed isn't the reason why you would use pseudo 2D. The reasons may be: fast rotation, scaling, alpha-transparency and antialiasing.

Also, if you have multiple overlapping alphatransparent objects or Polies, it may become pretty slow even on fast cards.

Speed is the reason why I'm using it. My current GUI is dog slow on most machines when trying to DrawImage, so I'm switching to a 2D-in-3D system. It's not even graphically intensive, it's just a problem with drawing images on those machines.

The extra features are a nice plus, mind you...