So I am attempting to write a multi window co-op game using directx12. (each player has their own window to allow no screen cheating if they have multiple monitors). But I am having trouble with dealing with VSync when presenting the swapchains for each player window.
How do I properly handle VSync so that presenting the windows don't cause a massive frame drop due to V-Sync?
Here is the rough outline of what I am doing:
uint32_t dwNumPlayers = 4;
sRENDERER.WindowClass.cbSize = sizeof( WNDCLASSEX );
sRENDERER.WindowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
sRENDERER.WindowClass.lpfnWndProc = Win32MainWindowCallback;
sRENDERER.WindowClass.cbClsExtra = 0;
sRENDERER.WindowClass.cbWndExtra = 0;
sRENDERER.WindowClass.hInstance = GetModuleHandle( NULL );
sRENDERER.WindowClass.hIcon = LoadIcon( 0, IDI_APPLICATION );
sRENDERER.WindowClass.hCursor = LoadCursor( 0, IDC_ARROW ); /
sRENDERER.WindowClass.hbrBackground = NULL;
sRENDERER.WindowClass.lpszMenuName = NULL;
sRENDERER.WindowClass.lpszClassName = "PlayerWindowClass";
sRENDERER.WindowClass.hIconSm = NULL;
if ( !RegisterClassEx( &sRENDERER.WindowClass ) )
{
logWindowsError( "Failed to Register Window Class:\n" );
return -1;
}
//... init directx and get a direct command queue and create all the win32 windows
uint32_t dwStartingWindowWidth = 600;
uint32_t dwStartingWindowHeight = 450;
HWND WindowHandles[MAX_NUM_PLAYERS];
IDXGISwapChain4* swapChain[MAX_NUM_PLAYERS];
for( uint32_t dwPlayer = 0; dwPlayer < dwNumPlayers; ++dwPlayer )
{
char buf[64];
sprintf_s( &buf[0], 64, "Player: %u", dwPlayer + 1 );
WindowHandles[dwPlayer] = CreateWindowEx( 0, sRENDERER.WindowClass.lpszClassName, buf,
WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, dwStartingWindowWidth , dwStartingWindowHeight,
0, 0, sRENDERER.WindowClass.hInstance, NULL );
if ( !sRENDERER.WindowHandles[dwPlayer] )
{
logWindowsError( "Failed to Instantiate Window Class:\n" );
return -1;
}
DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
swapChainDesc.Width = dwStartingWindowWidth;
swapChainDesc.Height = dwStartingWindowHeight;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.Stereo = 0;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 3;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
IDXGISwapChain1* tempSwapChain = NULL;
if( FAILED( sRENDERER.dxgiFactory->CreateSwapChainForHwnd( sRENDERER.gfxCommandQueue, WindowHandles[dwPlayer], &swapChainDesc, NULL, NULL, &tempSwapChain ) ) )
{
logError( "Failed to create swap chain!\n" );
return false;
}
if( FAILED( tempSwapChain->QueryInterface( IID_PPV_ARGS( &swapChain[dwPlayer] ) ) ) )
{
logError( "Failed to query swap chain interface!\n" );
return false;
}
tempSwapChain->Release();
//setup rtv's
}
while(bRunning)
{
//... render
sRENDERER.gfxCommandQueue->ExecuteCommandLists( dwNumPlayers, sRENDERER.gfxCommandList[dwFrame] );
SignalCurrentFrame();
//uint32_t syncInterval = 1;
//uint32_t presentFlags = 0;
uint32_t syncInterval = 0; //turning off vsync, but how to handle with VSync on?
uint32_t presentFlags = DXGI_PRESENT_ALLOW_TEARING; //can i do this without tearing?
for( uint32_t dwPlayer = 0; dwPlayer < dwNumPlayers; ++dwPlayer )
{
if( FAILED( swapChain[dwPlayer]->Present( syncInterval, presentFlags ) ) )
{
logError( "Error presenting swap chain buffer!\n" );
break;
}
}
}
These are my sources: