1 #include "stdafx.h"
2 #include "Renderer.h"
3 #include "DirectXTK/SpriteBatch.h"
4 #include "DirectXTK/SpriteFont.h"
5 #include "../Core/Console.h"
6 #include "../Core/Debugger.h"
7 #include "../Core/PPU.h"
8 #include "../Core/VideoRenderer.h"
9 #include "../Core/VideoDecoder.h"
10 #include "../Core/EmulationSettings.h"
11 #include "../Core/MessageManager.h"
12 #include "../Utilities/UTF8Util.h"
13 
14 using namespace DirectX;
15 
Renderer(shared_ptr<Console> console,HWND hWnd,bool registerAsMessageManager)16 Renderer::Renderer(shared_ptr<Console> console, HWND hWnd, bool registerAsMessageManager) : BaseRenderer(console, registerAsMessageManager)
17 {
18 	_hWnd = hWnd;
19 
20 	SetScreenSize(256, 240);
21 }
22 
~Renderer()23 Renderer::~Renderer()
24 {
25 	shared_ptr<VideoRenderer> videoRenderer = _console->GetVideoRenderer();
26 	if(videoRenderer) {
27 		videoRenderer->UnregisterRenderingDevice(this);
28 	}
29 	CleanupDevice();
30 }
31 
SetFullscreenMode(bool fullscreen,void * windowHandle,uint32_t monitorWidth,uint32_t monitorHeight)32 void Renderer::SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight)
33 {
34 	if(fullscreen != _fullscreen || _hWnd != (HWND)windowHandle) {
35 		_hWnd = (HWND)windowHandle;
36 		_monitorWidth = monitorWidth;
37 		_monitorHeight = monitorHeight;
38 		_newFullscreen = fullscreen;
39 	}
40 }
41 
SetScreenSize(uint32_t width,uint32_t height)42 void Renderer::SetScreenSize(uint32_t width, uint32_t height)
43 {
44 	ScreenSize screenSize;
45 	_console->GetVideoDecoder()->GetScreenSize(screenSize, false);
46 
47 	if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter() || _newFullscreen != _fullscreen) {
48 		auto frameLock = _frameLock.AcquireSafe();
49 		auto textureLock = _textureLock.AcquireSafe();
50 		_console->GetVideoDecoder()->GetScreenSize(screenSize, false);
51 		if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter() || _newFullscreen != _fullscreen) {
52 			_nesFrameHeight = height;
53 			_nesFrameWidth = width;
54 			_newFrameBufferSize = width*height;
55 
56 			bool needReset = _fullscreen != _newFullscreen || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter();
57 			bool fullscreenResizeMode = _fullscreen && _newFullscreen;
58 
59 			if(_pSwapChain && _fullscreen && !_newFullscreen) {
60 				HRESULT hr = _pSwapChain->SetFullscreenState(FALSE, NULL);
61 				if(FAILED(hr)) {
62 					MessageManager::Log("SetFullscreenState(FALSE) failed - Error:" + std::to_string(hr));
63 				}
64 			}
65 
66 			_fullscreen = _newFullscreen;
67 
68 			_screenHeight = screenSize.Height;
69 			_screenWidth = screenSize.Width;
70 
71 			if(_fullscreen) {
72 				_realScreenHeight = _monitorHeight;
73 				_realScreenWidth = _monitorWidth;
74 			} else {
75 				_realScreenHeight = screenSize.Height;
76 				_realScreenWidth = screenSize.Width;
77 			}
78 
79 			_leftMargin = (_realScreenWidth - _screenWidth) / 2;
80 			_topMargin = (_realScreenHeight - _screenHeight) / 2;
81 
82 			_screenBufferSize = _realScreenHeight*_realScreenWidth;
83 
84 			if(!_pSwapChain || needReset) {
85 				Reset();
86 			} else {
87 				if(fullscreenResizeMode) {
88 					ResetNesBuffers();
89 					CreateNesBuffers();
90 				} else {
91 					ResetNesBuffers();
92 					ReleaseRenderTargetView();
93 					_pSwapChain->ResizeBuffers(1, _realScreenWidth, _realScreenHeight, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
94 					CreateRenderTargetView();
95 					CreateNesBuffers();
96 				}
97 			}
98 		}
99 	}
100 }
101 
Reset()102 void Renderer::Reset()
103 {
104 	auto lock = _frameLock.AcquireSafe();
105 	CleanupDevice();
106 	if(FAILED(InitDevice())) {
107 		CleanupDevice();
108 	} else {
109 		_console->GetVideoRenderer()->RegisterRenderingDevice(this);
110 	}
111 }
112 
CleanupDevice()113 void Renderer::CleanupDevice()
114 {
115 	ResetNesBuffers();
116 	ReleaseRenderTargetView();
117 	if(_pAlphaEnableBlendingState) {
118 		_pAlphaEnableBlendingState->Release();
119 		_pAlphaEnableBlendingState = nullptr;
120 	}
121 	if(_pDepthDisabledStencilState) {
122 		_pDepthDisabledStencilState->Release();
123 		_pDepthDisabledStencilState = nullptr;
124 	}
125 	if(_samplerState) {
126 		_samplerState->Release();
127 		_samplerState = nullptr;
128 	}
129 	if(_pSwapChain) {
130 		_pSwapChain->SetFullscreenState(false, nullptr);
131 		_pSwapChain->Release();
132 		_pSwapChain = nullptr;
133 	}
134 	if(_pDeviceContext) {
135 		_pDeviceContext->Release();
136 		_pDeviceContext = nullptr;
137 	}
138 	if(_pd3dDevice) {
139 		_pd3dDevice->Release();
140 		_pd3dDevice = nullptr;
141 	}
142 }
143 
ResetNesBuffers()144 void Renderer::ResetNesBuffers()
145 {
146 	if(_pTexture) {
147 		_pTexture->Release();
148 		_pTexture = nullptr;
149 	}
150 	if(_overlayTexture) {
151 		_overlayTexture->Release();
152 		_overlayTexture = nullptr;
153 	}
154 	if(_pTextureSrv) {
155 		_pTextureSrv->Release();
156 		_pTextureSrv = nullptr;
157 	}
158 	if(_pOverlaySrv) {
159 		_pOverlaySrv->Release();
160 		_pOverlaySrv = nullptr;
161 	}
162 	if(_textureBuffer[0]) {
163 		delete[] _textureBuffer[0];
164 		_textureBuffer[0] = nullptr;
165 	}
166 	if(_textureBuffer[1]) {
167 		delete[] _textureBuffer[1];
168 		_textureBuffer[1] = nullptr;
169 	}
170 }
171 
ReleaseRenderTargetView()172 void Renderer::ReleaseRenderTargetView()
173 {
174 	if(_pRenderTargetView) {
175 		_pRenderTargetView->Release();
176 		_pRenderTargetView = nullptr;
177 	}
178 }
179 
CreateRenderTargetView()180 HRESULT Renderer::CreateRenderTargetView()
181 {
182 	// Create a render target view
183 	ID3D11Texture2D* pBackBuffer = nullptr;
184 	HRESULT hr = _pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
185 	if(FAILED(hr)) {
186 		MessageManager::Log("SwapChain::GetBuffer() failed - Error:" + std::to_string(hr));
187 		return hr;
188 	}
189 
190 	hr = _pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &_pRenderTargetView);
191 	pBackBuffer->Release();
192 	if(FAILED(hr)) {
193 		MessageManager::Log("D3DDevice::CreateRenderTargetView() failed - Error:" + std::to_string(hr));
194 		return hr;
195 	}
196 
197 	_pDeviceContext->OMSetRenderTargets(1, &_pRenderTargetView, nullptr);
198 
199 	return S_OK;
200 }
201 
CreateNesBuffers()202 HRESULT Renderer::CreateNesBuffers()
203 {
204 	// Setup the viewport
205 	D3D11_VIEWPORT vp;
206 	vp.Width = (FLOAT)_realScreenWidth;
207 	vp.Height = (FLOAT)_realScreenHeight;
208 	vp.MinDepth = 0.0f;
209 	vp.MaxDepth = 1.0f;
210 	vp.TopLeftX = 0;
211 	vp.TopLeftY = 0;
212 	_pDeviceContext->RSSetViewports(1, &vp);
213 
214 	_textureBuffer[0] = new uint8_t[_nesFrameWidth*_nesFrameHeight * 4];
215 	_textureBuffer[1] = new uint8_t[_nesFrameWidth*_nesFrameHeight * 4];
216 	memset(_textureBuffer[0], 0, _nesFrameWidth*_nesFrameHeight * 4);
217 	memset(_textureBuffer[1], 0, _nesFrameWidth*_nesFrameHeight * 4);
218 
219 	_pTexture = CreateTexture(_nesFrameWidth, _nesFrameHeight);
220 	if(!_pTexture) {
221 		return S_FALSE;
222 	}
223 	_overlayTexture = CreateTexture(8, 8);
224 	if(!_overlayTexture) {
225 		return S_FALSE;
226 	}
227 	_pTextureSrv = GetShaderResourceView(_pTexture);
228 	if(!_pTextureSrv) {
229 		return S_FALSE;
230 	}
231 	_pOverlaySrv = GetShaderResourceView(_overlayTexture);
232 	if(!_pOverlaySrv) {
233 		return S_FALSE;
234 	}
235 
236 	////////////////////////////////////////////////////////////////////////////
237 	_spriteBatch.reset(new SpriteBatch(_pDeviceContext));
238 
239 	_largeFont.reset(new SpriteFont(_pd3dDevice, L"Resources\\Font.64.spritefont"));
240 	_font.reset(new SpriteFont(_pd3dDevice, L"Resources\\Font.24.spritefont"));
241 	_font->SetDefaultCharacter('?');
242 
243 	return S_OK;
244 }
245 
246 //--------------------------------------------------------------------------------------
247 // Create Direct3D device and swap chain
248 //--------------------------------------------------------------------------------------
InitDevice()249 HRESULT Renderer::InitDevice()
250 {
251 	HRESULT hr = S_OK;
252 
253 	UINT createDeviceFlags = 0;
254 #ifdef _DEBUG
255 	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
256 #endif
257 
258 	D3D_DRIVER_TYPE driverTypes[] =
259 	{
260 		D3D_DRIVER_TYPE_HARDWARE,
261 		D3D_DRIVER_TYPE_WARP,
262 		D3D_DRIVER_TYPE_REFERENCE,
263 	};
264 	UINT numDriverTypes = ARRAYSIZE(driverTypes);
265 
266 	D3D_FEATURE_LEVEL featureLevels[] =
267 	{
268 		D3D_FEATURE_LEVEL_11_1,
269 		D3D_FEATURE_LEVEL_11_0,
270 		D3D_FEATURE_LEVEL_10_1,
271 		D3D_FEATURE_LEVEL_10_0,
272 	};
273 	UINT numFeatureLevels = ARRAYSIZE(featureLevels);
274 
275 	DXGI_SWAP_CHAIN_DESC sd;
276 	ZeroMemory(&sd, sizeof(sd));
277 	sd.BufferCount = 1;
278 	sd.BufferDesc.Width = _realScreenWidth;
279 	sd.BufferDesc.Height = _realScreenHeight;
280 	sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
281 	sd.BufferDesc.RefreshRate.Numerator = _console->GetSettings()->GetExclusiveRefreshRate();
282 	sd.BufferDesc.RefreshRate.Denominator = 1;
283 	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
284 	sd.Flags = _fullscreen ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0;
285 	sd.OutputWindow = _hWnd;
286 	sd.SampleDesc.Count = 1;
287 	sd.SampleDesc.Quality = 0;
288 	sd.Windowed = TRUE;
289 
290 	D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_NULL;
291 	D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_1;
292 	for(UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++) {
293 		driverType = driverTypes[driverTypeIndex];
294 		featureLevel = D3D_FEATURE_LEVEL_11_1;
295 		hr = D3D11CreateDeviceAndSwapChain(nullptr, driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &_pSwapChain, &_pd3dDevice, &featureLevel, &_pDeviceContext);
296 
297 		/*if(FAILED(hr)) {
298 			MessageManager::Log("D3D11CreateDeviceAndSwapChain() failed - Error:" + std::to_string(hr));
299 		}*/
300 
301 		if(hr == E_INVALIDARG) {
302 			// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
303 			featureLevel = D3D_FEATURE_LEVEL_11_0;
304 			hr = D3D11CreateDeviceAndSwapChain(nullptr, driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1, D3D11_SDK_VERSION, &sd, &_pSwapChain, &_pd3dDevice, &featureLevel, &_pDeviceContext);
305 		}
306 
307 		if(SUCCEEDED(hr)) {
308 			break;
309 		}
310 	}
311 
312 	if(FAILED(hr)) {
313 		MessageManager::Log("D3D11CreateDeviceAndSwapChain() failed - Error:" + std::to_string(hr));
314 		return hr;
315 	}
316 
317 	if(_fullscreen) {
318 		hr = _pSwapChain->SetFullscreenState(TRUE, NULL);
319 		if(FAILED(hr)) {
320 			MessageManager::Log("SetFullscreenState(true) failed - Error:" + std::to_string(hr));
321 			MessageManager::Log("Switching back to windowed mode");
322 			hr = _pSwapChain->SetFullscreenState(FALSE, NULL);
323 			if(FAILED(hr)) {
324 				MessageManager::Log("SetFullscreenState(false) failed - Error:" + std::to_string(hr));
325 				return hr;
326 			}
327 		}
328 	}
329 
330 	hr = CreateRenderTargetView();
331 	if(FAILED(hr)) {
332 		return hr;
333 	}
334 
335 	D3D11_DEPTH_STENCIL_DESC depthDisabledStencilDesc;
336 	ZeroMemory(&depthDisabledStencilDesc, sizeof(depthDisabledStencilDesc));
337 	depthDisabledStencilDesc.DepthEnable = false;
338 	depthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
339 	depthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
340 	depthDisabledStencilDesc.StencilEnable = true;
341 	depthDisabledStencilDesc.StencilReadMask = 0xFF;
342 	depthDisabledStencilDesc.StencilWriteMask = 0xFF;
343 	depthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
344 	depthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
345 	depthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
346 	depthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
347 	depthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
348 	depthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
349 	depthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
350 	depthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
351 
352 	// Create the state using the device.
353 	hr = _pd3dDevice->CreateDepthStencilState(&depthDisabledStencilDesc, &_pDepthDisabledStencilState);
354 	if(FAILED(hr)) {
355 		MessageManager::Log("D3DDevice::CreateDepthStencilState() failed - Error:" + std::to_string(hr));
356 		return hr;
357 	}
358 
359 	// Clear the blend state description.
360 	D3D11_BLEND_DESC blendStateDescription;
361 	ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
362 
363 	// Create an alpha enabled blend state description.
364 	blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
365 	blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
366 	blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
367 	blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
368 	blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
369 	blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
370 	blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
371 	blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;
372 
373 	// Create the blend state using the description.
374 	hr = _pd3dDevice->CreateBlendState(&blendStateDescription, &_pAlphaEnableBlendingState);
375 	if(FAILED(hr)) {
376 		MessageManager::Log("D3DDevice::CreateBlendState() failed - Error:" + std::to_string(hr));
377 		return hr;
378 	}
379 
380 	float blendFactor[4];
381 	blendFactor[0] = 0.0f;
382 	blendFactor[1] = 0.0f;
383 	blendFactor[2] = 0.0f;
384 	blendFactor[3] = 0.0f;
385 
386 	_pDeviceContext->OMSetBlendState(_pAlphaEnableBlendingState, blendFactor, 0xffffffff);
387 	_pDeviceContext->OMSetDepthStencilState(_pDepthDisabledStencilState, 1);
388 
389 	hr = CreateNesBuffers();
390 	if(FAILED(hr)) {
391 		return hr;
392 	}
393 
394 	hr = CreateSamplerState();
395 	if(FAILED(hr)) {
396 		return hr;
397 	}
398 
399 	return S_OK;
400 }
401 
CreateSamplerState()402 HRESULT Renderer::CreateSamplerState()
403 {
404 	_resizeFilter = _console->GetSettings()->GetVideoResizeFilter();
405 
406 	//Sample state
407 	D3D11_SAMPLER_DESC samplerDesc;
408 	ZeroMemory(&samplerDesc, sizeof(samplerDesc));
409 	samplerDesc.Filter = _resizeFilter == VideoResizeFilter::Bilinear ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT;
410 	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
411 	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
412 	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
413 	//samplerDesc.BorderColor = { 1.0f, 1.0f, 1.0f, 1.0f };
414 	samplerDesc.MinLOD = -FLT_MAX;
415 	samplerDesc.MaxLOD = FLT_MAX;
416 	samplerDesc.MipLODBias = 0.0f;
417 	samplerDesc.MaxAnisotropy = 1;
418 	samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
419 
420 	HRESULT hr = _pd3dDevice->CreateSamplerState(&samplerDesc, &_samplerState);
421 	if(FAILED(hr)) {
422 		MessageManager::Log("D3DDevice::CreateSamplerState() failed - Error:" + std::to_string(hr));
423 	}
424 
425 	return hr;
426 }
427 
CreateTexture(uint32_t width,uint32_t height)428 ID3D11Texture2D* Renderer::CreateTexture(uint32_t width, uint32_t height)
429 {
430 	ID3D11Texture2D* texture;
431 
432 	D3D11_TEXTURE2D_DESC desc;
433 	ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
434 	desc.ArraySize = 1;
435 	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
436 	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
437 	desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
438 	desc.MipLevels = 1;
439 	desc.MiscFlags = 0;
440 	desc.SampleDesc.Count = 1;
441 	desc.SampleDesc.Quality = 0;
442 	desc.Usage = D3D11_USAGE_DYNAMIC;
443 	desc.Width = width;
444 	desc.Height = height;
445 	desc.MiscFlags = 0;
446 
447 	HRESULT hr = _pd3dDevice->CreateTexture2D(&desc, nullptr, &texture);
448 	if(FAILED(hr)) {
449 		MessageManager::Log("D3DDevice::CreateTexture() failed - Error:" + std::to_string(hr));
450 		return nullptr;
451 	}
452 	return texture;
453 }
454 
GetShaderResourceView(ID3D11Texture2D * texture)455 ID3D11ShaderResourceView* Renderer::GetShaderResourceView(ID3D11Texture2D* texture)
456 {
457 	ID3D11ShaderResourceView *shaderResourceView = nullptr;
458 	HRESULT hr = _pd3dDevice->CreateShaderResourceView(texture, nullptr, &shaderResourceView);
459 	if(FAILED(hr)) {
460 		MessageManager::Log("D3DDevice::CreateShaderResourceView() failed - Error:" + std::to_string(hr));
461 		return nullptr;
462 	}
463 
464 	return shaderResourceView;
465 }
466 
DrawString(string message,float x,float y,DirectX::FXMVECTOR color,float scale,SpriteFont * font)467 void Renderer::DrawString(string message, float x, float y, DirectX::FXMVECTOR color, float scale, SpriteFont* font)
468 {
469 	std::wstring textStr = utf8::utf8::decode(message);
470 	DrawString(textStr, x, y, color, scale, font);
471 }
472 
DrawString(std::wstring message,float x,float y,DirectX::FXMVECTOR color,float scale,SpriteFont * font)473 void Renderer::DrawString(std::wstring message, float x, float y, DirectX::FXMVECTOR color, float scale, SpriteFont* font)
474 {
475 	const wchar_t *text = message.c_str();
476 	if(font == nullptr) {
477 		font = _font.get();
478 	}
479 
480 	font->DrawString(_spriteBatch.get(), text, XMFLOAT2(x+_leftMargin, y+_topMargin), color, 0.0f, XMFLOAT2(0, 0), scale);
481 }
482 
UpdateFrame(void * frameBuffer,uint32_t width,uint32_t height)483 void Renderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
484 {
485 	SetScreenSize(width, height);
486 
487 	uint32_t bpp = 4;
488 	auto lock = _textureLock.AcquireSafe();
489 	if(_textureBuffer[0]) {
490 		//_textureBuffer[0] may be null if directx failed to initialize properly
491 		memcpy(_textureBuffer[0], frameBuffer, width*height*bpp);
492 		_needFlip = true;
493 		_frameChanged = true;
494 	}
495 }
496 
DrawNESScreen()497 void Renderer::DrawNESScreen()
498 {
499 	//Swap buffers - emulator always writes to _textureBuffer[0], screen always draws _textureBuffer[1]
500 	if(_needFlip) {
501 		auto lock = _textureLock.AcquireSafe();
502 		uint8_t* textureBuffer = _textureBuffer[0];
503 		_textureBuffer[0] = _textureBuffer[1];
504 		_textureBuffer[1] = textureBuffer;
505 		_needFlip = false;
506 
507 		if(_frameChanged) {
508 			_frameChanged = false;
509 			_renderedFrameCount++;
510 		}
511 	}
512 
513 	//Copy buffer to texture
514 	uint32_t bpp = 4;
515 	uint32_t rowPitch = _nesFrameWidth * bpp;
516 	D3D11_MAPPED_SUBRESOURCE dd;
517 	HRESULT hr = _pDeviceContext->Map(_pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd);
518 	if(FAILED(hr)) {
519 		MessageManager::Log("DeviceContext::Map() failed - Error:" + std::to_string(hr));
520 		return;
521 	}
522 	uint8_t* surfacePointer = (uint8_t*)dd.pData;
523 	uint8_t* videoBuffer = _textureBuffer[1];
524 	for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) {
525 		memcpy(surfacePointer, videoBuffer, rowPitch);
526 		videoBuffer += rowPitch;
527 		surfacePointer += dd.RowPitch;
528 	}
529 	_pDeviceContext->Unmap(_pTexture, 0);
530 
531 	RECT destRect;
532 	destRect.left = _leftMargin;
533 	destRect.top = _topMargin;
534 	destRect.right = _screenWidth+_leftMargin;
535 	destRect.bottom = _screenHeight+_topMargin;
536 
537 	_spriteBatch->Draw(_pTextureSrv, destRect);
538 }
539 
DrawPauseScreen(bool disableOverlay)540 void Renderer::DrawPauseScreen(bool disableOverlay)
541 {
542 	if(disableOverlay) {
543 		const static XMVECTORF32 transparentBlue = { { { 0.415686309f, 0.352941185f, 0.803921640f, 0.66f } } };
544 		DrawString("I", 15, 15, transparentBlue, 2.0f, _font.get());
545 		DrawString("I", 32, 15, transparentBlue, 2.0f, _font.get());
546 	} else {
547 		RECT destRect;
548 		destRect.left = 0;
549 		destRect.top = 0;
550 		destRect.right = _realScreenWidth;
551 		destRect.bottom = _realScreenHeight;
552 
553 		D3D11_MAPPED_SUBRESOURCE dd;
554 		HRESULT hr = _pDeviceContext->Map(_overlayTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd);
555 		if(FAILED(hr)) {
556 			MessageManager::Log("(DrawPauseScreen) DeviceContext::Map() failed - Error:" + std::to_string(hr));
557 			return;
558 		}
559 
560 		uint8_t* surfacePointer = (uint8_t*)dd.pData;
561 		for(uint32_t i = 0, len = 8; i < len; i++) {
562 			//Gray transparent overlay
563 			for(int j = 0; j < 8; j++) {
564 				((uint32_t*)surfacePointer)[j] = 0xAA222222;
565 			}
566 			surfacePointer += dd.RowPitch;
567 		}
568 		_pDeviceContext->Unmap(_overlayTexture, 0);
569 
570 		_spriteBatch->Draw(_pOverlaySrv, destRect);
571 
572 		XMVECTOR stringDimensions = _largeFont->MeasureString(L"PAUSE");
573 		float x = (float)_screenWidth / 2 - stringDimensions.m128_f32[0] / 2;
574 		float y = (float)_screenHeight / 2 - stringDimensions.m128_f32[1] / 2 - 8;
575 		DrawString("PAUSE", x, y, Colors::AntiqueWhite, 1.0f, _largeFont.get());
576 
577 		string utf8Message = _console->GetSettings()->GetPauseScreenMessage();
578 		if(utf8Message.size() > 0) {
579 			std::wstring message = utf8::utf8::decode(utf8Message);
580 			float width = MeasureString(message);
581 			DrawString(message, (float)_screenWidth - width - 20, (float)_screenHeight - 40, Colors::AntiqueWhite, 1.0f, _font.get());
582 		}
583 	}
584 }
585 
Render()586 void Renderer::Render()
587 {
588 	bool paused = _console->IsPaused() && _console->IsRunning();
589 	bool disableOverlay = _console->GetSettings()->CheckFlag(EmulationFlags::HidePauseOverlay);
590 	shared_ptr<Debugger> debugger = _console->GetDebugger(false);
591 	if(debugger && debugger->IsExecutionStopped()) {
592 		paused = debugger->IsPauseIconShown();
593 		disableOverlay = true;
594 	}
595 
596 	if(_noUpdateCount > 10 || _frameChanged || paused || IsMessageShown()) {
597 		_noUpdateCount = 0;
598 
599 		auto lock = _frameLock.AcquireSafe();
600 		if(_newFullscreen != _fullscreen) {
601 			SetScreenSize(_nesFrameWidth, _nesFrameHeight);
602 		}
603 
604 		if(_pDeviceContext == nullptr) {
605 			//DirectX failed to initialize, try to init
606 			Reset();
607 			if(_pDeviceContext == nullptr) {
608 				//Can't init, prevent crash
609 				return;
610 			}
611 		}
612 
613 		// Clear the back buffer
614 		_pDeviceContext->ClearRenderTargetView(_pRenderTargetView, Colors::Black);
615 
616 		_spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, _samplerState);
617 
618 		//Draw nes screen
619 		DrawNESScreen();
620 
621 		if(paused) {
622 			DrawPauseScreen(disableOverlay);
623 		}
624 
625 		if(_console->IsRunning()) {
626 			DrawCounters();
627 		}
628 
629 		DrawToasts();
630 
631 		_spriteBatch->End();
632 
633 		// Present the information rendered to the back buffer to the front buffer (the screen)
634 		HRESULT hr = _pSwapChain->Present(_console->GetSettings()->CheckFlag(EmulationFlags::VerticalSync) ? 1 : 0, 0);
635 		if(FAILED(hr)) {
636 			MessageManager::Log("SwapChain::Present() failed - Error:" + std::to_string(hr));
637 			if(hr == DXGI_ERROR_DEVICE_REMOVED) {
638 				MessageManager::Log("D3DDevice: GetDeviceRemovedReason: " + std::to_string(_pd3dDevice->GetDeviceRemovedReason()));
639 			}
640 			MessageManager::Log("Trying to reset DX...");
641 			Reset();
642 		}
643 	} else {
644 		_noUpdateCount++;
645 	}
646 }
647 
DrawString(std::wstring message,int x,int y,uint8_t r,uint8_t g,uint8_t b,uint8_t opacity)648 void Renderer::DrawString(std::wstring message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity)
649 {
650 	XMVECTORF32 color = { (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)opacity / 255.0f };
651 	_font->DrawString(_spriteBatch.get(), message.c_str(), XMFLOAT2((float)x+_leftMargin, (float)y+_topMargin), color);
652 }
653 
MeasureString(std::wstring text)654 float Renderer::MeasureString(std::wstring text)
655 {
656 	XMVECTOR measure = _font->MeasureString(text.c_str());
657 	float* measureF = (float*)&measure;
658 	return measureF[0];
659 }
660 
ContainsCharacter(wchar_t character)661 bool Renderer::ContainsCharacter(wchar_t character)
662 {
663 	return _font->ContainsCharacter(character);
664 }