src/video/windowsrt/SDL_winrtrenderer.cpp
changeset 8369 6dfc4ba22640
parent 8367 df577bb2f2d7
child 8370 a7b682df854c
     1.1 --- a/src/video/windowsrt/SDL_winrtrenderer.cpp	Sun Nov 25 17:35:41 2012 -0500
     1.2 +++ b/src/video/windowsrt/SDL_winrtrenderer.cpp	Sun Nov 25 19:05:56 2012 -0500
     1.3 @@ -3,20 +3,93 @@
     1.4  
     1.5  using namespace DirectX;
     1.6  using namespace Microsoft::WRL;
     1.7 +using namespace Windows::UI::Core;
     1.8  using namespace Windows::Foundation;
     1.9 -using namespace Windows::UI::Core;
    1.10 +using namespace Windows::Graphics::Display;
    1.11  
    1.12 +// Constructor.
    1.13  SDL_winrtrenderer::SDL_winrtrenderer() :
    1.14 -	m_loadingComplete(false),
    1.15 +    m_loadingComplete(false),
    1.16  	m_vertexCount(0)
    1.17  {
    1.18  }
    1.19  
    1.20 +// Initialize the Direct3D resources required to run.
    1.21 +void SDL_winrtrenderer::Initialize(CoreWindow^ window)
    1.22 +{
    1.23 +	m_window = window;
    1.24 +	
    1.25 +	CreateDeviceResources();
    1.26 +	CreateWindowSizeDependentResources();
    1.27 +}
    1.28 +
    1.29 +// Recreate all device resources and set them back to the current state.
    1.30 +void SDL_winrtrenderer::HandleDeviceLost()
    1.31 +{
    1.32 +	// Reset these member variables to ensure that UpdateForWindowSizeChange recreates all resources.
    1.33 +	m_windowBounds.Width = 0;
    1.34 +	m_windowBounds.Height = 0;
    1.35 +	m_swapChain = nullptr;
    1.36 +
    1.37 +	CreateDeviceResources();
    1.38 +	UpdateForWindowSizeChange();
    1.39 +}
    1.40 +
    1.41 +// These are the resources that depend on the device.
    1.42  void SDL_winrtrenderer::CreateDeviceResources()
    1.43  {
    1.44 -	Direct3DBase::CreateDeviceResources();
    1.45 +	// This flag adds support for surfaces with a different color channel ordering
    1.46 +	// than the API default. It is required for compatibility with Direct2D.
    1.47 +	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    1.48  
    1.49 -	auto loadVSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimpleVertexShader.cso");
    1.50 +#if defined(_DEBUG)
    1.51 +	// If the project is in a debug build, enable debugging via SDK Layers with this flag.
    1.52 +	creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
    1.53 +#endif
    1.54 +
    1.55 +	// This array defines the set of DirectX hardware feature levels this app will support.
    1.56 +	// Note the ordering should be preserved.
    1.57 +	// Don't forget to declare your application's minimum required feature level in its
    1.58 +	// description.  All applications are assumed to support 9.1 unless otherwise stated.
    1.59 +	D3D_FEATURE_LEVEL featureLevels[] = 
    1.60 +	{
    1.61 +		D3D_FEATURE_LEVEL_11_1,
    1.62 +		D3D_FEATURE_LEVEL_11_0,
    1.63 +		D3D_FEATURE_LEVEL_10_1,
    1.64 +		D3D_FEATURE_LEVEL_10_0,
    1.65 +		D3D_FEATURE_LEVEL_9_3,
    1.66 +		D3D_FEATURE_LEVEL_9_2,
    1.67 +		D3D_FEATURE_LEVEL_9_1
    1.68 +	};
    1.69 +
    1.70 +	// Create the Direct3D 11 API device object and a corresponding context.
    1.71 +	ComPtr<ID3D11Device> device;
    1.72 +	ComPtr<ID3D11DeviceContext> context;
    1.73 +	DX::ThrowIfFailed(
    1.74 +		D3D11CreateDevice(
    1.75 +			nullptr, // Specify nullptr to use the default adapter.
    1.76 +			D3D_DRIVER_TYPE_HARDWARE,
    1.77 +			nullptr,
    1.78 +			creationFlags, // Set set debug and Direct2D compatibility flags.
    1.79 +			featureLevels, // List of feature levels this app can support.
    1.80 +			ARRAYSIZE(featureLevels),
    1.81 +			D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
    1.82 +			&device, // Returns the Direct3D device created.
    1.83 +			&m_featureLevel, // Returns feature level of device created.
    1.84 +			&context // Returns the device immediate context.
    1.85 +			)
    1.86 +		);
    1.87 +
    1.88 +	// Get the Direct3D 11.1 API device and context interfaces.
    1.89 +	DX::ThrowIfFailed(
    1.90 +		device.As(&m_d3dDevice)
    1.91 +		);
    1.92 +
    1.93 +	DX::ThrowIfFailed(
    1.94 +		context.As(&m_d3dContext)
    1.95 +		);
    1.96 +
    1.97 +    auto loadVSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimpleVertexShader.cso");
    1.98  	auto loadPSTask = DX::ReadDataAsync("SDL_VS2012_WinRT\\SimplePixelShader.cso");
    1.99  
   1.100  	auto createVSTask = loadVSTask.then([this](Platform::Array<byte>^ fileData) {
   1.101 @@ -110,6 +183,202 @@
   1.102  	});
   1.103  }
   1.104  
   1.105 +// Allocate all memory resources that change on a window SizeChanged event.
   1.106 +void SDL_winrtrenderer::CreateWindowSizeDependentResources()
   1.107 +{ 
   1.108 +	// Store the window bounds so the next time we get a SizeChanged event we can
   1.109 +	// avoid rebuilding everything if the size is identical.
   1.110 +	m_windowBounds = m_window->Bounds;
   1.111 +
   1.112 +	// Calculate the necessary swap chain and render target size in pixels.
   1.113 +	float windowWidth = ConvertDipsToPixels(m_windowBounds.Width);
   1.114 +	float windowHeight = ConvertDipsToPixels(m_windowBounds.Height);
   1.115 +
   1.116 +	// The width and height of the swap chain must be based on the window's
   1.117 +	// landscape-oriented width and height. If the window is in a portrait
   1.118 +	// orientation, the dimensions must be reversed.
   1.119 +	m_orientation = DisplayProperties::CurrentOrientation;
   1.120 +	bool swapDimensions =
   1.121 +		m_orientation == DisplayOrientations::Portrait ||
   1.122 +		m_orientation == DisplayOrientations::PortraitFlipped;
   1.123 +	m_renderTargetSize.Width = swapDimensions ? windowHeight : windowWidth;
   1.124 +	m_renderTargetSize.Height = swapDimensions ? windowWidth : windowHeight;
   1.125 +
   1.126 +	if(m_swapChain != nullptr)
   1.127 +	{
   1.128 +		// If the swap chain already exists, resize it.
   1.129 +		DX::ThrowIfFailed(
   1.130 +			m_swapChain->ResizeBuffers(
   1.131 +				2, // Double-buffered swap chain.
   1.132 +				static_cast<UINT>(m_renderTargetSize.Width),
   1.133 +				static_cast<UINT>(m_renderTargetSize.Height),
   1.134 +				DXGI_FORMAT_B8G8R8A8_UNORM,
   1.135 +				0
   1.136 +				)
   1.137 +			);
   1.138 +	}
   1.139 +	else
   1.140 +	{
   1.141 +		// Otherwise, create a new one using the same adapter as the existing Direct3D device.
   1.142 +		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
   1.143 +		swapChainDesc.Width = static_cast<UINT>(m_renderTargetSize.Width); // Match the size of the window.
   1.144 +		swapChainDesc.Height = static_cast<UINT>(m_renderTargetSize.Height);
   1.145 +		swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
   1.146 +		swapChainDesc.Stereo = false;
   1.147 +		swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
   1.148 +		swapChainDesc.SampleDesc.Quality = 0;
   1.149 +		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
   1.150 +		swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
   1.151 +		swapChainDesc.Scaling = DXGI_SCALING_NONE;
   1.152 +		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
   1.153 +		swapChainDesc.Flags = 0;
   1.154 +
   1.155 +		ComPtr<IDXGIDevice1>  dxgiDevice;
   1.156 +		DX::ThrowIfFailed(
   1.157 +			m_d3dDevice.As(&dxgiDevice)
   1.158 +			);
   1.159 +
   1.160 +		ComPtr<IDXGIAdapter> dxgiAdapter;
   1.161 +		DX::ThrowIfFailed(
   1.162 +			dxgiDevice->GetAdapter(&dxgiAdapter)
   1.163 +			);
   1.164 +
   1.165 +		ComPtr<IDXGIFactory2> dxgiFactory;
   1.166 +		DX::ThrowIfFailed(
   1.167 +			dxgiAdapter->GetParent(
   1.168 +				__uuidof(IDXGIFactory2), 
   1.169 +				&dxgiFactory
   1.170 +				)
   1.171 +			);
   1.172 +
   1.173 +		Windows::UI::Core::CoreWindow^ window = m_window.Get();
   1.174 +		DX::ThrowIfFailed(
   1.175 +			dxgiFactory->CreateSwapChainForCoreWindow(
   1.176 +				m_d3dDevice.Get(),
   1.177 +				reinterpret_cast<IUnknown*>(window),
   1.178 +				&swapChainDesc,
   1.179 +				nullptr, // Allow on all displays.
   1.180 +				&m_swapChain
   1.181 +				)
   1.182 +			);
   1.183 +			
   1.184 +		// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
   1.185 +		// ensures that the application will only render after each VSync, minimizing power consumption.
   1.186 +		DX::ThrowIfFailed(
   1.187 +			dxgiDevice->SetMaximumFrameLatency(1)
   1.188 +			);
   1.189 +	}
   1.190 +	
   1.191 +	// Set the proper orientation for the swap chain, and generate the
   1.192 +	// 3D matrix transformation for rendering to the rotated swap chain.
   1.193 +	DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
   1.194 +	switch (m_orientation)
   1.195 +	{
   1.196 +		case DisplayOrientations::Landscape:
   1.197 +			rotation = DXGI_MODE_ROTATION_IDENTITY;
   1.198 +			m_orientationTransform3D = XMFLOAT4X4( // 0-degree Z-rotation
   1.199 +				1.0f, 0.0f, 0.0f, 0.0f,
   1.200 +				0.0f, 1.0f, 0.0f, 0.0f,
   1.201 +				0.0f, 0.0f, 1.0f, 0.0f,
   1.202 +				0.0f, 0.0f, 0.0f, 1.0f
   1.203 +				);
   1.204 +			break;
   1.205 +
   1.206 +		case DisplayOrientations::Portrait:
   1.207 +			rotation = DXGI_MODE_ROTATION_ROTATE270;
   1.208 +			m_orientationTransform3D = XMFLOAT4X4( // 90-degree Z-rotation
   1.209 +				0.0f, 1.0f, 0.0f, 0.0f,
   1.210 +				-1.0f, 0.0f, 0.0f, 0.0f,
   1.211 +				0.0f, 0.0f, 1.0f, 0.0f,
   1.212 +				0.0f, 0.0f, 0.0f, 1.0f
   1.213 +				);
   1.214 +			break;
   1.215 +
   1.216 +		case DisplayOrientations::LandscapeFlipped:
   1.217 +			rotation = DXGI_MODE_ROTATION_ROTATE180;
   1.218 +			m_orientationTransform3D = XMFLOAT4X4( // 180-degree Z-rotation
   1.219 +				-1.0f, 0.0f, 0.0f, 0.0f,
   1.220 +				0.0f, -1.0f, 0.0f, 0.0f,
   1.221 +				0.0f, 0.0f, 1.0f, 0.0f,
   1.222 +				0.0f, 0.0f, 0.0f, 1.0f
   1.223 +				);
   1.224 +			break;
   1.225 +
   1.226 +		case DisplayOrientations::PortraitFlipped:
   1.227 +			rotation = DXGI_MODE_ROTATION_ROTATE90;
   1.228 +			m_orientationTransform3D = XMFLOAT4X4( // 270-degree Z-rotation
   1.229 +				0.0f, -1.0f, 0.0f, 0.0f,
   1.230 +				1.0f, 0.0f, 0.0f, 0.0f,
   1.231 +				0.0f, 0.0f, 1.0f, 0.0f,
   1.232 +				0.0f, 0.0f, 0.0f, 1.0f
   1.233 +				);
   1.234 +			break;
   1.235 +
   1.236 +		default:
   1.237 +			throw ref new Platform::FailureException();
   1.238 +	}
   1.239 +
   1.240 +	DX::ThrowIfFailed(
   1.241 +		m_swapChain->SetRotation(rotation)
   1.242 +		);
   1.243 +
   1.244 +	// Create a render target view of the swap chain back buffer.
   1.245 +	ComPtr<ID3D11Texture2D> backBuffer;
   1.246 +	DX::ThrowIfFailed(
   1.247 +		m_swapChain->GetBuffer(
   1.248 +			0,
   1.249 +			__uuidof(ID3D11Texture2D),
   1.250 +			&backBuffer
   1.251 +			)
   1.252 +		);
   1.253 +
   1.254 +	DX::ThrowIfFailed(
   1.255 +		m_d3dDevice->CreateRenderTargetView(
   1.256 +			backBuffer.Get(),
   1.257 +			nullptr,
   1.258 +			&m_renderTargetView
   1.259 +			)
   1.260 +		);
   1.261 +
   1.262 +	// Create a depth stencil view.
   1.263 +	CD3D11_TEXTURE2D_DESC depthStencilDesc(
   1.264 +		DXGI_FORMAT_D24_UNORM_S8_UINT, 
   1.265 +		static_cast<UINT>(m_renderTargetSize.Width),
   1.266 +		static_cast<UINT>(m_renderTargetSize.Height),
   1.267 +		1,
   1.268 +		1,
   1.269 +		D3D11_BIND_DEPTH_STENCIL
   1.270 +		);
   1.271 +
   1.272 +	ComPtr<ID3D11Texture2D> depthStencil;
   1.273 +	DX::ThrowIfFailed(
   1.274 +		m_d3dDevice->CreateTexture2D(
   1.275 +			&depthStencilDesc,
   1.276 +			nullptr,
   1.277 +			&depthStencil
   1.278 +			)
   1.279 +		);
   1.280 +
   1.281 +	CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
   1.282 +	DX::ThrowIfFailed(
   1.283 +		m_d3dDevice->CreateDepthStencilView(
   1.284 +			depthStencil.Get(),
   1.285 +			&depthStencilViewDesc,
   1.286 +			&m_depthStencilView
   1.287 +			)
   1.288 +		);
   1.289 +
   1.290 +	// Set the rendering viewport to target the entire window.
   1.291 +	CD3D11_VIEWPORT viewport(
   1.292 +		0.0f,
   1.293 +		0.0f,
   1.294 +		m_renderTargetSize.Width,
   1.295 +		m_renderTargetSize.Height
   1.296 +		);
   1.297 +
   1.298 +	m_d3dContext->RSSetViewports(1, &viewport);
   1.299 +}
   1.300 +
   1.301  void SDL_winrtrenderer::ResizeMainTexture(int w, int h)
   1.302  {
   1.303      D3D11_TEXTURE2D_DESC textureDesc = {0};
   1.304 @@ -152,6 +421,22 @@
   1.305  		);
   1.306  }
   1.307  
   1.308 +// This method is called in the event handler for the SizeChanged event.
   1.309 +void SDL_winrtrenderer::UpdateForWindowSizeChange()
   1.310 +{
   1.311 +	if (m_window->Bounds.Width  != m_windowBounds.Width ||
   1.312 +		m_window->Bounds.Height != m_windowBounds.Height ||
   1.313 +		m_orientation != DisplayProperties::CurrentOrientation)
   1.314 +	{
   1.315 +		ID3D11RenderTargetView* nullViews[] = {nullptr};
   1.316 +		m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
   1.317 +		m_renderTargetView = nullptr;
   1.318 +		m_depthStencilView = nullptr;
   1.319 +		m_d3dContext->Flush();
   1.320 +		CreateWindowSizeDependentResources();
   1.321 +	}
   1.322 +}
   1.323 +
   1.324  void SDL_winrtrenderer::Render(SDL_Surface * surface, SDL_Rect * rects, int numrects)
   1.325  {
   1.326  	const float blackColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
   1.327 @@ -237,3 +522,46 @@
   1.328  
   1.329  	m_d3dContext->Draw(4, 0);
   1.330  }
   1.331 +
   1.332 +// Method to deliver the final image to the display.
   1.333 +void SDL_winrtrenderer::Present()
   1.334 +{
   1.335 +	// The application may optionally specify "dirty" or "scroll"
   1.336 +	// rects to improve efficiency in certain scenarios.
   1.337 +	DXGI_PRESENT_PARAMETERS parameters = {0};
   1.338 +	parameters.DirtyRectsCount = 0;
   1.339 +	parameters.pDirtyRects = nullptr;
   1.340 +	parameters.pScrollRect = nullptr;
   1.341 +	parameters.pScrollOffset = nullptr;
   1.342 +	
   1.343 +	// The first argument instructs DXGI to block until VSync, putting the application
   1.344 +	// to sleep until the next VSync. This ensures we don't waste any cycles rendering
   1.345 +	// frames that will never be displayed to the screen.
   1.346 +	HRESULT hr = m_swapChain->Present1(1, 0, &parameters);
   1.347 +
   1.348 +	// Discard the contents of the render target.
   1.349 +	// This is a valid operation only when the existing contents will be entirely
   1.350 +	// overwritten. If dirty or scroll rects are used, this call should be removed.
   1.351 +	m_d3dContext->DiscardView(m_renderTargetView.Get());
   1.352 +
   1.353 +	// Discard the contents of the depth stencil.
   1.354 +	m_d3dContext->DiscardView(m_depthStencilView.Get());
   1.355 +
   1.356 +	// If the device was removed either by a disconnect or a driver upgrade, we 
   1.357 +	// must recreate all device resources.
   1.358 +	if (hr == DXGI_ERROR_DEVICE_REMOVED)
   1.359 +	{
   1.360 +		HandleDeviceLost();
   1.361 +	}
   1.362 +	else
   1.363 +	{
   1.364 +		DX::ThrowIfFailed(hr);
   1.365 +	}
   1.366 +}
   1.367 +
   1.368 +// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
   1.369 +float SDL_winrtrenderer::ConvertDipsToPixels(float dips)
   1.370 +{
   1.371 +	static const float dipsPerInch = 96.0f;
   1.372 +	return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer.
   1.373 +}