Code that heats CPU more than other code that do same thing

Everything else that doesn't fall into one of the other PB categories.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Code that heats CPU more than other code that do same thing

Post by Psychophanta »

I have a very curious quest which i have decided to write here, in "General Discussion".

I have tried several programs (in MS Windows) in 3 different notebooks PCs (all of them above pentium III epoch, this means, all of them with a fan which is activated depending of cpu and mainboard heat detected).

For example, many hardware platforms emulators using windowed or full screen (MAME (with most roms being emulated), ZSNES for windows, BlueMSX, VMWARE (with MSDOS 6.22 as virtual machine), MagicENGINE (all versiones), etc.) don't seem to make cpu fan to be acelerated.
VirtualPC not tested.

But others like NLMSX, ParaMSX, etc. make cpu fan to be accelerated.

NOTE: all tests performed in a 60 m^2 room at 25 degrees, about 50% humidity.

PB compiled programs, when using windowed or full screen always make cpu fan to be accelerated, at least use WaitWindowEvent() (instead of WindowEvent()) to check to exit the program, but there are not a "Wait" for the Keyboard library (to exit with ESC, for example), or some way to select the execution priority in the compiler for a program made in PB, or some way to say the compiler to say the final executeable that DO NOT work whenever there are nothing to do...

As an add comment, I've noticed that ALL the time used when the FlipBuffers() function WAITS FOR actually swap the screen buffers seems to be wasting CPU resources, what is innecessary. So, if that is true, it is sure that the FlipBuffers() function need to be modified.
Fred
Administrator
Administrator
Posts: 18350
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

FlipBuffers() now have a flags to control the swap synchronization. Full screen games often eats 100% CPU..
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Hey, hey, wait.
Fred wrote:
Full screen games often eats 100% CPU..
Why?

I play to Super Nintendo games in a notebook or desktop PC with ZSNES, at full screen, and emulating :o , and it DOES NOT eat 100% CPU, not even 50% :x
Please explain this.
Fred
Administrator
Administrator
Posts: 18350
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

When using the wait functionnality of DirectX, the CPU time is 100% and the program locks on this command (I don't have the prossibility to do anything else). There is probably some other (clever) way to do the same while keeping a perfect synchronization, but I didn't have found it for now.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Aha, Fred.
That's the answer i was waiting for.
But, uffhhh! I think you, or Freak, or anyone in PB developer team should work on it, because it is very important, and I'm sure that it is very easy to do, and i'm sure too that there are much programmers who know how to do it, because it is made in most of emulators as i have seen. :arrow: :arrow: :arrow: :arrow: :arrow: :arrow:

Surely, the trick remains in act (swap buffers) just when vsync IRQ happens.
I think when the FlipBuffers() function is reached, the thread process should be passed to the system inactive process, and when vsync happens then swap buffers and end the function. :arrow:
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

Generally, the display refresh rate could be from 60 to 120Hz. or 1/60 down to 1/120 second. DirectX function ::WaitForVerticalBlank is just to wait for the VSYNC interval, but it is by Polling. That is a little ridiculous. Since the windows system is not a real time operating system, that function consume a lot of CPU resource but sometimes it still misses VSYNC.

Here I use both multimedia Timer and DirectX function to track the vertical sync. The main point is starting a multimedia Timer with 2ms interval. In the multimedia Timer callback handler, use function ::GetScanLine to get the current scan line number, then judge whether it is the time to draw.
Quoted from
http://www.codeproject.com/gdi/tearingfreedrawing.asp
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

I insist about the importance of this.
Execute this example code and input firstly "1" for the 2 requests. Then ESCape and try again with max values for the 2 requests (switch off debugger).

Code: Select all

;   This example shows the way to make a "elastic perfectly collision" between 2 spheric (with masses
;centre same as geometrical centre) objects in the emptyness (no rub).
;Elastic perfectly collision in a closed system (closed system means no external forces but only the
;two ones of the both colliding objects forces) mean no lossing kinetic energy from the closed system.
;It is ideal, because in reality there is always a kinetic energy lossing in rub, deformation, rotation,
;heat, etc.
;   NOTE: decrease #lines constant and/or number of objects if your computer is too slow.
;   NOTE2: you can add external forces (like gravities between objects, or absolute to all)
;playing with dirX and dirY parameters.

;         2003-12-25 (Psychophanta)

;-INITS:
#PI=3.14159265
bitplanes.b=32:RX.w=1024:RY.w=768
If InitMouse()=0 Or InitSprite()=0 Or InitSprite3D()=0 Or InitKeyboard()=0
  MessageRequester("Error","Can't open DirectX",0)
  End
EndIf
Structure balls
  sprite.w;<-#Sprite
  x.f:y.f;<-coordenadas instantáneas
  dirX.f:dirY.f;<-Vector director
  HalfWidth.w:HalfHeight.w;<-centro geométrico
  Dimension.w
  Mass.f;<-masa
  Kinetic.f;<-energía cinética
EndStructure
NewList Capsule.balls():Cursor.balls
;-FUNCTIONS:
Procedure Shock0(*a.balls)
;*;al momento de chocar tenemos:
  ;ENTRADAS: Vectores directores de los movimientos ((dirX,dirY) para Objeto0 y (dirX,dirY) para Objeto1)
  ;          Masas de Objeto1 y de Objeto0 (Objeto0 Mass y Objeto1 Mass)
;*;Debemos calcular:
  ;SALIDAS: Nuevo vector director de los movimientos de Objeto1 y de Objeto0 ((dirX,dirY) de Objeto0 y (dirX,dirY) de Objeto1).

;*;CANTIDADES DE MOVIMIENTO:
;  MQBallX.f=Capsule()\dirX*Capsule()\Mass:MQBallY.f=Capsule()\dirY*Capsule()\Mass;<-cantidad de movimiento de Ball
;  MQCursorX.f=*a\dirX**a\Mass:MQCursorY.f=*a\dirY**a\Mass;<-cantidad de movimiento de Cursor
;  MQBall.f=Sqr(MQBallX.f*MQBallX.f+MQBallY.f*MQBallY.f)
;  MQCursor.f=Sqr(MQCursorX.f*MQCursorX.f+MQCursorY.f*MQCursorY.f)

;*;ENERGIAS CINETICAS:
  ;*a\Kinetic=(Pow(*a\dirX,2)+Pow(*a\dirY,2))**a\Mass/2;<-(m/2)*v^2 de Objeto0
  ;Capsule()\Kinetic=(Pow(Capsule()\dirX,2)+Pow(Capsule()\dirY,2))*Capsule()\Mass/2;<-(m/2)*v^2 de Objeto1

;*;COMPONENTES POR CONTACTO:
  ;Es un vector que está en la línea de choque (recta normal a la recta tangente, en el punto de contacto, sobre la superficie contra la que se choca).
  ;Obtenemos su sentido y dirección, que viene marcado por la diferencia de coordenadas de cada objeto al momento de chocar:
  DiffX.f=Capsule()\x-*a\x:DiffY.f=Capsule()\y-*a\y;<-rectángulo que forman las posiciones de los colisionantes
  Distance.f=Sqr(DiffX*DiffX+DiffY*DiffY)
  ;A continuación, su módulo, que depende de la de las cantidades de movimiento de ambos cuerpos:
  ;ModuloObjeto1.f=Sqr(1+Pow(*a\Mass/Capsule()\Mass,2));<-Módulo de la componente por contacto de Objeto1.
  ;ModuloObjeto0.f=Sqr(1+Pow(Capsule()\Mass/*a\Mass,2));<-Módulo de la componente por contacto de Objeto0.
  ModuloObjeto1.f=*a\Mass/(*a\Mass+Capsule()\Mass)
  ModuloObjeto0.f=Capsule()\Mass/(*a\Mass+Capsule()\Mass)
  ;Probar con:
  ;ModuloObjeto1.f=*a\Kinetic/(*a\Kinetic+Capsule()\Kinetic)
  ;ModuloObjeto0.f=Capsule()\Kinetic/(*a\Kinetic+Capsule()\Kinetic)
  ;y con:
  ;ModuloObjeto1.f=*a\Kinetic/Sqr(Pow(*a\Kinetic,2)+Pow(Capsule()\Kinetic,2))
  ;ModuloObjeto0.f=Capsule()\Kinetic/Sqr(Pow(*a\Kinetic,2)+Pow(Capsule()\Kinetic,2))
  ;y con:
  ;ModuloObjeto1.f=Sqr(Capsule()\Kinetic+*a\Kinetic)/2
  ;ModuloObjeto0.f=Sqr(Capsule()\Kinetic+*a\Kinetic)/2
  
;*;Calcular las COMPONENTES POR CONTACTO (un vector normal a la tangente
  ;en el punto de contacto con la superficie contra la que se choca); que tendrán sus módulo dados,
  ;añadiendo dichas componentes a las componentes de los vectores directores de los movimientos
  ;de Objeto0 y Objeto1
  Capsule()\dirX+DiffX*ModuloObjeto1/Distance.f
  Capsule()\dirY+DiffY*ModuloObjeto1/Distance.f
  *a\dirX-DiffX*ModuloObjeto0/Distance.f
  *a\dirY-DiffY*ModuloObjeto0/Distance.f
  
;*;Preservar distancia:
  Clip.f=(*a\Dimension/2+Capsule()\Dimension/2)-Distance.f
  If Clip.f>0:Clip.f/2
    Capsule()\x+DiffX.f*Clip.f/Distance.f
    Capsule()\y+DiffY.f*Clip.f/Distance.f
    *a\x-DiffX.f*Clip.f/Distance.f
    *a\y-DiffY.f*Clip.f/Distance.f
  EndIf
EndProcedure

Procedure Shock(*a.balls)
;*;al momento de chocar tenemos:
  ;ENTRADAS: Vectores directores de los movimientos ((dirX,dirY) para Objeto0 y (dirX,dirY) para Objeto1)
  ;          Masas de Objeto1 y de Objeto0 (Objeto0 Mass y Objeto1 Mass)
;*;Debemos calcular:
  ;SALIDAS: Nuevo vector director de los movimientos de Objeto1 y de Objeto0 ((dirX,dirY) de Objeto0 y (dirX,dirY) de Objeto1).

;*;CANTIDADES DE MOVIMIENTO:
;  MQBallX.f=Capsule()\dirX*Capsule()\Mass:MQBallY.f=Capsule()\dirY*Capsule()\Mass;<-cantidad de movimiento de Ball
;  MQCursorX.f=*a\dirX**a\Mass:MQCursorY.f=*a\dirY**a\Mass;<-cantidad de movimiento de Cursor
;  MQBall.f=Sqr(MQBallX.f*MQBallX.f+MQBallY.f*MQBallY.f)
;  MQCursor.f=Sqr(MQCursorX.f*MQCursorX.f+MQCursorY.f*MQCursorY.f)

;*;ENERGIAS CINETICAS:
  ;*a\Kinetic=(Pow(*a\dirX,2)+Pow(*a\dirY,2))**a\Mass/2;<-(m/2)*v^2 de Objeto0
  ;Capsule()\Kinetic=(Pow(Capsule()\dirX,2)+Pow(Capsule()\dirY,2))*Capsule()\Mass/2;<-(m/2)*v^2 de Objeto1

;*;COMPONENTES POR CONTACTO:
  ;Es un vector que está en la línea de choque (recta normal a la recta tangente, en el punto de contacto, sobre la superficie contra la que se choca).
  ;Obtenemos su sentido y dirección, que viene marcado por la diferencia de coordenadas de cada objeto al momento de chocar:
  DiffX.f=Capsule()\x-*a\x:DiffY.f=Capsule()\y-*a\y;<-Vector de distancia
  ;su módulo depende de la componente de la velocidad con respecto a la linea de choque (DiffX,DiffY) (proyección del vector (dirX,dirY) sobre el (DiffX,DiffY)):
  K.f=(DiffX.f**a\dirX+DiffY.f**a\dirY)/(DiffX.f*DiffX.f+DiffY.f*DiffY.f);<-constante de proyección del actual vector del movimiento de Cursor sobre la línea de choque.
  dirCursorXK.f=K.f*DiffX.f:dirCursorYK.f=K.f*DiffY.f;<-vector componente en línea de choque de la velocidad de Cursor.
  
  K.f=(-DiffX.f*Capsule()\dirX-DiffY.f*Capsule()\dirY)/(-DiffX.f*-DiffX.f+-DiffY.f*-DiffY.f);<-constante de proyección del actual vector del movimiento de Ball sobre la línea de choque.
  dirBallXK.f=K.f*-DiffX.f:dirBallYK.f=K.f*-DiffY.f;<-vector componente en línea de choque de la velocidad de Ball.
  
  CX.f=(2*Capsule()\Mass*dirBallXK.f+*a\Mass*dirCursorXK.f-Capsule()\Mass*dirCursorXK.f)/(*a\Mass+Capsule()\Mass)
  CY.f=(2*Capsule()\Mass*dirBallYK.f+*a\Mass*dirCursorYK.f-Capsule()\Mass*dirCursorYK.f)/(*a\Mass+Capsule()\Mass)
  BX.f=(2**a\Mass*dirCursorXK.f+Capsule()\Mass*dirBallXK.f-*a\Mass*dirBallXK.f)/(*a\Mass+Capsule()\Mass)
  BY.f=(2**a\Mass*dirCursorYK.f+Capsule()\Mass*dirBallYK.f-*a\Mass*dirBallYK.f)/(*a\Mass+Capsule()\Mass)
  
;*;Ahora obtener el vector resultante para el movimiento de Ball:
  ;Se mantiene la componente perpendicular a la linea de choque del movimiento de Ball (componente que no afecta al choque):
  Capsule()\dirX-dirBallXK.f+BX.f:Capsule()\dirY-dirBallYK.f+BY.f;<-se suman, obteniendo el vector buscado.
  
;*;Finalmente obtener el vector director resultante para el movimiento de Cursor:
  ;Se mantiene la componente perpendicular a la linea de choque del movimiento de Cursor (componente que no afecta al choque):
  *a\dirX-dirCursorXK.f+CX.f:*a\dirY-dirCursorYK.f+CY.f;<-se suman, obteniendo el vector buscado.
  
;*;Preservar distancia:
  Distance.f=Sqr(DiffX*DiffX+DiffY*DiffY)
  Clip.f=(*a\Dimension/2+Capsule()\Dimension/2)-Distance.f
  If Clip.f>0:Clip.f/2
    Capsule()\x+DiffX.f*Clip.f/Distance.f
    Capsule()\y+DiffY.f*Clip.f/Distance.f
    *a\x-DiffX.f*Clip.f/Distance.f
    *a\y-DiffY.f*Clip.f/Distance.f
  EndIf
EndProcedure

Procedure CreateBallSprite(c.l,size.l,color.l)
  CreateSprite(c,size,size,#PB_Sprite_Texture)
  StartDrawing(SpriteOutput(c))
  BackColor(0,0,0):R.w=color&$FF:G.w=color>>8&$FF:B.w=color>>16&$FF
  For t.l=size/2 To 1 Step -1
    R+160/size:G+160/size:B+160/size:If R>255:R=255:EndIf:If G>255:G=255:EndIf:If B>255:B=255:EndIf
    Circle(size/2,size/2,t,RGB(R,G,B))
  Next
  StopDrawing()
EndProcedure

;-MOREINITS:

BallsDiameter.l=76;Val(InputRequester("Input","Please input 2D spheres diameter in #pixels (max. "+Str(RX.w/3)+")",Str(76)))
Lines.l=Val(InputRequester("Input","Please input number of lines of 2D spheres (max. "+Str(RY.w/BallsDiameter.l)+")",Str(2)))
BallsPerLine.l=Val(InputRequester("Input","Please input number of 2D spheres per line (max. "+Str(RX.w/BallsDiameter.l)+")",Str(RX.w/BallsDiameter.l-1)));<-Número de Objeto1 por lineas
While OpenScreen(RX.w,RY.w,bitplanes.b,"Balls")=0
  If bitplanes.b>16:bitplanes.b-8
  ElseIf RY.w>600:RX.w=800:RY.w=600
  ElseIf RY.w>480:RX.w=640:RY.w=480
  ElseIf RY.w>400:RX.w=640:RY.w=400
  ElseIf RY.w>240:RX.w=320:RY.w=240
  ElseIf RY.w>200:RX.w=320:RY.w=200
  Else:MessageRequester("VGA limitation","Can't open Screen!",0):End
  EndIf
Wend
Cursor\sprite=0
CreateBallSprite(Cursor\sprite,64,$009eae)
CreateSprite3D(Cursor\sprite,Cursor\sprite):ZoomSprite3D(Cursor\sprite,64,64)
Cursor\HalfWidth=SpriteWidth(Cursor\sprite)/2:Cursor\HalfHeight=SpriteHeight(Cursor\sprite)/2;<-centro de Objeto0
Cursor\Dimension=(SpriteWidth(Cursor\sprite)+SpriteHeight(Cursor\sprite))/2
Cursor\Mass=4*#PI*Pow(Cursor\Dimension/2,3)/3;<-masa de Objeto0

For g.b=1 To Lines.l
  For t.w=1 To BallsPerLine.l
    AddElement(Capsule())
    Capsule()\sprite=t
    CreateBallSprite(Capsule()\sprite,BallsDiameter.l,$6dae00)
    CreateSprite3D(Capsule()\sprite,Capsule()\sprite):ZoomSprite3D(Capsule()\sprite,BallsDiameter.l,BallsDiameter.l)
    Capsule()\HalfWidth=SpriteWidth(Capsule()\sprite)/2:Capsule()\HalfHeight=SpriteHeight(Capsule()\sprite)/2;<-centro de Objeto1
    Capsule()\Dimension=(SpriteWidth(Capsule()\sprite)+SpriteHeight(Capsule()\sprite))/2
    Capsule()\Mass=4*#PI*Pow(Capsule()\Dimension/2,3)/3;<-masa de Objeto1
    Capsule()\x=t.w*BallsDiameter.l-Capsule()\HalfWidth+((RX.w-BallsDiameter.l*BallsPerLine.l)/2);<-Posición inicial X
    Capsule()\y=g.b*BallsDiameter.l-Capsule()\HalfHeight;<-Posición inicial Y
  Next
Next
mouseX=RX.w/2:mouseY=RY.w*4/5
Cursor\x=mouseX:Cursor\y=mouseY
MouseLocate(mouseX,mouseY);<-Posición inicial de Objeto0
Cursor\dirX=Random(1000000)/100000-5:Cursor\dirY=-Random(4000000)/1000000-1

;-MAIN:
Repeat
  ExamineKeyboard()
  ExamineMouse()
  ClearScreen(0,0,0)
  ; Cursor position and vector:
  prevmouseX=mouseX:prevmouseY=mouseY;<-previous mouse coordinates
  mouseX=MouseX():mouseY=MouseY();<-current mouse coordinates
  If mouseX<>prevmouseX Or mouseY<>prevmouseY;If mouse is moved:
    Cursor\dirX=mouseX-prevmouseX:Cursor\dirY=mouseY-prevmouseY;<-vector director del movimiento de Cursor
    mouseX=Cursor\x+Cursor\dirX:mouseY=Cursor\y+Cursor\dirY;<-actualizamos las coordenadas del mouse
    MouseLocate(mouseX,mouseY);<-y reposicionamos el mouse en las actuales coordenadas de Cursor
  EndIf
  Cursor\x+Cursor\dirX:Cursor\y+Cursor\dirY;<-que son estas (se suma el vector director a las anteriores)
  ; Cursor-Screen limits:
  If Cursor\x<=0:Cursor\dirX=Abs(Cursor\dirX):EndIf
  If Cursor\x>=RX.w:Cursor\dirX=-Abs(Cursor\dirX):EndIf
  If Cursor\y<=0:Cursor\dirY=Abs(Cursor\dirY):EndIf
  If Cursor\y>=RY.w:Cursor\dirY=-Abs(Cursor\dirY):EndIf
  Start3D()
  ForEach Capsule()
  ; Ball-Screen limits:
    If Capsule()\x<=0:Capsule()\dirX=Abs(Capsule()\dirX):EndIf
    If Capsule()\x>=RX.w:Capsule()\dirX=-Abs(Capsule()\dirX):EndIf
    If Capsule()\y<=0:Capsule()\dirY=Abs(Capsule()\dirY):EndIf
    If Capsule()\y>=RY.w:Capsule()\dirY=-Abs(Capsule()\dirY):EndIf
  ; Ball-moving:
    Capsule()\x+Capsule()\dirX:Capsule()\y+Capsule()\dirY;<-Coordenadas actuales de Ball (se suma a las anteriores el vector director)

  ; collision:
    *i.balls=@Capsule()
    While NextElement(Capsule())
      If SpritePixelCollision(*i\sprite,*i\x-*i\HalfWidth,*i\y-*i\HalfHeight,Capsule()\sprite,Capsule()\x-Capsule()\HalfWidth,Capsule()\y-Capsule()\HalfHeight);Si hay colisión:
        Shock(*i)
      EndIf
    Wend
    ChangeCurrentElement(Capsule(),*i)
    If SpritePixelCollision(Cursor\sprite,Cursor\x-Cursor\HalfWidth,Cursor\y-Cursor\HalfHeight,*i\sprite,*i\x-*i\HalfWidth,*i\y-*i\HalfHeight);Si hay colisión:
      Shock(@Cursor)
    EndIf
    DisplaySprite3D(*i\sprite,*i\x-*i\HalfWidth,*i\y-*i\HalfHeight,255)
  Next
  DisplaySprite3D(Cursor\sprite,Cursor\x-Cursor\HalfWidth,Cursor\y-Cursor\HalfHeight,255)
  Stop3D()
  FlipBuffers()
Until KeyboardPushed(#PB_Key_All)
CloseScreen()
End
If you chack the CPU % use in the first time and compare to the second time, you will see it is the same 8O 8O 8O
Do you think that the first execution eats 100% CPU and 2nd 100% too? That is simply a deceit.

I am not sure if wilbert method is the best or the right one.

What I can and must say to Fred is that blueMSX, MAME32, and ZSNES are optimal in this ESSENTIAL matter, and all three are OPEN SOURCE.
I've wrote to blueMSX main author about this quest, but surely and logically he will answer me something like: sorry, i have no time to teach; blueMSX is OPENSOURCE software.

Sorry my insistence, Fred, but you must consider this matter as crucial if you want that PB is good for 2D and 3D graphics performance.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3943
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

Psychophanta, your code example crashes on my computer (win xp sp2). :(

A little off topic...
I never heard of blueMSX, thought RuMSX was the most advanced emulator. My favorites were the MSX games made by Konami. Amazing what they could do with 16KB (KnightMare) or 32KB (The Goonies). And of course MSX Basic... One MicroSoft product that wasn't buggy :wink:
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

About blueMSX; is now the best MSX emulator, RuMSX and fMSX are worse than ParaMSX and NLMSX, and these two are worse than blueMSX. If you doubt on my words, then compare yourself.
And of course MSX Basic... One MicroSoft product that wasn't buggy
Yeah. Perhaps because B. Gates brain was more clean to do well done things than after 1983, and so on, hehehe :twisted:

By the way, with MSX basic (year 1983 Microsoft basic interpreter) i can do this:

Code: Select all

10 A=2.46779045923852834394506004705067054*350959858377373743848834834883
20 PRINT A
the result is shown as scientific notation. This is:
8.6609539007937E+29

...and Z80 have no FPU :o . This is for Fred too, and perhaps i write another post thread saying this. :P :twisted:
Psychophanta, your code example crashes on my computer (win xp sp2).
Very, very strange. I use WinXP SP2 too. I guess you have a PBlib which cause problems or something. :?
Anyway, never mind.








I just want Fred and PB comunity to understand that this matter treated in this post is essential for a well optimiced final code for graphics.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Fred, I have just receved the Daniel Vik (blueMSX main author) answer.
Sure it is a good help.
The reason why some DirectX apps use 100% cpu is actually not because of
DirectX itself. In normal windows apps, the message loop looks something
like:
while (GetMessage(&msx, NULL, 0, 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

With this method you will not get the good timing required for DirectX
apps to run smoothly.
A common practice in DirectX apps is to busy wait in the main message loop
in order to get more accurate timing. This is recommended in most DirectX
getting started books and for a game that runs in fullscreen it is ok to
use 100% cpu since no other apps needs to get much response. They do it
like this:

for (;;) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
time = getTime();
if (time >= frameTime) {
drawFrame();
}
}

The getTime() method (has to be implemented) uses the high performance
counters to get a high resolution time stamp. If it is time to draw a
frame it does it otherwise, the loop continues. The PeekMessage function
returns immediately if no windows message has arrived so most of the cpu
time is spent spinning in this loop.

In blueMSX I changed this loop to not busy wait using PeekMessage. To get
the accurate timing I have a 1 ms timer that sets an event which breaks
the blocking message call MsgWaitForMultipleObjects and the loop is
something like:

while (!doExit) {
DWORD rv = MsgWaitForMultipleObjects(1, &Prt.ddrawEvent, FALSE,
INFINITE, QS_ALLINPUT);
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
doExit = 1;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (rv == WAIT_OBJECT_0) { // The 1ms timer expired...
time = getTime();
if (time >= frameTime) {
drawFrame();
}
}
}

So what happens is that the Message receive method is intercepted every 1
ms and then I do the checks if the DirectX frame needs to be redrawn. This
adds only a little overhead to the common message loop used in regular
windows apps but i gets pretty much the same good accuracy as the busy
wait one.


On top of this I use two threads. One that does the directx drawing and
one that does all the emulation. This actually gives some performance
gains since as you said, the flip and other directx commands take some
time. But the time spent in directx waits are not that long though so
running everything in one thread also works ok.

I hope this explanation was not too confusing ;)

Take care,
Daniel
This is complex to perform in a function in PB, because this uses 1 thread dedicated only to perform DirectX drawing. And on the other hand it creates a 1ms. timer to use it to check needs of DX drawings instead to check it continuosly.
I'm sure that it is possible to do it still better. :?
TheBeck
User
User
Posts: 39
Joined: Mon May 12, 2003 6:04 am
Location: the far west
Contact:

Post by TheBeck »

This code will add a dynamic delay so FlipBuffer() will be called just before 1/60 second. The delay will not be added at all if the frame takes > 1/60 second to compute.

Change both 16 in the code below to 1000/refresh rate to make this code work at different refresh rates. Always round the number down to the nearest whole number, and if it already is a whole number or real close to one, subtract 1 more.

Oh, I am at work so if the code contains bugs or doesn't work, sorry.

Code: Select all

While exit = 0

; do everything here

;*** keep this code together
timer = ElapsedMilliseconds() - timer
If timer < 16
  delay(16 - timer)
EndIf
FlipBuffer()
timer = ElapsedMilliseconds()
Wend
;*** keep this code together
Fred:
Maybe you can do something like this in the FlipBuffer() procedure. ;)
Nathan Beckstrand -- XPSP2, AMD Athlon XP 3000+, GF2 GTS, 512MB RAM
The_Pharao
User
User
Posts: 57
Joined: Sun Jan 04, 2004 2:11 pm

Post by The_Pharao »

there is no reason to limit the frame rate in a fullscreen game, IMHO.
more frames -> more cpu usage -> more heat.... ok.
but also means -> smoot movement

everybody who plays first person shooters knows there's a BIIIIIG difference between 30fps, 60fps or 120fps!

of course there are games that do not need to display > 30 frames a second :wink:
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

TheBeck, that method is of course better than the used now in PB (probed and it works :wink: ), but there are still wasted time.

wilbert, try to update your VGA drivers.

The_Pharao, I am agree with your post, but we are not talking about limit or extend the frame rate, but about to optimize and save CPU resources.



Is it possible to patch vsync display Interrupt Service Routine in Windows? This should be the best and perfect solution for FlipBuffers().
I mean; if the Default ISR for Vsync in Windows is:

Code: Select all

Windows_VSYNC_ISR: code a
                   code b
                   ...code c...
                   RETurn from ISR
then patch it and set up as:

Code: Select all

Windows_VSYNC_ISR: FLIP the Screen Buffers
                   CALL [our drawing code]
                   code a
                   code b
                   ...code c...
                   RETurn from ISR

I asked to Daniel Vik if it possible to do an even better job for freeing the wasted time in waiting for swap buffers, and he answered.
I think it would be possible to do an even better job. I think there are
some waiting in the DirectX calls that probably could be avoided by using
the No Delay option. That requires some more knowledge about DirectX
though. In blueMSX at least there are some commands that are issued after
each other so only the last one (which is the flip) can be done with the
No Delay option. I'm not sure how this can be made more effective.
Fred, At least keep in mind the TheBeck solution :idea: :!:
Fred
Administrator
Administrator
Posts: 18350
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

The problem with GetElapsedMilliseconds() is its inaccuracy. It's more or less 20 ms on a Windows system, which means you will probably won't be accurate on the flip. That's why an high resolution timer is used in the blueMSX author answer. I will try to find more infos on this subject.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thanks to listen, Fred :D
Post Reply