#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_GRAPHICS_SOURCE 1 #include "Direct3D11Rasterizer.Impl.h" #include #include namespace { // ------------------------------------------------------------------------------------------- // /// Available feature levels for Direct3D 11 /// /// Direct3D 11 can run on hardware ranging from dated DirectX 9 GPUs to GPUs with /// DirectX 11.1 features and later. A feature level defines the minimum features /// required by the GPU to be usable by the application. /// const D3D_FEATURE_LEVEL FeatureLevels[] = { #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) D3D_FEATURE_LEVEL_11_1, #endif D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; // ------------------------------------------------------------------------------------------- // } namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WINRT) Direct3D11Rasterizer::Impl::Impl(Windows::UI::Core::CoreWindow ^window) : supportedFeatureLevel(D3D_FEATURE_LEVEL_9_1), swapChainDescription(getDefaultSwapChainDescription()), window(window) { using namespace Windows::UI::Core; using namespace Windows::Foundation; Size viewSize = GetViewSize(); this->width = viewSize.Width; this->height = viewSize.Height; createDirect3DDevice(); createSwapChain(); // Register to the CoreWindow's SizeChanged event this->sizeChangedEventToken = ( this->window->SizeChanged += ref new TypedEventHandler< CoreWindow ^, WindowSizeChangedEventArgs ^ >(this, &Impl::sizeChanged) ); } #endif // ------------------------------------------------------------------------------------------- // #if !defined(NUCLEX_GRAPHICS_WINRT) Direct3D11Rasterizer::Impl::Impl(HWND windowHandle) : supportedFeatureLevel(D3D_FEATURE_LEVEL_9_1), swapChainDescription(getDefaultSwapChainDescription()), windowHandle(windowHandle) { Size viewSize = GetViewSize(); this->width = viewSize.Width; this->height = viewSize.Height; createDirect3DDevice(); createSwapChain(); // Subclass the window so we know when it is resized subclassWindow(); } #endif // ------------------------------------------------------------------------------------------- // Direct3D11Rasterizer::Impl::~Impl() { #if defined(NUCLEX_GRAPHICS_WINRT) this->window->SizeChanged -= this->sizeChangedEventToken; #else unsubclassWindow(); #endif // The smart pointers would release memory on their own, but let's make sure objects // are destroyed in the right order. this->swapChain = nullptr; this->context = nullptr; this->device = nullptr; } // ------------------------------------------------------------------------------------------- // void Direct3D11Rasterizer::Impl::viewResized(std::size_t width, std::size_t height) { bool sizeChanged = ((width != this->width) || (height != this->height)); if(sizeChanged) { this->width = width; this->height = height; this->State.ShutdownForSwapChainRecreation(); #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) HRESULT resultHandle = this->swapChain->ResizeBuffers( this->swapChainDescription.BufferCount, this->swapChainDescription.Width, this->swapChainDescription.Height, this->swapChainDescription.Format, this->swapChainDescription.Flags ); #else HRESULT resultHandle = this->swapChain->ResizeBuffers( this->swapChainDescription.BufferCount, this->swapChainDescription.BufferDesc.Width, this->swapChainDescription.BufferDesc.Height, this->swapChainDescription.BufferDesc.Format, this->swapChainDescription.Flags ); #endif if(FAILED(resultHandle)) { throw std::runtime_error("Could not resize swap chain"); } Size viewSize = GetViewSize(); this->State.RestoreAfterSwapChainRecreation(viewSize.Width, viewSize.Height); } } // ------------------------------------------------------------------------------------------- // const ID3D11DeviceNPtr &Direct3D11Rasterizer::Impl::GetDirect3DDevice() const { using namespace std; assert( (!!this->device) && "Method must only be called after a Direct3D device was created" ); return this->device; } // ------------------------------------------------------------------------------------------- // const ID3D11DeviceContextNPtr &Direct3D11Rasterizer::Impl::GetDirect3DDeviceContext() const { using namespace std; assert( (!!this->context) && "Method must only be called after a Direct3D device was created" ); return this->context; } // ------------------------------------------------------------------------------------------- // const IDXGISwapChainNPtr &Direct3D11Rasterizer::Impl::GetSwapChain() const { using namespace std; assert( (!!this->device) && "Method must only be called after a swap chain was created" ); return this->swapChain; } // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WINRT) Size Direct3D11Rasterizer::Impl::GetViewSize() const { return Size( static_cast(this->window->Bounds.Width), static_cast(this->window->Bounds.Height) ); } #endif // defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WIN32) Size Direct3D11Rasterizer::Impl::GetViewSize() const { RECT clientRectangle; BOOL result = ::GetClientRect(windowHandle, &clientRectangle); if(result == FALSE) { throw std::runtime_error("Could not determine window client rectangle"); } return Size( static_cast(clientRectangle.right - clientRectangle.left), static_cast(clientRectangle.bottom - clientRectangle.top) ); } #endif // defined(NUCLEX_GRAPHICS_WIN32) // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) DXGI_SWAP_CHAIN_DESCN Direct3D11Rasterizer::Impl::getDefaultSwapChainDescription() { DXGI_SWAP_CHAIN_DESCN swapChainDescription = {0}; swapChainDescription.Width = 0; // auto swapChainDescription.Height = 0; // auto swapChainDescription.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDescription.Stereo = FALSE; swapChainDescription.SampleDesc.Count = 1; // no antialiasing swapChainDescription.SampleDesc.Quality = 0; swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDescription.BufferCount = 2; // allow page flipping swapChainDescription.Scaling = DXGI_SCALING_NONE; #if defined(NUCLEX_GRAPHICS_WINRT) swapChainDescription.Scaling = DXGI_SCALING_NONE; swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Metro requires this #else swapChainDescription.Scaling = DXGI_SCALING_STRETCH; swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // Let the driver choose #endif swapChainDescription.Flags = 0; return swapChainDescription; } #endif // defined(NUCLEX_GRAPHICS_DIRECT3D11_1) // ------------------------------------------------------------------------------------------- // #if !defined(NUCLEX_GRAPHICS_DIRECT3D11_1) DXGI_SWAP_CHAIN_DESCN Direct3D11Rasterizer::Impl::getDefaultSwapChainDescription() { DXGI_SWAP_CHAIN_DESCN swapChainDescription = {0}; swapChainDescription.BufferDesc.Width = 0; // auto swapChainDescription.BufferDesc.Height = 0; // auto swapChainDescription.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDescription.BufferDesc.RefreshRate.Denominator = 0; swapChainDescription.BufferDesc.RefreshRate.Numerator = 0; swapChainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_STRETCHED; swapChainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; swapChainDescription.SampleDesc.Count = 1; // no antialiasing swapChainDescription.SampleDesc.Quality = 0; swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDescription.BufferCount = 2; // allow page flipping swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // Let the driver choose swapChainDescription.OutputWindow = 0; // will be filled when device is created swapChainDescription.Windowed = true; swapChainDescription.Flags = 0; return swapChainDescription; } #endif // !defined(NUCLEX_GRAPHICS_DIRECT3D11_1) // ------------------------------------------------------------------------------------------- // IDXGIFactoryNPtr Direct3D11Rasterizer::Impl::getDxgiFactory(const ID3D11DevicePtr &device) { IDXGIDeviceNPtr dxgiDevice = device; // The device knows its adapter IDXGIAdapterPtr dxgiAdapter; HRESULT resultHandle = dxgiDevice->GetAdapter(&dxgiAdapter); if(FAILED(resultHandle)) { throw std::runtime_error("Could not query Direct3D 11 graphics adapter from device"); } // And the adapter knows its factory void *dxgiFactoryAsVoid = nullptr; resultHandle = dxgiAdapter->GetParent(IID_IDXGIFactoryN, &dxgiFactoryAsVoid); if(FAILED(resultHandle)) { throw std::runtime_error("Could not query Direct3D 11 resource factory from adapter"); } // Wrap the factory in a smart pointer bool addRef = false; // GetParent() has already called AddRef() return IDXGIFactoryNPtr(reinterpret_cast(dxgiFactoryAsVoid), addRef); } // ------------------------------------------------------------------------------------------- // void Direct3D11Rasterizer::Impl::createDirect3DDevice() { ID3D11DevicePtr device; ID3D11DeviceContextPtr context; // Allows us to specialize the created device UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #if defined(_DEBUG) creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif // Set up a Direct3D 11 device with the highest feature level we can get. // 10 arguments for a single function? Come on...! HRESULT resultHandle = ::D3D11CreateDevice( nullptr, // Use default DXGI adapter D3D_DRIVER_TYPE_HARDWARE, nullptr, // no software device DLL creationFlags, FeatureLevels, ARRAYSIZE(FeatureLevels), D3D11_SDK_VERSION, &device, &this->supportedFeatureLevel, &context ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 device"); } this->device = device; this->context = context; this->Resources.SetDirect3DDevice(this->device); this->Resources.SetDirect3DDeviceContext(this->context); this->Settings.SetDirect3DDevice(this->device); this->State.SetResorces(&this->Resources); this->State.SetInputLayouts(&this->InputLayouts); this->State.SetDirect3DDevice(this->device); this->State.SetDirect3DDeviceContext(this->context); this->InputLayouts.SetDirect3DDevice(this->device); } // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WINRT) void Direct3D11Rasterizer::Impl::createSwapChain() { IDXGIFactoryNPtr dxgiFactory = getDxgiFactory(this->device); Windows::UI::Core::CoreWindow ^windowPointer = this->window.Get(); HRESULT resultHandle = dxgiFactory->CreateSwapChainForCoreWindow( this->device, reinterpret_cast(windowPointer), &this->swapChainDescription, nullptr, // which display to use, nullptr means any &this->swapChain ); if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 swap chain"); } } #endif // !defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WIN32) void Direct3D11Rasterizer::Impl::createSwapChain() { IDXGIFactoryNPtr dxgiFactory = getDxgiFactory(this->device); // Let Direct3D listen in on window message to support Alt+Enter. HRESULT resultHandle = dxgiFactory->MakeWindowAssociation(this->windowHandle, 0); if(FAILED(resultHandle)) { throw std::runtime_error("Could not setup Direct3D 11 device to window assocation"); } #if defined(NUCLEX_GRAPHICS_DIRECT3D11_1) resultHandle = dxgiFactory->CreateSwapChainForHwnd( this->device, this->windowHandle, &this->swapChainDescription, nullptr, nullptr, // which display to use, nullptr means any &this->swapChain ); #else this->swapChainDescription.OutputWindow = this->windowHandle; resultHandle = dxgiFactory->CreateSwapChain( this->device, &this->swapChainDescription, &this->swapChain ); #endif if(FAILED(resultHandle)) { throw std::runtime_error("Could not create Direct3D 11 swap chain"); } this->State.CreateBackBufferRenderTarget(this->swapChain); } #endif // defined(NUCLEX_GRAPHICS_WIN32) // ------------------------------------------------------------------------------------------- // #if defined(NUCLEX_GRAPHICS_WINRT) void Direct3D11Rasterizer::Impl::sizeChanged( Windows::UI::Core::CoreWindow ^window, Windows::UI::Core::WindowSizeChangedEventArgs ^arguments ) { viewResized( static_cast(arguments->Size.Width), static_cast(arguments->Size.Height) ); } #endif // defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // #if !defined(NUCLEX_GRAPHICS_WINRT) void Direct3D11Rasterizer::Impl::subclassWindow() { BOOL result = ::SetPropW( this->windowHandle, L"Nuclex::Graphics::Rasterization::Direct3D11Rasterizer::Impl", reinterpret_cast(this) ); if(result == FALSE) { throw std::runtime_error("Could not assign property to window"); } this->oldWindowProc = reinterpret_cast( ::SetWindowLongPtrA( this->windowHandle, GWLP_WNDPROC, reinterpret_cast(&Direct3D11Rasterizer::Impl::windowProcedure) ) ); } #endif // !defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // #if !defined(NUCLEX_GRAPHICS_WINRT) void Direct3D11Rasterizer::Impl::unsubclassWindow() { ::SetWindowLongPtrA( this->windowHandle, GWLP_WNDPROC, reinterpret_cast(this->oldWindowProc) ); } #endif // !defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // #if !defined(NUCLEX_GRAPHICS_WINRT) LRESULT CALLBACK Direct3D11Rasterizer::Impl::windowProcedure( HWND windowHandle, UINT message, WPARAM parameter1, LPARAM parameter2 ) { Direct3D11Rasterizer::Impl *self = reinterpret_cast( ::GetPropW(windowHandle, L"Nuclex::Graphics::Rasterization::Direct3D11Rasterizer::Impl") ); if(self == nullptr) { return ::DefWindowProcW(windowHandle, message, parameter1, parameter2); } try { switch(message) { // WM_SIZE is generated from WM_WINDOWPOSCHANGED through DefWindowProc()! // If the window's owner processes WM_WINDOWPOSCHANGED himself without calling // DefWindowProc(), there might not even be a WM_SIZE. case WM_WINDOWPOSCHANGED: { Size viewSize = self->GetViewSize(); self->viewResized(viewSize.Width, viewSize.Height); break; } } } catch(const std::exception &) { using namespace std; assert(!"Exceptions must not reach the window procedure"); } // Call the previous window proc since we're only listening in, not owning the window return ::CallWindowProcA( self->oldWindowProc, windowHandle, message, parameter1, parameter2 ); } #endif // !defined(NUCLEX_GRAPHICS_WINRT) // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization