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
Στα προηγούμενα παραδείγματα έβγαζα τα 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 στο οποίο θα βασιστούν τα επόμενα
#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); } };
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου