Παρασκευή 16 Δεκεμβρίου 2011

DirectX3d 10 part 4 ( game/render timer )

Render time / Game time


Στα προηγούμενα παραδείγματα έβγαζα τα frame με βάση vsync. Vsync, συγχρονίζει τα frame με βάση τα Hz της οθόνης. Πχ 60Hz οθόνη, το πρόγραμμα θα τρέχει με 60 FPS, οθόνη 100Hz το πρόγραμμα τρέχει με 100FPS κλπ.



Αυτό προκαλεί ενα σοβαρό πρόβλημα. Η ταχύτητα των κινήσεων είναι εξαρτώμενη από τα Hz της οθόνης. Για να το λύσουμε αυτο πρέπει να φτιάξουμε μια ρουτίνα που θα πυροδοτεί την render με βαση δικό μας Δt.

Για κάτι τέτοιο θέλουμε δυο πράγματα.
1) Να μην έχουμε blocking functions
2) Κάποιες συναρτήσεις που μετράνε το χρόνο με μεγάλη ακρίβεια

Για το 1 αντί της GetMessage θα χρησιμοποιήσουμε την PeekMessage, και στο swap των back-front buffers θα βγάλουμε το vsync (swapchain::present(0,0)).

Για το 2 θα χρησιμοποιήσω
QueryPerformanceCounter   -> C
QueryPerformanceFrequency  -> F
χωρίς να σημαίνει οτι είναι μονόδρομος. ( C,F ειναι συντομογραφία για να εξηγήσω πως βγαίνει το fps)

FPS
Για να βγει το fps με τις παραπάνω συναρτήσεις πρέπει Δc > 1/fps =>
(C2 - C1) / F > 1/fps αν ισχύει αυτό βγάζεις frame αν όχι δεν βγάζεις :p

Για να μετρήσεις τα frames ώστε να ξέρεις σε πόσα fps είσαι

Fps = 1 / (Δc / F)  => fps = 1/ ((C2-C1)/F)

* Όπου C1 τωρινή στιγμή, C2 τελευταίο frame.

Να και το καινούριο message loop

int MainLoop()
{
 MSG msg;
 LARGE_INTEGER liCur,liFreq,liFpsCounter;
 LARGE_INTEGER liLastSys ={0};
 LARGE_INTEGER liLastFrame = {0};
 long frameCount =0;

 QueryPerformanceCounter(&liFpsCounter);
 for(;;)
 {
  //window part
  if ( PeekMessage(&msg,0,0,0,PM_REMOVE) > 0)
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
   if(msg.message == WM_QUIT)
    return msg.lParam;
   continue;
  }
  //dx part
  else 
  {
   if(!QueryPerformanceCounter(&liCur)
    || !QueryPerformanceFrequency(&liFreq)
    || !liFreq.QuadPart)
   {
    AppCriticalError("Timer Error");
    continue;
   }
   
   if( double(liCur.QuadPart - liLastSys.QuadPart) / double(liFreq.QuadPart)  > 1.0 / g_SysFreq ) 
   // call sysproc
   {
    if(!QueryPerformanceCounter(&liLastSys)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderSys();
   }

   if((double(liCur.QuadPart - liLastFrame.QuadPart) / double(liFreq.QuadPart) ) >  1.0 / g_FPS ) 
   {
    frameCount++;
    if(!QueryPerformanceCounter(&liLastFrame)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderFrame();
    //calc fps
    if(frameCount == 20)
    {
     g_CurFPS=
      20.0/ (double(liCur.QuadPart - liFpsCounter.QuadPart) / double(liFreq.QuadPart));
     QueryPerformanceCounter(&liFpsCounter);
     frameCount = 0;
    }
    continue;
   }
  }
  Sleep(1);
 }

 return 0;
}
.


Τώρα έχουμε δυο renders έναν για το frame εκεί που ζωγραφίζουμε, και ένα για το σύστημα. -Ο λόγος που έχουμε δυο renders θα το δείτε στη συνέχεια του project-

Το καινούριο project στο οποίο θα βασιστούν τα επόμενα


Main.cpp
#include "dxandwindow.h"
#include "InformationLable.hpp" 



//config
int       Height = 600;
int       Width = 800;
double      g_FPS = 100.0;
double      g_SysFreq = 100.0;


//globals
LPCWSTR lpszWndClsName  = L"{BAF17182-20D1-46EB-8CAB-B4AAF5388552}";
LPCWSTR lpszWndTitle = L"Hi2!";
double      g_CurFPS;
HWND      g_Wnd;

ID3D10Device    *pDevice;
IDXGISwapChain    *pSwapChain;
ID3D10RenderTargetView  *pRenderView;

InformationLable   *pInformationLable;

HRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
void AppCriticalError(LPCSTR msg);
int MainLoop();

int InitDevice(HWND hWnd,int w,int h);
int InitModels();
int ReleaseDevice();
int ReleaseModels();
void RenderFrame();
void RenderSys();

//Window app begin
int WINAPI wWinMain(HINSTANCE hInst,HINSTANCE,LPWSTR,int nShow)
{
 WNDCLASS wc;
 int   res;
 ZeroMemory(&wc,sizeof(wc));

 wc.lpfnWndProc  = WndProc;
 wc.lpszClassName = lpszWndClsName;
 wc.hCursor   = LoadCursor(NULL,IDC_ARROW);


 if(!RegisterClass(&wc))
  return 1;
 g_Wnd = CreateWindow(lpszWndClsName,lpszWndTitle,
  WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,Width,Height,
  NULL,NULL,hInst,NULL);
 if(!g_Wnd)
  return 1;
 if(InitDevice(g_Wnd,Width,Height)
  ||InitModels()
  )
  return 1;
 ShowWindow(g_Wnd, nShow);
 res = MainLoop();
 ReleaseModels();
 ReleaseDevice();
 return res;
}
HRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
 case WM_CLOSE:
  PostQuitMessage(0);
  break;
 default:
  return DefWindowProc(hWnd,msg,wParam,lParam);
 }
 return 0;
}
void AppCriticalError(LPCSTR msg)
{ 
 MessageBoxA(g_Wnd,msg,"Critical Error",MB_ICONERROR);
 SendMessage(g_Wnd,WM_QUIT,0,0);
}
//Window app end

