// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "MSAARenderingSample.h" static const UINT sc_msaaSampleCount = 4; static const PCWSTR sc_fontName = L"Calibri"; static const float sc_fontSize = 20.0f; static const UINT sc_defaultNumSquares = 16; static const UINT sc_minNumSquares = 1; static const UINT sc_maxNumSquares = 1024; static const float sc_boardWidth = 700.0f; static const float sc_loupeSize = 300.0f; static const float sc_loupeDefaultLogZoom = 2.0f; static const float sc_loupeMinLogZoom = 0.0f; static const float sc_loupeMaxLogZoom = 4.0f; static const float sc_rotationSpeed = 0.25f; static const float sc_loupeInset = 20.0f; static const float sc_srcLoupeRingThickness = 2.0f; static const float sc_destLoupeRingThickness = 5.0f; /****************************************************************** * * * WinMain * * * * Application entrypoint * * * ******************************************************************/ int WINAPI WinMain( HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int /* nCmdShow */ ) { // Ignoring the return value because we want to continue running even in the // unlikely event that HeapSetInformation fails. HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (SUCCEEDED(CoInitialize(NULL))) { { MSAASampleApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; } /****************************************************************** * * * MSAASampleApp::MSAASampleApp * * * * Constructor -- initialize member data * * * ******************************************************************/ MSAASampleApp::MSAASampleApp() : m_hwnd(NULL), m_antialiasMode(MyAntialiasMode::Aliased), m_sampleType(SampleType::Filled), m_paused(false), m_pausedTime(0), m_drawLoupe(true), m_numSquares(sc_defaultNumSquares), m_logZoom(sc_loupeDefaultLogZoom*WHEEL_DELTA), m_pDevice(NULL), m_pSwapChain(NULL), m_pState(NULL), m_pRenderTargetView(NULL), m_pLoupeTexture(NULL), m_pLoupeBitmap(NULL), m_pBackBufferRT(NULL), m_pTextBrush(NULL), m_pLoupeBrush(NULL), m_pD2DFactory(NULL), m_pDWriteFactory(NULL), m_pTextFormat(NULL) { LARGE_INTEGER time; QueryPerformanceCounter(&time); m_timeDelta = -time.QuadPart; } /****************************************************************** * * * MSAASampleApp::~MSAASampleApp * * * * Destructor -- tear down member data * * * ******************************************************************/ MSAASampleApp::~MSAASampleApp() { SafeRelease(&m_pDevice); SafeRelease(&m_pSwapChain); SafeRelease(&m_pState); SafeRelease(&m_pRenderTargetView); SafeRelease(&m_pLoupeTexture); SafeRelease(&m_pLoupeBitmap); SafeRelease(&m_pBackBufferRT); SafeRelease(&m_pTextBrush); SafeRelease(&m_pLoupeBrush); SafeRelease(&m_pD2DFactory); SafeRelease(&m_pDWriteFactory); SafeRelease(&m_pTextFormat); } /****************************************************************** * * * MSAASampleApp::Initialize * * * * This method is used to create and display the application * * window, and provides a convenient place to create any device * * independent resources that will be required. * * * ******************************************************************/ HRESULT MSAASampleApp::Initialize() { HRESULT hr; hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Register window class WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = MSAASampleApp::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"D2DMSAASampleApp"; RegisterClassEx(&wcex); // Create the application window. // // This sample does not handle resize so we create the window such that // it can't be resized. // // Because the CreateWindow function takes its size in pixels, we // obtain the system DPI and use it to scale the window size. // FLOAT dpiX; FLOAT dpiY; m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); m_hwnd = CreateWindow( L"D2DMSAASampleApp", L"D2D Demo App", WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_SIZEBOX), CW_USEDEFAULT, CW_USEDEFAULT, static_cast(ceil(1024.f * dpiX / 96.f)), static_cast(ceil(768.f * dpiY / 96.f)), NULL, NULL, HINST_THISCOMPONENT, this ); hr = m_hwnd ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } } return hr; } /****************************************************************** * * * MSAASampleApp::CreateDeviceIndependentResources * * * * This method is used to create resources which are not bound * * to any device. Their lifetime effectively extends for the * * duration of the app. * * * ******************************************************************/ HRESULT MSAASampleApp::CreateDeviceIndependentResources() { static const WCHAR msc_fontName[] = L"Calibri"; static const FLOAT msc_fontSize = 50; HRESULT hr = S_OK; // Create a Direct2D factory. hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory); if (SUCCEEDED(hr)) { hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_pDWriteFactory), reinterpret_cast(&m_pDWriteFactory) ); } if (SUCCEEDED(hr)) { hr = m_pDWriteFactory->CreateTextFormat( sc_fontName, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, static_cast(sc_fontSize), L"", // locale &m_pTextFormat ); } return hr; } /****************************************************************** * * * MSAASampleApp::CreateDeviceResources * * * * This method is responsible for creating the D3D device and * * all corresponding device-bound resources that are required to * * render. * * * * Of the objects created in this method, 5 are interesting ... * * * * 1. D3D Device (m_pDevice) -- This device must support 32bpp * * BGRA * * * * 2. DXGI Swap Chain (m_pSwapChain) -- Mapped to current * * window (m_hwnd) * * * * 3. D2D Surface Render Target (m_pBackBufferRT) -- Allows * * us to use D2D to draw directly into a swap chain * * buffer, in order to show (for example) a background * * gradient. * * * * 4. D3D Offscreen Texture (m_pOffscreenTexture) -- Texture * * which is mapped to our 3D model. * * * * 5. D2D Surface Render Target (m_pBackBufferRT) -- * * Allows us to use D2D to draw into the (4) D3D * * offscreen texture. This texture will then be mapped * * and displayed on our 3D model. * * * ******************************************************************/ HRESULT MSAASampleApp::CreateDeviceResources() { HRESULT hr = S_OK; RECT rcClient; ID3D10Resource *pBackBufferResource = NULL; IDXGIDevice *pDXGIDevice = NULL; IDXGIAdapter *pAdapter = NULL; IDXGIFactory *pDXGIFactory = NULL; IDXGISurface *pSurface = NULL; IDXGISurface *pDxgiSurface = NULL; IDXGISurface *pResolveSurface = NULL; IDXGISurface *pBackBuffer = NULL; Assert(m_hwnd); GetClientRect(m_hwnd, &rcClient); UINT nWidth = abs(rcClient.right - rcClient.left); UINT nHeight = abs(rcClient.bottom - rcClient.top); // If we don't have a device, need to create one now and all // accompanying D3D resources. if (!m_pDevice) { ID3D10Device1 *pDevice = NULL; UINT nDeviceFlags = D3D10_CREATE_DEVICE_BGRA_SUPPORT; // Create device hr = CreateD3DDevice( NULL, D3D10_DRIVER_TYPE_HARDWARE, nDeviceFlags, &pDevice ); if (SUCCEEDED(hr)) { UINT msaaQuality = 0; hr = pDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM, sc_msaaSampleCount, &msaaQuality); if (SUCCEEDED(hr) && msaaQuality == 0) { // If the hardware doesn't support MSAA, fall back to WARP. hr = E_FAIL; SafeRelease(&pDevice); } } if (FAILED(hr)) { hr = CreateD3DDevice( NULL, D3D10_DRIVER_TYPE_WARP, nDeviceFlags, &pDevice ); } if (SUCCEEDED(hr)) { hr = pDevice->QueryInterface(&m_pDevice); } SafeRelease(&pDevice); } // This sample discards all resources except for the device when // transitioning between antialiasing states. While this is not the fastest // way to toggle multisampling at runtime, it simplifies the code to reuse // the device creation logic here. // // Given that these transitions discard all resources except for the // device, we have different checks for to see if we have a swap chain or a // device. if (SUCCEEDED(hr) && !m_pSwapChain) { UINT msaaSampleCount = (m_antialiasMode == MyAntialiasMode::MSAA) ? sc_msaaSampleCount : 1; UINT msaaQuality = 0; if (SUCCEEDED(hr)) { hr = m_pDevice->QueryInterface(&pDXGIDevice); } if (SUCCEEDED(hr)) { hr = pDXGIDevice->GetAdapter(&pAdapter); } if (SUCCEEDED(hr)) { hr = pAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory)); } if (SUCCEEDED(hr)) { hr = m_pDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM, msaaSampleCount, &msaaQuality); if (SUCCEEDED(hr) && msaaQuality == 0) { hr = E_FAIL; } } if (SUCCEEDED(hr)) { DXGI_SWAP_CHAIN_DESC swapDesc; ::ZeroMemory(&swapDesc, sizeof(swapDesc)); swapDesc.BufferDesc.Width = nWidth; swapDesc.BufferDesc.Height = nHeight; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = msaaSampleCount; swapDesc.SampleDesc.Quality = msaaQuality-1; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; swapDesc.OutputWindow = m_hwnd; swapDesc.Windowed = TRUE; hr = pDXGIFactory->CreateSwapChain(m_pDevice, &swapDesc, &m_pSwapChain); } if (SUCCEEDED(hr)) { // Create rasterizer state object D3D10_RASTERIZER_DESC rsDesc; rsDesc.AntialiasedLineEnable = FALSE; rsDesc.CullMode = D3D10_CULL_NONE; rsDesc.DepthBias = 0; rsDesc.DepthBiasClamp = 0; rsDesc.DepthClipEnable = TRUE; rsDesc.FillMode = D3D10_FILL_SOLID; rsDesc.FrontCounterClockwise = FALSE; // Must be FALSE for 10on9 rsDesc.MultisampleEnable = TRUE; rsDesc.ScissorEnable = FALSE; rsDesc.SlopeScaledDepthBias = 0; hr = m_pDevice->CreateRasterizerState(&rsDesc, &m_pState); } if (SUCCEEDED(hr)) { m_pDevice->RSSetState(m_pState); // The debug layer will output a spurious warning message when restoring // state if we leave the state as D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED, // so we set it to something else to suppress the warning. m_pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); } if (SUCCEEDED(hr)) { // Create views on the RT buffers and set them on the device hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBufferResource) ); } if (SUCCEEDED(hr)) { D3D10_RENDER_TARGET_VIEW_DESC renderDesc; renderDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; renderDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2DMS; renderDesc.Texture2D.MipSlice = 0; hr = m_pDevice->CreateRenderTargetView(pBackBufferResource, &renderDesc, &m_pRenderTargetView); } if (SUCCEEDED(hr)) { ID3D10RenderTargetView *viewList[1] = {m_pRenderTargetView}; m_pDevice->OMSetRenderTargets(1, viewList, NULL); // Set a new viewport based on the new dimensions D3D10_VIEWPORT viewport; viewport.Width = nWidth; viewport.Height = nHeight; viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.MinDepth = 0; viewport.MaxDepth = 1; m_pDevice->RSSetViewports(1, &viewport); D3D10_TEXTURE2D_DESC texDesc; ::ZeroMemory(&texDesc, sizeof(texDesc)); texDesc.Width = nWidth; texDesc.Height = nHeight; texDesc.MipLevels = 1; texDesc.ArraySize = 1; texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; texDesc.SampleDesc.Count = 1; texDesc.SampleDesc.Quality = 0; texDesc.BindFlags = D3D10_BIND_SHADER_RESOURCE; texDesc.Usage = D3D10_USAGE_DEFAULT; hr = m_pDevice->CreateTexture2D(&texDesc, NULL, &m_pLoupeTexture); } if (SUCCEEDED(hr)) { // Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) ); } if (SUCCEEDED(hr)) { FLOAT dpiX; FLOAT dpiY; m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); // Create a D2D render target which can draw into the surface in the swap chain D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY ); hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pBackBuffer, &props, &m_pBackBufferRT ); } if (SUCCEEDED(hr)) { // create brushes hr = m_pBackBufferRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White), &m_pTextBrush ); } if (SUCCEEDED(hr)) { hr = m_pBackBufferRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Red), &m_pLoupeBrush ); } if (SUCCEEDED(hr)) { hr = m_pLoupeTexture->QueryInterface( IID_PPV_ARGS(&pResolveSurface) ); } if (SUCCEEDED(hr)) { FLOAT dpiX; FLOAT dpiY; m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties( D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY ); hr = m_pBackBufferRT->CreateSharedBitmap( __uuidof(pResolveSurface), pResolveSurface, &bp, &m_pLoupeBitmap ); } } SafeRelease(&pBackBufferResource); SafeRelease(&pDXGIDevice); SafeRelease(&pAdapter); SafeRelease(&pDXGIFactory); SafeRelease(&pSurface); SafeRelease(&pDxgiSurface); SafeRelease(&pResolveSurface); SafeRelease(&pBackBuffer); return hr; } /****************************************************************** * * * MSAASampleApp::DiscardDeviceResources * * * * Certain resources (eg. device, swap chain, RT) are bound to a * * particular D3D device. Under certain conditions (eg. change * * display mode, remoting, removing a video adapter), it is * * necessary to discard device-specific resources. This method * * just releases all of the device-bound resources that we're * * holding onto. * * * ******************************************************************/ void MSAASampleApp::DiscardDeviceResources() { SafeRelease(&m_pSwapChain); SafeRelease(&m_pState); SafeRelease(&m_pRenderTargetView); SafeRelease(&m_pBackBufferRT); SafeRelease(&m_pTextBrush); SafeRelease(&m_pLoupeTexture); SafeRelease(&m_pLoupeBitmap); } /****************************************************************** * * * MSAASampleApp::RunMessageLoop * * * * This is the main message pump for the application * * * ******************************************************************/ void MSAASampleApp::RunMessageLoop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } /****************************************************************** * * * MSAASampleApp::OnRender * * * * This method is called when the app needs to paint the window. * * It uses a D2D RT to draw a gradient background into the swap * * chain buffer. Then, it uses a separate D2D RT to draw a * * 2D scene into a D3D texture. This texture is mapped onto a * * simple planar 3D model and displayed using D3D. * * * ******************************************************************/ HRESULT MSAASampleApp::OnRender() { HRESULT hr; static float t = 0.0f; static DWORD dwTimeStart = 0; hr = CreateDeviceResources(); if (SUCCEEDED(hr) && m_pBackBufferRT) { LARGE_INTEGER time; LARGE_INTEGER frequency; QueryPerformanceCounter(&time); QueryPerformanceFrequency(&frequency); float floatTime; if (!m_paused) { floatTime = static_cast(time.QuadPart + m_timeDelta)/static_cast(frequency.QuadPart); } else { floatTime = static_cast(m_pausedTime + m_timeDelta)/static_cast(frequency.QuadPart); } m_times.Add(time.QuadPart); // Swap chain will tell us how big the back buffer is DXGI_SWAP_CHAIN_DESC swapDesc; hr = m_pSwapChain->GetDesc(&swapDesc); if (SUCCEEDED(hr)) { if (m_pBackBufferRT) { hr = RenderD2DContentIntoSurface(floatTime); if (SUCCEEDED(hr)) { if (m_drawLoupe) { hr = RenderLoupe(); } if (SUCCEEDED(hr)) { hr = RenderTextInfo(); } } } if (SUCCEEDED(hr)) { hr = m_pSwapChain->Present(1, 0); } } } // If the device is lost for any reason, we need to recreate it if (hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED || hr == D2DERR_RECREATE_TARGET) { hr = S_OK; SafeRelease(&m_pDevice); DiscardDeviceResources(); } return hr; } /****************************************************************** * * * MSAASampleApp::GetAntialiasModeString * * * ******************************************************************/ PCWSTR MSAASampleApp::GetAntialiasModeString() { if (m_antialiasMode == MyAntialiasMode::Aliased) { return L"Aliased"; } else if (m_antialiasMode == MyAntialiasMode::PerPrimitive) { return L"PerPrimitive"; } else { Assert(m_antialiasMode == MyAntialiasMode::MSAA); return L"MSAA"; } } /****************************************************************** * * * MSAASampleApp::RenderD2DContentIntoSurface * * * * This method renders a simple 2D scene into a D2D render target * * that maps to a D3D texture. It's important that the return * * code from RT->EndDraw() is handed back to the caller, since * * it's possible for the render target device to be lost while * * the application is running, and the caller needs to handle * * that error condition. * * * ******************************************************************/ HRESULT MSAASampleApp::RenderD2DContentIntoSurface(float time) { HRESULT hr = S_OK; static DWORD dwTimeStart = 0; static DWORD dwTimeLast = 0; D2D1_SIZE_F rtSize = m_pBackBufferRT->GetSize(); float squareWidth = sc_boardWidth / m_numSquares; m_pBackBufferRT->SetAntialiasMode( m_antialiasMode == MyAntialiasMode::PerPrimitive ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED ); m_pBackBufferRT->BeginDraw(); m_pBackBufferRT->Clear(D2D1::ColorF(D2D1::ColorF::Black)); m_pBackBufferRT->SetTransform( D2D1::Matrix3x2F::Translation( 0.5f * (rtSize.width - squareWidth * m_numSquares), 0.5f * (rtSize.height - squareWidth * m_numSquares) ) * D2D1::Matrix3x2F::Rotation( (sc_rotationSpeed * time * 360.0f) * ((float)D3DX_PI / 180.0f), D2D1::Point2F(rtSize.width / 2, rtSize.height / 2) ) ); ID2D1SolidColorBrush *pBrush = NULL; hr = m_pBackBufferRT->CreateSolidColorBrush( D2D1::ColorF(0, 0, 0), &pBrush ); if (SUCCEEDED(hr)) { if (m_sampleType == SampleType::Filled) { for (UINT i = 0; i < m_numSquares; ++i) { for (UINT j = 0; j < m_numSquares; ++j) { D2D1_RECT_F rect = D2D1::RectF( i*squareWidth, j*squareWidth, (i+1)*squareWidth, (j+1)*squareWidth ); float dx = i+0.5f - 0.5f*m_numSquares; float dy = j+0.5f - 0.5f*m_numSquares; float length = sqrtf(2)*m_numSquares; float intensity = 0.5f * (1+sinf( (0.2f * time + 10.0f * sqrtf(static_cast(dx*dx+dy*dy))/length))); pBrush->SetColor( D2D1::ColorF( 0.0f, intensity, 1.0f - intensity) ); m_pBackBufferRT->FillRectangle(rect, pBrush); } } } else { pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::AntiqueWhite)); for (UINT i = 0; i < m_numSquares+1; ++i) { m_pBackBufferRT->DrawLine( D2D1::Point2F(i*squareWidth, 0), D2D1::Point2F(i*squareWidth, m_numSquares*squareWidth), pBrush, 1.0f ); m_pBackBufferRT->DrawLine( D2D1::Point2F(0, i*squareWidth), D2D1::Point2F(m_numSquares*squareWidth, i*squareWidth), pBrush, 1.0f ); } } hr = m_pBackBufferRT->EndDraw(); pBrush->Release(); } return hr; } /****************************************************************** * * * MSAASampleApp::RenderLoupe * * * * Draw the magnification Loupe * * * ******************************************************************/ HRESULT MSAASampleApp::RenderLoupe() { HRESULT hr = S_OK; // // Read back the current contents of the swap chain buffer. // ID3D10Resource *pBackBufferResource = NULL; hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBufferResource) ); if (SUCCEEDED(hr)) { m_pDevice->ResolveSubresource( m_pLoupeTexture, 0, // DstSubresource pBackBufferResource, 0, // SrcSubresource, DXGI_FORMAT_B8G8R8A8_UNORM ); // // Render the magnified view. // D2D1_SIZE_F size = m_pBackBufferRT->GetSize(); D2D1_POINT_2F destCenter = D2D1::Point2F( size.width - sc_loupeInset - 0.5f*sc_loupeSize, sc_loupeInset + 0.5f*sc_loupeSize ); D2D1_ELLIPSE destEllipse = D2D1::Ellipse( destCenter, sc_loupeSize / 2, sc_loupeSize / 2 ); float loupeSrcRadius = sc_loupeSize / (2*GetZoom()); D2D1_ELLIPSE srcEllipse = D2D1::Ellipse( m_mousePos, loupeSrcRadius, loupeSrcRadius ); ID2D1BitmapBrush *pLoupeBrush = NULL; hr = m_pBackBufferRT->CreateBitmapBrush( m_pLoupeBitmap, D2D1::BitmapBrushProperties( D2D1_EXTEND_MODE_CLAMP, D2D1_EXTEND_MODE_CLAMP, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR), D2D1::BrushProperties( 1.0f, // opacity D2D1::Matrix3x2F::Scale( GetZoom(), GetZoom(), m_mousePos ) * D2D1::Matrix3x2F::Translation( destCenter.x - m_mousePos.x, destCenter.y - m_mousePos.y )), &pLoupeBrush ); if (SUCCEEDED(hr)) { m_pBackBufferRT->BeginDraw(); m_pBackBufferRT->SetTransform(D2D1::Matrix3x2F::Identity()); m_pBackBufferRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); m_pBackBufferRT->DrawEllipse(srcEllipse, m_pLoupeBrush, sc_srcLoupeRingThickness); m_pBackBufferRT->FillEllipse(destEllipse, pLoupeBrush); m_pBackBufferRT->DrawEllipse(destEllipse, m_pLoupeBrush, sc_destLoupeRingThickness); hr = m_pBackBufferRT->EndDraw(); pLoupeBrush->Release(); } pBackBufferResource->Release(); } return hr; } /****************************************************************** * * * MSAASampleApp::RenderTextInfo * * * * Draw the stats text (AA type, fps, etc...). * * * ******************************************************************/ HRESULT MSAASampleApp::RenderTextInfo() { HRESULT hr = S_OK; WCHAR textBuffer[400]; LARGE_INTEGER frequency; float fps = 0.0f; float primsPerSecond = 0.0f; QueryPerformanceFrequency(&frequency); if (m_times.GetCount() > 0) { fps = (m_times.GetCount()-1) * frequency.QuadPart / static_cast((m_times.GetLast() - m_times.GetFirst())); primsPerSecond = fps * m_numSquares * m_numSquares; } hr = StringCchPrintf( textBuffer, 400, L"%s\n" L"# squares: %d x %d = %d\n" L"Fps: %.2f\n" L"Primitives / sec : %.0f\n", GetAntialiasModeString(), m_numSquares, m_numSquares, m_numSquares*m_numSquares, fps, primsPerSecond ); if (SUCCEEDED(hr)) { m_pBackBufferRT->BeginDraw(); m_pBackBufferRT->SetTransform(D2D1::Matrix3x2F::Identity()); m_pBackBufferRT->DrawText( textBuffer, static_cast(wcsnlen(textBuffer, ARRAYSIZE(textBuffer))), m_pTextFormat, D2D1::RectF(10.0f, 10.0f, 1000.0f, 1000.0f), m_pTextBrush, D2D1_DRAW_TEXT_OPTIONS_NONE ); hr = m_pBackBufferRT->EndDraw(); } return hr; } /****************************************************************** * * * MSAASampleApp::OnKeyDown * * * ******************************************************************/ void MSAASampleApp::OnKeyDown(SHORT vkey) { switch (vkey) { case 'A': case VK_RIGHT: m_antialiasMode = static_cast( (m_antialiasMode+1)%MyAntialiasMode::Count ); // This sample could be smarter about only discarding resources it doesn't // need in order to make transitions from one state to another more quickly. DiscardDeviceResources(); break; case VK_LEFT: m_antialiasMode = static_cast( (m_antialiasMode+MyAntialiasMode::Count-1)%MyAntialiasMode::Count ); // This sample could be smarter about only discarding resources it doesn't // need in order to make transitions from one state to another more quickly. DiscardDeviceResources(); break; case VK_SPACE: LARGE_INTEGER time; QueryPerformanceCounter(&time); if (!m_paused) { m_pausedTime = time.QuadPart; } else { m_timeDelta += (m_pausedTime - time.QuadPart); } m_paused = !m_paused; break; case VK_UP: m_numSquares = min(m_numSquares*2, sc_maxNumSquares); break; case VK_DOWN: m_numSquares = max(m_numSquares/2, sc_minNumSquares); break; case 'W': m_sampleType = static_cast((m_sampleType+1) % SampleType::Count); break; case 'L': m_drawLoupe = !m_drawLoupe; break; default: break; } } /****************************************************************** * * * OnMouseMove * * * ******************************************************************/ void MSAASampleApp::OnMouseMove(LPARAM lParam) { // Calculate the mouse position in DIPs. FLOAT dpiX; FLOAT dpiY; m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); m_mousePos = D2D1::Point2F(LOWORD(lParam) * 96.0f / dpiX, HIWORD(lParam) * 96.0f / dpiY); } /****************************************************************** * * * MSAASampleApp::OnWheel * * * ******************************************************************/ void MSAASampleApp::OnWheel(WPARAM wParam) { m_logZoom += static_cast(GET_WHEEL_DELTA_WPARAM(wParam)); m_logZoom = min( max(m_logZoom, sc_loupeMinLogZoom*WHEEL_DELTA), sc_loupeMaxLogZoom*WHEEL_DELTA ); } /****************************************************************** * * * MSAASampleApp::WndProc * * * * This static method handles our app's window messages * * * ******************************************************************/ LRESULT CALLBACK MSAASampleApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; if (message == WM_CREATE) { LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam; MSAASampleApp *pMSAASampleApp = (MSAASampleApp *)pcs->lpCreateParams; ::SetWindowLongPtrW( hwnd, GWLP_USERDATA, reinterpret_cast(pMSAASampleApp) ); result = 1; } else { MSAASampleApp *pMSAASampleApp = reinterpret_cast( ::GetWindowLongPtrW( hwnd, GWLP_USERDATA )); bool wasHandled = false; if (pMSAASampleApp) { switch (message) { case WM_PAINT: case WM_DISPLAYCHANGE: { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); pMSAASampleApp->OnRender(); EndPaint(hwnd, &ps); InvalidateRect(hwnd, NULL, FALSE); } result = 0; wasHandled = true; break; case WM_KEYDOWN: { pMSAASampleApp->OnKeyDown(static_cast(wParam)); } result = 0; wasHandled = true; break; case WM_MOUSEMOVE: { pMSAASampleApp->OnMouseMove(lParam); } result = 0; wasHandled = true; break; case WM_MOUSEWHEEL: { pMSAASampleApp->OnWheel(wParam); } result = 0; wasHandled = true; break; case WM_DESTROY: { PostQuitMessage(0); } result = 1; wasHandled = true; break; } } if (!wasHandled) { result = DefWindowProc(hwnd, message, wParam, lParam); } } return result; } /****************************************************************** * * * MSAASampleApp::CreateD3DDevice * * * ******************************************************************/ HRESULT MSAASampleApp::CreateD3DDevice( IDXGIAdapter *pAdapter, D3D10_DRIVER_TYPE driverType, UINT flags, ID3D10Device1 **ppDevice ) { HRESULT hr = S_OK; static const D3D10_FEATURE_LEVEL1 levelAttempts[] = { D3D10_FEATURE_LEVEL_10_0, D3D10_FEATURE_LEVEL_9_3, D3D10_FEATURE_LEVEL_9_2, D3D10_FEATURE_LEVEL_9_1, }; for (UINT level = 0; level < ARRAYSIZE(levelAttempts); level++) { ID3D10Device1 *pDevice = NULL; hr = D3D10CreateDevice1( pAdapter, driverType, NULL, flags, levelAttempts[level], D3D10_1_SDK_VERSION, &pDevice ); if (SUCCEEDED(hr)) { // transfer reference *ppDevice = pDevice; pDevice = NULL; break; } } return hr; }