2014-06-12 21:48:04 -04:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "Renderer.h"
|
2014-06-18 22:54:23 -04:00
|
|
|
#include "..\Core\PPU.h"
|
2014-06-23 16:38:01 -04:00
|
|
|
#include "..\Core\Console.h"
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
namespace NES
|
|
|
|
{
|
2014-06-23 19:02:09 -04:00
|
|
|
Renderer::Renderer(HWND hWnd)
|
|
|
|
{
|
2014-06-19 17:06:00 -04:00
|
|
|
PPU::RegisterVideoDevice(this);
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
_hWnd = hWnd;
|
2014-06-23 16:38:01 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
if(FAILED(InitDevice())) {
|
|
|
|
CleanupDevice();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-23 19:02:09 -04:00
|
|
|
Renderer::~Renderer()
|
|
|
|
{
|
|
|
|
CleanupDevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::CleanupDevice()
|
|
|
|
{
|
2014-06-23 20:00:51 -04:00
|
|
|
if(_pDeviceContext) _pDeviceContext->ClearState();
|
2014-06-23 19:02:09 -04:00
|
|
|
|
|
|
|
if(_pRenderTargetView) _pRenderTargetView->Release();
|
|
|
|
if(_samplerState) _samplerState->Release();
|
|
|
|
|
|
|
|
if(_pSwapChain) _pSwapChain->Release();
|
2014-06-23 20:00:51 -04:00
|
|
|
if(_pDeviceContext1) _pDeviceContext1->Release();
|
2014-06-23 19:02:09 -04:00
|
|
|
if(_pd3dDevice1) _pd3dDevice1->Release();
|
|
|
|
if(_pd3dDevice) _pd3dDevice->Release();
|
|
|
|
}
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Create Direct3D device and swap chain
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT Renderer::InitDevice()
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
RECT rc;
|
|
|
|
GetClientRect(_hWnd, &rc);
|
2014-06-21 19:03:13 -04:00
|
|
|
UINT width = 256; // rc.right - rc.left;
|
|
|
|
UINT height = 240; // rc.bottom - rc.top;
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
UINT createDeviceFlags = 0;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
D3D_DRIVER_TYPE driverTypes[] =
|
|
|
|
{
|
|
|
|
D3D_DRIVER_TYPE_HARDWARE,
|
|
|
|
D3D_DRIVER_TYPE_WARP,
|
|
|
|
D3D_DRIVER_TYPE_REFERENCE,
|
|
|
|
};
|
|
|
|
UINT numDriverTypes = ARRAYSIZE(driverTypes);
|
|
|
|
|
|
|
|
D3D_FEATURE_LEVEL featureLevels[] =
|
|
|
|
{
|
|
|
|
D3D_FEATURE_LEVEL_11_1,
|
|
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
|
|
D3D_FEATURE_LEVEL_10_1,
|
|
|
|
D3D_FEATURE_LEVEL_10_0,
|
|
|
|
};
|
|
|
|
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
|
|
|
|
|
|
|
|
DXGI_SWAP_CHAIN_DESC sd;
|
|
|
|
ZeroMemory(&sd, sizeof(sd));
|
|
|
|
sd.BufferCount = 1;
|
2014-06-23 16:38:01 -04:00
|
|
|
sd.BufferDesc.Width = width*4;
|
|
|
|
sd.BufferDesc.Height = height*4;
|
2014-06-19 16:07:37 -04:00
|
|
|
sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
2014-06-12 21:48:04 -04:00
|
|
|
sd.BufferDesc.RefreshRate.Numerator = 60;
|
|
|
|
sd.BufferDesc.RefreshRate.Denominator = 1;
|
|
|
|
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
|
|
sd.OutputWindow = _hWnd;
|
|
|
|
sd.SampleDesc.Count = 1;
|
|
|
|
sd.SampleDesc.Quality = 0;
|
|
|
|
sd.Windowed = TRUE;
|
|
|
|
|
|
|
|
for(UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++) {
|
|
|
|
_driverType = driverTypes[driverTypeIndex];
|
|
|
|
hr = D3D11CreateDeviceAndSwapChain(nullptr, _driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
|
2014-06-23 20:00:51 -04:00
|
|
|
D3D11_SDK_VERSION, &sd, &_pSwapChain, &_pd3dDevice, &_featureLevel, &_pDeviceContext);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
if(hr == E_INVALIDARG) {
|
|
|
|
// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
|
|
|
|
hr = D3D11CreateDeviceAndSwapChain(nullptr, _driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
|
2014-06-23 20:00:51 -04:00
|
|
|
D3D11_SDK_VERSION, &sd, &_pSwapChain, &_pd3dDevice, &_featureLevel, &_pDeviceContext);
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain the Direct3D 11.1 versions if available
|
|
|
|
hr = _pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&_pd3dDevice1));
|
|
|
|
if(SUCCEEDED(hr)) {
|
2014-06-23 20:00:51 -04:00
|
|
|
(void)_pDeviceContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&_pDeviceContext1));
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a render target view
|
|
|
|
ID3D11Texture2D* pBackBuffer = nullptr;
|
|
|
|
hr = _pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
|
|
|
|
if(FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = _pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &_pRenderTargetView);
|
|
|
|
pBackBuffer->Release();
|
|
|
|
if(FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->OMSetRenderTargets(1, &_pRenderTargetView, nullptr);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
// Setup the viewport
|
|
|
|
UINT fred;
|
|
|
|
D3D11_VIEWPORT vp;
|
2014-06-23 16:38:01 -04:00
|
|
|
vp.Width = (FLOAT)width*4;
|
|
|
|
vp.Height = (FLOAT)height*4;
|
2014-06-12 21:48:04 -04:00
|
|
|
vp.MinDepth = 0.0f;
|
|
|
|
vp.MaxDepth = 1.0f;
|
|
|
|
vp.TopLeftX = 0;
|
|
|
|
vp.TopLeftY = 0;
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->RSSetViewports(1, &vp);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-19 16:07:37 -04:00
|
|
|
_pd3dDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM, 16, &fred);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-18 22:54:23 -04:00
|
|
|
uint16_t screenwidth = 256;
|
2014-06-12 21:48:04 -04:00
|
|
|
uint16_t screenheight = 240;
|
|
|
|
|
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
|
|
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
|
|
|
|
desc.ArraySize = 1;
|
|
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
2014-06-19 16:07:37 -04:00
|
|
|
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
2014-06-12 21:48:04 -04:00
|
|
|
desc.MipLevels = 1;
|
|
|
|
desc.MiscFlags = 0;
|
|
|
|
desc.SampleDesc.Count = 1;
|
|
|
|
desc.SampleDesc.Quality = fred;
|
|
|
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
|
|
desc.Width = screenwidth;
|
|
|
|
desc.Height = screenheight;
|
|
|
|
desc.MiscFlags = 0;
|
|
|
|
|
2014-06-19 17:06:00 -04:00
|
|
|
_videoRAM = new uint8_t[screenwidth*screenheight * 4];
|
|
|
|
_nextFrameBuffer = new uint8_t[screenwidth*screenheight * 4];
|
|
|
|
memset(_videoRAM, 0x00, screenwidth*screenheight*4);
|
|
|
|
memset(_nextFrameBuffer, 0x00, screenwidth*screenheight*4);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
D3D11_SUBRESOURCE_DATA tbsd;
|
|
|
|
tbsd.pSysMem = (void *)_videoRAM;
|
|
|
|
tbsd.SysMemPitch = screenwidth * 4;
|
|
|
|
tbsd.SysMemSlicePitch = screenwidth*screenheight * 4; // Not needed since this is a 2d texture
|
|
|
|
|
|
|
|
if(FAILED(_pd3dDevice->CreateTexture2D(&desc, &tbsd, &_pTexture))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-23 16:38:01 -04:00
|
|
|
_overlayBuffer = new uint8_t[screenwidth*screenheight*4*4*4]; //High res overlay for UI elements (4x res)
|
|
|
|
memset(_overlayBuffer, 0x00, screenwidth*screenheight*4*4*4);
|
|
|
|
|
|
|
|
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
|
|
|
|
desc.ArraySize = 1;
|
|
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
desc.MipLevels = 1;
|
|
|
|
desc.MiscFlags = 0;
|
|
|
|
desc.SampleDesc.Count = 1;
|
|
|
|
desc.SampleDesc.Quality = fred;
|
|
|
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
|
|
desc.Width = screenwidth * 4;
|
|
|
|
desc.Height = screenheight * 4;
|
|
|
|
desc.MiscFlags = 0;
|
|
|
|
|
|
|
|
tbsd.pSysMem = (void *)_overlayBuffer;
|
|
|
|
tbsd.SysMemPitch = screenwidth * 4 * 4;
|
|
|
|
tbsd.SysMemSlicePitch = screenwidth*screenheight * 4 * 4;
|
|
|
|
|
|
|
|
if(FAILED(_pd3dDevice->CreateTexture2D(&desc, &tbsd, &_overlayTexture))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
2014-06-23 20:00:51 -04:00
|
|
|
_spriteBatch.reset(new SpriteBatch(_pDeviceContext));
|
2014-06-23 16:38:01 -04:00
|
|
|
|
|
|
|
_font.reset(new SpriteFont(_pd3dDevice, L"Calibri.30.spritefont"));
|
|
|
|
|
|
|
|
//Sample state
|
|
|
|
D3D11_SAMPLER_DESC samplerDesc;
|
|
|
|
ZeroMemory(&desc, sizeof(desc));
|
|
|
|
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
|
|
|
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
|
|
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
|
|
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
|
|
//samplerDesc.BorderColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
|
|
samplerDesc.MinLOD = -FLT_MAX;
|
|
|
|
samplerDesc.MaxLOD = FLT_MAX;
|
|
|
|
samplerDesc.MipLODBias = 0.0f;
|
|
|
|
samplerDesc.MaxAnisotropy = 1;
|
|
|
|
samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
|
|
|
|
|
|
|
_pd3dDevice->CreateSamplerState(&samplerDesc, &_samplerState);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
ID3D11ShaderResourceView* Renderer::GetShaderResourceView(ID3D11Texture2D* texture)
|
2014-06-12 21:48:04 -04:00
|
|
|
{
|
2014-06-23 20:00:51 -04:00
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
|
|
D3D11_RESOURCE_DIMENSION type;
|
|
|
|
texture->GetType(&type);
|
|
|
|
texture->GetDesc(&desc);
|
|
|
|
srvDesc.Format = desc.Format;
|
|
|
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
|
|
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
|
|
|
srvDesc.Texture2D.MostDetailedMip = desc.MipLevels - 1;
|
|
|
|
|
|
|
|
ID3D11ShaderResourceView *shaderResourceView = nullptr;
|
|
|
|
_pd3dDevice->CreateShaderResourceView(texture, &srvDesc, &shaderResourceView);
|
|
|
|
|
|
|
|
return shaderResourceView;
|
|
|
|
}
|
|
|
|
|
2014-06-25 21:52:37 -04:00
|
|
|
void Renderer::DisplayMessage(wstring text, uint32_t duration)
|
|
|
|
{
|
|
|
|
_displayMessage = text;
|
|
|
|
_displayTimestamp = timeGetTime() + duration;
|
|
|
|
}
|
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
void Renderer::DrawNESScreen()
|
|
|
|
{
|
|
|
|
RECT sourceRect;
|
|
|
|
sourceRect.left = 0;
|
|
|
|
sourceRect.right = 256;
|
|
|
|
sourceRect.bottom = 240;
|
|
|
|
sourceRect.top = 0;
|
|
|
|
|
|
|
|
XMVECTOR position{ { 0, 0 } };
|
|
|
|
|
2014-06-18 22:54:23 -04:00
|
|
|
UINT screenwidth = 256, screenheight = 240;
|
2014-06-12 21:48:04 -04:00
|
|
|
|
|
|
|
D3D11_MAPPED_SUBRESOURCE dd;
|
|
|
|
dd.pData = (void *)_videoRAM;
|
|
|
|
dd.RowPitch = screenwidth * 4;
|
|
|
|
dd.DepthPitch = screenwidth* screenheight * 4;
|
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->Map(_pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd);
|
2014-06-19 17:06:00 -04:00
|
|
|
memcpy(dd.pData, _nextFrameBuffer, screenwidth*screenheight * 4);
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->Unmap(_pTexture, 0);
|
2014-06-23 16:38:01 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
ID3D11ShaderResourceView *nesOutputBuffer = GetShaderResourceView(_pTexture);
|
|
|
|
_spriteBatch->Draw(nesOutputBuffer, position, &sourceRect, Colors::White, 0.0f, position, 4.0f);
|
|
|
|
nesOutputBuffer->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Renderer::DrawPauseScreen()
|
|
|
|
{
|
|
|
|
RECT destRect;
|
|
|
|
destRect.left = 0;
|
|
|
|
destRect.right = 256*4;
|
|
|
|
destRect.bottom = 240*4;
|
|
|
|
destRect.top = 0;
|
|
|
|
|
|
|
|
XMVECTOR position{ { 0, 0 } };
|
|
|
|
|
|
|
|
UINT screenwidth = 256*4, screenheight = 240*4;
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
D3D11_MAPPED_SUBRESOURCE dd;
|
|
|
|
dd.pData = (void *)_overlayBuffer;
|
|
|
|
dd.RowPitch = screenwidth * 4;
|
|
|
|
dd.DepthPitch = screenwidth* screenheight * 4;
|
|
|
|
|
|
|
|
_pDeviceContext->Map(_overlayTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd);
|
2014-06-24 02:48:31 -04:00
|
|
|
for(uint32_t i = 0, len = screenwidth*screenheight; i < len; i++) {
|
|
|
|
//Gray transparent overlay
|
|
|
|
((uint32_t*)dd.pData)[i] = 0x99222222;
|
|
|
|
}
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->Unmap(_overlayTexture, 0);
|
|
|
|
|
|
|
|
ID3D11ShaderResourceView *shaderResourceView = GetShaderResourceView(_overlayTexture);
|
|
|
|
_spriteBatch->Draw(shaderResourceView, destRect); // , position, &sourceRect, Colors::White, 0.0f, position, 4.0f);
|
|
|
|
shaderResourceView->Release();
|
|
|
|
|
|
|
|
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2(256 * 2 - 142, 240 * 2 - 37), Colors::Black, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
|
|
|
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2(256 * 2 - 145, 240 * 2 - 40), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-23 16:38:01 -04:00
|
|
|
}
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-23 16:38:01 -04:00
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Render a frame
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
void Renderer::Render()
|
|
|
|
{
|
|
|
|
// Clear the back buffer
|
2014-06-23 20:00:51 -04:00
|
|
|
_pDeviceContext->ClearRenderTargetView(_pRenderTargetView, Colors::Black);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
_spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, _samplerState);
|
2014-06-12 21:48:04 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
//Draw nes screen
|
|
|
|
DrawNESScreen();
|
2014-06-19 17:06:00 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
//Draw FPS counter
|
|
|
|
if(CheckFlag(UIFlags::ShowFPS)) {
|
2014-06-25 17:34:46 -04:00
|
|
|
_font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 149, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
2014-06-25 21:52:37 -04:00
|
|
|
_font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 150, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!_displayMessage.empty() && _displayTimestamp > timeGetTime()) {
|
|
|
|
_font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(12, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
|
|
|
_font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(11, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f);
|
2014-06-23 20:00:51 -04:00
|
|
|
}
|
2014-06-23 16:38:01 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
if(CheckFlag(UIFlags::ShowPauseScreen)) {
|
|
|
|
DrawPauseScreen();
|
|
|
|
}
|
2014-06-23 16:38:01 -04:00
|
|
|
|
2014-06-23 20:00:51 -04:00
|
|
|
_spriteBatch->End();
|
2014-06-23 16:38:01 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
// Present the information rendered to the back buffer to the front buffer (the screen)
|
|
|
|
_pSwapChain->Present(0, 0);
|
|
|
|
}
|
|
|
|
}
|