//Dx begin
int MainLoop()
{
 MSG msg;
 LARGE_INTEGER liCur,liFreq,liFpsCounter;
 LARGE_INTEGER liLastSys ={0};
 LARGE_INTEGER liLastFrame = {0};
 long frameCount =0;

 QueryPerformanceCounter(&liFpsCounter);
 for(;;)
 {
  //window part
  if ( PeekMessage(&msg,0,0,0,PM_REMOVE) > 0)
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
   if(msg.message == WM_QUIT)
    return msg.lParam;
   continue;
  }
  //dx part
  else 
  {
   if(!QueryPerformanceCounter(&liCur)
    || !QueryPerformanceFrequency(&liFreq)
    || !liFreq.QuadPart)
   {
    AppCriticalError("Timer Error");
    continue;
   }
   
   if( double(liCur.QuadPart - liLastSys.QuadPart) / double(liFreq.QuadPart)  > 1.0 / g_SysFreq ) 
   // call sysproc
   {
    if(!QueryPerformanceCounter(&liLastSys)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderSys();
   }

   if((double(liCur.QuadPart - liLastFrame.QuadPart) / double(liFreq.QuadPart) ) >  1.0 / g_FPS ) 
   {
    frameCount++;
    if(!QueryPerformanceCounter(&liLastFrame)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderFrame();
    //calc fps
    if(frameCount == 20)
    {
     g_CurFPS=
      20.0/ (double(liCur.QuadPart - liFpsCounter.QuadPart) / double(liFreq.QuadPart));
     QueryPerformanceCounter(&liFpsCounter);
     frameCount = 0;
    }
    continue;
   }
  }
  Sleep(1);
 }

 return 0;
}
int InitDevice(HWND hWnd,int w,int h)
{

 ID3D10Texture2D    *pBackBuffer;
 DXGI_SWAP_CHAIN_DESC  swapChainDesc;

 
 ZeroMemory(&swapChainDesc,sizeof(swapChainDesc));
 swapChainDesc.BufferCount   = 1;
 swapChainDesc.BufferUsage   = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 swapChainDesc.SampleDesc.Count  = 1;
 swapChainDesc.OutputWindow   = hWnd;
 swapChainDesc.Windowed    = true;

 swapChainDesc.BufferDesc.Height  = h;
 swapChainDesc.BufferDesc.Width  = w;
 swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
 swapChainDesc.BufferDesc.Format  = DXGI_FORMAT_R8G8B8A8_UNORM;

 if(FAILED(D3D10CreateDeviceAndSwapChain(
  NULL,D3D10_DRIVER_TYPE_HARDWARE,NULL,D3D10_CREATE_DEVICE_DEBUG,
  D3D10_SDK_VERSION,&swapChainDesc,&pSwapChain,&pDevice)))
 {
  return 1;
 }

 if(FAILED(pSwapChain->GetBuffer(0,__uuidof(ID3D10Texture2D),(void**)&pBackBuffer)))
 {
  pSwapChain->Release();
  pDevice->Release();
  return 1;
 }
 
 if(FAILED(pDevice->CreateRenderTargetView(pBackBuffer,0,&pRenderView)))
 {
  pSwapChain->Release();
  pDevice->Release();
  pBackBuffer->Release();
  return 1;
 }
 pBackBuffer->Release();

 pDevice->OMSetRenderTargets(1,&pRenderView,NULL);
 
 D3D10_VIEWPORT vp;
 vp.Height = h;
 vp.Width = w;
 vp.MaxDepth = 1.0f;
 vp.MinDepth = 0.0f;
 vp.TopLeftX = 0;
 vp.TopLeftY = 0;
 pDevice->RSSetViewports(1,&vp);
 
 return 0;
}
int ReleaseDevice()
{
 pRenderView->Release();
 pSwapChain->Release();
 pDevice->Release();
 return 0;
}


int InitModels()
{
 try
 {
  pInformationLable = new InformationLable(double(Width)/3.0 ,0,D3DXCOLOR(1.0f,1.0f,1.0f,1.0f),pDevice);
 }
 catch(std::exception e)
 {
  MessageBoxA(NULL,e.what(),"Error",0);
  return 1;
 }
 
 return 0;
}
int ReleaseModels()
{
 delete pInformationLable;
 return 0;
}

void RenderSys()
{
 pInformationLable->Text()
  <<L"FPS:  "
  <<g_CurFPS;
}
void RenderFrame()
{
 pDevice->ClearRenderTargetView(pRenderView,D3DXCOLOR(0.0f,0.0f,0.30f,1.0f));
 pInformationLable->Render();
 pSwapChain->Present(0,0);
}
//dx end
. dxandwindow.h
#pragma once
#include <Windows.h>
#include <exception>
#include </Program Files/Microsoft DirectX SDK (June 2008)/Include/D3D10.h>
#include </Program Files/Microsoft DirectX SDK (June 2008)/Include/D3DX10.h>
#pragma comment ( lib,"/Program Files/Microsoft DirectX SDK (June 2008)/Lib/x86/d3d10.lib")
#pragma comment ( lib,"/Program Files/Microsoft DirectX SDK (June 2008)/Lib/x86/d3dx10.lib")
InformationLable.hpp
#pragma once
#include "dxandwindow.h"

#include <sstream>
class InformationLable
{
 ID3D10Device  *pRefDev;
 ID3DX10Font   *pFont;
 std::wstringstream curText;
 RECT    rc;
 D3DXCOLOR   color;

 ID3D10RasterizerState *pRsSolid;
  
public:
 InformationLable(int x,int y,D3DXCOLOR color,ID3D10Device *pDevice)
  :color(color),pRefDev(pDevice)
 {
  pRefDev->AddRef();
  D3DX10_FONT_DESC fontDesc;
  ZeroMemory(&rc,sizeof(rc));
  ZeroMemory(&fontDesc,sizeof(fontDesc));
  rc.top   = y;
  rc.left   = x;
  fontDesc.Height = 25;
  ::wcscpy_s(fontDesc.FaceName,L"Arial");
  if(FAILED(D3DX10CreateFontIndirect(pDevice,&fontDesc,&pFont)))
   throw std::exception("InformationLable failed");
  
  D3D10_RASTERIZER_DESC rsDesc;
  ZeroMemory(&rsDesc,sizeof(rsDesc));
  rsDesc.CullMode  = D3D10_CULL_BACK;
  rsDesc.FillMode  = D3D10_FILL_SOLID;
  pDevice->CreateRasterizerState(&rsDesc,&pRsSolid);
 }

 ~InformationLable()
 {
  pFont->Release();
  pRsSolid->Release();
  pRefDev->Release();
 }
 std::wstringstream& Text()
 {
  curText.str(L"");
  return curText;
 }
 void Render()
 {
  pRefDev->RSSetState(pRsSolid);
  pFont->DrawTextW(NULL,curText.str().c_str(),-1,&rc,DT_NOCLIP,color);
 }
};

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου