static bool compositor_active(MPGLContext *ctx) { // For Windows 7. BOOL enabled = 0; if (FAILED(DwmIsCompositionEnabled(&enabled)) || !enabled) return false; // This works at least on Windows 8.1: it returns an error in fullscreen, // which is also when we get consistent timings without DwmFlush. Might // be cargo-cult. DWM_TIMING_INFO info = { .cbSize = sizeof(DWM_TIMING_INFO) }; if (FAILED(DwmGetCompositionTimingInfo(0, &info))) return false; // Test if a program is running in exclusive fullscreen mode. If so, it's // probably this one, so it's not getting redirected by the compositor. if (mp_w32_is_in_exclusive_mode()) return false; return true; } static void w32_swap_buffers(MPGLContext *ctx) { struct w32_context *w32_ctx = ctx->priv; SwapBuffers(w32_ctx->hdc); // default if we don't DwmFLush int new_swapinterval = w32_ctx->opt_swapinterval; if (ctx->dwm_flush_opt >= 0) { if ((ctx->dwm_flush_opt == 1 && !ctx->vo->opts->fullscreen) || (ctx->dwm_flush_opt == 2) || (ctx->dwm_flush_opt == 0 && compositor_active(ctx))) { if (DwmFlush() == S_OK) new_swapinterval = 0; } } if (new_swapinterval != w32_ctx->current_swapinterval && w32_ctx->real_wglSwapInterval) { w32_ctx->real_wglSwapInterval(new_swapinterval); MP_VERBOSE(ctx->vo, "set SwapInterval(%d)\n", new_swapinterval); } w32_ctx->current_swapinterval = new_swapinterval; } static int w32_control(MPGLContext *ctx, int *events, int request, void *arg) { return vo_w32_control(ctx->vo, events, request, arg); } const struct mpgl_driver mpgl_driver_w32 = { .name = "w32", .priv_size = sizeof(struct w32_context), .init = w32_init, .reconfig = w32_reconfig, .swap_buffers = w32_swap_buffers, .control = w32_control, .uninit = w32_uninit, };
HRESULT DemoApp::Initialize() { HRESULT hr; //register window class WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = DemoApp::WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(LONG_PTR); wcex.hInstance = HINST_THISCOMPONENT; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.lpszClassName = L"D2DDemoApp"; RegisterClassEx(&wcex); hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Create the application window. // // Because the CreateWindow function takes its size in pixels, we // obtain the system DPI and use it to scale the window size. FLOAT dpiX, dpiY; m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); m_hwnd = CreateWindow( L"D2DDemoApp", L"D2D Simple Path Animation Sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_cast<UINT>(ceil(512.f * dpiX / 96.f)), static_cast<UINT>(ceil(512.f * dpiY / 96.f)), NULL, NULL, HINST_THISCOMPONENT, this ); hr = m_hwnd ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { float length = 0; hr = m_pPathGeometry->ComputeLength( NULL, //no transform &length ); if (SUCCEEDED(hr)) { m_Animation.SetStart(0); //start at beginning of path m_Animation.SetEnd(length); //length at end of path m_Animation.SetDuration(5.0f); //seconds ZeroMemory(&m_DwmTimingInfo, sizeof(m_DwmTimingInfo)); m_DwmTimingInfo.cbSize = sizeof(m_DwmTimingInfo); // Get the composition refresh rate. If the DWM isn't running, // get the refresh rate from GDI -- probably going to be 60Hz if (FAILED(DwmGetCompositionTimingInfo(NULL, &m_DwmTimingInfo))) { HDC hdc = GetDC(m_hwnd); m_DwmTimingInfo.rateCompose.uiDenominator = 1; m_DwmTimingInfo.rateCompose.uiNumerator = GetDeviceCaps(hdc, VREFRESH); ReleaseDC(m_hwnd, hdc); } ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } } return hr; }
HRESULT DemoApp::Initialize() { HRESULT hr; //register window class WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = DemoApp::WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(LONG_PTR); wcex.hInstance = HINST_THISCOMPONENT; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.lpszClassName = L"App"; RegisterClassEx(&wcex); hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Create the application window. m_hwnd = CreateWindow( L"App", L"달걀 채색 공장(201001622 이진용)", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1200, 600, NULL, NULL, HINST_THISCOMPONENT, this ); hr = m_hwnd ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { float length = 0; hr = m_wheelPath->ComputeLength( NULL, //no transform &length ); if (SUCCEEDED(hr) ) { m_Animation.SetStart(0); //start at beginning of path m_Animation.SetEnd(length); //length at end of path m_Animation.SetDuration(5.f ); //seconds ZeroMemory(&m_DwmTimingInfo, sizeof(m_DwmTimingInfo)); m_DwmTimingInfo.cbSize = sizeof(m_DwmTimingInfo); // Get the composition refresh rate. If the DWM isn't running, // get the refresh rate from GDI -- probably going to be 60Hz if (FAILED(DwmGetCompositionTimingInfo(NULL, &m_DwmTimingInfo))) { HDC hdc = GetDC(m_hwnd); m_DwmTimingInfo.rateCompose.uiDenominator = 1; m_DwmTimingInfo.rateCompose.uiNumerator = GetDeviceCaps(hdc, VREFRESH); ReleaseDC(m_hwnd, hdc); } ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } if (SUCCEEDED(hr)) { float pLength = 0; hr = m_flyPath->ComputeLength( NULL, //no transform &pLength ); if (SUCCEEDED(hr) ) { m_linearAnimation.SetStart(0); //start at beginning of path m_linearAnimation.SetEnd(pLength); //length at end of path m_linearAnimation.SetDuration(10.f); //seconds ZeroMemory(&m_DwmTimingInfo, sizeof(m_DwmTimingInfo)); m_DwmTimingInfo.cbSize = sizeof(m_DwmTimingInfo); // Get the composition refresh rate. If the DWM isn't running, // get the refresh rate from GDI -- probably going to be 60Hz if (FAILED(DwmGetCompositionTimingInfo(NULL, &m_DwmTimingInfo))) { HDC hdc = GetDC(m_hwnd); m_DwmTimingInfo.rateCompose.uiDenominator = 1; m_DwmTimingInfo.rateCompose.uiNumerator = GetDeviceCaps(hdc, VREFRESH); ReleaseDC(m_hwnd, hdc); } ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } } //장치 객체 생성 IDirectSound8* directSound; DirectSoundCreate8(NULL,&directSound, NULL); //협력레벨 지정 directSound->SetCooperativeLevel(m_hwnd,DSSCL_PRIORITY); //주버퍼 포맷 지정 DSBUFFERDESC dsbd; ZeroMemory(&dsbd,sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; //IDirectSoundBuffer* pDSBPrimary = NULL; directSound->CreateSoundBuffer(&dsbd, &pDSBPrimary, NULL); WAVEFORMATEX wfx; ZeroMemory(&wfx, sizeof(WAVEFORMATEX) ); wfx.wFormatTag = (WORD)WAVE_FORMAT_PCM; wfx.nChannels =(WORD)2; wfx.nSamplesPerSec = (DWORD)22050; wfx.wBitsPerSample = (WORD)16; wfx.nBlockAlign = (WORD)(wfx.wBitsPerSample / 8 * wfx.nChannels ); wfx.nAvgBytesPerSec = (DWORD)(wfx.nSamplesPerSec * wfx.nBlockAlign ); pDSBPrimary->SetFormat(&wfx); pDSBPrimary->Release(); //2차 버퍼 생성 BGM CWaveFile* pWaveFile = NULL; pWaveFile = new CWaveFile(); pWaveFile->Open( L".\\pokemonCenter.wav", NULL, WAVEFILE_READ ); //DSBUFFERDESC dsbd; ZeroMemory(&dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_CTRLVOLUME; dsbd.guid3DAlgorithm = GUID_NULL; dsbd.lpwfxFormat = pWaveFile->m_pwfx; dsbd.dwBufferBytes = pWaveFile->GetSize(); directSound->CreateSoundBuffer(&dsbd, &pDSBuffer, NULL); //이차버퍼에 데이터 채우기 sound = new CSound(&pDSBuffer, dsbd.dwBufferBytes, 1 , pWaveFile, 0 ); // pDSBuffer 위에 전역변수임 pDSBPrimary->SetVolume(scaledVolume); sound->Play(0,DSBPLAY_LOOPING); soundManager = new CSoundManager; if( ! soundManager->init(m_hwnd) ) return FALSE; int id; if (! soundManager->add(L".\\boss.wav", &id)) //id=0부터 시작함. return FALSE; if (! soundManager->add(L".\\storm.wav",&id)); return hr; }
/** Blocks the CPU to synchronize with vblank by communicating with DWM. */ void FD3D11Viewport::PresentWithVsyncDWM() { #if D3D11_WITH_DWMAPI LARGE_INTEGER Cycles; DWM_TIMING_INFO TimingInfo; // Find out how long since we last flipped and query DWM for timing information. QueryPerformanceCounter(&Cycles); FMemory::MemZero(TimingInfo); TimingInfo.cbSize = sizeof(DWM_TIMING_INFO); DwmGetCompositionTimingInfo(WindowHandle, &TimingInfo); uint64 QpcAtFlip = Cycles.QuadPart; uint64 CyclesSinceLastFlip = Cycles.QuadPart - LastFlipTime; float CPUTime = FPlatformTime::ToMilliseconds(CyclesSinceLastFlip); float GPUTime = FPlatformTime::ToMilliseconds(TimingInfo.qpcFrameComplete - LastCompleteTime); float DisplayRefreshPeriod = FPlatformTime::ToMilliseconds(TimingInfo.qpcRefreshPeriod); // Find the smallest multiple of the refresh rate that is >= 33ms, our target frame rate. float RefreshPeriod = DisplayRefreshPeriod; if (RHIConsoleVariables::bForceThirtyHz && RefreshPeriod > 1.0f) { while (RefreshPeriod - (1000.0f / 30.0f) < -1.0f) { RefreshPeriod *= 2.0f; } } // If the last frame hasn't completed yet, we don't know how long the GPU took. bool bValidGPUTime = (TimingInfo.cFrameComplete > LastFrameComplete); if (bValidGPUTime) { GPUTime /= (float)(TimingInfo.cFrameComplete - LastFrameComplete); } // Update the sync counter depending on how much time it took to complete the previous frame. float FrameTime = FMath::Max<float>(CPUTime, GPUTime); if (FrameTime >= RHIConsoleVariables::SyncRefreshThreshold * RefreshPeriod) { SyncCounter--; } else if (bValidGPUTime) { SyncCounter++; } SyncCounter = FMath::Clamp<int32>(SyncCounter, 0, RHIConsoleVariables::MaxSyncCounter); // If frames are being completed quickly enough, block for vsync. bool bSync = (SyncCounter >= RHIConsoleVariables::SyncThreshold); if (bSync) { // This flushes the previous present call and blocks until it is made available to DWM. D3DRHI->GetDeviceContext()->Flush(); DwmFlush(); // We sleep a percentage of the remaining time. The trick is to get the // present call in after the vblank we just synced for but with time to // spare for the next vblank. float MinFrameTime = RefreshPeriod * RHIConsoleVariables::RefreshPercentageBeforePresent; float TimeToSleep; do { QueryPerformanceCounter(&Cycles); float TimeSinceFlip = FPlatformTime::ToMilliseconds(Cycles.QuadPart - LastFlipTime); TimeToSleep = (MinFrameTime - TimeSinceFlip); if (TimeToSleep > 0.0f) { FPlatformProcess::Sleep(TimeToSleep * 0.001f); } } while (TimeToSleep > 0.0f); } // Present. PresentChecked(/*SyncInterval=*/ 0); // If we are forcing <= 30Hz, block the CPU an additional amount of time if needed. // This second block is only needed when RefreshPercentageBeforePresent < 1.0. if (bSync) { LARGE_INTEGER LocalCycles; float TimeToSleep; bool bSaveCycles = false; do { QueryPerformanceCounter(&LocalCycles); float TimeSinceFlip = FPlatformTime::ToMilliseconds(LocalCycles.QuadPart - LastFlipTime); TimeToSleep = (RefreshPeriod - TimeSinceFlip); if (TimeToSleep > 0.0f) { bSaveCycles = true; FPlatformProcess::Sleep(TimeToSleep * 0.001f); } } while (TimeToSleep > 0.0f); if (bSaveCycles) { Cycles = LocalCycles; } } // If we are dropping vsync reset the counter. This provides a debounce time // before which we try to vsync again. if (!bSync && bSyncedLastFrame) { SyncCounter = 0; } if (bSync != bSyncedLastFrame || UE_LOG_ACTIVE(LogRHI,VeryVerbose)) { UE_LOG(LogRHI,Verbose,TEXT("BlockForVsync[%d]: CPUTime:%.2fms GPUTime[%d]:%.2fms Blocked:%.2fms Pending/Complete:%d/%d"), bSync, CPUTime, bValidGPUTime, GPUTime, FPlatformTime::ToMilliseconds(Cycles.QuadPart - QpcAtFlip), TimingInfo.cFramePending, TimingInfo.cFrameComplete); } // Remember if we synced, when the frame completed, etc. bSyncedLastFrame = bSync; LastFlipTime = Cycles.QuadPart; LastFrameComplete = TimingInfo.cFrameComplete; LastCompleteTime = TimingInfo.qpcFrameComplete; #endif //D3D11_WITH_DWMAPI }
void VSyncWin::VSyncLoop() { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); DWM_TIMING_INFO vblankTime; // Make sure to init the cbSize, otherwise GetCompositionTiming will fail vblankTime.cbSize = sizeof(DWM_TIMING_INFO); LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); TimeStamp vsync = Now(); // On Windows 10, DwmGetCompositionInfo returns the upcoming vsync. // See GetAdjustedVsyncTimestamp. // On start, set mPrevVsync to the "next" vsync // So we'll use this timestamp on the 2nd loop iteration. mPrevVsync = vsync + mSoftwareVsyncRate; for (;;) { #if !USING_DWM_FLUSH TimeStamp here = Now(); ::QueryPerformanceCounter(&gSS); //Sleep(1); //Sleep(200); m_pOutput->WaitForVBlank(); //Sleep(1); m_pSwapChain->Present(0, 0); //DwmFlush(); //Gdi: NtGdiDdDDIWaitForVerticalBlankEvent() //TimerBlit(); //gS = gSS; //::PostMessageA(gHwnd, WM_BLIT_REQUEST, 0, 0); //TimerBlit(); FILE *f; fopen_s(&f, "C:\\VSyncThread", "rb"); //char buf[200]; LARGE_INTEGER fi; char buf[200]; ::QueryPerformanceFrequency(&fi); sprintf_s(buf, "\n+++++ %d", int((Now() - here) * 1000000 / fi.QuadPart)); OutputDebugStringA(buf); #else ::QueryPerformanceCounter(&gS); gTS = vsync; /*mWork = CreateThreadpoolWork(BlittingThread, (void*)&gTS, &mCallBackEnviron); SubmitThreadpoolWork(mWork);*/ //mVSyncTimeStamp.store(vsync); char buf[1000]; LARGE_INTEGER fi; //::QueryPerformanceFrequency(&fi); //sprintf_s(buf, "\n+++++%d", int((vsync - here) * 1000000 / fi.QuadPart)); //OutputDebugStringA(buf); // Use a combination of DwmFlush + DwmGetCompositionTimingInfoPtr // Using WaitForVBlank, the whole system dies :/ //m_pSwapChain->Present(0, 0); //::PostMessageA(gHwnd, WM_BLIT_REQUEST, 0, 0); //m_pSwapChain->Present(1, 0); //TimerBlit(); HRESULT hr; hr = DwmFlush();//WinUtils::dwmFlushProcPtr(); if (!SUCCEEDED(hr)) { // // We don't actually know how long we had to wait on DWMFlush // // Instead of trying to calculate how long DwmFlush actually took // // Fallback to software vsync. // //-->ScheduleSoftwareVsync(Now()); return; } //dwmflush does not exactly return at next vblank //TimerBlit(); hr = DwmGetCompositionTimingInfo(0, &vblankTime); /*vsync = SUCCEEDED(hr) ? GetAdjustedVsyncTimeStamp(frequency, vblankTime.qpcVBlank) : Now();*/ vsync = vblankTime.qpcVBlank;// +vblankTime.qpcRefreshPeriod; mVSyncTimeStamp.store(vsync); mBlit = true; ////char buf[200]; //LARGE_INTEGER l; // //QueryPerformanceFrequency(&l); ////if (((vsync - Now()) * 1000000 / l.QuadPart) > 10000) // //continue; ///*LARGE_INTEGER le, lf; //QueryPerformanceCounter(&le); //QueryPerformanceFrequency(&lf);*/ //sprintf_s(buf, "\nvblank Time Now = %u", (vsync - Now()) * 1000000 / l.QuadPart); //OutputDebugStringA(buf); //sprintf_s(buf, "\n****** ---- %llu --", vsync); //OutputDebugStringA(buf); /*sprintf_s(buf, "\nv Timed %d ", ((le.QuadPart - gS.QuadPart) * 1000000 / lf.QuadPart)); OutputDebugStringA(buf);*/ #endif } // end for }