issue with tile map refresh speed

Game development specific discussions.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

issue with tile map refresh speed

Postby leopardpm » Jan 22, 2016 21:49

Hello all,
I am trying to display an isometric tile map, which scrolls, of a 3D world(voxelish). I pre-calc the screen positions of every tile that is displayed (since scrolling movement is not pixel, but rather entire tile, so the screen positions are static). So, basically my display loop is rather simple:

Screen lock
Cls
loop through display position array
Grab each x,y
Do some easy checks and simple calcs
Put correct tile on screen at x,y
Keep looping through display tile array
Screen unlock
Sleep 1 <------- to give OS some time

To blit tile onto screen, this is my statement:

Put (xloc-z3, yloc-z1-z2) , terrain(map(mx, my, z)), alpha

All the variables are DIMmed as integer
The array map(0,0,0) is DIMmed as uByte
The array terrain(0) is DIMmed as fb.image ptr

All the logic is good and it works, BUT it is too slow! I am getting about 30 fps throwing up about 4,600 images (displays 96 x 48 tiles on screen), each being 16 x 16 pixels (I think). I can post code, just not at computer atm. I think the slowness is from the "put" statement, is there a faster way? Maybe is ASM without bounds checking, etc? Should I use use different variable/array types?

I am using a newer, but low end ($200) notebook, which is the minimum platform I want my game to run on.....
Specs:
ASUS X551M
4gb ram
No vram
Probably dual core Intel chip around 2ghz

Any ideas are helpful! Thank you!
badidea
Posts: 1782
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: issue with tile map refresh speed

Postby badidea » Jan 22, 2016 22:32

I don't think that put is slow, but I could be wrong, maybe the alpha thing. And the images are pretty small.

If the whole screen is redrawn, you can skip the 'cls'

Disable each step in the loop one at the time (if possible) and check if the fps increases.
fxm
Posts: 9474
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: issue with tile map refresh speed

Postby fxm » Jan 22, 2016 22:37

What is your fps value when you suppress 'Sleep 1'?

On my PC, 'Sleep 1' corresponds to about 15 ms.
Therefore with already one 'Sleep 1' at each loop, the fps cannot be higher than 1000/15=66.

Code to estimate 'Sleep 1':

Code: Select all

Dim As Double t1, t2
t1 = Timer
For I As Integer = 1 To 100
  Sleep 1
Next I
t2 = Timer
Print "'Sleep 1' =>"; Cint(10 * (t2 - t1));" ms"

Sleep
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: issue with tile map refresh speed

Postby leopardpm » Jan 22, 2016 23:20

Suppressing sleep 1 gives about 40-50 fps
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: issue with tile map refresh speed

Postby leopardpm » Jan 22, 2016 23:31

With your sleep test code I get sleep 1= 10ms, so fps limit is 100 with sleep 1?
fxm
Posts: 9474
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: issue with tile map refresh speed

Postby fxm » Jan 22, 2016 23:35

Yes.
Linux?
(Windows 7 for me)
Last edited by fxm on Jan 22, 2016 23:40, edited 1 time in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: issue with tile map refresh speed

Postby leopardpm » Jan 22, 2016 23:39

Windows 10

Is there so sort of basic graphics accel that I can directly access? I would suppose that FB already uses it thought... I have seen some FB prove that write directly to the screen, perhaps a simple routine to throw up an image without error checking or any other checks...... It is just hard for me to believe that I can't get faster than basically 40fps before even doing rest of game stuff....
badidea
Posts: 1782
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: issue with tile map refresh speed

Postby badidea » Jan 22, 2016 23:51

I get: 'Sleep 1' => 1 ms on linux here (this time can be 15ms old older windows version if I remember correct).

How does this code run:

Code: Select all

#include once "fbgfx.bi"

const as integer screenWidth = 640, screenHeight = 480

type mouseType
 x as integer
 y as integer
 wheel as integer
 buttons as integer
end type

type int2d
   as integer x, y
end type

Type bitmap_header Field = 1
    bfType          As UShort
    bfsize          As UInteger
    bfReserved1     As UShort
    bfReserved2     As UShort
    bfOffBits       As UInteger
    biSize          As UInteger
    biWidth         As UInteger
    biHeight        As UInteger
    biPlanes        As UShort
    biBitCount      As UShort
    biCompression   As UInteger
    biSizeImage     As UInteger
    biXPelsPerMeter As UInteger
    biYPelsPerMeter As UInteger
    biClrUsed       As UInteger
    biClrImportant  As UInteger
End Type

'----------------------- Sprite class (stripped) -----------------------

#include once "file.bi"

#define SPRITE_DRAW_NO_CENTER 0
#define SPRITE_DRAW_CENTER    1

#define SPRITE_DRAW_PSET   0
#define SPRITE_DRAW_TRANS  1
#define SPRITE_DRAW_ALPHA  2

type sprite_type
   dim as any ptr pImage
   dim as int2d size
   declare sub create(sizeInit as int2d, colorInit as integer)
   declare function createFromBmp(fileName as string) as integer
   declare sub destroy()
   declare sub drawxym(x as integer, y as integer, m as integer)
   declare destructor()
end type

sub sprite_type.create(sizeInit as int2d, colorInit as integer)
  pImage = imagecreate(sizeInit.x, sizeInit.y, colorInit)
  size = sizeInit
end sub

function sprite_type.createFromBmp(fileName as string) as integer
  dim as bitmap_header bmp_header
  dim as int2d bmpSize
  if fileExists(filename) then
    open fileName for binary as #1
      get #1, , bmp_header
    close #1
    bmpSize.x = bmp_header.biWidth
    bmpSize.y = bmp_header.biHeight
    create(bmpSize, &hff000000)
    bload fileName, pImage
    print "Bitmap loaded: " & filename
  else
    print "File not found: " & filename
    beep: sleep
    return -1
  end if
  return 0
end function

sub sprite_type.destroy()
  if (pImage <> 0) then
    imagedestroy(pImage)
    pImage = 0
  end if
end sub

destructor sprite_type()
  if (pImage <> 0) then
    imagedestroy(pImage)
    pImage = 0
  end if
end destructor

sub sprite_type.drawxym(x as integer, y as integer, m as integer)
  select case m
  case 1
    put (x , y), pImage, trans
  case else
    put (x , y), pImage, pset
  end select
end sub

'----------------------- Bitmap extractor -----------------------

type sprite_details_type
   dim as integer xPos, yPos, xSize, ySize
   dim as string * 512 fileName 'big enough?
end type

type spr_extractor_type
   dim as integer nSpr
   dim as sprite_details_type ptr pSprDetais
   dim as sprite_type ptr pSpr
   dim as sprite_type sprAll
   declare destructor
   declare function execute(fileName as string) as integer
   declare sub printDetails()
end type

destructor spr_extractor_type
   if pSprDetais <> 0 then   deallocate(pSprDetais)
   if pSpr <> 0 then deallocate(pSpr)
end destructor

function spr_extractor_type.execute(fileName as string) as integer
   dim as integer i
   dim as string fileNameBmp = fileName & ".bmp"
   dim as string fileNameDescr = fileName & ".txt"

   'hard coded bitmap data
   nSpr = 7
   pSprDetais = callocate(nSpr, sizeof(sprite_details_type)) 'add error check
   pSpr = callocate(nSpr, sizeof(sprite_type)) 'add error check
   i = 0: pSprDetais[i].xPos = 0: pSprDetais[i].yPos = 0: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 44
   i = 1: pSprDetais[i].xPos = 82: pSprDetais[i].yPos = 0: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 44
   i = 2: pSprDetais[i].xPos = 164: pSprDetais[i].yPos = 0: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 44
   i = 3: pSprDetais[i].xPos = 0: pSprDetais[i].yPos = 44: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 150
   i = 4: pSprDetais[i].xPos = 82: pSprDetais[i].yPos = 44: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 120
   i = 5: pSprDetais[i].xPos = 164: pSprDetais[i].yPos = 44: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 120
   i = 6: pSprDetais[i].xPos = 0: pSprDetais[i].yPos = 194: pSprDetais[i].xSize = 82: pSprDetais[i].ySize = 80

   'open description file first
   '~ open fileNameDescr for input as #1
      '~ input #1, nSpr
      '~ pSprDetais = callocate(nSpr, sizeof(sprite_details_type)) 'add error check
      '~ pSpr = callocate(nSpr, sizeof(sprite_type)) 'add error check
      '~ for i = 0 to nSpr-1
         '~ if eof(1) then
            '~ close #1
            '~ return -1 'error
         '~ end if
         '~ input #1, pSprDetais[i].fileName, _
            '~ pSprDetais[i].xPos, pSprDetais[i].yPos, _
            '~ pSprDetais[i].xSize, pSprDetais[i].ySize
      '~ next
   '~ close #1

   if sprAll.createFromBmp(fileNameBmp) <> 0 then return -1' not ok
   sprAll.drawxym(0, 0, 0)
   for i = 0 to nSpr-1
      pSpr[i].create(type<int2d>(pSprDetais[i].xSize, pSprDetais[i].ySize), 0)
      get (pSprDetais[i].xPos, pSprDetais[i].yPos) _
         - step(pSprDetais[i].xSize - 1, pSprDetais[i].ySize - 1), _
         pSpr[i].pImage
      line (pSprDetais[i].xPos, pSprDetais[i].yPos) _
         - step(pSprDetais[i].xSize - 1, pSprDetais[i].ySize - 1), &h00ffffff, b
   next

   return 0 'ok
end function

sub spr_extractor_type.printDetails
   dim as integer i
   for i = 0 to nSpr-1
      print trim(pSprDetais[i].fileName), _
         pSprDetais[i].xPos, pSprDetais[i].yPos, _
         pSprDetais[i].xSize, pSprDetais[i].ySize
   next
end sub

'----------------------- Helper functions -----------------------

'integer divider with modified output for negative numbers
function divDownInt(byval numerator as integer, byval denominator as integer) as integer
   if numerator < 0 then numerator -= (denominator - 1)
   return numerator \ denominator
end function

sub drawHexagon(x1 as integer, y1 as integer, xs as integer, ys as integer, c as integer)
   dim as integer x2, x3, x4, y2, y3, y4
   xs -= 1
   ys -= 1
   x2 = x1 + ys \ 2
   x3 = (x1 + xs) - ys \ 2
   x4 = x1 + xs
   y2 = y1 + ys \ 2
   y3 = y2 +1
   y4 = y1 + ys
   line(x1, y2)-(x2, y1), c 'left top
   line(x1, y3)-(x2, y4), c 'left bottom
   line(x2, y1)-(x3, y1), c 'top
   line(x2, y4)-(x3, y4), c 'bottom
   line(x3, y1)-(x4, y2), c 'right top
   line(x3, y4)-(x4, y3), c 'right bottom
end sub

'----------------------- Consts & vars -----------------------

const as integer nTilesxMap = 30, nTilesyMap = 30
const as integer imageWidth = 82, imageHeight = 44, imageDist = 61, imageHeightHalf = 22
const as integer nBaseTiles = 3, nOverTiles = 4

dim as integer baseMap(nTilesxMap-1, nTilesyMap-1)
dim as integer overMap(nTilesxMap-1, nTilesyMap-1)

dim as spr_extractor_type sprExtr
dim as sprite_type ptr pBaseTile
dim as sprite_type ptr pOverTile

dim as integer i, loopCounter = 0
dim as integer xi, xi1, xi2, x, xView, xOnRect, xMouseMap ', nTilesxView
dim as integer yi, yi1, yi2, y, yView, yOnRect, yMouseMap ', nTilesyView
dim as single xViewSgl = 100.0, yViewSgl = 100.0, vSgl = 400.0
dim as integer xTileIndex, xFirstTileIndex, xLastTileIndex
dim as integer yTileIndex, yFirstTileIndex, yLastTileIndex
dim as integer yFirstTileIndexEven, yFirstTileIndexOdd, yLastTileIndexEven, yLastTileIndexOdd

dim as mousetype mouse
dim as integer mouseEvent
dim as string key
dim as double t0, t1, t, dt

'----------------------- Main program -----------------------

'setting up map
for xi = 0 to nTilesxMap-1
   for yi = 0 to nTilesyMap-1
      baseMap(xi, yi) = int(rnd()*(nBaseTiles)) '0...4
      overMap(xi, yi) = int(rnd()*(nOverTiles+2))-2 '-2...1
   next
next

'set screen before image memory allocation
screenres screenWidth, screenHeight, 32

if sprExtr.execute("sprites") <> 0 then end

'This is ugly, split in 2 bitmaps later
pBaseTile = @sprExtr.pSpr[0]
pOverTile = @sprExtr.pSpr[3]

'main loop
t0 = timer
t = timer
dt = 0

while not multikey(FB.SC_ESCAPE)
   getmouse mouse.x, mouse.y, mouse.wheel, mouse.buttons
   key = inkey
   
   if multikey(fb.sc_down)  or multikey(fb.sc_s) then yViewSgl += vSgl * dt
   if multikey(fb.sc_up)    or multikey(fb.sc_w) then yViewSgl -= vSgl * dt
   if multikey(fb.sc_right) or multikey(fb.sc_d) then xViewSgl += vSgl * dt
   if multikey(fb.sc_left)  or multikey(fb.sc_a) then xViewSgl -= vSgl * dt

   xView = int(xViewSgl)
   yView = int(yViewSgl)

   xFirstTileIndex = divDownInt(xView - imageHeightHalf, imageDist)
   xLastTileIndex = divDownInt(xView + screenWidth, imageDist)

   yFirstTileIndexEven = divDownInt(yView, imageHeight)
   yLastTileIndexEven = divDownInt(yView + screenHeight, imageHeight)
   yFirstTileIndexOdd = divDownInt(yView - imageHeightHalf, imageHeight)
   yLastTileIndexOdd = divDownInt(yView + screenHeight - imageHeightHalf, imageHeight)
   
   screenlock()
   cls()
   'draw base map tiles
   for xTileIndex = xFirstTileIndex to xLastTileIndex
      if (xTileIndex and 1) = 0 then 'Even
         yFirstTileIndex = yFirstTileIndexEven
         yLastTileIndex = yLastTileIndexEven
      else 'Odd
         yFirstTileIndex = yFirstTileIndexOdd
         yLastTileIndex = yLastTileIndexOdd
      end if
      for yTileIndex = yFirstTileIndex to yLastTileIndex
         x = xTileIndex * imageDist - xView
         y = yTileIndex * imageHeight - yView + (xTileIndex and 1) * imageHeightHalf
         if (xTileIndex >= 0) and (yTileIndex >= 0) and (xTileIndex < nTilesxMap) and (yTileIndex < nTilesyMap) then
            i = baseMap(xTileIndex, yTileIndex)
            pBaseTile[i].drawxym(x, y, 1)
            'put(x, y), pBaseTile[i], trans
         else
         '   put(x, y), pBaseTile(3), trans
         end if
      next
   next
   
   'locate tile under mouse cursor
   'getting correct index numbers
   if mouse.buttons = -1 then
      'locate 2,1: print "invalid position";
   else
      xMouseMap = mouse.x + xView
      yMouseMap = mouse.y + yView
      xi = divDownInt(xMouseMap, imageDist)
      yi = divDownInt(yMouseMap - (xi and 1) * imageHeightHalf, imageHeight)
      xOnRect = xMouseMap - (xi * imageDist)
      yOnRect = yMouseMap - (yi * imageHeight + (xi and 1) * imageHeightHalf)

      if (xOnRect + yOnRect) < imageHeightHalf then
         'top left section
         if (xi and 1) = 0 then
            yi -= 1
         end if
         xi -= 1
      end if
      if (xOnRect + (imageHeight - yOnRect)) < imageHeightHalf then
         'bottom left section
         if (xi and 1) = 1 then
            yi += 1
         end if
         xi -= 1
      end if
      
      'drawing tile marker at the correct index location
      x = xi * imageDist
      y = yi * imageHeight + (xi and 1) * imageHeightHalf
      'locate 2,1: print "Mouse index: "; divDownInt(x, imageDist); divDownInt(y, imageHeight);
      drawHexagon(x - xView, y - yView, imageWidth, imageHeight, &h00ffffff)
   end if

   'draw next layer tiles
   for xTileIndex = xFirstTileIndex to xLastTileIndex
      if (xTileIndex and 1) = 0 then 'Even
         yFirstTileIndex = yFirstTileIndexEven
         yLastTileIndex = yLastTileIndexEven
      else 'Odd
         yFirstTileIndex = yFirstTileIndexOdd
         yLastTileIndex = yLastTileIndexOdd
      end if

      for yTileIndex = yFirstTileIndex to yLastTileIndex + 2
         x = xTileIndex * imageDist - xView
         y = yTileIndex * imageHeight - yView + (xTileIndex and 1) * imageHeightHalf
         if (xTileIndex >= 0) and (yTileIndex >= 0) and (xTileIndex < nTilesxMap) and (yTileIndex < nTilesyMap) then
            i = overMap(xTileIndex, yTileIndex)
            if i >= 0 then
               y -= (pOverTile[i].size.y - imageHeight)
               pOverTile[i].drawxym(x, y, 1)
            end if
         end if
      next
   next

   t1 = timer - t0
   locate 1,1: print "Time:"; using "####.##"; t1
   locate 2,1: print "loopCounter:"; loopCounter
   locate 3,1: print "loopCounter / time:"; using "####.##"; loopCounter / t1
   locate 4,1: print "ESC to exit"

   screenunlock()
   
   sleep 1,1
   dt = timer - t
   t = timer
   loopCounter += 1
wend

end

Image needed to run:
Image
My result:
Image
The 'loopCounter / time' should be the fps.
Arrows key to move around, ESC to exit.

(Code is 3 years old, don't ask me what does what :-)
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: issue with tile map refresh speed

Postby leopardpm » Jan 22, 2016 23:59

here is a screen blitting ASM routine I found here some years ago... it is not an apples to apples comparison, BUT, still, this thing cooks at 37,000 FPS! (Yes, 37k frames per second)... was thinking of modifying the routine for my tilemap.... don't know if I can, or if it would be any speedier ( I would like to get at least 100 fps before adding in all the other 'game' stuff (AI especially!)

Code: Select all


' Updated as of 10,10,2009. Screen buffer blit reutines by ShawnLG and MichaelW

' ********* Note: These routines only work for 8 bit modes. **************

Const options = 5 'use options below to select different blit routines.
'program options
'1 = (320,240)buffer --> (640,480)screen - non MMX 2x scale.
'2 = (320,240)buffer --> (640,480)screen - 2X using MMX.
'3 = (320,240)buffer --> (640,480)BUFFER - 2X using SSE. (WARNING, SSE does not work with Free Basic's graphics, beg the developers to support SSE.)
'4 = (320,240)buffer --> (640,480)screen - experimental 2X HD scale.
'5 = (640,480)buffer --> (640,480)screen - a normal unscaled buffer copy.
'6 = (640,480)buffer --> (640,480)screen - a normal unscaled buffer copy using MMX.(may be slower than above.)

Declare Sub updatefraimcounter
Declare Sub blitbuffer_320_240(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
Declare Sub blitbuffer_320_240_MMX(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
Declare Sub blitbuffer_320_240_SSE(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
Declare Sub blitbuffer_320_240_HD(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
Declare Sub blitbuffer_640_480(byref sourcepointer_640_480 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
Declare Sub blitbuffer_640_480_MMX(byref sourcepointer_640_480 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)

dim shared screenbuffer_320_240(0 to 76799) As byte ' a memory buffer for 320*240 graphics.
dim shared screenbuffer_640_480(0 to 307199) As byte ' a memory buffer for 640*480 graphics.

dim As UBYTE ptr bufferpointer_320_240,bufferpointer_640_480,bufferpointer2_640_480, screenpointer_640_480
bufferpointer_320_240 = @screenbuffer_320_240(0) ' Set our buffer pointers
bufferpointer_640_480 = @screenbuffer_640_480(0)

dim shared As integer fraimcount = 0
dim shared As double timeplusone = 0
dim As integer i
dim As ubyte c

Screen 18
screenpointer_640_480 = screenptr ' Set the screenpointer to the screen

' Why screen 18(640,480)? For some reason, some newer graphics cards or the OS do not
' support full screen in low screen modes such as 320,200 and/or 320,240.
' Lower resolutions can be faked while using a higher mode. When writing a low
' resolution game use (640,480) or higher. 640,400 is not supported on some cards.

For i = 0 to 307199 ' put some graphics in the 640_480 buffer
        c = Rnd * 4
        Poke bufferpointer_640_480 + i, c
Next i
For i = 0 to 76799 ' put some graphics in the 320_240 buffer
        c = Rnd * 4
        Poke bufferpointer_320_240 + i, c
Next i

Do ' The main loop where your game/graphics code goes...
    Locate 1,1
    Select Case options' select your option from above.
    Case 1:blitbuffer_320_240 bufferpointer_320_240, screenpointer_640_480, 1
    Case 2:blitbuffer_320_240_MMX bufferpointer_320_240, screenpointer_640_480, 1
    Case 3:blitbuffer_320_240_SSE bufferpointer_320_240, bufferpointer_640_480, 0
    Case 4:blitbuffer_320_240_HD bufferpointer_320_240, screenpointer_640_480, 1
    Case 5:blitbuffer_640_480 bufferpointer_640_480, screenpointer_640_480, 1
    Case 6:blitbuffer_640_480_MMX bufferpointer_640_480, screenpointer_640_480, 1
    End Select
    updatefraimcounter ' update the fraims per second counter
loop while not multikey(&h1)' Loop until Esc is pressed...
' Clear input buffer
while inkey$ = "": Wend

End ' End of program

Sub updatefraimcounter
    screenlock
    screenunlock
    If Timer > timeplusone Then
        timeplusone = Timer + 1
         windowtitle "FPS: " + str$(fraimcount) + "  Option: " + str$(options)
         fraimcount = 0
    End If
    fraimcount = fraimcount + 1
End Sub

Sub blitbuffer_320_240(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_320_240
    dtemp = destpointer_640_480
    If flag = 1 Then screenlock ' this is the only time when the screen needs to be locked, that if the flag = 1.
    asm 'Optimized non MMX 320,240 to 640,480 2X buffer scaler by ShawnLG
        mov esi, [stemp]
        mov edi, [dtemp]
        push ebp
        mov ebx, 0
        mov ebp, 320/2
        mov edx, 0
    scanline1:
        movzx eax, byte ptr [esi+ebx]
        movzx ecx, byte ptr [esi+ebx+1]
        movb ah, al ' duplicate
        movb ch, cl ' duplicate
        add ebx, 2 ' incriment source addr
        shl ecx, 16 ' move the CX bits to extended area
        add eax, ecx ' combind the two into eax
        mov [edi + edx], eax' write buffer
        mov [edi + edx + 640], eax ' write buffer
        add edx, 4
        Sub ebp, 1
        jnz scanline1
        mov ebp, 320/2
        add edx, 640
        cmp edx,  307200
        jl scanline1
        pop ebp
    End asm
    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub

Sub blitbuffer_320_240_MMX(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_320_240
    dtemp = destpointer_640_480
    If flag = 1 Then screenlock ' this is the only time when the screen needs to be locked, that if the flag = 1.
    asm 'Totally sickening fast that it may burn a hole in your ram 320,240 to 640,480 2X buffer scaler by ShawnLG
        mov esi, [stemp]
        mov edi, [dtemp]
        mov eax, 0
        mov ecx, 320/8
        mov edx, 0
    scanline2:
        movq mm6, [esi+eax]' get 8 bytes of data from the source
        movq mm7, mm6 ' make a copy
        punpcklbw mm6, mm6 ' These two instructions do all the hard work for us
        punpckhbw mm7, mm7 ' and saving a boat load of cpu cycles compared to the non MMX version.
        movq [edi + edx], mm6
        movq [edi + edx + 640], mm6
        add eax, 8
        movq [edi + edx +8], mm7
        movq [edi + edx +8+ 640], mm7
        add edx, 16
        Sub ecx, 1
        jnz scanline2
        mov ecx, 320/8
        add edx, 640
        cmp edx,  307200
        jl scanline2
        emms
    End asm
    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub

Sub blitbuffer_320_240_SSE(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_320_240
    dtemp = destpointer_640_480
    'WARNING, 2X scale with SSE can not write directly to the video buffer because it is not alligned for SSE.
    If flag = 1 Then screenlock ' this is the only time when the screen needs to be locked, that if the flag = 1.
    asm 'Totally sickening fast(er) that it may burn a hole in your ram 320,240 to 640,480 2X buffer scaler by ShawnLG
        mov esi, [stemp]
        mov edi, [dtemp]
        mov eax, 0
        mov ecx, 320/16
        mov edx, 0
    scanline3:
        movdqa xmm6, [esi+eax]' get 16 bytes of data from the source
        movdqa xmm7, xmm6 ' make a copy
        punpcklbw xmm6, xmm6
        punpckhbw xmm7, xmm7
        movdqa [edi + edx], xmm6
        movdqa [edi + edx + 640], xmm6
        add eax, 16
        movdqa [edi + edx +16], xmm7
        movdqa [edi + edx +16+ 640], xmm7
        add edx, 32
        Sub ecx, 1
        jnz scanline3
        mov ecx, 320/16
        add edx, 640
        cmp edx,  307200
        jl scanline3
        emms
    End asm
    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub




Sub blitbuffer_640_480(byref sourcepointer_640_480 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_640_480
    dtemp = destpointer_640_480
    If flag = 1 Then screenlock ' this is the only time when the screen needs to be locked, that if the flag = 1.
    asm 'Fast buffer copy by MichaelW (movsd is very fast on Intel chips, faster than using MMX, other chips may vary.)
        mov esi, [stemp]
        mov edi, [dtemp]
        mov ecx, 640*480/4
        rep movsd
    End asm
    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub

Sub blitbuffer_640_480_MMX(byref sourcepointer_640_480 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_640_480
    dtemp = destpointer_640_480
    If flag = 1 Then screenlock ' this is the only time when the screen needs to be locked, that if the flag = 1.
    asm 'buffer copy using MMX by ShawnLG
        mov esi, [stemp]
        mov edi, [dtemp]
        mov ecx, 0
        mmxloop:
        movq mm6, [esi + ecx]
        movq [edi + ecx], mm6
        movq mm7, [esi + ecx + 8]
        movq [edi + ecx + 8], mm7
        add ecx, 16
        cmp ecx, 307200
        jne mmxloop
        emms
    End asm
    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub

Sub blitbuffer_320_240_HD(byref sourcepointer_320_240 As ubyte ptr, byref destpointer_640_480 As ubyte ptr, ByVal flag As integer)
    dim As ubyte ptr stemp,dtemp
    stemp = sourcepointer_320_240
    dtemp = destpointer_640_480
    If flag = 1 Then screenlock
    dim E As Ubyte, E0 As Ubyte, E1 As Ubyte, E2 As Ubyte, E3 As Ubyte
    dim B As Ubyte, D As Ubyte, F As Ubyte, H As Ubyte
    dim As integer x,y,x2,y2
    'Warning! blitbuffer_320_240_HD is experimental and not optimized.
    'blitbuffer_320_240_2X attempt to make 2X image looks like a High definition image.
    For y = 1 to (238)
        For x = 1 to (318)
            x2 = (x * 2)
            y2 = (y * 2)
            B = Peek (stemp + x + (y-1)*320)
            D = Peek (stemp + x-1 + (y)*320)
            E = Peek (stemp + x + (y)*320)
            F = Peek (stemp + x+1 + (y)*320)
            H = Peek (stemp + x + (y+1)*320)
   
            If B <> H And D <> F Then
                If D = B Then E0 = D Else E0 = E
                If B = F Then E1 = F Else E1 = E
                If D = H Then E2 = D Else E2 = E
                If H = F Then E3 = F Else E3 = E
            Else
                E0 = E
                E1 = E
                E2 = E
                E3 = E
            End If
            Poke (dtemp + x2 + (y2 * 640)), E0
            Poke (dtemp + x2+1 + (y2 * 640)), E1
            Poke (dtemp + x2 + ((y2+1) * 640)), E2
            Poke (dtemp + x2+1 + ((y2+1) * 640)), E3
   
        Next x
    Next y

    If flag = 1 Then screenunlock ' we are now done copying the buffer, now unlock the screen.
End Sub

 


Actually, after looking at this code, it is just copying from one memory location to another the same size. I would have to know MUCH more about many things before I could do a custom spriting routine to the screen.... still some fun code though - love that FPS!

will try your code next, badidea
Last edited by leopardpm on Jan 23, 2016 0:30, edited 1 time in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: issue with tile map refresh speed

Postby leopardpm » Jan 23, 2016 0:06

badidea,
I get 65+ fps with your code... I like it! I always love hexmap scrollers and games - I might clean up my code (is horrifying mess at the moment) and post it as well just so you all can visualize it better - how would I get my PNGs up here though?

PS: what machine you running that on - I just noticed you getting 250+ FPS... nice!
Tourist Trap
Posts: 2792
Joined: Jun 02, 2015 16:24

Re: issue with tile map refresh speed

Postby Tourist Trap » Jan 23, 2016 10:39

badidea wrote:If the whole screen is redrawn, you can skip the 'cls'

Good advice, I gain 20% speed on the actual implementation of my tile game (other topic). Is there not somewhere a replacement for cls to speed it up? I can remember of this, maybe read in the doc.
fxm
Posts: 9474
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: issue with tile map refresh speed

Postby fxm » Jan 23, 2016 11:15

If a part only of the screen is to be cleared, you can try:
Line (x1, y1)-(x2, y2), 0, BF
badidea
Posts: 1782
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: issue with tile map refresh speed

Postby badidea » Jan 23, 2016 12:58

CPU: Intel(R) Core(TM)2 Duo CPU E8400 @ 3.00GHz
GPU: GeForce GTS 250
OS: Ubuntu 14.04 (64bit)
FreeBASIC Compiler - Version 1.04.0 (10-01-2015), built for linux-x86 (32bit)

Maybe not the right graphics driver loaded?
I'll test my code on my old netbook...
fxm
Posts: 9474
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: issue with tile map refresh speed

Postby fxm » Jan 23, 2016 15:03

For those using Windows and who get a 'Sleep 1' too long, one can improve the timer resolution to a true 1 ms, using 'timeBeginPeriod(1)' then 'timeEndPeriod(1)'.
It is recommend to do that just around the 'Sleep' in order to minimize disruption to the system:

Code: Select all

#include "windows.bi"
#include once "win\mmsystem.bi"

Dim As Double t1, t2

t1 = Timer
For I As Integer = 1 To 100
  Sleep 1
Next I
t2 = Timer
Print "'Sleep 1' in normal resolution", "=>"; Cint(10 * (t2 - t1));" ms"

t1 = Timer
For I As Integer = 1 To 100
  timeBeginPeriod(1)
  Sleep 1
  timeEndPeriod(1)
Next I
t2 = Timer
Print "'Sleep 1' in hight resolution 1 ms", "=>"; Cint(10 * (t2 - t1));" ms"

t1 = Timer
For I As Integer = 1 To 100
  Sleep 1
Next I
t2 = Timer
Print "'Sleep 1' in normal resolution", "=>"; Cint(10 * (t2 - t1));" ms"

Sleep
On my PC:

Code: Select all

'Sleep 1' in normal resolution            => 15 ms
'Sleep 1' in hight resolution 1 ms        => 1 ms
'Sleep 1' in normal resolution            => 15 ms
Tourist Trap
Posts: 2792
Joined: Jun 02, 2015 16:24

Re: issue with tile map refresh speed

Postby Tourist Trap » Jan 23, 2016 15:12

fxm wrote:For those using Windows and who get a 'Sleep 1' too long, one can improve the timer resolution to a true 1 ms, using 'timeBeginPeriod(1)' then 'timeEndPeriod(1)

Interesting tip, I think, mostly for tuning a pure animation (frame regulation).

Otherwise, I use sleep in the main loop just to slow down the program, very useful at development start when the loop is mostly filled with void. If the loop is busy enough, why not simply removing it?

Return to “Game Dev”

Who is online

Users browsing this forum: No registered users and 1 guest