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 }