diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index e3797ff886..87aa9480b3 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -511,6 +511,7 @@ const char *GeneratePixelShaderCode(u32 texture_mask, bool dstAlphaEnable, u32 H // alpha test will always fail, so restart the shader and just make it an empty function p = pmainstart; WRITE(p, "ocol0 = 0;\n"); + WRITE(p, "depth = 1.f;\n"); WRITE(p, "discard;return;\n"); } else @@ -952,7 +953,7 @@ static bool WriteAlphaTest(char *&p, u32 HLSL) compindex = bpmem.alphaFunc.comp1 % 8; WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table - WRITE(p, ")){ocol0 = 0;discard;return;}\n"); + WRITE(p, ")){ocol0 = 0;depth = 1.f;discard;return;}\n"); return true; } diff --git a/Source/Dolphin.sln b/Source/Dolphin.sln index 0e37aca610..746d3579fe 100644 --- a/Source/Dolphin.sln +++ b/Source/Dolphin.sln @@ -198,6 +198,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_WiimoteNew", "Plugin EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InputUICommon", "Core\InputUICommon\InputUICommon.vcproj", "{374E2DB7-42DF-4E59-8474-62B6687F4978}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_VideoDX11", "Plugins\Plugin_VideoDX11\Plugin_VideoDX11.vcproj", "{21DBE606-2958-43AC-A14E-B6B798D56554}" + ProjectSection(ProjectDependencies) = postProject + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} = {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -570,6 +578,18 @@ Global {374E2DB7-42DF-4E59-8474-62B6687F4978}.Release|Win32.Build.0 = Release|Win32 {374E2DB7-42DF-4E59-8474-62B6687F4978}.Release|x64.ActiveCfg = Release|x64 {374E2DB7-42DF-4E59-8474-62B6687F4978}.Release|x64.Build.0 = Release|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Debug|Win32.ActiveCfg = Debug|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Debug|Win32.Build.0 = Debug|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Debug|x64.ActiveCfg = Debug|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Debug|x64.Build.0 = Debug|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.DebugFast|x64.Build.0 = DebugFast|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Release|Win32.ActiveCfg = Release|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Release|Win32.Build.0 = Release|Win32 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Release|x64.ActiveCfg = Release|x64 + {21DBE606-2958-43AC-A14E-B6B798D56554}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcproj b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcproj new file mode 100644 index 0000000000..0ad6e9e6d9 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcproj @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp new file mode 100644 index 0000000000..fa6be1f2dd --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp @@ -0,0 +1,139 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "BPFunctions.h" +#include "D3DBase.h" +#include "VideoConfig.h" +#include "Common.h" +#include "TextureCache.h" +#include "VertexManager.h" +#include "VertexShaderManager.h" +#include "Render.h" + +namespace BPFunctions +{ + +void FlushPipeline() +{ + VertexManager::Flush(); +} + +void SetGenerationMode(const BPCmd &bp) +{ + Renderer::SetGenerationMode(); +} + +void SetScissor(const BPCmd &bp) +{ + Renderer::SetScissorRect(); +} + +void SetLineWidth(const BPCmd &bp) +{ + Renderer::SetLineWidth(); +} + +void SetDepthMode(const BPCmd &bp) +{ + Renderer::SetDepthMode(); +} + +void SetBlendMode(const BPCmd &bp) +{ + Renderer::SetBlendMode(false); +} +void SetDitherMode(const BPCmd &bp) +{ + Renderer::SetDitherMode(); +} +void SetLogicOpMode(const BPCmd &bp) +{ + Renderer::SetLogicOpMode(); +} + +void SetColorMask(const BPCmd &bp) +{ + Renderer::SetColorMask(); +} + +void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const bool &fromZBuffer, const bool &isIntensityFmt, const u32 ©fmt, const int &scaleByHalf) +{ + if (!g_ActiveConfig.bEFBCopyDisable) + { + if (g_ActiveConfig.bCopyEFBToTexture) + { + TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc); + } + else + { + PanicAlert("TODO: Implement EFB copying to RAM %s %d\n", __FILE__, __LINE__); + } + } +} + +void ClearScreen(const BPCmd &bp, const EFBRectangle &rc) +{ + bool colorEnable = bpmem.blendmode.colorupdate; + bool alphaEnable = (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24 && bpmem.blendmode.alphaupdate); + bool zEnable = bpmem.zmode.updateenable; + + if (colorEnable || alphaEnable || zEnable) + { + u32 color = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB; + u32 z = bpmem.clearZValue; + + Renderer::ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z); + } +} + +void RestoreRenderState(const BPCmd &bp) +{ + Renderer::RestoreAPIState(); +} + +bool GetConfig(const int &type) +{ + switch (type) + { + case CONFIG_ISWII: + return g_VideoInitialize.bWii; + case CONFIG_DISABLEFOG: + return g_ActiveConfig.bDisableFog; + case CONFIG_SHOWEFBREGIONS: + return false; + default: + PanicAlert("GetConfig Error: Unknown Config Type!"); + return false; + } +} + +u8 *GetPointer(const u32 &address) +{ + return g_VideoInitialize.pGetMemoryPointer(address); +} + +void SetTextureMode(const BPCmd &bp) +{ + Renderer::SetSamplerState(bp.address & 3, (bp.address & 0xE0) == 0xA0); +} + +void SetInterlacingMode(const BPCmd &bp) +{ + // TODO +} + +}; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.cpp b/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.cpp new file mode 100644 index 0000000000..9766d545c2 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.cpp @@ -0,0 +1,391 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "D3DBase.h" +#include "D3DTexture.h" +#include "D3DShader.h" +#include "D3Dcompiler.h" +#include "VideoConfig.h" +#include "Render.h" +#include "XFStructs.h" + +#include +#include +#include +using namespace std; + +namespace D3D +{ + +ID3D11Device* device = NULL; +ID3D11DeviceContext* context = NULL; +IDXGISwapChain* swapchain = NULL; +D3D_FEATURE_LEVEL featlevel; +D3DTexture2D* backbuf = NULL; +HWND hWnd; + +unsigned int xres, yres; + +bool bFrameInProgress = false; + +EmuGfxState::EmuGfxState() : vertexshader(NULL), vsbytecode(NULL), pixelshader(NULL), psbytecode(NULL) +{ + for (unsigned int k = 0;k < 8;k++) + { + float border[4] = {0.f, 0.f, 0.f, 0.f}; + shader_resources[k] = NULL; + samplerdesc[k] = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 16, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + } + + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + + depthdesc.DepthEnable = TRUE; + depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + depthdesc.DepthFunc = D3D11_COMPARISON_LESS; + depthdesc.StencilEnable = FALSE; + depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + + // this probably must be changed once multisampling support gets added + rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0, false, false, false, false); + + pscbuf = NULL; + vscbuf = NULL; + vshaderchanged = false; + inp_layout = NULL; + num_inp_elems = 0; + + pscbufchanged = false; + vscbufchanged = false; +} + +EmuGfxState::~EmuGfxState() +{ + for (unsigned int k = 0;k < 8;k++) + SAFE_RELEASE(shader_resources[k]) + + SAFE_RELEASE(vsbytecode); + SAFE_RELEASE(psbytecode); + SAFE_RELEASE(vertexshader); + SAFE_RELEASE(pixelshader); + + SAFE_RELEASE(pscbuf); + SAFE_RELEASE(vscbuf); + + SAFE_RELEASE(inp_layout); +} + +// TODO: No need to store the whole bytecode, signature might be enough (?) +void EmuGfxState::SetVShader(ID3D11VertexShader* shader, ID3D10Blob* bcode) +{ + // TODO: vshaderchanged actually just needs to be true if the signature changed + if (bcode && vsbytecode != bcode->GetBufferPointer()) vshaderchanged = true; + SAFE_RELEASE(vsbytecode); + SAFE_RELEASE(vertexshader); + + if (shader && bcode) + { + vertexshader = shader; + shader->AddRef(); + vsbytecode = bcode; + bcode->AddRef(); + } + else if (shader || bcode) + { + PanicAlert("Invalid parameters!\n"); + } +} + +void EmuGfxState::SetPShader(ID3D11PixelShader* shader) +{ + if (pixelshader) pixelshader->Release(); + pixelshader = shader; + if (shader) shader->AddRef(); +} + +void EmuGfxState::SetInputElements(const D3D11_INPUT_ELEMENT_DESC* elems, UINT num) +{ + num_inp_elems = num; + memcpy(inp_elems, elems, num*sizeof(D3D11_INPUT_ELEMENT_DESC)); +} + +void EmuGfxState::SetShaderResource(int stage, ID3D11ShaderResourceView* srv) +{ + if (shader_resources[stage]) shader_resources[stage]->Release(); + shader_resources[stage] = srv; + if (srv) srv->AddRef(); +} + +void EmuGfxState::SetAlphaBlendEnable(bool enable) +{ + blenddesc.RenderTarget[0].BlendEnable = enable; +} + +void EmuGfxState::SetRenderTargetWriteMask(UINT8 mask) +{ + blenddesc.RenderTarget[0].RenderTargetWriteMask = mask; +} + +void EmuGfxState::ApplyState() +{ + HRESULT hr; + + // input layout (only needs to be updated if the vertex shader signature changed) + if (vshaderchanged) + { + if (inp_layout) inp_layout->Release(); + hr = D3D::device->CreateInputLayout(inp_elems, num_inp_elems, vsbytecode->GetBufferPointer(), vsbytecode->GetBufferSize(), &inp_layout); + if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)inp_layout, "an input layout of EmuGfxState"); + vshaderchanged = false; + } + D3D::context->IASetInputLayout(inp_layout); + + // vertex shader + // TODO: divide the global variables of the generated shaders into about 5 constant buffers + // TODO: improve interaction between EmuGfxState and global state management, so that we don't need to set the constant buffers every time + if (!vscbuf) + { + unsigned int size = ((sizeof(float)*952)&(~0xffff))+0x10000; // must be a multiple of 16 + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + device->CreateBuffer(&cbdesc, NULL, &vscbuf); + SetDebugObjectName((ID3D11DeviceChild*)vscbuf, "a vertex shader constant buffer of EmuGfxState"); + } + if (vscbufchanged) + { + D3D11_MAPPED_SUBRESOURCE map; + context->Map(vscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, vsconstants, sizeof(vsconstants)); + context->Unmap(vscbuf, 0); + } + D3D::context->VSSetConstantBuffers(0, 1, &vscbuf); + + // pixel shader + if (!pscbuf) + { + unsigned int size = ((sizeof(float)*116)&(~0xffff))+0x10000; // must be a multiple of 16 + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + device->CreateBuffer(&cbdesc, NULL, &pscbuf); + SetDebugObjectName((ID3D11DeviceChild*)pscbuf, "a pixel shader constant buffer of EmuGfxState"); + } + if (pscbufchanged) + { + D3D11_MAPPED_SUBRESOURCE map; + context->Map(pscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, psconstants, sizeof(psconstants)); + context->Unmap(pscbuf, 0); + pscbufchanged = false; + } + D3D::context->PSSetConstantBuffers(0, 1, &pscbuf); + + ID3D11SamplerState* samplerstate[8]; + for (unsigned int stage = 0; stage < 8; stage++) + { + if (shader_resources[stage]) + { + hr = D3D::device->CreateSamplerState(&samplerdesc[stage], &samplerstate[stage]); + if (FAILED(hr)) PanicAlert("Fail %s %d, stage=%d\n", __FILE__, __LINE__, stage); + else SetDebugObjectName((ID3D11DeviceChild*)samplerstate[stage], "a sampler state of EmuGfxState"); + } + else samplerstate[stage] = NULL; + } + D3D::context->PSSetSamplers(0, 8, samplerstate); + for (unsigned int stage = 0; stage < 8; stage++) + SAFE_RELEASE(samplerstate[stage]); + + ID3D11BlendState* blstate; + hr = device->CreateBlendState(&blenddesc, &blstate); + if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__); + context->OMSetBlendState(blstate, NULL, 0xFFFFFFFF); + SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState"); + blstate->Release(); + + rastdesc.FillMode = (g_ActiveConfig.bWireFrame) ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID; + ID3D11RasterizerState* raststate; + hr = device->CreateRasterizerState(&rastdesc, &raststate); + if (FAILED(hr)) PanicAlert("Failed to create rasterizer state at %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)raststate, "a rasterizer state of EmuGfxState"); + context->RSSetState(raststate); + raststate->Release(); + + ID3D11DepthStencilState* depth_state; + hr = device->CreateDepthStencilState(&depthdesc, &depth_state); + if (SUCCEEDED(hr)) SetDebugObjectName((ID3D11DeviceChild*)depth_state, "a depth-stencil state of EmuGfxState"); + else PanicAlert("Failed to create depth state at %s %d\n", __FILE__, __LINE__); + context->OMSetDepthStencilState(depth_state, 0); + depth_state->Release(); + + context->PSSetShader(pixelshader, NULL, 0); + context->VSSetShader(vertexshader, NULL, 0); + context->PSSetShaderResources(0, 8, shader_resources); +} + +void EmuGfxState::AlphaPass() +{ + context->PSSetShader(pixelshader, NULL, 0); + + ID3D11BlendState* blstate; + D3D11_BLEND_DESC desc = blenddesc; + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA; + desc.RenderTarget[0].BlendEnable = FALSE; + HRESULT hr = device->CreateBlendState(&desc, &blstate); + if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__); + SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState (created during alpha pass)"); + context->OMSetBlendState(blstate, NULL, 0xFFFFFFFF); + blstate->Release(); +} + +void EmuGfxState::ResetShaderResources() +{ + for (unsigned int k = 0;k < 8;k++) + SAFE_RELEASE(shader_resources[k]); +} + +EmuGfxState* gfxstate = NULL; + +HRESULT Create(HWND wnd) +{ + hWnd = wnd; + HRESULT hr; + + RECT client; + GetClientRect(hWnd, &client); + xres = client.right - client.left; + yres = client.bottom - client.top; + + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferCount = 1; + sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + sd.BufferDesc.Width = xres; + sd.BufferDesc.Height = yres; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = wnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.Windowed = TRUE; + +#if defined(_DEBUG) || defined(DEBUGFAST) + D3D11_CREATE_DEVICE_FLAG device_flags = (D3D11_CREATE_DEVICE_FLAG)(D3D11_CREATE_DEVICE_DEBUG|D3D11_CREATE_DEVICE_SINGLETHREADED); +#else + D3D11_CREATE_DEVICE_FLAG device_flags = D3D11_CREATE_DEVICE_SINGLETHREADED; +#endif + hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, device_flags, NULL, 0, D3D11_SDK_VERSION, &sd, &swapchain, &device, &featlevel, &context); + if (FAILED(hr) || !device || !context || !swapchain) + { + MessageBox(wnd, _T("Failed to initialize Direct3D."), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + SAFE_RELEASE(device); + SAFE_RELEASE(context); + SAFE_RELEASE(swapchain); + return E_FAIL; + } + SetDebugObjectName((ID3D11DeviceChild*)context, "device context"); + + ID3D11Texture2D* buf; + hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf); + if (FAILED(hr)) + { + MessageBox(wnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 plugin"), MB_OK | MB_ICONERROR); + device->Release(); + context->Release(); + swapchain->Release(); + return E_FAIL; + } + backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + buf->Release(); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetTex(), "backbuffer texture"); + SetDebugObjectName((ID3D11DeviceChild*)backbuf->GetRTV(), "backbuffer render target view"); + + context->OMSetRenderTargets(1, &backbuf->GetRTV(), NULL); + + gfxstate = new EmuGfxState; + return S_OK; +} + +void Close() +{ + // release all bound resources + context->ClearState(); + + if (gfxstate) delete gfxstate; + SAFE_RELEASE(backbuf); + SAFE_RELEASE(context); + SAFE_RELEASE(swapchain); + ULONG references = device->Release(); + if (references) + { + ERROR_LOG(VIDEO, "Unreleased references: %i.", references); + } + else + { + NOTICE_LOG(VIDEO, "Successfully released all device references!"); + } + device = NULL; +} + +/* just returning the 4_0 ones here */ +const char* VertexShaderVersionString() { return "vs_4_0"; } +const char* PixelShaderVersionString() { return "ps_4_0"; } + +D3DTexture2D* &GetBackBuffer() { return backbuf; } +unsigned int GetBackBufferWidth() { return xres; } +unsigned int GetBackBufferHeight() { return yres; } + +void Reset() +{ + // TODO: Check whether we need to do anything here +} + +bool BeginFrame() +{ + if (bFrameInProgress) + { + PanicAlert("BeginFrame called although a frame is already in progress"); + return false; + } + bFrameInProgress = true; + return (device != NULL); +} + +void EndFrame() +{ + if (!bFrameInProgress) + { + PanicAlert("EndFrame called although no frame is in progress"); + return; + } + bFrameInProgress = false; +} + +void Present() +{ + // TODO: Is 1 the correct value for vsyncing? + swapchain->Present((UINT)g_ActiveConfig.bVSync, 0); +} + +} // namespace diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.h b/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.h new file mode 100644 index 0000000000..06a219a990 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DBase.h @@ -0,0 +1,147 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include +#include + +#include + +#include "Common.h" + +#include +#include +#include +using std::vector; +using std::list; +using std::map; +using std::pair; + +#define SAFE_RELEASE(x) { if (x) (x)->Release(); (x) = NULL; } + +class D3DTexture2D; +namespace D3D +{ + +HRESULT Create(HWND wnd); +void Close(); + +extern ID3D11Device *device; +extern ID3D11DeviceContext *context; +extern IDXGISwapChain *swapchain; +extern bool bFrameInProgress; + +void Reset(); +bool BeginFrame(); +void EndFrame(); +void Present(); + +unsigned int GetBackBufferWidth(); +unsigned int GetBackBufferHeight(); +D3DTexture2D* &GetBackBuffer(); +const char *PixelShaderVersionString(); +const char *VertexShaderVersionString(); + +// Ihis function will assign a name to the given resource. +// The DirectX debug layer will make it easier to identify resources that way, +// e.g. when listing up all resources who have unreleased references. +inline void SetDebugObjectName(ID3D11DeviceChild* resource, const char *name) +{ +#if defined(_DEBUG) || defined(DEBUGFAST) + resource->SetPrivateData( WKPDID_D3DDebugObjectName, strlen(name), name); +#endif +} + +// stores the pipeline state to use when calling VertexManager::Flush() +class EmuGfxState +{ +public: + EmuGfxState(); + ~EmuGfxState(); + + void SetVShader(ID3D11VertexShader *shader, ID3D10Blob *bcode); + void SetPShader(ID3D11PixelShader *shader); + void SetInputElements(const D3D11_INPUT_ELEMENT_DESC *elems, UINT num); + void SetShaderResource(int stage, ID3D11ShaderResourceView *srv); + + void ApplyState(); // apply current state + void AlphaPass(); // only modify the current state to enable the alpha pass + void ResetShaderResources(); // disable all shader resources + + // blend state + void SetAlphaBlendEnable(bool enable); + void SetRenderTargetWriteMask(UINT8 mask); + void SetSrcBlend(D3D11_BLEND val) // TODO: Check whether e.g. the dest color check is needed here + { + blenddesc.RenderTarget[0].SrcBlend = val; + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].SrcBlendAlpha = val; + } + void SetDestBlend(D3D11_BLEND val) + { + blenddesc.RenderTarget[0].DestBlend = val; + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].DestBlendAlpha = val; + } + void SetBlendOp(D3D11_BLEND_OP val) + { + blenddesc.RenderTarget[0].BlendOp = val; + blenddesc.RenderTarget[0].BlendOpAlpha = val; + } + + // sampler states + void SetSamplerFilter(DWORD stage, D3D11_FILTER filter) { samplerdesc[stage].Filter = filter; } + + // TODO: add methods for changing the other states instead of modifying them directly + + D3D11_SAMPLER_DESC samplerdesc[8]; + D3D11_RASTERIZER_DESC rastdesc; + D3D11_DEPTH_STENCIL_DESC depthdesc; + + float psconstants[116]; + float vsconstants[952]; + bool vscbufchanged; + bool pscbufchanged; + +private: + ID3D11VertexShader* vertexshader; + ID3D10Blob* vsbytecode; + ID3D11PixelShader* pixelshader; + ID3D10Blob* psbytecode; + bool vshaderchanged; + + ID3D11Buffer* vscbuf; + ID3D11Buffer* pscbuf; + + ID3D11InputLayout* inp_layout; + D3D11_INPUT_ELEMENT_DESC inp_elems[32]; + int num_inp_elems; + + ID3D11ShaderResourceView* shader_resources[8]; + D3D11_BLEND_DESC blenddesc; +}; + +extern EmuGfxState *gfxstate; + +} // namespace diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.cpp b/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.cpp new file mode 100644 index 0000000000..73fdfd83c6 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include "VideoConfig.h" +#include "D3DShader.h" + +namespace D3D +{ + +// bytecode->shader +ID3D11VertexShader* CreateVertexShaderFromByteCode(void* bytecode, unsigned int len) +{ + ID3D11VertexShader* v_shader; + HRESULT hr = D3D::device->CreateVertexShader(bytecode, len, NULL, &v_shader); + if (FAILED(hr)) + { + PanicAlert("CreateVertexShaderFromByteCode failed from %p (size %d) at %s %d\n", bytecode, len, __FILE__, __LINE__); + v_shader = NULL; + } + return v_shader; +} + +// code->bytecode +bool CompileVertexShader(const char* code, unsigned int len, ID3D10Blob** blob) +{ + ID3D10Blob* shaderBuffer = NULL; + ID3D10Blob* errorBuffer = NULL; + +#if defined(_DEBUG) || defined(DEBUGFAST) + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_DEBUG|D3D10_SHADER_WARNINGS_ARE_ERRORS; +#else + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_OPTIMIZATION_LEVEL3|D3D10_SHADER_SKIP_VALIDATION; +#endif + HRESULT hr = D3DCompile(code, len, NULL, NULL, NULL, "main", D3D::VertexShaderVersionString(), + flags, 0, &shaderBuffer, &errorBuffer); + + if (FAILED(hr)) + { + std::string msg = (char*)errorBuffer->GetBufferPointer(); + msg += "\n\n"; + msg += code; + MessageBoxA(0, msg.c_str(), "Error compiling pixel shader", MB_ICONERROR); + + *blob = NULL; + } + else *blob = shaderBuffer; + + //cleanup + if (errorBuffer) errorBuffer->Release(); + return SUCCEEDED(hr); +} + +// bytecode->shader +ID3D11PixelShader* CreatePixelShaderFromByteCode(void* bytecode, unsigned int len) +{ + ID3D11PixelShader* p_shader; + HRESULT hr = D3D::device->CreatePixelShader(bytecode, len, NULL, &p_shader); + if (FAILED(hr)) + { + PanicAlert("CreatePixelShaderFromByteCode failed at %s %d\n", __FILE__, __LINE__); + p_shader = NULL; + } + return p_shader; +} + +// code->bytecode +bool CompilePixelShader(const char* code, unsigned int len, ID3D10Blob** blob) +{ + ID3D10Blob* shaderBuffer = NULL; + ID3D10Blob* errorBuffer = NULL; + +#if defined(_DEBUG) || defined(DEBUGFAST) + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_DEBUG|D3D10_SHADER_WARNINGS_ARE_ERRORS; +#else + UINT flags = D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY|D3D10_SHADER_OPTIMIZATION_LEVEL3|D3D10_SHADER_SKIP_VALIDATION; +#endif + HRESULT hr = D3DCompile(code, len, NULL, NULL, NULL, "main", D3D::PixelShaderVersionString(), + flags, 0, &shaderBuffer, &errorBuffer); + + if (FAILED(hr)) + { + std::string msg = (char*)errorBuffer->GetBufferPointer(); + msg += "\n\n"; + msg += code; + MessageBoxA(0, msg.c_str(), "Error compiling pixel shader", MB_ICONERROR); + + *blob = NULL; + } + else *blob = shaderBuffer; + + // cleanup + if (errorBuffer) errorBuffer->Release(); + return SUCCEEDED(hr); +} + +ID3D11VertexShader* CompileAndCreateVertexShader(const char* code, unsigned int len) +{ + ID3D10Blob* blob = NULL; + if (CompileVertexShader(code, len, &blob)) + { + ID3D11VertexShader* v_shader = CreateVertexShaderFromByteCode(blob); + blob->Release(); + return v_shader; + } + PanicAlert("Failed to compile and create vertex shader from %p (size %d) at %s %d\n", code, len, __FILE__, __LINE__); + return NULL; +} + +ID3D11PixelShader* CompileAndCreatePixelShader(const char* code, unsigned int len) +{ + ID3D10Blob* blob = NULL; + CompilePixelShader(code, len, &blob); + if (blob) + { + ID3D11PixelShader* p_shader = CreatePixelShaderFromByteCode(blob); + blob->Release(); + return p_shader; + } + PanicAlert("Failed to compile and create pixel shader, %s %d\n", __FILE__, __LINE__); + return NULL; +} + +} // namespace diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.h b/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.h new file mode 100644 index 0000000000..1c3e014da5 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DShader.h @@ -0,0 +1,39 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "D3DBase.h" + +namespace D3D +{ + ID3D11VertexShader* CreateVertexShaderFromByteCode(void* bytecode, unsigned int len); + ID3D11PixelShader* CreatePixelShaderFromByteCode(void* bytecode, unsigned int len); + + // The returned bytecode buffers should be Release()d. + bool CompileVertexShader(const char* code, unsigned int len, ID3D10Blob** blob); + bool CompilePixelShader(const char* code, unsigned int len, ID3D10Blob** blob); + + // Utility functions + ID3D11VertexShader* CompileAndCreateVertexShader(const char* code, unsigned int len); + ID3D11PixelShader* CompileAndCreatePixelShader(const char* code, unsigned int len); + + inline ID3D11VertexShader* CreateVertexShaderFromByteCode(ID3D10Blob* bytecode) { return CreateVertexShaderFromByteCode(bytecode->GetBufferPointer(), bytecode->GetBufferSize()); } + inline ID3D11PixelShader* CreatePixelShaderFromByteCode(ID3D10Blob* bytecode) { return CreatePixelShaderFromByteCode(bytecode->GetBufferPointer(), bytecode->GetBufferSize()); } + inline ID3D11VertexShader* CompileAndCreateVertexShader(ID3D10Blob* code) { return CompileAndCreateVertexShader((const char*)code->GetBufferPointer(), code->GetBufferSize()); } + inline ID3D11PixelShader* CompileAndCreatePixelShader(ID3D10Blob* code) { return CompileAndCreatePixelShader((const char*)code->GetBufferPointer(), code->GetBufferSize()); } +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.cpp b/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.cpp new file mode 100644 index 0000000000..7206d1db4c --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.cpp @@ -0,0 +1,207 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "d3dx11.h" +#include "D3DBase.h" +#include "D3DTexture.h" + +namespace D3D +{ + +// buffers for storing the data for DEFAULT textures +const char* texbuf = NULL; +unsigned int texbufsize = 0; + +void ReplaceTexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, DXGI_FORMAT fmt, PC_TexFormat pcfmt, unsigned int level, D3D11_USAGE usage) +{ + void* outptr; + unsigned int destPitch; + bool bExpand = false; + + if (usage == D3D11_USAGE_DYNAMIC || usage == D3D11_USAGE_STAGING) + { + if (level != 0) PanicAlert("Dynamic textures don't support mipmaps, but given level is not 0 at %s %d\n", __FILE__, __LINE__); + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + outptr = map.pData; + destPitch = map.RowPitch; + } + else if (usage == D3D11_USAGE_DEFAULT) + { + if (texbufsize < 4*width*height) + { + if (texbuf) delete[] texbuf; + texbuf = new char[4*width*height]; + texbufsize = 4*width*height; + } + outptr = (void*)texbuf; + destPitch = width * 4; + } + else + { + PanicAlert("ReplaceTexture2D called on an immutable texture!\n"); + return; + } + + // TODO: Merge the conversions done here to VideoDecoder + switch (pcfmt) + { + case PC_TEX_FMT_IA4_AS_IA8: + case PC_TEX_FMT_IA8: + for (unsigned int y = 0; y < height; y++) + { + u16* in = (u16*)buffer + y * pitch; + u32* pBits = (u32*)((u8*)outptr + y * destPitch); + for (unsigned int x = 0; x < width; x++) + { + const u8 I = (*in & 0xFF); + const u8 A = (*in & 0xFF00) >> 8; + *pBits = (A << 24) | (I << 16) | (I << 8) | I; + in++; + pBits++; + } + } + break; + case PC_TEX_FMT_I8: + case PC_TEX_FMT_I4_AS_I8: + for (unsigned int y = 0; y < height; y++) + { + u8* in = (u8*)buffer + y * pitch; + u32* pBits = (u32*)((u8*)outptr + y * destPitch); + for (unsigned int x = 0; x < width; x++) + { + const u8 col = *in; + *pBits = 0xFF000000 | (col << 16) | (col << 8) | col; + in++; + pBits++; + } + } + break; + case PC_TEX_FMT_BGRA32: + // BGRA32 textures can be uploaded directly to VRAM when using DEFAULT textures + if (usage == D3D11_USAGE_DEFAULT) break; + for (unsigned int y = 0; y < height; y++) + { + u32* in = (u32*)buffer + y * pitch; + u32* pBits = (u32*)((u8*)outptr + y * destPitch); + for (unsigned int x = 0; x < width; x++) + { + const u32 col = *in; + *pBits = col; + in++; + pBits++; + } + } + break; + case PC_TEX_FMT_RGB565: + for (unsigned int y = 0; y < height; y++) + { + u16* in = (u16*)buffer + y * pitch; + u32* pBits = (u32*)((u8*)outptr + y * destPitch); + for (unsigned int x = 0; x < width; x++) + { + const u16 col = *in; + *pBits = 0xFF000000 | (((col&0x1f)<<3)) | ((col&0x7e0)<<5) | ((col&0xF800)<<8); + pBits++; + in++; + } + } + break; + default: + PanicAlert("Unknown tex fmt %d\n", pcfmt); + break; + } + if (usage == D3D11_USAGE_DYNAMIC) + { + // TODO: UpdateSubresource might be faster than mapping + D3D::context->Unmap(pTexture, 0); + } + else if (usage == D3D11_USAGE_DEFAULT) + { + D3D11_BOX dest_region = CD3D11_BOX(0, 0, 0, width, height, 1); + if (pcfmt == PC_TEX_FMT_BGRA32) + D3D::context->UpdateSubresource(pTexture, level, &dest_region, buffer, 4*pitch, 4*(4*pitch)*height); + else + D3D::context->UpdateSubresource(pTexture, level, &dest_region, outptr, destPitch, 4*width*height); + } +} + +} // namespace + +D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt, unsigned int levels) +{ + ID3D11Texture2D* pTexture = NULL; + HRESULT hr; + + D3D11_CPU_ACCESS_FLAG cpuflags; + if (usage == D3D11_USAGE_STAGING) cpuflags = (D3D11_CPU_ACCESS_FLAG)((int)D3D11_CPU_ACCESS_WRITE|(int)D3D11_CPU_ACCESS_READ); + else if (usage == D3D11_USAGE_DYNAMIC) cpuflags = D3D11_CPU_ACCESS_WRITE; + else cpuflags = (D3D11_CPU_ACCESS_FLAG)0; + D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(fmt, width, height, 1, levels, bind, usage, cpuflags); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &pTexture); + if (FAILED(hr)) + { + PanicAlert("Failed to create texture at %s %d\n", __FILE__, __LINE__); + return NULL; + } + + D3DTexture2D* ret = new D3DTexture2D(pTexture, bind); + pTexture->Release(); + return ret; +} + +void D3DTexture2D::AddRef() +{ + ref++; +} + +UINT D3DTexture2D::Release() +{ + ref--; + if (ref == 0) + { + delete this; + return 0; + } + return ref; +} + +ID3D11Texture2D* &D3DTexture2D::GetTex() { return tex; } +ID3D11ShaderResourceView* &D3DTexture2D::GetSRV() { return srv; } +ID3D11RenderTargetView* &D3DTexture2D::GetRTV() { return rtv; } +ID3D11DepthStencilView* &D3DTexture2D::GetDSV() { return dsv; } + +D3DTexture2D::D3DTexture2D(ID3D11Texture2D* texptr, D3D11_BIND_FLAG bind, + DXGI_FORMAT srv_format, DXGI_FORMAT dsv_format, DXGI_FORMAT rtv_format) + : ref(1), tex(texptr), srv(NULL), rtv(NULL), dsv(NULL) +{ + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D, srv_format); + D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D, dsv_format); + D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = CD3D11_RENDER_TARGET_VIEW_DESC(D3D11_RTV_DIMENSION_TEXTURE2D, rtv_format); + if (bind & D3D11_BIND_SHADER_RESOURCE) D3D::device->CreateShaderResourceView(tex, &srv_desc, &srv); + if (bind & D3D11_BIND_RENDER_TARGET) D3D::device->CreateRenderTargetView(tex, &rtv_desc, &rtv); + if (bind & D3D11_BIND_DEPTH_STENCIL) D3D::device->CreateDepthStencilView(tex, &dsv_desc, &dsv); + tex->AddRef(); +} + +D3DTexture2D::~D3DTexture2D() +{ + if (srv) srv->Release(); + if (rtv) rtv->Release(); + if (dsv) dsv->Release(); + tex->Release(); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.h b/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.h new file mode 100644 index 0000000000..db8920ba62 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DTexture.h @@ -0,0 +1,56 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "D3DBase.h" +#include "TextureDecoder.h" + +namespace D3D +{ + void ReplaceTexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, DXGI_FORMAT fmt, PC_TexFormat pcfmt, unsigned int level, D3D11_USAGE usage); +} + +class D3DTexture2D +{ +public: + // there are two ways to create a D3DTexture2D object: + // either create an ID3D11Texture2D object, pass it to the constructor and specify what views to create + // or let the texture automatically be created by D3DTexture2D::Create + + D3DTexture2D(ID3D11Texture2D* texptr, D3D11_BIND_FLAG bind, DXGI_FORMAT srv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN, DXGI_FORMAT rtv_format = DXGI_FORMAT_UNKNOWN); + static D3DTexture2D* Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt = DXGI_FORMAT_B8G8R8A8_UNORM, unsigned int levels = 1); + + // reference counting, use AddRef() when creating a new reference and Release() it when you don't need it anymore + void AddRef(); + UINT Release(); + + ID3D11Texture2D* &GetTex(); + ID3D11ShaderResourceView* &GetSRV(); + ID3D11RenderTargetView* &GetRTV(); + ID3D11DepthStencilView* &GetDSV(); + +private: + ~D3DTexture2D(); + + ID3D11Texture2D* tex; + ID3D11ShaderResourceView* srv; + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + D3D11_BIND_FLAG bindflags; + UINT ref; +}; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp new file mode 100644 index 0000000000..da14ac49ae --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp @@ -0,0 +1,610 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "D3DBase.h" +#include "D3DUtil.h" +#include "D3DTexture.h" +#include "Render.h" +#include "PixelShaderCache.h" +#include "VertexShaderCache.h" +#include "D3DShader.h" + +namespace D3D +{ + +CD3DFont font; + +#define MAX_NUM_VERTICES 300 +struct FONT2DVERTEX { + float x,y,z; + float col[4]; + float tu, tv; +}; + +inline FONT2DVERTEX InitFont2DVertex(float x, float y, u32 color, float tu, float tv) +{ + FONT2DVERTEX v; v.x=x; v.y=y; v.z=0; v.tu = tu; v.tv = tv; + v.col[0] = ((float)((color >> 16) & 0xFF)) / 255.f; + v.col[1] = ((float)((color >> 8) & 0xFF)) / 255.f; + v.col[2] = ((float)((color >> 0) & 0xFF)) / 255.f; + v.col[3] = ((float)((color >> 24) & 0xFF)) / 255.f; + return v; +} + +CD3DFont::CD3DFont() : m_dwTexWidth(512), m_dwTexHeight(512) +{ + m_pTexture = NULL; + m_pVB = NULL; + m_InputLayout = NULL; + m_pshader = NULL; + m_vshader = NULL; +} + +const char fontpixshader[] = { + "Texture2D tex2D;\n" + "SamplerState linearSampler\n" + "{\n" + " Filter = MIN_MAG_MIP_LINEAR;\n" + " AddressU = Wrap;\n" + " AddressV = Wrap;\n" + "};\n" + "struct PS_INPUT\n" + "{\n" + " float4 pos : SV_POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "float4 main( PS_INPUT input ) : SV_Target\n" + "{\n" + " return tex2D.Sample( linearSampler, input.tex ) * input.col;\n" + "};\n" +}; + +const char fontvertshader[] = { + "struct VS_INPUT\n" + "{\n" + " float4 pos : POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "struct PS_INPUT\n" + "{\n" + " float4 pos : SV_POSITION;\n" + " float4 col : COLOR;\n" + " float2 tex : TEXCOORD;\n" + "};\n" + "PS_INPUT main( VS_INPUT input )\n" + "{\n" + " PS_INPUT output;\n" + " output.pos = input.pos;\n" + " output.col = input.col;\n" + " output.tex = input.tex;\n" + " return output;\n" + "};\n" +}; + +int CD3DFont::Init() +{ + HRESULT hr; + + // prepare to create a bitmap + unsigned int* pBitmapBits; + BITMAPINFO bmi; + ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (int)m_dwTexWidth; + bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 32; + + // create a DC and a bitmap for the font + HDC hDC = CreateCompatibleDC(NULL); + HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, NULL, 0); + SetMapMode(hDC, MM_TEXT); + + // create a GDI font + int m_dwFontHeight = 24; + int nHeight = -MulDiv(m_dwFontHeight, (int)GetDeviceCaps(hDC, LOGPIXELSY), 72); + int dwBold = FW_NORMAL; + HFONT hFont = CreateFont(nHeight, 0, 0, 0, dwBold, 0, + FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, + VARIABLE_PITCH, _T("Tahoma")); + if (NULL == hFont) return E_FAIL; + + HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap); + HGDIOBJ hOldFont = SelectObject(hDC, hFont); + + // set text properties + SetTextColor(hDC, 0xFFFFFF); + SetBkColor (hDC, 0); + SetTextAlign(hDC, TA_TOP); + + // loop through all printable characters and output them to the bitmap + // meanwhile, keep track of the corresponding tex coords for each character. + int x = 0, y = 0; + char str[2] = "\0"; + for (int c = 0; c < 127 - 32; c++) + { + str[0] = c + 32; + SIZE size; + GetTextExtentPoint32A(hDC, str, 1, &size); + if ((int)(x+size.cx+1) > m_dwTexWidth) + { + x = 0; + y += size.cy + 1; + } + + ExtTextOutA(hDC, x+1, y+0, ETO_OPAQUE | ETO_CLIPPED, NULL, str, 1, NULL); + m_fTexCoords[c][0] = ((float)(x+0))/m_dwTexWidth; + m_fTexCoords[c][1] = ((float)(y+0))/m_dwTexHeight; + m_fTexCoords[c][2] = ((float)(x+0+size.cx))/m_dwTexWidth; + m_fTexCoords[c][3] = ((float)(y+0+size.cy))/m_dwTexHeight; + + x += size.cx + 3; // 3 to work around annoying ij conflict (part of the j ends up with the i) + } + + // create a new texture for the font + // possible optimization: store the converted data in a buffer and fill the texture on creation. + // That way, we can use a static texture + ID3D11Texture2D* buftex; + D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, m_dwTexWidth, m_dwTexHeight, + 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DYNAMIC, + D3D11_CPU_ACCESS_WRITE); + hr = device->CreateTexture2D(&texdesc, NULL, &buftex); + if (FAILED(hr)) + { + PanicAlert("Failed to create font texture"); + return hr; + } + + // lock the surface and write the alpha values for the set pixels + D3D11_MAPPED_SUBRESOURCE texmap; + hr = context->Map(buftex, 0, D3D11_MAP_WRITE_DISCARD, 0, &texmap); + if (FAILED(hr)) PanicAlert("Failed to map a texture at %s %d\n", __FILE__, __LINE__); + + for (y = 0; y < m_dwTexHeight; y++) + { + u32* pDst32 = (u32*)((u8*)texmap.pData + y * texmap.RowPitch); + for (x = 0; x < m_dwTexWidth; x++) + { + const u8 bAlpha = (pBitmapBits[m_dwTexWidth * y + x] & 0xff); + pDst32[x] = ((bAlpha * 255 / 15) << 24) | 0xFFFFFF; + } + } + + // clean up + context->Unmap(buftex, 0); + hr = D3D::device->CreateShaderResourceView(buftex, NULL, &m_pTexture); + if (FAILED(hr)) PanicAlert("Failed to create shader resource view at %s %d\n", __FILE__, __LINE__); + buftex->Release(); + + SelectObject(hDC, hOldbmBitmap); + DeleteObject(hbmBitmap); + + SelectObject(hDC, hOldFont); + DeleteObject(hFont); + + // setup device objects for drawing + m_pshader = D3D::CompileAndCreatePixelShader(fontpixshader, sizeof(fontpixshader)); + if (m_pshader == NULL) PanicAlert("Failed to create pixel shader, %s %d\n", __FILE__, __LINE__); + + ID3D10Blob* vsbytecode; + D3D::CompileVertexShader(fontvertshader, sizeof(fontvertshader), &vsbytecode); + if (vsbytecode == NULL) PanicAlert("Failed to compile vertex shader, %s %d\n", __FILE__, __LINE__); + m_vshader = D3D::CreateVertexShaderFromByteCode(vsbytecode); + if (m_vshader == NULL) PanicAlert("Failed to create vertex shader, %s %d\n", __FILE__, __LINE__); + + const D3D11_INPUT_ELEMENT_DESC desc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + hr = D3D::device->CreateInputLayout(desc, 3, vsbytecode->GetBufferPointer(), vsbytecode->GetBufferSize(), &m_InputLayout); + if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); + vsbytecode->Release(); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = TRUE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + D3D::device->CreateBlendState(&blenddesc, &m_blendstate); + + // this might need to be changed when adding multisampling support + D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); + D3D::device->CreateRasterizerState(&rastdesc, &m_raststate); + + D3D11_BUFFER_DESC vbdesc = CD3D11_BUFFER_DESC(MAX_NUM_VERTICES*sizeof(FONT2DVERTEX), D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + if (FAILED(hr = device->CreateBuffer(&vbdesc, NULL, &m_pVB))) + { + PanicAlert("Failed to create vertex buffer!\n"); + return hr; + } + return S_OK; +} + +int CD3DFont::Shutdown() +{ + SAFE_RELEASE(m_pVB); + SAFE_RELEASE(m_pTexture); + SAFE_RELEASE(m_InputLayout); + SAFE_RELEASE(m_pshader); + SAFE_RELEASE(m_vshader); + + SAFE_RELEASE(m_blendstate); + SAFE_RELEASE(m_raststate); + + return S_OK; +} + +int CD3DFont::DrawTextScaled(float x, float y, float scale, float spacing, u32 dwColor, const char* strText, bool center) +{ + if (!m_pVB) return 0; + + UINT stride = sizeof(FONT2DVERTEX); + UINT bufoffset = 0; + + float sx = x; + float sy = y; + + float fStartX = sx; + + float invLineHeight = 1.0f / ((m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight); + // fill vertex buffer + FONT2DVERTEX* pVertices; + int dwNumTriangles = 0L; + + D3D11_MAPPED_SUBRESOURCE vbmap; + HRESULT hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vbmap); + if (FAILED(hr)) PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); + pVertices = (D3D::FONT2DVERTEX*)vbmap.pData; + + const char* oldstrText = strText; + // first, let's measure the text + float tw=0; + float mx=0; + float maxx=0; + + while (*strText) + { + char c = *strText++; + + if (c == ('\n')) + mx = 0; + if (c < (' ')) + continue; + + float tx1 = m_fTexCoords[c-32][0]; + float tx2 = m_fTexCoords[c-32][2]; + + float w = (tx2-tx1)*m_dwTexWidth; + w *= scale*invLineHeight; + mx += w + spacing*scale; + if (mx > maxx) maxx = mx; + } + + float offset = -maxx/2; + strText = oldstrText; + //Then let's draw it + if (center) + { + sx += offset; + fStartX += offset; + } + + float wScale = scale*invLineHeight; + float hScale = scale*invLineHeight; + + // set general pipeline state + D3D::context->OMSetBlendState(m_blendstate, NULL, 0xFFFFFFFF); + D3D::context->RSSetState(m_raststate); + + D3D::context->PSSetShader(m_pshader, NULL, 0); + D3D::context->VSSetShader(m_vshader, NULL, 0); + + D3D::context->IASetInputLayout(m_InputLayout); + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + D3D::context->PSSetShaderResources(0, 1, &m_pTexture); + + while (*strText) + { + char c = *strText++; + + if (c == ('\n')) + { + sx = fStartX; + sy += scale; + } + if (c < (' ')) + continue; + + c-=32; + float tx1 = m_fTexCoords[c][0]; + float ty1 = m_fTexCoords[c][1]; + float tx2 = m_fTexCoords[c][2]; + float ty2 = m_fTexCoords[c][3]; + + float w = (tx2-tx1)*m_dwTexWidth/2; + float h = (ty2-ty1)*m_dwTexHeight/2; + + FONT2DVERTEX v[6]; + v[0] = InitFont2DVertex( sx /m_dwTexWidth-1.f, 1-((sy+h)/m_dwTexHeight), dwColor, tx1, ty2); + v[1] = InitFont2DVertex( sx /m_dwTexWidth-1.f, 1-( sy /m_dwTexHeight), dwColor, tx1, ty1); + v[2] = InitFont2DVertex((sx+w)/m_dwTexWidth-1.f, 1-((sy+h)/m_dwTexHeight), dwColor, tx2, ty2); + v[3] = InitFont2DVertex((sx+w)/m_dwTexWidth-1.f, 1-( sy /m_dwTexHeight), dwColor, tx2, ty1); + v[4] = v[2]; + v[5] = v[1]; + + memcpy(pVertices, v, 6*sizeof(FONT2DVERTEX)); + + pVertices+=6; + dwNumTriangles += 2; + + if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6)) + { + context->Unmap(m_pVB, 0); + + D3D::context->IASetVertexBuffers(0, 1, &m_pVB, &stride, &bufoffset); + D3D::context->Draw(3 * dwNumTriangles, 0); + + dwNumTriangles = 0; + D3D11_MAPPED_SUBRESOURCE vbmap; + HRESULT hr = context->Map(m_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vbmap); + if (FAILED(hr)) PanicAlert("Mapping vertex buffer failed, %s %d\n", __FILE__, __LINE__); + pVertices = (D3D::FONT2DVERTEX*)vbmap.pData; + } + sx += w + spacing*scale; + } + + // Unlock and render the vertex buffer + context->Unmap(m_pVB, 0); + if (dwNumTriangles > 0) + { + D3D::context->IASetVertexBuffers(0, 1, &m_pVB, &stride, &bufoffset); + D3D::context->Draw(3 * dwNumTriangles, 0); + } + D3D::gfxstate->SetShaderResource(0, NULL); + return S_OK; +} + +ID3D11Buffer* CreateQuadVertexBuffer(unsigned int size, void* data) +{ + ID3D11Buffer* vb; + D3D11_BUFFER_DESC vbdesc; + vbdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vbdesc.ByteWidth = size; + vbdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + vbdesc.MiscFlags = 0; + vbdesc.Usage = D3D11_USAGE_DYNAMIC; + if (data) + { + D3D11_SUBRESOURCE_DATA bufdata; + bufdata.pSysMem = data; + if (FAILED(device->CreateBuffer(&vbdesc, &bufdata, &vb))) return NULL; + } + else if (FAILED(device->CreateBuffer(&vbdesc, NULL, &vb))) return NULL; + + return vb; +} + +ID3D11SamplerState* stqsamplerstate = NULL; +ID3D11SamplerState* stsqsamplerstate = NULL; +ID3D11Buffer* stqvb = NULL; +ID3D11Buffer* stsqvb = NULL; +ID3D11Buffer* clearvb = NULL; + +typedef struct { float x,y,z,u,v; } STQVertex; +typedef struct { float x,y,z,u,v; } STSQVertex; +typedef struct { float x,y,z; float col[4];} ClearVertex; + +void InitUtils() +{ + float border[4] = { 0.f, 0.f, 0.f, 0.f }; + D3D11_SAMPLER_DESC samDesc = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 16, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + HRESULT hr = D3D::device->CreateSamplerState(&samDesc, &stqsamplerstate); + if (FAILED(hr)) PanicAlert("Failed to create sampler state at %s %d\n", __FILE__, __LINE__); + else SetDebugObjectName((ID3D11DeviceChild*)stqsamplerstate, "sampler state of drawShadedTexQuad"); + + stqvb = CreateQuadVertexBuffer(4*sizeof(STQVertex), NULL); + SetDebugObjectName((ID3D11DeviceChild*)stqvb, "vertex buffer of drawShadedTexQuad"); + + stsqvb = CreateQuadVertexBuffer(4*sizeof(STSQVertex), NULL); + SetDebugObjectName((ID3D11DeviceChild*)stsqvb, "vertex buffer of drawShadedTexSubQuad"); + + clearvb = CreateQuadVertexBuffer(4*sizeof(ClearVertex), NULL); + SetDebugObjectName((ID3D11DeviceChild*)clearvb, "vertex buffer of drawClearQuad"); + + samDesc = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 16, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + hr = D3D::device->CreateSamplerState(&samDesc, &stsqsamplerstate); + if (FAILED(hr)) PanicAlert("Failed to create sampler state at %s %d\n", __FILE__, __LINE__); + else SetDebugObjectName((ID3D11DeviceChild*)stsqsamplerstate, "sampler state of drawShadedTexSubQuad"); +} + +void ShutdownUtils() +{ + SAFE_RELEASE(stqsamplerstate); + SAFE_RELEASE(stsqsamplerstate); + SAFE_RELEASE(stqvb); + SAFE_RELEASE(stsqvb); + SAFE_RELEASE(clearvb); +} + +void drawShadedTexQuad(ID3D11ShaderResourceView* texture, + const D3D11_RECT* rSource, + int SourceWidth, + int SourceHeight, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float u1 = ((float)rSource->left) * sw; + float u2 = ((float)rSource->right) * sw; + float v1=((float)rSource->top) * sh; + float v2=((float)rSource->bottom) * sh; + + static float lastu1 = 0.f, lastv1 = 0.f, lastu2 = 0.f, lastv2 = 0.f; + + STQVertex coords[4] = { + {-1.0f, 1.0f, 0.0f, u1, v1}, + { 1.0f, 1.0f, 0.0f, u2, v1}, + {-1.0f,-1.0f, 0.0f, u1, v2}, + { 1.0f,-1.0f, 0.0f, u2, v2}, + }; + + // only upload the data to VRAM if it changed + if (lastu1 != u1 || lastv1 != v1 || lastu2 != u2 || lastv2 != v2) + { + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(stqvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + D3D::context->Unmap(stqvb, 0); + } + UINT stride = sizeof(STQVertex); + UINT offset = 0; + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + D3D::context->IASetInputLayout(layout); + D3D::context->IASetVertexBuffers(0, 1, &stqvb, &stride, &offset); + D3D::context->PSSetSamplers(0, 1, &stqsamplerstate); + D3D::context->PSSetShader(PShader, NULL, 0); + D3D::context->PSSetShaderResources(0, 1, &texture); + D3D::context->VSSetShader(Vshader, NULL, 0); + D3D::context->Draw(4, 0); + + ID3D11ShaderResourceView* texres = NULL; + context->PSSetShaderResources(0, 1, &texres); // immediately unbind the texture + D3D::gfxstate->SetShaderResource(0, NULL); + + lastu1 = u1; + lastv1 = v1; + lastu2 = u2; + lastv2 = v2; +} + +void drawShadedTexSubQuad(ID3D11ShaderResourceView* texture, + const MathUtil::Rectangle* rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle* rDest, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout) +{ + float sw = 1.0f /(float) SourceWidth; + float sh = 1.0f /(float) SourceHeight; + float u1 = (rSource->left ) * sw; + float u2 = (rSource->right ) * sw; + float v1 = (rSource->top ) * sh; + float v2 = (rSource->bottom) * sh; + + static MathUtil::Rectangle lastrdest = {0.f}; + static float lastu1 = 0.f, lastv1 = 0.f, lastu2 = 0.f, lastv2 = 0.f; + + STSQVertex coords[4] = { + { rDest->left , rDest->bottom, 0.0f, u1, v1}, + { rDest->right, rDest->bottom, 0.0f, u2, v1}, + { rDest->left , rDest->top , 0.0f, u1, v2}, + { rDest->right, rDest->top , 0.0f, u2, v2}, + }; + + // only upload the data to VRAM if it changed + if (memcmp(rDest, &lastrdest, sizeof(lastrdest)) != 0 || lastu1 != u1 || lastv1 != v1 || lastu2 != u2 || lastv2 != v2) + { + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(stsqvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + D3D::context->Unmap(stsqvb, 0); + } + UINT stride = sizeof(STSQVertex); + UINT offset = 0; + + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context->IASetVertexBuffers(0, 1, &stsqvb, &stride, &offset); + context->IASetInputLayout(layout); + context->PSSetShaderResources(0, 1, &texture); + context->PSSetSamplers(0, 1, &stsqsamplerstate); + context->PSSetShader(PShader, NULL, 0); + context->VSSetShader(Vshader, NULL, 0); + context->Draw(4, 0); + + ID3D11ShaderResourceView* texres = NULL; + context->PSSetShaderResources(0, 1, &texres); // immediately unbind the texture + D3D::gfxstate->SetShaderResource(0, NULL); + + lastu1 = u1; + lastv1 = v1; + lastu2 = u2; + lastv2 = v2; + lastrdest.left = rDest->left; + lastrdest.right = rDest->right; + lastrdest.top = rDest->top; + lastrdest.bottom = rDest->bottom; +} + +void drawClearQuad(u32 Color, float z, ID3D11PixelShader* PShader, ID3D11VertexShader* Vshader, ID3D11InputLayout* layout) +{ + static u32 lastcol = 0; + static float lastz = -15325.376f; // random value + + if (lastcol != Color || lastz != z) + { + float col[4]; + col[0] = (float)((Color & 0xFF) << 24); + col[1] = (float)((Color & 0xFF00) << 8); + col[2] = (float)((Color & 0xFF0000) >> 8); + col[3] = (float)((Color & 0xFF000000) >> 24); + ClearVertex coords[4] = { + {-1.0f, 1.0f, z, {col[0],col[1],col[2],col[3]}}, + { 1.0f, 1.0f, z, {col[0],col[1],col[2],col[3]}}, + {-1.0f, -1.0f, z, {col[0],col[1],col[2],col[3]}}, + { 1.0f, -1.0f, z, {col[0],col[1],col[2],col[3]}}, + }; + + D3D11_MAPPED_SUBRESOURCE map; + context->Map(clearvb, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, coords, sizeof(coords)); + context->Unmap(clearvb, 0); + } + context->VSSetShader(Vshader, NULL, 0); + context->PSSetShader(PShader, NULL, 0); + context->IASetInputLayout(layout); + + UINT stride = sizeof(ClearVertex); + UINT offset = 0; + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context->IASetVertexBuffers(0, 1, &clearvb, &stride, &offset); + context->Draw(4, 0); + + lastcol = Color; + lastz = z; +} + + +} // namespace diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.h b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.h new file mode 100644 index 0000000000..d30ab18956 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.h @@ -0,0 +1,81 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "D3DBase.h" +#include +#include + +namespace D3D +{ + // Font creation flags + #define D3DFONT_BOLD 0x0001 + #define D3DFONT_ITALIC 0x0002 + + // Font rendering flags + #define D3DFONT_CENTERED 0x0001 + + class CD3DFont + { + ID3D11ShaderResourceView* m_pTexture; + ID3D11Buffer* m_pVB; + ID3D11InputLayout* m_InputLayout; + ID3D11PixelShader* m_pshader; + ID3D11VertexShader* m_vshader; + ID3D11BlendState* m_blendstate; + ID3D11RasterizerState* m_raststate; + const int m_dwTexWidth; + const int m_dwTexHeight; + float m_fTexCoords[128-32][4]; + + public: + CD3DFont(); + // 2D text drawing function + // Initializing and destroying device-dependent objects + int Init(); + int Shutdown(); + int DrawTextScaled(float x, float y, + float scale, + float spacing, u32 dwColor, + const char* strText, bool center=true); + }; + + extern CD3DFont font; + + void InitUtils(); + void ShutdownUtils(); + + void drawShadedTexQuad(ID3D11ShaderResourceView* texture, + const D3D11_RECT* rSource, + int SourceWidth, + int SourceHeight, + ID3D11PixelShader* PShader, + ID3D11VertexShader* VShader, + ID3D11InputLayout* layout); + void drawShadedTexSubQuad(ID3D11ShaderResourceView* texture, + const MathUtil::Rectangle* rSource, + int SourceWidth, + int SourceHeight, + const MathUtil::Rectangle* rDest, + ID3D11PixelShader* PShader, + ID3D11VertexShader* Vshader, + ID3D11InputLayout* layout); + void drawClearQuad(u32 Color, float z, ID3D11PixelShader* PShader, ID3D11VertexShader* Vshader, ID3D11InputLayout* layout); + void SaveRenderStates(); + void RestoreRenderStates(); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.cpp b/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.cpp new file mode 100644 index 0000000000..17d6785a0f --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.cpp @@ -0,0 +1,225 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "resource.h" +#include "W32Util/PropertySheet.h" +#include "FileUtil.h" + +#include "D3DBase.h" + +#include "VideoConfig.h" + +#include "TextureCache.h" + +const char* aspect_ratio_names[4] = { + "Auto", + "Force 16:9 Widescreen", + "Force 4:3 Standard", + "Stretch to Window", +}; + +struct TabDirect3D : public W32Util::Tab +{ + void Init(HWND hDlg) + { + WCHAR tempwstr[2000]; + + for (int i = 0; i < 4; i++) + { + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, aspect_ratio_names[i], -1, tempwstr, 2000); + ComboBox_AddString(GetDlgItem(hDlg, IDC_ASPECTRATIO), tempwstr); + } + ComboBox_SetCurSel(GetDlgItem(hDlg, IDC_ASPECTRATIO), g_Config.iAspectRatio); + + Button_SetCheck(GetDlgItem(hDlg, IDC_WIDESCREEN_HACK), g_Config.bWidescreenHack); + Button_SetCheck(GetDlgItem(hDlg, IDC_VSYNC), g_Config.bVSync); + Button_SetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE), g_Config.bSafeTextureCache); + + if (g_Config.iSafeTextureCache_ColorSamples == 0) + { + Button_SetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_SAFE), true); + } + else + { + if (g_Config.iSafeTextureCache_ColorSamples > 128) + { + Button_SetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_NORMAL), true); + } + else + { + Button_SetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_FAST), true); + } + } + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_SAFE), g_Config.bSafeTextureCache); + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_NORMAL), g_Config.bSafeTextureCache); + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_FAST), g_Config.bSafeTextureCache); + + Button_SetCheck(GetDlgItem(hDlg, IDC_EFB_ACCESS_ENABLE), g_Config.bEFBAccessEnable); + } + + void Command(HWND hDlg,WPARAM wParam) + { + switch (LOWORD(wParam)) + { + case IDC_ASPECTRATIO: + g_Config.iAspectRatio = ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_ASPECTRATIO)); + break; + case IDC_VSYNC: + g_Config.bVSync = Button_GetCheck(GetDlgItem(hDlg, IDC_VSYNC)) ? true : false; + break; + case IDC_WIDESCREEN_HACK: + g_Config.bWidescreenHack = Button_GetCheck(GetDlgItem(hDlg, IDC_WIDESCREEN_HACK)) ? true : false; + break; + case IDC_SAFE_TEXTURE_CACHE: + g_Config.bSafeTextureCache = Button_GetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE)) == 0 ? false : true; + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_SAFE), g_Config.bSafeTextureCache); + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_NORMAL), g_Config.bSafeTextureCache); + Button_Enable(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_FAST), g_Config.bSafeTextureCache); + break; + case IDC_EFB_ACCESS_ENABLE: + g_Config.bEFBAccessEnable = Button_GetCheck(GetDlgItem(hDlg, IDC_EFB_ACCESS_ENABLE)) == 0 ? false : true; + break; + default: + break; + } + } + + void Apply(HWND hDlg) + { + if (Button_GetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_SAFE))) + { + g_Config.iSafeTextureCache_ColorSamples = 0; + } + else + { + if (Button_GetCheck(GetDlgItem(hDlg, IDC_SAFE_TEXTURE_CACHE_NORMAL))) + { + if (g_Config.iSafeTextureCache_ColorSamples < 512) + { + g_Config.iSafeTextureCache_ColorSamples = 512; + } + } + else + { + if (g_Config.iSafeTextureCache_ColorSamples > 128 || g_Config.iSafeTextureCache_ColorSamples == 0) + { + g_Config.iSafeTextureCache_ColorSamples = 128; + } + } + } + g_Config.Save((std::string(File::GetUserPath(D_CONFIG_IDX)) + "gfx_dx11.ini").c_str()); + } +}; + +struct TabAdvanced : public W32Util::Tab +{ + void Init(HWND hDlg) + { + Button_SetCheck(GetDlgItem(hDlg, IDC_OSDHOTKEY), g_Config.bOSDHotKey); + Button_SetCheck(GetDlgItem(hDlg, IDC_OVERLAYFPS), g_Config.bShowFPS); + Button_SetCheck(GetDlgItem(hDlg, IDC_OVERLAYSTATS), g_Config.bOverlayStats); + Button_SetCheck(GetDlgItem(hDlg, IDC_OVERLAYPROJSTATS), g_Config.bOverlayProjStats); + Button_SetCheck(GetDlgItem(hDlg, IDC_WIREFRAME), g_Config.bWireFrame); + Button_SetCheck(GetDlgItem(hDlg, IDC_DISABLEFOG), g_Config.bDisableFog); + Button_SetCheck(GetDlgItem(hDlg, IDC_ENABLEEFBCOPY), !g_Config.bEFBCopyDisable); + + Button_SetCheck(GetDlgItem(hDlg, IDC_TEXFMT_OVERLAY), g_Config.bTexFmtOverlayEnable); + Button_SetCheck(GetDlgItem(hDlg, IDC_TEXFMT_CENTER), g_Config.bTexFmtOverlayCenter); + Button_GetCheck(GetDlgItem(hDlg, IDC_TEXFMT_OVERLAY)) ? Button_Enable(GetDlgItem(hDlg,IDC_TEXFMT_CENTER), true) : Button_Enable(GetDlgItem(hDlg,IDC_TEXFMT_CENTER), false); + + Button_SetCheck(GetDlgItem(hDlg, IDC_FORCEANISOTROPY),g_Config.iMaxAnisotropy > 1); + Button_SetCheck(GetDlgItem(hDlg, IDC_EFBSCALEDCOPY), g_Config.bCopyEFBScaled); + + if (Button_GetCheck(GetDlgItem(hDlg, IDC_ENABLEEFBCOPY))) Button_Enable(GetDlgItem(hDlg,IDC_EFBSCALEDCOPY), true); + else Button_Enable(GetDlgItem(hDlg, IDC_EFBSCALEDCOPY), false); + } + void Command(HWND hDlg,WPARAM wParam) + { + switch (LOWORD(wParam)) + { + case IDC_TEXFMT_OVERLAY: + Button_GetCheck(GetDlgItem(hDlg, IDC_TEXFMT_OVERLAY)) ? Button_Enable(GetDlgItem(hDlg, IDC_TEXFMT_CENTER), true) : Button_Enable(GetDlgItem(hDlg, IDC_TEXFMT_CENTER), false); + break; + + case IDC_ENABLEEFBCOPY: + if (Button_GetCheck(GetDlgItem(hDlg, IDC_ENABLEEFBCOPY))) Button_Enable(GetDlgItem(hDlg, IDC_EFBSCALEDCOPY), true); + else Button_Enable(GetDlgItem(hDlg, IDC_EFBSCALEDCOPY), false); + break; + + default: break; + } + } + void Apply(HWND hDlg) + { + g_Config.bTexFmtOverlayEnable = Button_GetCheck(GetDlgItem(hDlg, IDC_TEXFMT_OVERLAY)) ? true : false; + g_Config.bTexFmtOverlayCenter = Button_GetCheck(GetDlgItem(hDlg, IDC_TEXFMT_CENTER)) ? true : false; + + g_Config.bOSDHotKey = Button_GetCheck(GetDlgItem(hDlg, IDC_OSDHOTKEY)) ? true : false; + g_Config.bShowFPS = Button_GetCheck(GetDlgItem(hDlg, IDC_OVERLAYFPS)) ? true : false; + g_Config.bOverlayStats = Button_GetCheck(GetDlgItem(hDlg, IDC_OVERLAYSTATS)) ? true : false; + g_Config.bOverlayProjStats = Button_GetCheck(GetDlgItem(hDlg, IDC_OVERLAYPROJSTATS)) ? true : false; + g_Config.bWireFrame = Button_GetCheck(GetDlgItem(hDlg, IDC_WIREFRAME)) ? true : false; + g_Config.bDisableFog = Button_GetCheck(GetDlgItem(hDlg, IDC_DISABLEFOG)) ? true : false; + g_Config.bEFBCopyDisable = Button_GetCheck(GetDlgItem(hDlg, IDC_ENABLEEFBCOPY)) ? false : true; + g_Config.bCopyEFBToTexture = !g_Config.bEFBCopyDisable; + g_Config.bDumpTextures = false; + g_Config.bDumpFrames = false; + g_Config.bShowShaderErrors = true; + g_Config.bUseNativeMips = true; + + g_Config.iMaxAnisotropy = Button_GetCheck(GetDlgItem(hDlg, IDC_FORCEANISOTROPY)) ? 8 : 1; + g_Config.bForceFiltering = false; + g_Config.bHiresTextures = false; + g_Config.bCopyEFBScaled = Button_GetCheck(GetDlgItem(hDlg, IDC_EFBSCALEDCOPY)) ? true : false; + g_Config.Save((std::string(File::GetUserPath(D_CONFIG_IDX)) + "gfx_dx11.ini").c_str()); + } +}; + +struct TabAbout : public W32Util::Tab +{ + void Init(HWND hDlg) {} + void Command(HWND hDlg,WPARAM wParam) {} + void Apply(HWND hDlg) {} +}; + +void DlgSettings_Show(HINSTANCE hInstance, HWND _hParent) +{ + bool tfoe = g_Config.bTexFmtOverlayEnable; + bool tfoc = g_Config.bTexFmtOverlayCenter; + + g_Config.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + "gfx_dx11.ini").c_str()); + W32Util::PropSheet sheet; + sheet.Add(new TabDirect3D, (LPCTSTR)IDD_SETTINGS, _T("Direct3D")); + sheet.Add(new TabAdvanced, (LPCTSTR)IDD_ADVANCED, _T("Advanced")); + sheet.Add(new TabAbout, (LPCTSTR)IDD_ABOUT, _T("About")); + +#ifdef DEBUGFAST + sheet.Show(hInstance,_hParent,_T("DX11 Graphics Plugin (DEBUGFAST)")); +#elif defined _DEBUG + sheet.Show(hInstance,_hParent,_T("DX11 Graphics Plugin")); +#else + sheet.Show(hInstance,_hParent,_T("DX11 Graphics Plugin (DEBUG)")); +#endif + + if ((tfoe != g_Config.bTexFmtOverlayEnable) || + ((g_Config.bTexFmtOverlayEnable) && ( tfoc != g_Config.bTexFmtOverlayCenter))) + { + TextureCache::Invalidate(false); + } +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.h b/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.h new file mode 100644 index 0000000000..894f893a85 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/DlgSettings.h @@ -0,0 +1,20 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +void DlgSettings_Show(HINSTANCE hInstance, HWND parent); \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.cpp b/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.cpp new file mode 100644 index 0000000000..1f7ff18960 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.cpp @@ -0,0 +1,246 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "VideoConfig.h" +#include "main.h" +#include "EmuWindow.h" +#include "D3DBase.h" +#include "Fifo.h" + + +int OSDChoice = 0 , OSDTime = 0, OSDInternalW = 0, OSDInternalH = 0; + +namespace EmuWindow +{ +HWND m_hWnd = NULL; +HWND m_hParent = NULL; +HINSTANCE m_hInstance = NULL; +WNDCLASSEX wndClass; +const TCHAR m_szClassName[] = _T("DolphinEmuWnd"); +int g_winstyle; +static volatile bool s_sizing; + +bool IsSizing() +{ + return s_sizing; +} + +HWND GetWnd() +{ + return m_hWnd; +} + +HWND GetParentWnd() +{ + return m_hParent; +} + +// --------------------------------------------------------------------- +// KeyDown events +// ------------- +void OnKeyDown(WPARAM wParam) +{ + switch (LOWORD( wParam )) + { + case '3': // OSD keys + case '4': + case '5': + case '6': + case '7': + if (g_Config.bOSDHotKey) + OSDMenu(wParam); + break; + } +} +// --------------------------------------------------------------------- + +LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( iMsg ) + { + case WM_PAINT: + { + HDC hdc; + PAINTSTRUCT ps; + hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + } + break; + + case WM_ENTERSIZEMOVE: + s_sizing = true; + break; + + case WM_EXITSIZEMOVE: + s_sizing = false; + break; + + /* Post thes mouse events to the main window, it's nessesary because in difference to the + keyboard inputs these events only appear here, not in the parent window or any other WndProc()*/ + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + PostMessage(GetParentWnd(), iMsg, wParam, lParam); + break; + + case WM_CLOSE: + // When the user closes the window, we post an event to the main window to call Stop() + // Which then handles all the necessary steps to Shutdown the core + the plugins + if (m_hParent == NULL) + PostMessage(m_hParent, WM_USER, WM_USER_STOP, 0); + break; + + case WM_USER: + if (wParam == WM_USER_KEYDOWN) + OnKeyDown(lParam); + else if (wParam == WIIMOTE_DISCONNECT) + PostMessage(m_hParent, WM_USER, wParam, lParam); + break; + + case WM_SYSCOMMAND: + switch (wParam) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + break; + default: + return DefWindowProc(hWnd, iMsg, wParam, lParam); + } + break; + case WM_SETCURSOR: + PostMessage(m_hParent, WM_USER, WM_USER_SETCURSOR, 0); + return true; + break; + default: + return DefWindowProc(hWnd, iMsg, wParam, lParam); + } + return 0; +} + +// --------------------------------------------------------------------- +// OSD Menu +// ------------- +// Let's begin with 3 since 1 and 2 are default Wii keys +// ------------- +void OSDMenu(WPARAM wParam) +{ + switch( LOWORD( wParam )) + { + case '3': + OSDChoice = 1; + // Toggle native resolution + OSDInternalW = D3D::GetBackBufferWidth(); + OSDInternalH = D3D::GetBackBufferHeight(); + break; + case '4': + OSDChoice = 2; + // Toggle aspect ratio + g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3; + break; + case '5': + OSDChoice = 3; + PanicAlert("Toggling EFB copy not implemented!\n"); + break; + case '6': + OSDChoice = 4; + g_Config.bDisableFog = !g_Config.bDisableFog; + break; + case '7': + OSDChoice = 5; + g_Config.bDisableLighting = !g_Config.bDisableLighting; + break; + } +} + +HWND OpenWindow(HWND parent, HINSTANCE hInstance, int width, int height, const TCHAR *title) +{ + wndClass.cbSize = sizeof( wndClass ); + wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInstance; + wndClass.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wndClass.hCursor = NULL; + wndClass.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = m_szClassName; + wndClass.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); + + m_hInstance = hInstance; + RegisterClassEx( &wndClass ); + + m_hParent = parent; + + m_hWnd = CreateWindow(m_szClassName, title, WS_CHILD, + 0, 0, width, height, m_hParent, NULL, hInstance, NULL); + + return m_hWnd; +} + +void Show() +{ + ShowWindow(m_hWnd, SW_SHOW); + BringWindowToTop(m_hWnd); + UpdateWindow(m_hWnd); + SetFocus(m_hParent); +} + +HWND Create(HWND hParent, HINSTANCE hInstance, const TCHAR *title) +{ + // TODO: + // 1. Remove redundant window manipulation, + // 2. Make DX11 in fullscreen can be overlapped by other dialogs + HWND Ret; + int x=0, y=0, width=640, height=480; + g_VideoInitialize.pRequestWindowSize(x, y, width, height); + + Ret = OpenWindow(hParent, hInstance, width, height, title); + + if (Ret) + { + Show(); + } + return Ret; +} + +void Close() +{ + if (m_hParent == NULL) + DestroyWindow(m_hWnd); + UnregisterClass(m_szClassName, m_hInstance); +} + +void SetSize(int width, int height) +{ + RECT rc = {0, 0, width, height}; + DWORD style = GetWindowLong(m_hWnd, GWL_STYLE); + AdjustWindowRect(&rc, style, false); + + int w = rc.right - rc.left; + int h = rc.bottom - rc.top; + + rc.left = (1280 - w)/2; + rc.right = rc.left + w; + rc.top = (1024 - h)/2; + rc.bottom = rc.top + h; + MoveWindow(m_hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE); +} + +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.h b/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.h new file mode 100644 index 0000000000..8f925527da --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/EmuWindow.h @@ -0,0 +1,20 @@ +#ifndef _EMUWINDOW_H +#define _EMUWINDOW_H + +#include + +namespace EmuWindow +{ + +HWND GetWnd(); +HWND GetParentWnd(); +HWND Create(HWND hParent, HINSTANCE hInstance, const TCHAR* title); +void Show(); +void Close(); +void SetSize(int displayWidth, int displayHeight); +bool IsSizing(); +void OSDMenu(WPARAM wParam); + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/FBManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/FBManager.cpp new file mode 100644 index 0000000000..35f4b8327d --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/FBManager.cpp @@ -0,0 +1,314 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "D3DBase.h" +#include "D3DTexture.h" +#include "D3DUtil.h" +#include "Render.h" +#include "FBManager.h" +#include "VideoConfig.h" +#include "PixelShaderCache.h" +#include "VertexShaderCache.h" + +#undef CHECK +#define CHECK(cond, Message) if (!(cond)) { PanicAlert(__FUNCTION__ "Failed in %s at line %d: %s" , __FILE__, __LINE__, Message); } + +FramebufferManager FBManager; +ID3D11SamplerState* copytoVirtualXFBsampler = NULL; + +D3DTexture2D* &FramebufferManager::GetEFBColorTexture() { return m_efb.color_tex; } +ID3D11Texture2D* &FramebufferManager::GetEFBColorStagingBuffer() { return m_efb.color_staging_buf; } + +D3DTexture2D* &FramebufferManager::GetEFBDepthTexture() { return m_efb.depth_tex; } +D3DTexture2D* &FramebufferManager::GetEFBDepthReadTexture() { return m_efb.depth_read_texture; } +ID3D11Texture2D* &FramebufferManager::GetEFBDepthStagingBuffer() { return m_efb.depth_staging_buf; } + +void FramebufferManager::Create() +{ + unsigned int target_width = Renderer::GetFullTargetWidth(); + unsigned int target_height = Renderer::GetFullTargetHeight(); + ID3D11Texture2D* buf; + D3D11_TEXTURE2D_DESC texdesc; + HRESULT hr; + + // create framebuffer color texture + m_efb.color_tex = D3DTexture2D::Create(target_width, target_height, (D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM); + CHECK(m_efb.color_tex != NULL, "create EFB color texture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetTex(), "EFB color texture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetRTV(), "EFB color texture render target view"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_tex->GetSRV(), "EFB color texture shader resource view"); + + // create a staging texture for Renderer::AccessEFB + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, 1, 1, 1, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &m_efb.color_staging_buf); + CHECK(hr==S_OK, "create EFB color staging buffer"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.color_staging_buf, "EFB color staging texture (used for Renderer::AccessEFB)"); + + // EFB depth buffer + // TODO: Only bind as shader resource if EFB access enabled + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R24G8_TYPELESS, target_width, target_height, 1, 1, D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &buf); + CHECK(hr==S_OK, "create EFB depth texture"); + m_efb.depth_tex = new D3DTexture2D(buf, (D3D11_BIND_FLAG)(D3D11_BIND_DEPTH_STENCIL|D3D11_BIND_SHADER_RESOURCE), DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT); + buf->Release(); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetTex(), "EFB depth texture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetDSV(), "EFB depth texture depth stencil view"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_tex->GetSRV(), "EFB depth texture shader resource view"); + + // render target for depth buffer access in Renderer::AccessEFB + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 4, 4, 1, 1, D3D11_BIND_RENDER_TARGET); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &buf); + CHECK(hr==S_OK, "create EFB depth read texture"); + m_efb.depth_read_texture = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET); + buf->Release(); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_read_texture->GetTex(), "EFB depth read texture (used in Renderer::AccessEFB)"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_read_texture->GetRTV(), "EFB depth read texture render target view (used in Renderer::AccessEFB)"); + + // staging texture to which we copy the data from m_efb.depth_read_texture + texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R32_FLOAT, 4, 4, 1, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ|D3D11_CPU_ACCESS_WRITE); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &m_efb.depth_staging_buf); + CHECK(hr==S_OK, "create EFB depth staging buffer"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)m_efb.depth_staging_buf, "EFB depth staging texture (used for Renderer::AccessEFB)"); + + // sampler state for FramebufferManager::copyToVirtualXFB + float border[4] = {0.f, 0.f, 0.f, 0.f}; + D3D11_SAMPLER_DESC samplerdesc = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 1, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX); + D3D::device->CreateSamplerState(&samplerdesc, ©toVirtualXFBsampler); + D3D::SetDebugObjectName((ID3D11DeviceChild*)copytoVirtualXFBsampler, "sampler state used for FramebufferManager::copyToVirtualXFB"); +} + +void FramebufferManager::Destroy() +{ + SAFE_RELEASE(m_efb.color_tex); + SAFE_RELEASE(m_efb.color_staging_buf); + SAFE_RELEASE(m_efb.depth_tex); + SAFE_RELEASE(m_efb.depth_staging_buf); + SAFE_RELEASE(m_efb.depth_read_texture); + + SAFE_RELEASE(copytoVirtualXFBsampler); + + for (VirtualXFBListType::iterator it = m_virtualXFBList.begin(); it != m_virtualXFBList.end(); ++it) + SAFE_RELEASE(it->xfbSource.tex); + + m_virtualXFBList.clear(); + SAFE_RELEASE(m_realXFBSource.tex); +} + +void FramebufferManager::CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + copyToVirtualXFB(xfbAddr, fbWidth, fbHeight, sourceRc); +} + +const XFBSource** FramebufferManager::GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + return getVirtualXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); +} + +FramebufferManager::VirtualXFBListType::iterator FramebufferManager::findVirtualXFB(u32 xfbAddr, u32 width, u32 height) +{ + u32 srcLower = xfbAddr; + u32 srcUpper = xfbAddr + 2 * width * height; + + VirtualXFBListType::iterator it; + for (it = m_virtualXFBList.begin(); it != m_virtualXFBList.end(); ++it) + { + u32 dstLower = it->xfbAddr; + u32 dstUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight; + + if (dstLower >= srcLower && dstUpper <= srcUpper) + return it; + } + + // that address is not in the Virtual XFB list. + return m_virtualXFBList.end(); +} + +void FramebufferManager::replaceVirtualXFB() +{ + VirtualXFBListType::iterator it = m_virtualXFBList.begin(); + + s32 srcLower = it->xfbAddr; + s32 srcUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight; + s32 lineSize = 2 * it->xfbWidth; + + it++; + + while (it != m_virtualXFBList.end()) + { + s32 dstLower = it->xfbAddr; + s32 dstUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight; + + if (dstLower >= srcLower && dstUpper <= srcUpper) + { + // invalidate the data + it->xfbAddr = 0; + it->xfbHeight = 0; + it->xfbWidth = 0; + } + else if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper)) + { + s32 upperOverlap = (srcUpper - dstLower) / lineSize; + s32 lowerOverlap = (dstUpper - srcLower) / lineSize; + + if (upperOverlap > 0 && lowerOverlap < 0) + { + it->xfbAddr += lineSize * upperOverlap; + it->xfbHeight -= upperOverlap; + } + else if (lowerOverlap > 0) + { + it->xfbHeight -= lowerOverlap; + } + } + + it++; + } +} + +void FramebufferManager::copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + // TODO + PanicAlert("copyToRealXFB not implemented, yet\n"); +} + +void FramebufferManager::copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + D3DTexture2D* xfbTex; + HRESULT hr = 0; + + VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight); + + if (it == m_virtualXFBList.end() && (int)m_virtualXFBList.size() >= MAX_VIRTUAL_XFB) + { + PanicAlert("Requested creating a new virtual XFB although the maximum number has already been reached! Report this to the devs"); + return; + } + + float scaleX = Renderer::GetTargetScaleX(); + float scaleY = Renderer::GetTargetScaleY(); + TargetRectangle targetSource,efbSource; + efbSource = Renderer::ConvertEFBRectangle(sourceRc); + targetSource.top = (int)(sourceRc.top * scaleY); + targetSource.bottom = (int)(sourceRc.bottom * scaleY); + targetSource.left = (int)(sourceRc.left * scaleX); + targetSource.right = (int)(sourceRc.right * scaleX); + unsigned int target_width = targetSource.right - targetSource.left; + unsigned int target_height = targetSource.bottom - targetSource.top; + if (it != m_virtualXFBList.end()) // overwrite an existing Virtual XFB + { + it->xfbAddr = xfbAddr; + it->xfbWidth = fbWidth; + it->xfbHeight = fbHeight; + + it->xfbSource.srcAddr = xfbAddr; + it->xfbSource.srcWidth = fbWidth; + it->xfbSource.srcHeight = fbHeight; + + if (it->xfbSource.texWidth != target_width || it->xfbSource.texHeight != target_height || !(it->xfbSource.tex)) + { + SAFE_RELEASE(it->xfbSource.tex); + it->xfbSource.tex = D3DTexture2D::Create(target_width, target_height, (D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM); + if (it->xfbSource.tex == NULL) PanicAlert("Failed to create XFB texture\n"); + } + xfbTex = it->xfbSource.tex; + + it->xfbSource.texWidth = target_width; + it->xfbSource.texHeight = target_height; + + // move this Virtual XFB to the front of the list. + m_virtualXFBList.splice(m_virtualXFBList.begin(), m_virtualXFBList, it); + + // keep stale XFB data from being used + replaceVirtualXFB(); + } + else // create a new Virtual XFB and place it at the front of the list + { + VirtualXFB newVirt; + + newVirt.xfbSource.tex = D3DTexture2D::Create(target_width, target_height, (D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET|D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM); + if (newVirt.xfbSource.tex == NULL) PanicAlert("Failed to create a new virtual XFB"); + + newVirt.xfbAddr = xfbAddr; + newVirt.xfbWidth = fbWidth; + newVirt.xfbHeight = fbHeight; + + xfbTex = newVirt.xfbSource.tex; + newVirt.xfbSource.texWidth = target_width; + newVirt.xfbSource.texHeight = target_height; + + // Add the new Virtual XFB to the list + if (m_virtualXFBList.size() >= MAX_VIRTUAL_XFB) + { + PanicAlert("Requested creating a new virtual XFB although the maximum number has already been reached! Report this to the devs"); + newVirt.xfbSource.tex->Release(); + return; + // TODO, possible alternative to failing: just delete the oldest virtual XFB: + // m_virtualXFBList.back().xfbSource.tex->Release(); + // m_virtualXFBList.pop_back(); + } + m_virtualXFBList.push_front(newVirt); + } + if (!xfbTex->GetRTV()) return; + + Renderer::ResetAPIState(); // reset any game specific settings + + // copy EFB data to XFB and restore render target again + D3D11_RECT sourcerect = CD3D11_RECT(efbSource.left, efbSource.top, efbSource.right, efbSource.bottom); + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)target_width, (float)target_height); + D3D::context->RSSetViewports(1, &vp); + D3D::context->PSSetSamplers(0, 1, ©toVirtualXFBsampler); + D3D::context->OMSetRenderTargets(1, &xfbTex->GetRTV(), NULL); + D3D::drawShadedTexQuad(GetEFBColorTexture()->GetSRV(), &sourcerect, + Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), + PixelShaderCache::GetColorCopyProgram(), VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout()); + D3D::context->OMSetRenderTargets(1, &GetEFBColorTexture()->GetRTV(), GetEFBDepthTexture()->GetDSV()); + Renderer::RestoreAPIState(); +} + +const XFBSource** FramebufferManager::getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + PanicAlert("getRealXFBSource not implemented, yet\n"); + return NULL; +} + +const XFBSource** FramebufferManager::getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount) +{ + xfbCount = 0; + + if (m_virtualXFBList.size() == 0) // no Virtual XFBs available + return NULL; + + u32 srcLower = xfbAddr; + u32 srcUpper = xfbAddr + 2 * fbWidth * fbHeight; + + VirtualXFBListType::iterator it; + for (it = m_virtualXFBList.end(); it != m_virtualXFBList.begin();) + { + --it; + + u32 dstLower = it->xfbAddr; + u32 dstUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight; + + if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper)) + { + m_overlappingXFBArray[xfbCount] = &(it->xfbSource); + xfbCount++; + } + } + return &m_overlappingXFBArray[0]; +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/FBManager.h b/Source/Plugins/Plugin_VideoDX11/Src/FBManager.h new file mode 100644 index 0000000000..b06a0c40d0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/FBManager.h @@ -0,0 +1,151 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FBMANAGER_D3D_H_ +#define _FBMANAGER_D3D_H_ + +#include +#include "D3DBase.h" + +// On the GameCube, the game sends a request for the graphics processor to +// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM +// called the XFB (External Framebuffer). The size and location of the XFB is +// decided at the time of the copy, and the format is always YUYV. The video +// interface is given a pointer to the XFB, which will be decoded and +// displayed on the TV. +// +// There are two ways for Dolphin to emulate this: +// +// Real XFB mode: +// +// Dolphin will behave like the GameCube and encode the EFB to +// a portion of GameCube RAM. The emulated video interface will decode the data +// for output to the screen. +// +// Advantages: Behaves exactly like the GameCube. +// Disadvantages: Resolution will be limited. +// +// Virtual XFB mode: +// +// When a request is made to copy the EFB to an XFB, Dolphin +// will remember the RAM location and size of the XFB in a Virtual XFB list. +// The video interface will look up the XFB in the list and use the enhanced +// data stored there, if available. +// +// Advantages: Enables high resolution graphics, better than real hardware. +// Disadvantages: If the GameCube CPU writes directly to the XFB (which is +// possible but uncommon), the Virtual XFB will not capture this information. + +// There may be multiple XFBs in GameCube RAM. This is the maximum number to +// virtualize. +const int MAX_VIRTUAL_XFB = 8; + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +struct XFBSource +{ + XFBSource() + { + this->srcAddr = 0; + this->srcWidth = 0; + this->srcHeight = 0; + this->tex = NULL; + this->texWidth = 0; + this->texHeight = 0; + } + + u32 srcAddr; + u32 srcWidth; + u32 srcHeight; + + D3DTexture2D* tex; + unsigned int texWidth; + unsigned int texHeight; +}; + +class FramebufferManager +{ +public: + FramebufferManager() + { + m_efb.color_tex = NULL; + m_efb.color_staging_buf = NULL; + m_efb.depth_tex = NULL; + m_efb.depth_staging_buf = NULL; + m_efb.depth_read_texture = NULL; + + m_realXFBSource.tex = NULL; + } + + void Create(); + void Destroy(); + + void CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + const XFBSource** GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + + D3DTexture2D* &GetEFBColorTexture(); + ID3D11Texture2D* &GetEFBColorStagingBuffer(); + + D3DTexture2D* &GetEFBDepthTexture(); + D3DTexture2D* &GetEFBDepthReadTexture(); + ID3D11Texture2D* &GetEFBDepthStagingBuffer(); + +private: + + struct VirtualXFB + { + // Address and size in GameCube RAM + u32 xfbAddr; + u32 xfbWidth; + u32 xfbHeight; + + XFBSource xfbSource; + }; + + typedef std::list VirtualXFBListType; + + VirtualXFBListType::iterator findVirtualXFB(u32 xfbAddr, u32 width, u32 height); + + void replaceVirtualXFB(); + + void copyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + void copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc); + const XFBSource** getRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + const XFBSource** getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount); + + XFBSource m_realXFBSource; // used in real XFB mode + VirtualXFBListType m_virtualXFBList; // used in virtual XFB mode + + const XFBSource* m_overlappingXFBArray[MAX_VIRTUAL_XFB]; + + struct + { + D3DTexture2D* color_tex; + ID3D11Texture2D* color_staging_buf; + + D3DTexture2D* depth_tex; + ID3D11Texture2D* depth_staging_buf; + D3DTexture2D* depth_read_texture; + } m_efb; +}; + +extern FramebufferManager FBManager; + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Globals.h b/Source/Plugins/Plugin_VideoDX11/Src/Globals.h new file mode 100644 index 0000000000..f1c13d728c --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/Globals.h @@ -0,0 +1,31 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _GLOBALS_H_ +#define _GLOBALS_H_ + +#include "Common.h" +#include "VideoConfig.h" +#include "main.h" + +#include "VideoCommon.h" +#include "pluginspecs_video.h" + +// A global plugin specification +extern PLUGIN_GLOBALS* globals; + +#endif // _GLOBALS_H_ \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/NativeVertexFormat.cpp b/Source/Plugins/Plugin_VideoDX11/Src/NativeVertexFormat.cpp new file mode 100644 index 0000000000..7df91d92fe --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/NativeVertexFormat.cpp @@ -0,0 +1,148 @@ + +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "D3DBase.h" + +#include "Profiler.h" +#include "x64Emitter.h" +#include "ABI.h" +#include "MemoryUtil.h" +#include "VertexShaderGen.h" +#include "VertexShaderCache.h" + +#include "CPMemory.h" +#include "NativeVertexFormat.h" + +#include +using std::string; + +class D3DVertexFormat : public NativeVertexFormat +{ + D3D11_INPUT_ELEMENT_DESC m_elems[32]; + UINT m_num_elems; + +public: + D3DVertexFormat(); + ~D3DVertexFormat(); + void Initialize(const PortableVertexDeclaration &_vtx_decl); + void SetupVertexPointers() const; +}; + +NativeVertexFormat* NativeVertexFormat::Create() +{ + return new D3DVertexFormat(); +} + +D3DVertexFormat::D3DVertexFormat() : m_num_elems(0) { } +D3DVertexFormat::~D3DVertexFormat() {} + +DXGI_FORMAT VarToD3D(VarType t, int size) +{ + DXGI_FORMAT retval = DXGI_FORMAT_UNKNOWN; + static const DXGI_FORMAT lookup1[5] = { + DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT + }; + static const DXGI_FORMAT lookup2[5] = { + DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16_SNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R32G32_FLOAT + }; + static const DXGI_FORMAT lookup3[5] = { + DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32_FLOAT + }; + static const DXGI_FORMAT lookup4[5] = { + DXGI_FORMAT_R8G8B8A8_SNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R16G16B16A16_SNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R32G32B32A32_FLOAT + }; + + switch (size) + { + case 1: retval = lookup1[t]; break; + case 2: retval = lookup2[t]; break; + case 3: retval = lookup3[t]; break; + case 4: retval = lookup4[t]; break; + default: break; + } + if (retval == DXGI_FORMAT_UNKNOWN) + { + PanicAlert("VarToD3D: Invalid type/size combo %i , %i", (int)t, size); + } + return retval; +} + +void D3DVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl) +{ + vertex_stride = _vtx_decl.stride; + memset(m_elems, 0, sizeof(m_elems)); + + m_elems[m_num_elems].SemanticName = "POSITION"; + m_elems[m_num_elems].AlignedByteOffset = 0; + m_elems[m_num_elems].Format = DXGI_FORMAT_R32G32B32_FLOAT; + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + + for (int i = 0; i < 3; i++) + { + if (_vtx_decl.normal_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "NORMAL"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.normal_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.normal_gl_type, _vtx_decl.normal_gl_size); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + for (int i = 0; i < 2; i++) + { + if (_vtx_decl.color_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "COLOR"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.color_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.color_gl_type, 4); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + for (int i = 0; i < 8; i++) + { + if (_vtx_decl.texcoord_offset[i] > 0) + { + m_elems[m_num_elems].SemanticName = "TEXCOORD"; + m_elems[m_num_elems].SemanticIndex = i; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.texcoord_offset[i]; + m_elems[m_num_elems].Format = VarToD3D(_vtx_decl.texcoord_gl_type[i], _vtx_decl.texcoord_size[i]); + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } + } + + if (_vtx_decl.posmtx_offset != -1) + { + m_elems[m_num_elems].SemanticName = "BLENDINDICES"; + m_elems[m_num_elems].AlignedByteOffset = _vtx_decl.posmtx_offset; + m_elems[m_num_elems].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + m_elems[m_num_elems].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + ++m_num_elems; + } +} + +void D3DVertexFormat::SetupVertexPointers() const +{ + D3D::gfxstate->SetInputElements(m_elems, m_num_elems); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.cpp b/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.cpp new file mode 100644 index 0000000000..5967e7ec12 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.cpp @@ -0,0 +1,274 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +#include "Globals.h" +#include "D3DBase.h" +#include "D3Dcompiler.h" +#include "D3DShader.h" +#include "Statistics.h" +#include "VideoConfig.h" +#include "PixelShaderGen.h" +#include "PixelShaderManager.h" +#include "PixelShaderCache.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" +#include "ImageWrite.h" + +extern int frameCount; + +PixelShaderCache::PSCache PixelShaderCache::PixelShaders; +const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry; + +LinearDiskCache g_ps_disk_cache; + +ID3D11PixelShader* s_ColorMatrixProgram = NULL; +ID3D11PixelShader* s_ColorCopyProgram = NULL; +ID3D11PixelShader* s_DepthMatrixProgram = NULL; +ID3D11PixelShader* s_ClearProgram = NULL; + +const char clear_program_code[] = { + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 pos : POSITION,\n" + "in float4 incol0 : COLOR0){\n" + "ocol0 = incol0;\n" + "}\n" +}; + +const char color_copy_program_code[] = { + "uniform sampler samp0 : register(s0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 pos : POSITION,\n" + "in float2 uv0 : TEXCOORD0){\n" + "ocol0 = tex2D(samp0,uv0);\n" + "}\n" +}; + +const char color_matrix_program_code[] = { + "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + "in float4 pos : POSITION,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = tex2D(samp0,uv0);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n" +}; + +const char depth_matrix_program[] = { + "uniform sampler samp0 : register(s0);\n" + "uniform float4 cColMatrix[5] : register(c0);\n" + "void main(\n" + "out float4 ocol0 : COLOR0,\n" + " in float4 pos : POSITION,\n" + " in float2 uv0 : TEXCOORD0){\n" + "float4 texcol = tex2D(samp0,uv0);\n" + "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n" + "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n" + "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n" + "}\n" +}; + +ID3D11PixelShader* PixelShaderCache::GetColorMatrixProgram() +{ + return s_ColorMatrixProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetDepthMatrixProgram() +{ + return s_DepthMatrixProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetColorCopyProgram() +{ + return s_ColorCopyProgram; +} + +ID3D11PixelShader* PixelShaderCache::GetClearProgram() +{ + return s_ClearProgram; +} + +// HACK to avoid some invasive VideoCommon changes +// these values are hardcoded, they depend on internal D3DCompile behavior; TODO: Solve this with D3DReflect or something +// offset given in floats, table index is float4 +unsigned int ps_constant_offset_table[] = { + 0, 4, 8, 12, // C_COLORS, 16 + 16, 20, 24, 28, // C_KCOLORS, 16 + 32, // C_ALPHA, 4 + 36, 40, 44, 48, 52, 56, 60, 64, // C_TEXDIMS, 32 + 68, 72, // C_ZBIAS, 8 + 76, 80, // C_INDTEXSCALE, 8 + 84, 88, 92, 96, 100, 104, // C_INDTEXMTX, 24 + 108, 112, // C_FOG, 8 +}; +void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number] ] = f1; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+1] = f2; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+2] = f3; + D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+3] = f4; + D3D::gfxstate->pscbufchanged = true; +} + +void SetPSConstant4fv(unsigned int const_number, const float* f) +{ + memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4); + D3D::gfxstate->pscbufchanged = true; +} + +void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float* f) +{ + memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4*count); + D3D::gfxstate->pscbufchanged = true; +} + +// this class will load the precompiled shaders into our cache +class PixelShaderCacheInserter : public LinearDiskCacheReader { +public: + void Read(const u8* key, int key_size, const u8* value, int value_size) + { + PIXELSHADERUID uid; + if (key_size != sizeof(uid)) { + ERROR_LOG(VIDEO, "Wrong key size in pixel shader cache"); + return; + } + memcpy(&uid, key, key_size); + PixelShaderCache::InsertByteCode(uid, (void*)value, value_size); + } +}; + +void PixelShaderCache::Init() +{ + // used when drawing clear quads + s_ClearProgram = D3D::CompileAndCreatePixelShader(clear_program_code, strlen(clear_program_code)); + + // used when copying/resolving the color buffer + s_ColorCopyProgram = D3D::CompileAndCreatePixelShader(color_copy_program_code, strlen(color_copy_program_code)); + + // used for color conversion + s_ColorMatrixProgram = D3D::CompileAndCreatePixelShader(color_matrix_program_code, strlen(color_matrix_program_code)); + + // used for depth copy + s_DepthMatrixProgram = D3D::CompileAndCreatePixelShader(depth_matrix_program, strlen(depth_matrix_program)); + + Clear(); + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx11-%s-ps.cache", File::GetUserPath(D_SHADERCACHE_IDX), globals->unique_id); + PixelShaderCacheInserter inserter; + g_ps_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +// ONLY to be used during shutdown. +void PixelShaderCache::Clear() +{ + for (PSCache::iterator iter = PixelShaders.begin(); iter != PixelShaders.end(); iter++) + iter->second.Destroy(); + PixelShaders.clear(); +} + +void PixelShaderCache::Shutdown() +{ + SAFE_RELEASE(s_ColorMatrixProgram); + SAFE_RELEASE(s_ColorCopyProgram); + SAFE_RELEASE(s_DepthMatrixProgram); + SAFE_RELEASE(s_ClearProgram); + + Clear(); + g_ps_disk_cache.Sync(); + g_ps_disk_cache.Close(); +} + +bool PixelShaderCache::SetShader(bool dstAlpha) +{ + PIXELSHADERUID uid; + GetPixelShaderId(&uid, PixelShaderManager::GetTextureMask(), dstAlpha); + + // check if the shader is already set + if (uid == last_pixel_shader_uid && PixelShaders[uid].frameCount == frameCount) + { + PSCache::const_iterator iter = PixelShaders.find(uid); + return (iter != PixelShaders.end() && iter->second.shader); + } + + memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID)); + + // check if the shader is already in the cache + PSCache::iterator iter; + iter = PixelShaders.find(uid); + if (iter != PixelShaders.end()) + { + iter->second.frameCount = frameCount; + const PSCacheEntry &entry = iter->second; + last_entry = &entry; + + D3D::gfxstate->SetPShader(entry.shader); + return (entry.shader != NULL); + } + + // need to compile a new shader + const char* code = GeneratePixelShaderCode(PixelShaderManager::GetTextureMask(), dstAlpha, 2); + + ID3D10Blob* pbytecode; + if (!D3D::CompilePixelShader(code, strlen(code), &pbytecode)) + { + PanicAlert("Failed to compile Pixel Shader:\n\n%s", code); + return false; + } + + // insert the bytecode into the caches + g_ps_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->GetBufferPointer(), pbytecode->GetBufferSize()); + g_ps_disk_cache.Sync(); + + bool result = InsertByteCode(uid, pbytecode->GetBufferPointer(), pbytecode->GetBufferSize()); + D3D::gfxstate->SetPShader(last_entry->shader); + pbytecode->Release(); + return result; +} + +bool PixelShaderCache::InsertByteCode(const PIXELSHADERUID &uid, void* bytecode, unsigned int bytecodelen) +{ + ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen); + if (shader == NULL) + { + PanicAlert("Failed to create pixel shader at %s %d\n", __FILE__, __LINE__); + return false; + } + + // make an entry in the table + PSCacheEntry newentry; + newentry.shader = shader; + newentry.frameCount = frameCount; + PixelShaders[uid] = newentry; + last_entry = &PixelShaders[uid]; + + INCSTAT(stats.numPixelShadersCreated); + SETSTAT(stats.numPixelShadersAlive, PixelShaders.size()); + + return true; +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.h b/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.h new file mode 100644 index 0000000000..d105503647 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/PixelShaderCache.h @@ -0,0 +1,57 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "Common.h" +#include "LinearDiskCache.h" +#include "D3DBase.h" + +#include + +#include "PixelShaderGen.h" +#include "VertexShaderGen.h" + +class PixelShaderCache +{ +public: + static void Init(); + static void Clear(); + static void Shutdown(); + static bool SetShader(bool dstAlpha); + static bool InsertByteCode(const PIXELSHADERUID &uid, void* bytecode, unsigned int bytecodelen); + + static ID3D11PixelShader* GetColorMatrixProgram(); + static ID3D11PixelShader* GetColorCopyProgram(); + static ID3D11PixelShader* GetDepthMatrixProgram(); + static ID3D11PixelShader* GetClearProgram(); + +private: + struct PSCacheEntry + { + ID3D11PixelShader* shader; + int frameCount; + + PSCacheEntry() : shader(NULL), frameCount(0) {} + void Destroy() { SAFE_RELEASE(shader); } + }; + + typedef std::map PSCache; + + static PSCache PixelShaders; + static const PSCacheEntry* last_entry; +}; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp new file mode 100644 index 0000000000..7ed3e83f01 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp @@ -0,0 +1,1102 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "StringUtil.h" +#include "Common.h" +#include "Atomic.h" +#include "FileUtil.h" +#include "Thread.h" +#include "Timer.h" +#include "Statistics.h" + +#include "VideoConfig.h" +#include "main.h" +#include "VertexManager.h" +#include "Render.h" +#include "OpcodeDecoding.h" +#include "BPStructs.h" +#include "XFStructs.h" +#include "D3DUtil.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "VertexShaderCache.h" +#include "PixelShaderCache.h" +#include "VertexLoaderManager.h" +#include "TextureCache.h" +#include "EmuWindow.h" +#include "AVIDump.h" +#include "OnScreenDisplay.h" +#include "FBManager.h" +#include "Fifo.h" + +#include + +int frameCount = 0; +static int s_fps = 0; + +static bool WindowResized; +static int s_target_width; +static int s_target_height; + +static int s_Fulltarget_width; +static int s_Fulltarget_height; + +static int s_backbuffer_width; +static int s_backbuffer_height; + +static int s_XFB_width; +static int s_XFB_height; + +static float xScale; +static float yScale; + +static u32 s_blendMode; +static bool XFBWrited; + +ID3D11Buffer* access_efb_cbuf = NULL; +ID3D11DepthStencilState* cleardepthstates[2] = {NULL}; +ID3D11RasterizerState* clearraststate = NULL; +ID3D11BlendState* resetblendstate = NULL; +ID3D11DepthStencilState* resetdepthstate = NULL; +ID3D11RasterizerState* resetraststate = NULL; + +// state translation lookup tables +static const D3D11_BLEND d3dSrcFactors[8] = +{ + D3D11_BLEND_ZERO, + D3D11_BLEND_ONE, + D3D11_BLEND_DEST_COLOR, + D3D11_BLEND_INV_DEST_COLOR, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_DEST_ALPHA, + D3D11_BLEND_INV_DEST_ALPHA +}; + +static const D3D11_BLEND d3dDestFactors[8] = +{ + D3D11_BLEND_ZERO, + D3D11_BLEND_ONE, + D3D11_BLEND_SRC_COLOR, + D3D11_BLEND_INV_SRC_COLOR, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_DEST_ALPHA, + D3D11_BLEND_INV_DEST_ALPHA +}; + +// 0 0x00 +// 1 Source & destination +// 2 Source & ~destination +// 3 Source +// 4 ~Source & destination +// 5 Destination +// 6 Source ^ destination = Source & ~destination | ~Source & destination +// 7 Source | destination + +// 8 ~(Source | destination) +// 9 ~(Source ^ destination) = ~Source & ~destination | Source & destination +// 10 ~Destination +// 11 Source | ~destination +// 12 ~Source +// 13 ~Source | destination +// 14 ~(Source & destination) +// 15 0xff + +static const D3D11_BLEND_OP d3dLogicOps[16] = +{ + D3D11_BLEND_OP_ADD,//0 + D3D11_BLEND_OP_ADD,//1 + D3D11_BLEND_OP_SUBTRACT,//2 + D3D11_BLEND_OP_ADD,//3 + D3D11_BLEND_OP_REV_SUBTRACT,//4 + D3D11_BLEND_OP_ADD,//5 + D3D11_BLEND_OP_MAX,//6 + D3D11_BLEND_OP_ADD,//7 + + D3D11_BLEND_OP_MAX,//8 + D3D11_BLEND_OP_MAX,//9 + D3D11_BLEND_OP_ADD,//10 + D3D11_BLEND_OP_ADD,//11 + D3D11_BLEND_OP_ADD,//12 + D3D11_BLEND_OP_ADD,//13 + D3D11_BLEND_OP_ADD,//14 + D3D11_BLEND_OP_ADD//15 +}; + +static const D3D11_BLEND d3dLogicOpSrcFactors[16] = +{ + D3D11_BLEND_ZERO,//0 + D3D11_BLEND_DEST_COLOR,//1 + D3D11_BLEND_ONE,//2 + D3D11_BLEND_ONE,//3 + D3D11_BLEND_DEST_COLOR,//4 + D3D11_BLEND_ZERO,//5 + D3D11_BLEND_INV_DEST_COLOR,//6 + D3D11_BLEND_INV_DEST_COLOR,//7 + + D3D11_BLEND_INV_SRC_COLOR,//8 + D3D11_BLEND_INV_SRC_COLOR,//9 + D3D11_BLEND_INV_DEST_COLOR,//10 + D3D11_BLEND_ONE,//11 + D3D11_BLEND_INV_SRC_COLOR,//12 + D3D11_BLEND_INV_SRC_COLOR,//13 + D3D11_BLEND_INV_DEST_COLOR,//14 + D3D11_BLEND_ONE//15 +}; + +static const D3D11_BLEND d3dLogicOpDestFactors[16] = +{ + D3D11_BLEND_ZERO,//0 + D3D11_BLEND_ZERO,//1 + D3D11_BLEND_INV_SRC_COLOR,//2 + D3D11_BLEND_ZERO,//3 + D3D11_BLEND_ONE,//4 + D3D11_BLEND_ONE,//5 + D3D11_BLEND_INV_SRC_COLOR,//6 + D3D11_BLEND_ONE,//7 + + D3D11_BLEND_INV_DEST_COLOR,//8 + D3D11_BLEND_SRC_COLOR,//9 + D3D11_BLEND_INV_DEST_COLOR,//10 + D3D11_BLEND_INV_DEST_COLOR,//11 + D3D11_BLEND_INV_SRC_COLOR,//12 + D3D11_BLEND_ONE,//13 + D3D11_BLEND_INV_SRC_COLOR,//14 + D3D11_BLEND_ONE//15 +}; + +static const D3D11_CULL_MODE d3dCullModes[4] = +{ + D3D11_CULL_NONE, + D3D11_CULL_BACK, + D3D11_CULL_FRONT, + D3D11_CULL_BACK +}; + +static const D3D11_COMPARISON_FUNC d3dCmpFuncs[8] = +{ + D3D11_COMPARISON_NEVER, + D3D11_COMPARISON_LESS, + D3D11_COMPARISON_EQUAL, + D3D11_COMPARISON_LESS_EQUAL, + D3D11_COMPARISON_GREATER, + D3D11_COMPARISON_NOT_EQUAL, + D3D11_COMPARISON_GREATER_EQUAL, + D3D11_COMPARISON_ALWAYS +}; + +#define TEXF_NONE 0 +#define TEXF_POINT 1 +#define TEXF_LINEAR 2 +static const unsigned int d3dMipFilters[4] = +{ + TEXF_NONE, + TEXF_POINT, + TEXF_LINEAR, + TEXF_NONE, //reserved +}; + +static const D3D11_TEXTURE_ADDRESS_MODE d3dClamps[4] = +{ + D3D11_TEXTURE_ADDRESS_CLAMP, + D3D11_TEXTURE_ADDRESS_WRAP, + D3D11_TEXTURE_ADDRESS_MIRROR, + D3D11_TEXTURE_ADDRESS_WRAP //reserved +}; + +void SetupDeviceObjects() +{ + D3D::font.Init(); + VertexLoaderManager::Init(); + FBManager.Create(); + + VertexShaderManager::Dirty(); + PixelShaderManager::Dirty(); + + float colmat[20]= {0.0f}; + colmat[0] = colmat[5] = colmat[10] = 1.0f; + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + D3D::device->CreateBuffer(&cbdesc, &data, &access_efb_cbuf); + + D3D11_DEPTH_STENCIL_DESC ddesc; + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + ddesc.DepthFunc = D3D11_COMPARISON_ALWAYS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + D3D::device->CreateDepthStencilState(&ddesc, &cleardepthstates[0]); + ddesc.DepthEnable = TRUE; + D3D::device->CreateDepthStencilState(&ddesc, &cleardepthstates[1]); + D3D::SetDebugObjectName((ID3D11DeviceChild*)cleardepthstates[0], "depth state for Renderer::ClearScreen (depth buffer disabled)"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)cleardepthstates[1], "depth state for Renderer::ClearScreen (depth buffer enabled)"); + + // TODO: once multisampling gets implemented, this might need to be changed + D3D11_RASTERIZER_DESC rdesc= CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); + D3D::device->CreateRasterizerState(&rdesc, &clearraststate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)clearraststate, "rasterizer state for Renderer::ClearScreen"); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + D3D::device->CreateBlendState(&blenddesc, &resetblendstate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetblendstate, "blend state for Renderer::ResetAPIState"); + + // TODO: For some reason this overwrites existing resource private data... + ddesc.DepthEnable = FALSE; + ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + ddesc.DepthFunc = D3D11_COMPARISON_LESS; + ddesc.StencilEnable = FALSE; + ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + D3D::device->CreateDepthStencilState(&ddesc, &resetdepthstate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetdepthstate, "depth stencil state for Renderer::ResetAPIState"); + + // this might need to be changed once multisampling support gets added + D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); + D3D::device->CreateRasterizerState(&rastdesc, &resetraststate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)resetraststate, "rasterizer state for Renderer::ResetAPIState"); +} + +void TeardownDeviceObjects() +{ + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + FBManager.Destroy(); + D3D::font.Shutdown(); + TextureCache::Invalidate(false); + VertexManager::DestroyDeviceObjects(); + VertexLoaderManager::Shutdown(); + VertexShaderCache::Clear(); + PixelShaderCache::Clear(); + + SAFE_RELEASE(access_efb_cbuf); + SAFE_RELEASE(cleardepthstates[0]); + SAFE_RELEASE(cleardepthstates[1]); + SAFE_RELEASE(clearraststate); + SAFE_RELEASE(resetblendstate); + SAFE_RELEASE(resetdepthstate); + SAFE_RELEASE(resetraststate); +} + +bool Renderer::Init() +{ + UpdateActiveConfig(); + int x, y, w_temp, h_temp; + s_blendMode = 0; + + g_VideoInitialize.pRequestWindowSize(x, y, w_temp, h_temp); + + D3D::Create(EmuWindow::GetWnd()); + + s_backbuffer_width = D3D::GetBackBufferWidth(); + s_backbuffer_height = D3D::GetBackBufferHeight(); + + s_XFB_width = MAX_XFB_WIDTH; + s_XFB_height = MAX_XFB_HEIGHT; + + TargetRectangle dst_rect; + ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect); + + xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width; + yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height; + + s_target_width = (int)(EFB_WIDTH * xScale); + s_target_height = (int)(EFB_HEIGHT * yScale); + + s_Fulltarget_width = s_target_width; + s_Fulltarget_height = s_target_height; + + SetupDeviceObjects(); + + for (unsigned int stage = 0; stage < 8; stage++) + D3D::gfxstate->samplerdesc[stage].MaxAnisotropy = g_ActiveConfig.iMaxAnisotropy; + + float ClearColor[4] = { 0.f, 0.f, 0.f, 0.f }; + D3D::context->ClearRenderTargetView(FBManager.GetEFBColorTexture()->GetRTV(), ClearColor); + D3D::context->ClearDepthStencilView(FBManager.GetEFBDepthTexture()->GetDSV(), D3D11_CLEAR_DEPTH, 1.f, 0); + + D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)(s_Fulltarget_width - s_target_width) / 2.f, + (float)(s_Fulltarget_height - s_target_height) / 2.f, + (float)s_target_width, (float)s_target_height); + D3D::context->RSSetViewports(1, &vp); + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + D3D::BeginFrame(); + D3D::gfxstate->rastdesc.ScissorEnable = TRUE; + return true; +} + +void Renderer::Shutdown() +{ + TeardownDeviceObjects(); + D3D::EndFrame(); + D3D::Present(); + D3D::Close(); +} + +int Renderer::GetTargetWidth() { return s_target_width; } +int Renderer::GetTargetHeight() { return s_target_height; } +int Renderer::GetFullTargetWidth() { return s_Fulltarget_width; } +int Renderer::GetFullTargetHeight() { return s_Fulltarget_height; } +float Renderer::GetTargetScaleX() { return xScale; } +float Renderer::GetTargetScaleY() { return yScale; } + +int Renderer::GetFrameBufferWidth() +{ + return s_backbuffer_width; +} +int Renderer::GetFrameBufferHeight() +{ + return s_backbuffer_height; +} + +// create On-Screen-Messages +void Renderer::DrawDebugText() +{ + // OSD menu messages + if (g_ActiveConfig.bOSDHotKey) + { + if (OSDChoice > 0) + { + OSDTime = Common::Timer::GetTimeMs() + 3000; + OSDChoice = -OSDChoice; + } + if ((u32)OSDTime > Common::Timer::GetTimeMs()) + { + std::string T1 = "", T2 = ""; + std::vector T0; + + std::string OSDM1 = StringFromFormat("%i x %i", OSDInternalW, OSDInternalH); + std::string OSDM21; + switch(g_ActiveConfig.iAspectRatio) + { + case ASPECT_AUTO: + OSDM21 = "Auto"; + break; + case ASPECT_FORCE_16_9: + OSDM21 = "16:9"; + break; + case ASPECT_FORCE_4_3: + OSDM21 = "4:3"; + break; + case ASPECT_STRETCH: + OSDM21 = "Stretch"; + break; + } + std::string OSDM22 = + g_ActiveConfig.bCrop ? " (crop)" : ""; + std::string OSDM3 = "Disabled"; + + // if there is more text than this we will have a collission + if (g_ActiveConfig.bShowFPS) + { T1 += "\n\n"; T2 += "\n\n"; } + + T0.push_back(StringFromFormat("3: Internal Resolution: %s\n", OSDM1.c_str())); + T0.push_back(StringFromFormat("4: Aspect Ratio: %s%s\n", OSDM21.c_str(), OSDM22.c_str())); + T0.push_back(StringFromFormat("5: Copy EFB: %s\n", OSDM3.c_str())); + T0.push_back(StringFromFormat("6: Fog: %s\n", g_ActiveConfig.bDisableFog ? "Disabled" : "Enabled")); + T0.push_back(StringFromFormat("7: Material Lighting: %s\n", g_ActiveConfig.bDisableLighting ? "Disabled" : "Enabled")); + + // latest changed setting in yellow + T1 += (OSDChoice == -1) ? T0.at(0) : "\n"; + T1 += (OSDChoice == -2) ? T0.at(1) : "\n"; + T1 += (OSDChoice == -3) ? T0.at(2) : "\n"; + T1 += (OSDChoice == -4) ? T0.at(3) : "\n"; + T1 += (OSDChoice == -5) ? T0.at(4) : "\n"; + + // other settings in cyan + T2 += (OSDChoice != -1) ? T0.at(0) : "\n"; + T2 += (OSDChoice != -2) ? T0.at(1) : "\n"; + T2 += (OSDChoice != -3) ? T0.at(2) : "\n"; + T2 += (OSDChoice != -4) ? T0.at(3) : "\n"; + T2 += (OSDChoice != -5) ? T0.at(4) : "\n"; + + // render a shadow, and then the text + Renderer::RenderText(T1.c_str(), 21, 21, 0xDD000000); + Renderer::RenderText(T1.c_str(), 20, 20, 0xFFffff00); + Renderer::RenderText(T2.c_str(), 21, 21, 0xDD000000); + Renderer::RenderText(T2.c_str(), 20, 20, 0xFF00FFFF); + } + } +} + +void Renderer::RenderText(const char* text, int left, int top, u32 color) +{ + D3D::font.DrawTextScaled((float)left, (float)top, 15, 0.0f, color, text, false); +} + +TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) +{ + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + TargetRectangle result; + result.left = (int)(rc.left * xScale) + Xstride; + result.top = (int)(rc.top * yScale) + Ystride; + result.right = (int)(rc.right * xScale) + Xstride; + result.bottom = (int)(rc.bottom * yScale) + Ystride; + return result; +} + +void CheckForResize() +{ + while (EmuWindow::IsSizing()) + Sleep(10); + + if (EmuWindow::GetParentWnd()) + { + // re-stretch window to parent window size again, if it has a parent window. + RECT rcParentWindow; + GetWindowRect(EmuWindow::GetParentWnd(), &rcParentWindow); + int width = rcParentWindow.right - rcParentWindow.left; + int height = rcParentWindow.bottom - rcParentWindow.top; + if (width != s_backbuffer_width || height != s_backbuffer_height) + ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); + } + RECT rcWindow; + GetClientRect(EmuWindow::GetWnd(), &rcWindow); + int client_width = rcWindow.right - rcWindow.left; + int client_height = rcWindow.bottom - rcWindow.top; + + // sanity check + if ((client_width != s_backbuffer_width || + client_height != s_backbuffer_height) && + client_width >= 4 && client_height >= 4) + { + TeardownDeviceObjects(); + + D3D::Reset(); + s_backbuffer_width = D3D::GetBackBufferWidth(); + s_backbuffer_height = D3D::GetBackBufferHeight(); + WindowResized = true; + } +} + +void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc) +{ + if (!fbWidth || !fbHeight) + return; + VideoFifo_CheckEFBAccess(); + VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight); + FBManager.CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc); + XFBWrited = true; + + // XXX: Without the VI, how would we know what kind of field this is? So + // just use progressive. + Renderer::Swap(xfbAddr, FIELD_PROGRESSIVE, fbWidth, fbHeight); + Common::AtomicStoreRelease(s_swapRequested, FALSE); +} + +bool Renderer::SetScissorRect() +{ + int xoff = bpmem.scissorOffset.x * 2 - 342; + int yoff = bpmem.scissorOffset.y * 2 - 342; + D3D11_RECT rc = CD3D11_RECT(bpmem.scissorTL.x - xoff - 342, + bpmem.scissorTL.y - yoff - 342, + bpmem.scissorBR.x - xoff - 341, + bpmem.scissorBR.y - yoff - 341); + + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + + rc.left = (int)(rc.left * xScale); + rc.top = (int)(rc.top * yScale); + rc.right = (int)(rc.right * xScale); + rc.bottom = (int)(rc.bottom * yScale); + + if (rc.left < 0) rc.left = 0; + if (rc.right < 0) rc.right = 0; + if (rc.left > s_target_width) rc.left = s_target_width; + if (rc.right > s_target_width) rc.right = s_target_width; + if (rc.top < 0) rc.top = 0; + if (rc.bottom < 0) rc.bottom = 0; + if (rc.top > s_target_height) rc.top = s_target_height; + if (rc.bottom > s_target_height) rc.bottom = s_target_height; + + rc.left += Xstride; + rc.top += Ystride; + rc.right += Xstride; + rc.bottom += Ystride; + + if (rc.left > rc.right) + { + int temp = rc.right; + rc.right = rc.left; + rc.left = temp; + } + if (rc.top > rc.bottom) + { + int temp = rc.bottom; + rc.bottom = rc.top; + rc.top = temp; + } + + if (rc.right >= rc.left && rc.bottom >= rc.top) + { + D3D::context->RSSetScissorRects(1, &rc); + return true; + } + else + { + //WARN_LOG(VIDEO, "Bad scissor rectangle: %i %i %i %i", rc.left, rc.top, rc.right, rc.bottom); + rc = CD3D11_RECT(Xstride, Ystride, Xstride + s_target_width, Ystride + s_target_height); + D3D::context->RSSetScissorRects(1, &rc); + return false; + } + return false; +} + +void Renderer::SetColorMask() +{ + UINT8 color_mask = 0; + if (bpmem.blendmode.alphaupdate) color_mask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + if (bpmem.blendmode.colorupdate) color_mask |= D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE; + D3D::gfxstate->SetRenderTargetWriteMask(color_mask); +} + +u32 Renderer::AccessEFB(EFBAccessType type, int x, int y) +{ + ID3D11Texture2D* tex; + + if (!g_ActiveConfig.bEFBAccessEnable) + return 0; + + // get the rectangular target region covered by the EFB pixel + EFBRectangle efbPixelRc; + efbPixelRc.left = x; + efbPixelRc.top = y; + efbPixelRc.right = x + 1; + efbPixelRc.bottom = y + 1; + + TargetRectangle targetPixelRc = Renderer::ConvertEFBRectangle(efbPixelRc); + + u32 z = 0; + float val = 0.0f; + D3D11_RECT RectToLock = CD3D11_RECT(targetPixelRc.left, targetPixelRc.top, targetPixelRc.right, targetPixelRc.bottom); + if (type == PEEK_Z) + { + RectToLock.bottom+=2; + RectToLock.right+=1; + RectToLock.top-=1; + RectToLock.left-=2; + if ((RectToLock.bottom - RectToLock.top) > 4) + RectToLock.bottom--; + if ((RectToLock.right - RectToLock.left) > 4) + RectToLock.left++; + ResetAPIState(); // reset any game specific settings + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBDepthReadTexture()->GetRTV(), NULL); + + // Stretch picture with increased internal resolution + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, 4.f, 4.f); + D3D::context->RSSetViewports(1, &vp); + + D3D::context->PSSetConstantBuffers(0, 1, &access_efb_cbuf); + + // TODO! +// D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + + D3D::drawShadedTexQuad(FBManager.GetEFBDepthTexture()->GetSRV(), + &RectToLock, + Renderer::GetFullTargetWidth(), + Renderer::GetFullTargetHeight(), + PixelShaderCache::GetDepthMatrixProgram(), + VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout()); + +// D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); + + // TODO: ?? check this code.. + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + RestoreAPIState(); + RectToLock = CD3D11_RECT(0, 0, 4, 4); + + D3D11_BOX box = CD3D11_BOX(0, 0, 0, 4, 4, 1); + tex = FBManager.GetEFBDepthStagingBuffer(); + D3D::context->CopySubresourceRegion(tex, 0, 0, 0, 0, FBManager.GetEFBDepthReadTexture()->GetTex(), 0, &box); + } + else + { + tex = FBManager.GetEFBColorStagingBuffer(); + D3D11_BOX box = CD3D11_BOX(RectToLock.left, RectToLock.top, 0, RectToLock.right, RectToLock.bottom, 1); + D3D::context->CopySubresourceRegion(tex, 0, 0, 0, 0, FBManager.GetEFBColorTexture()->GetTex(), 0, &box); + //change the rect to lock the entire one pixel buffer + RectToLock = CD3D11_RECT(0, 0, 1, 1); + } + + D3D11_MAPPED_SUBRESOURCE map; + D3D::context->Map(tex, 0, D3D11_MAP_READ, 0, &map); + + switch(type) { + case PEEK_Z: + val = ((float*)map.pData)[6]; + z = ((u32)(val * 0xffffff)); + break; + + case POKE_Z: + PanicAlert("Poke Z-buffer not implemented"); + break; + + case PEEK_COLOR: + z = ((u32*)map.pData)[0]; + break; + + case POKE_COLOR: + PanicAlert("Poke color EFB not implemented"); + break; + } + D3D::context->Unmap(tex, 0); + return z; +} + +// Called from VertexShaderManager +void UpdateViewport() +{ + // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz) + // [0] = width/2 + // [1] = height/2 + // [2] = 16777215 * (farz - nearz) + // [3] = xorig + width/2 + 342 + // [4] = yorig + height/2 + 342 + // [5] = 16777215 * farz + int scissorXOff = bpmem.scissorOffset.x * 2; + int scissorYOff = bpmem.scissorOffset.y * 2; + + float MValueX = Renderer::GetTargetScaleX(); + float MValueY = Renderer::GetTargetScaleY(); + + int Xstride = (s_Fulltarget_width - s_target_width) / 2; + int Ystride = (s_Fulltarget_height - s_target_height) / 2; + + // Stretch picture with increased internal resolution + int X = (int)(ceil(xfregs.rawViewport[3] - xfregs.rawViewport[0] - (scissorXOff)) * MValueX) + Xstride; + int Y = (int)(ceil(xfregs.rawViewport[4] + xfregs.rawViewport[1] - (scissorYOff)) * MValueY) + Ystride; + int Width = (int)ceil((int)(2 * xfregs.rawViewport[0]) * MValueX); + int Height = (int)ceil((int)(-2 * xfregs.rawViewport[1]) * MValueY); + if (Width < 0) + { + X += Width; + Width*=-1; + } + if (Height < 0) + { + Y += Height; + Height *= -1; + } + bool sizeChanged = false; + if (X < 0) + { + s_Fulltarget_width -= 2 * X; + X = 0; + sizeChanged=true; + } + if (Y < 0) + { + s_Fulltarget_height -= 2 * Y; + Y = 0; + sizeChanged=true; + } + if (sizeChanged) + { + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + FBManager.Destroy(); + FBManager.Create(); + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + } + + // some games set invalids values MinDepth and MaxDepth so fix them to the max an min allowed and let the shaders do this work + D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)X, (float)Y, (float)Width, (float)Height, + 0.f, // (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f; + 1.f); // xfregs.rawViewport[5] / 16777216.0f; + D3D::context->RSSetViewports(1, &vp); +} + +void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) +{ + // update the view port for clearing the picture + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)Renderer::GetFullTargetWidth(), (float)Renderer::GetFullTargetHeight()); + D3D::context->RSSetViewports(1, &vp); + + TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc); + + // always set the scissor in case it was set by the game and has not been reset + // TODO: Do we really need to set the scissor rect? Why not just disable scissor testing? + D3D11_RECT sirc = CD3D11_RECT(targetRc.left, targetRc.top, targetRc.right, targetRc.bottom); + D3D::context->RSSetScissorRects(1, &sirc); + D3D::context->OMSetDepthStencilState(cleardepthstates[zEnable], 0); + D3D::context->RSSetState(clearraststate); + D3D::drawClearQuad(color, (z & 0xFFFFFF) / float(0xFFFFFF),PixelShaderCache::GetClearProgram(),VertexShaderCache::GetClearVertexShader(), VertexShaderCache::GetClearInputLayout()); + UpdateViewport(); + SetScissorRect(); +} + +void Renderer::SetBlendMode(bool forceUpdate) +{ + #define BLEND_ENABLE_MASK 1 + #define BLENDOP_SHIFT 2 + #define BLENDOP_MASK (1<SetAlphaBlendEnable(newval & BLEND_ENABLE_MASK); + + if (changes & BLENDOP_MASK) // subtract enable change + D3D::gfxstate->SetBlendOp((newval & BLENDOP_MASK) ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD); + + if (changes & 0x1F8) // blend RGB change + { + D3D::gfxstate->SetSrcBlend(d3dSrcFactors[(newval & SRCFACTOR_MASK) >> SRCFACTOR_SHIFT]); + D3D::gfxstate->SetDestBlend(d3dDestFactors[(newval & DESTFACTOR_MASK) >> DESTFACTOR_SHIFT]); + } + s_blendMode = newval; +} + +void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight) +{ + if (g_bSkipCurrentFrame || !XFBWrited || !fbWidth || !fbHeight) + { + g_VideoInitialize.pCopiedToXFB(false); + return; + } + // this function is called after the XFB field is changed, not after + // EFB is copied to XFB. In this way, flickering is reduced in games + // and seems to also give more FPS in ZTP + + if (field == FIELD_LOWER) xfbAddr -= fbWidth * 2; + u32 xfbCount = 0; + const XFBSource** xfbSourceList = FBManager.GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); + if (!xfbSourceList || xfbCount == 0) + { + g_VideoInitialize.pCopiedToXFB(false); + return; + } + + Renderer::ResetAPIState(); + // set the backbuffer as the rendering target + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + + TargetRectangle dst_rect; + ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect); + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)s_backbuffer_width, (float)s_backbuffer_height); + D3D::context->RSSetViewports(1, &vp); + float ClearColor[4] = { 0.f, 0.f, 0.f, 1.f }; + D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), ClearColor); + + int X = dst_rect.left; + int Y = dst_rect.top; + int Width = dst_rect.right - dst_rect.left; + int Height = dst_rect.bottom - dst_rect.top; + + if (X < 0) X = 0; + if (Y < 0) Y = 0; + if (X > s_backbuffer_width) X = s_backbuffer_width; + if (Y > s_backbuffer_height) Y = s_backbuffer_height; + if (Width < 0) Width = 0; + if (Height < 0) Height = 0; + if (Width > (s_backbuffer_width - X)) Width = s_backbuffer_width - X; + if (Height > (s_backbuffer_height - Y)) Height = s_backbuffer_height - Y; + vp = CD3D11_VIEWPORT((float)X, (float)Y, (float)Width, (float)Height); + D3D::context->RSSetViewports(1, &vp); + + // TODO: Enable linear filtering here + + const XFBSource* xfbSource; + + // draw each xfb source + for (u32 i = 0; i < xfbCount; ++i) + { + xfbSource = xfbSourceList[i]; + + MathUtil::Rectangle sourceRc; + sourceRc.left = 0; + sourceRc.top = 0; + sourceRc.right = xfbSource->texWidth; + sourceRc.bottom = xfbSource->texHeight; + + MathUtil::Rectangle drawRc; + drawRc.top = -1; + drawRc.bottom = 1; + drawRc.left = -1; + drawRc.right = 1; + + D3D::drawShadedTexSubQuad(xfbSource->tex->GetSRV(), &sourceRc, xfbSource->texWidth, xfbSource->texHeight, &drawRc, PixelShaderCache::GetColorCopyProgram(),VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); + } + + vp = CD3D11_VIEWPORT(0.f, 0.f, s_backbuffer_width, s_backbuffer_height); + D3D::context->RSSetViewports(1, &vp); + + // print some stats + if (g_ActiveConfig.bShowFPS) + { + char fps[20]; + StringCchPrintfA(fps, 20, "FPS: %d\n", s_fps); + D3D::font.DrawTextScaled(0,30,30,0.0f,0xFF00FFFF,fps,false); + } + Renderer::DrawDebugText(); + + if (g_ActiveConfig.bOverlayStats) + { + char buf[32768]; + Statistics::ToString(buf); + D3D::font.DrawTextScaled(0,30,30,0.0f,0xFF00FFFF,buf,false); + } + else if (g_ActiveConfig.bOverlayProjStats) + { + char buf[32768]; + Statistics::ToStringProj(buf); + D3D::font.DrawTextScaled(0,30,30,0.0f,0xFF00FFFF,buf,false); + } + + OSD::DrawMessages(); + + D3D::EndFrame(); + + frameCount++; + TextureCache::Cleanup(); + + // make any new configuration settings active. + UpdateActiveConfig(); + WindowResized = false; + CheckForResize(); + + bool xfbchanged = false; + + if (s_XFB_width != fbWidth || s_XFB_height != fbHeight) + { + xfbchanged = true; + s_XFB_width = fbWidth; + s_XFB_height = fbHeight; + if (s_XFB_width < 1) s_XFB_width = MAX_XFB_WIDTH; + if (s_XFB_width > MAX_XFB_WIDTH) s_XFB_width = MAX_XFB_WIDTH; + if (s_XFB_height < 1) s_XFB_height = MAX_XFB_HEIGHT; + if (s_XFB_height > MAX_XFB_HEIGHT) s_XFB_height = MAX_XFB_HEIGHT; + } + + if (xfbchanged || WindowResized) + { + ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect); + + xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width; + yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height; + + s_target_width = EFB_WIDTH * xScale; + s_target_height = EFB_HEIGHT * yScale; + + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL); + if (WindowResized) + { + SetupDeviceObjects(); + } + else + { + FBManager.Destroy(); + FBManager.Create(); + } + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + } + + // update FPS counter + static int fpscount = 1; + static unsigned long lasttime; + if (XFBWrited) ++fpscount; + if (Common::Timer::GetTimeMs() - lasttime > 1000) + { + lasttime = Common::Timer::GetTimeMs(); + s_fps = fpscount - 1; + fpscount = 1; + } + + // set default viewport and scissor, for the clear to work correctly + stats.ResetFrame(); + + // present backbuffer and begin next frame + D3D::Present(); + D3D::BeginFrame(); + Renderer::RestoreAPIState(); + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + UpdateViewport(); + VertexShaderManager::SetViewportChanged(); + g_VideoInitialize.pCopiedToXFB(XFBWrited); + XFBWrited = false; +} + +void Renderer::ResetAPIState() +{ + D3D::context->OMSetBlendState(resetblendstate, NULL, 0xFFFFFFFF); + D3D::context->OMSetDepthStencilState(resetdepthstate, 0); + D3D::context->RSSetState(resetraststate); +} + +void Renderer::RestoreAPIState() +{ + // TODO: How much of this is actually needed? + // gets us back into a more game-like state. + D3D::gfxstate->rastdesc.ScissorEnable = TRUE; + UpdateViewport(); + SetScissorRect(); + if (bpmem.zmode.testenable) D3D::gfxstate->depthdesc.DepthEnable = TRUE; + if (bpmem.zmode.updateenable) D3D::gfxstate->depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + SetColorMask(); + SetLogicOpMode(); +} + +void Renderer::SetGenerationMode() +{ + // rastdesc.FrontCounterClockwise must be false for this to work + D3D::gfxstate->rastdesc.CullMode = d3dCullModes[bpmem.genMode.cullmode]; +} + +void Renderer::SetDepthMode() +{ + if (bpmem.zmode.testenable) + { + D3D::gfxstate->depthdesc.DepthEnable = TRUE; + D3D::gfxstate->depthdesc.DepthWriteMask = bpmem.zmode.updateenable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + D3D::gfxstate->depthdesc.DepthFunc = d3dCmpFuncs[bpmem.zmode.func]; + } + else + { + D3D::gfxstate->depthdesc.DepthEnable = TRUE; + D3D::gfxstate->depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + } +} + +void Renderer::SetLogicOpMode() +{ + if (bpmem.blendmode.logicopenable && bpmem.blendmode.logicmode != 3) + { + s_blendMode = 0; + D3D::gfxstate->SetAlphaBlendEnable(true); + D3D::gfxstate->SetBlendOp(d3dLogicOps[bpmem.blendmode.logicmode]); + D3D::gfxstate->SetSrcBlend(d3dLogicOpSrcFactors[bpmem.blendmode.logicmode]); + D3D::gfxstate->SetDestBlend(d3dLogicOpDestFactors[bpmem.blendmode.logicmode]); + } + else + { + SetBlendMode(true); + } +} + +void Renderer::SetDitherMode() +{ + // TODO: Set dither mode to bpmem.blendmode.dither +} + +void Renderer::SetLineWidth() +{ + // TODO +} + +void Renderer::SetSamplerState(int stage, int texindex) +{ + const FourTexUnits &tex = bpmem.tex[texindex]; + const TexMode0 &tm0 = tex.texMode0[stage]; + const TexMode1 &tm1 = tex.texMode1[stage]; + + unsigned int mip; + mip = (tm0.min_filter == 8) ? TEXF_NONE:d3dMipFilters[tm0.min_filter & 3]; + if ((tm0.min_filter & 3) && (tm0.min_filter != 8) && ((tm1.max_lod >> 4) == 0)) mip = TEXF_NONE; + + if (texindex) stage += 4; + + // TODO: Clarify whether these values are correct + // NOTE: since there's no "no filter" in DX11 we're using point filters in these cases + if (tm0.min_filter & 4) // linear min filter + { + if (tm0.mag_filter) // linear mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_LINEAR); + } + else // point mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR); + } + } + else // point min filter + { + if (tm0.mag_filter) // linear mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR); + } + else // point mag filter + { + if (mip == TEXF_NONE) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_POINT); + else if (mip == TEXF_POINT) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_MIP_POINT); + else if (mip == TEXF_LINEAR) D3D::gfxstate->SetSamplerFilter(stage, D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR); + } + } + + D3D::gfxstate->samplerdesc[stage].AddressU = d3dClamps[tm0.wrap_s]; + D3D::gfxstate->samplerdesc[stage].AddressV = d3dClamps[tm0.wrap_t]; + + D3D::gfxstate->samplerdesc[stage].MipLODBias = (float)tm0.lod_bias/32.0f; + D3D::gfxstate->samplerdesc[stage].MaxLOD = (float)tm1.max_lod/16.f; + D3D::gfxstate->samplerdesc[stage].MinLOD = (float)tm1.min_lod/16.f; +} + +void Renderer::SetInterlacingMode() +{ + // TODO +} + +// Save screenshot +void Renderer::SetScreenshot(const char* filename) +{ + PanicAlert("Renderer::SetScreenshot not implemented\n"); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp new file mode 100644 index 0000000000..5ee8daf978 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp @@ -0,0 +1,537 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Globals.h" +#include "Statistics.h" +#include "MemoryUtil.h" +#include "Hash.h" + +#include "CommonPaths.h" +#include "FileUtil.h" + +#include "D3DBase.h" +#include "D3DTexture.h" +#include "D3DUtil.h" +#include "FBManager.h" +#include "PixelShaderCache.h" +#include "PixelShaderManager.h" +#include "VertexShaderManager.h" +#include "VertexShaderCache.h" + +#include "Render.h" + +#include "TextureDecoder.h" +#include "TextureCache.h" +#include "HiresTextures.h" + +ID3D11BlendState* efbcopyblendstate = NULL; +ID3D11RasterizerState* efbcopyraststate = NULL; +ID3D11DepthStencilState* efbcopydepthstate = NULL; +ID3D11Buffer* efbcopycbuf[20] = { NULL }; + +u8* TextureCache::temp = NULL; +TextureCache::TexCache TextureCache::textures; + +extern int frameCount; + +#define TEMP_SIZE (1024*1024*4) +#define TEXTURE_KILL_THRESHOLD 200 + +void TextureCache::TCacheEntry::Destroy(bool shutdown) +{ + SAFE_RELEASE(texture); + if (!isRenderTarget && !shutdown) + { + u32* ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); + if (ptr && *ptr == hash) + *ptr = oldpixel; + } +} + +void TextureCache::Init() +{ + temp = (u8*)AllocateMemoryPages(TEMP_SIZE); + TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); + HiresTextures::Init(globals->unique_id); + + D3D11_BLEND_DESC blenddesc; + blenddesc.AlphaToCoverageEnable = FALSE; + blenddesc.IndependentBlendEnable = FALSE; + blenddesc.RenderTarget[0].BlendEnable = FALSE; + blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + D3D::device->CreateBlendState(&blenddesc, &efbcopyblendstate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in TextureCache::CopyRenderTargetToTexture"); + + D3D11_DEPTH_STENCIL_DESC depthdesc; + depthdesc.DepthEnable = FALSE; + depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + depthdesc.DepthFunc = D3D11_COMPARISON_LESS; + depthdesc.StencilEnable = FALSE; + depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + D3D::device->CreateDepthStencilState(&depthdesc, &efbcopydepthstate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in TextureCache::CopyRenderTargetToTexture"); + + D3D11_RASTERIZER_DESC rastdesc; + rastdesc.CullMode = D3D11_CULL_NONE; + rastdesc.FillMode = D3D11_FILL_SOLID; + rastdesc.FrontCounterClockwise = false; + rastdesc.DepthBias = false; + rastdesc.DepthBiasClamp = 0; + rastdesc.SlopeScaledDepthBias = 0; + rastdesc.DepthClipEnable = false; + rastdesc.ScissorEnable = false; + rastdesc.MultisampleEnable = false; + rastdesc.AntialiasedLineEnable = false; + D3D::device->CreateRasterizerState(&rastdesc, &efbcopyraststate); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in TextureCache::CopyRenderTargetToTexture"); +} + +void TextureCache::Invalidate(bool shutdown) +{ + for (TexCache::iterator iter = textures.begin(); iter != textures.end(); iter++) + iter->second.Destroy(shutdown); + textures.clear(); + HiresTextures::Shutdown(); +} + +void TextureCache::InvalidateRange(u32 start_address, u32 size) +{ + TexCache::iterator iter = textures.begin(); + while (iter != textures.end()) + { + if (iter->second.IntersectsMemoryRange(start_address, size)) + { + iter->second.Destroy(false); + textures.erase(iter++); + } + else + { + ++iter; + } + } +} + +bool TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size) +{ + if (addr + size_in_bytes < range_address) return false; + if (addr >= range_address + range_size) return false; + return true; +} + +void TextureCache::Shutdown() +{ + Invalidate(true); + FreeMemoryPages(temp, TEMP_SIZE); + temp = NULL; + + SAFE_RELEASE(efbcopyblendstate); + SAFE_RELEASE(efbcopyraststate); + SAFE_RELEASE(efbcopydepthstate); + + for (unsigned int k = 0; k < 20;k++) + SAFE_RELEASE(efbcopycbuf[k]); +} + +void TextureCache::Cleanup() +{ + TexCache::iterator iter = textures.begin(); + while (iter != textures.end()) + { + if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount) + { + iter->second.Destroy(false); + iter = textures.erase(iter); + } + else + { + iter++; + } + } +} + +// TODO: Verify that this actually returns the value needed below +unsigned int GetPow2(unsigned int val) +{ + unsigned int ret = 0; + for (;val;val>>=1) ret++; + return ret; +} + +TextureCache::TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel) +{ + if (address == 0) return NULL; + + u8* ptr = g_VideoInitialize.pGetMemoryPointer(address); + unsigned int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; + unsigned int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; + unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format); + unsigned int expandedWidth = (width + bsw) & (~bsw); + unsigned int expandedHeight = (height + bsh) & (~bsh); + + u64 hash_value; + u32 texID = address; + u32 FullFormat = tex_format; + if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) + u32 FullFormat = (tex_format | (tlutfmt << 16)); + + bool skip_texture_create = false; + TexCache::iterator iter = textures.find(texID); + + if (iter != textures.end()) + { + TCacheEntry &entry = iter->second; + + hash_value = ((u32*)ptr)[0]; + + // TODO: Is the (entry.MipLevels == maxlevel) check needed? + if (entry.isRenderTarget || ((address == entry.addr) && (hash_value == entry.hash) && FullFormat == entry.fmt && entry.MipLevels == maxlevel)) + { + entry.frameCount = frameCount; + D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV()); + return &entry; + } + else + { + // Let's reload the new texture data into the same texture, + // instead of destroying it and having to create a new one. + // Might speed up movie playback very, very slightly. + + // TODO: Is the (entry.MipLevels < maxlevel) check needed? + if (width == entry.w && height==entry.h && FullFormat == entry.fmt && entry.MipLevels < maxlevel) + { + skip_texture_create = true; + } + else + { + entry.Destroy(false); + textures.erase(iter); + } + } + } + + // make an entry in the table + TCacheEntry& entry = textures[texID]; + PC_TexFormat pcfmt = PC_TEX_FMT_NONE; + + if (pcfmt == PC_TEX_FMT_NONE) + pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); + + DXGI_FORMAT d3d_fmt = DXGI_FORMAT_B8G8R8A8_UNORM; + bool swap_r_b = false; + + entry.oldpixel = ((u32*)ptr)[0]; + entry.hash = ((u32*)ptr)[0] = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); + + entry.addr = address; + entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format); + entry.isRenderTarget = false; + bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); + entry.isNonPow2 = false; + unsigned int TexLevels = (isPow2 && UseNativeMips && maxlevel) ? (GetPow2(max(width, height)) + 1) : ((isPow2)? 0 : 1); + if (TexLevels > (maxlevel + 1) && maxlevel) + TexLevels = maxlevel + 1; + entry.MipLevels = maxlevel; + D3D11_USAGE usage = (TexLevels == 1) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; + if (!skip_texture_create) + { + if (usage == D3D11_USAGE_DYNAMIC) entry.texture = D3DTexture2D::Create(width, height, D3D11_BIND_SHADER_RESOURCE, usage, d3d_fmt, TexLevels); + else // need to use default textures + { + ID3D11Texture2D* pTexture = NULL; + HRESULT hr; + + // possible optimization: Manually create the mipmaps, so that we don't need to bind the texture as render target + // if we automatically generate the mipmaps we need to bind the texture as rendertarget as well + D3D11_BIND_FLAG bindflags = D3D11_BIND_SHADER_RESOURCE; + if (TexLevels == 0) bindflags = (D3D11_BIND_FLAG)(bindflags | D3D11_BIND_RENDER_TARGET); + D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(d3d_fmt, width, height, 1, TexLevels, bindflags, usage, 0, 1, 0, (TexLevels==0)?D3D11_RESOURCE_MISC_GENERATE_MIPS:0); + hr = D3D::device->CreateTexture2D(&texdesc, NULL, &pTexture); + if (FAILED(hr)) + { + PanicAlert("Failed to create texture at %s %d\n", __FILE__, __LINE__); + return NULL; + } + // we only need the shader resource view + entry.texture = new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE); + pTexture->Release(); + } + if (entry.texture == NULL) PanicAlert("Failed to create texture at %s %d\n", __FILE__, __LINE__); + D3D::ReplaceTexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, d3d_fmt, pcfmt, 0, usage); + } + else + { + D3D::ReplaceTexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, d3d_fmt, pcfmt, 0, usage); + } + if (TexLevels == 0 && usage == D3D11_USAGE_DEFAULT) D3D::context->GenerateMips(entry.texture->GetSRV()); + else if (TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE) + { + unsigned int level = 1; + unsigned int mipWidth = (width + 1) >> 1; + unsigned int mipHeight = (height + 1) >> 1; + ptr += entry.size_in_bytes; + while ((mipHeight || mipWidth) && (level < TexLevels)) + { + unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; + unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; + expandedWidth = (currentWidth + bsw) & (~bsw); + expandedHeight = (currentHeight + bsh) & (~bsh); + PC_TexFormat texfmtbuf = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); + D3D::ReplaceTexture2D(entry.texture->GetTex(), (BYTE*)temp, currentWidth, currentHeight, expandedWidth, d3d_fmt, texfmtbuf, level, usage); + u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1; + ptr += size; + mipWidth >>= 1; + mipHeight >>= 1; + level++; + } + } + entry.frameCount = frameCount; + entry.w = width; + entry.h = height; + entry.fmt = FullFormat; + + INCSTAT(stats.numTexturesCreated); + SETSTAT(stats.numTexturesAlive, (int)textures.size()); + + D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV()); + + return &entry; +} + +// TODO: this doesn't work quite right, yet +void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect) +{ + int efb_w = source_rect.GetWidth(); + int efb_h = source_rect.GetHeight(); + + int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf); + int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf); + + int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled)?((int)(Renderer::GetTargetScaleX() * tex_w)):tex_w; + int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled)?((int)(Renderer::GetTargetScaleY() * tex_h)):tex_h; + + TexCache::iterator iter; + D3DTexture2D* tex = NULL; + iter = textures.find(address); + if (iter != textures.end()) + { + if (iter->second.isRenderTarget && iter->second.Scaledw == Scaledtex_w && iter->second.Scaledh == Scaledtex_h) + { + tex = iter->second.texture; + iter->second.frameCount = frameCount; + } + else + { + // remove it and recreate it as a render target + SAFE_RELEASE(iter->second.texture); + textures.erase(iter); + } + } + + if (!tex) + { + TCacheEntry entry; + entry.isRenderTarget = true; + entry.hash = 0; + entry.frameCount = frameCount; + entry.w = tex_w; + entry.h = tex_h; + entry.Scaledw = Scaledtex_w; + entry.Scaledh = Scaledtex_h; + entry.fmt = copyfmt; + entry.isNonPow2 = true; + entry.texture = D3DTexture2D::Create(Scaledtex_w, Scaledtex_h, (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET|(int)D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM); + if (entry.texture == NULL) PanicAlert("CopyRenderTargetToTexture failed to create entry.texture at %s %d\n", __FILE__, __LINE__); + textures[address] = entry; + tex = entry.texture; + } + + float colmat[20]= {0.0f}; // last four floats for fConstAdd + unsigned int cbufid = (unsigned int)-1; + + // TODO: Move this to TextureCache::Init() + if (bFromZBuffer) + { + switch(copyfmt) + { + case 0: // Z4 + case 1: // Z8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] =1.0f; + cbufid = 12; + break; + case 3: // Z16 //? + colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; + cbufid = 13; + case 11: // Z16 (reverse order) + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f; + cbufid = 14; + break; + case 6: // Z24X8 + colmat[0] = colmat[5] = colmat[10] = 1.0f; + cbufid = 15; + break; + case 9: // Z8M + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f; + cbufid = 16; + break; + case 10: // Z8L + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f; + cbufid = 17; + break; + case 12: // Z16L + colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; + cbufid = 18; + break; + default: + ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); + colmat[2] = colmat[5] = colmat[8] = 1.0f; + cbufid = 19; + break; + } + } + else if (bIsIntensityFmt) + { + colmat[16] = colmat[17] = colmat[18] = 16.0f/255.0f; + switch (copyfmt) + { + case 0: // I4 + case 1: // I8 + case 2: // IA4 + case 3: // IA8 + // TODO - verify these coefficients + colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; + colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; + colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; + + if (copyfmt < 2) + { + colmat[19] = 16.0f / 255.0f; + colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; + cbufid = 0; + } + else// alpha + { + colmat[15] = 1; + cbufid = 1; + } + + break; + default: + ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + break; + } + } + else + { + switch (copyfmt) + { + case 0: // R4 + case 8: // R8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; + cbufid = 2; + break; + case 2: // RA4 + case 3: // RA8 + colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; + cbufid = 3; + break; + + case 7: // A8 + colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; + cbufid = 4; + break; + case 9: // G8 + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; + cbufid = 5; + break; + case 10: // B8 + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; + cbufid = 6; + break; + case 11: // RG8 + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; + cbufid = 7; + break; + case 12: // GB8 + colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; + cbufid = 8; + break; + + case 4: // RGB565 + colmat[0] = colmat[5] = colmat[10] = 1; + colmat[19] = 1; // set alpha to 1 + cbufid = 9; + break; + case 5: // RGB5A3 + case 6: // RGBA8 + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 10; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 11; + break; + } + } + + Renderer::ResetAPIState(); // reset any game specific settings + + // stretch picture with increased internal resolution + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)Scaledtex_w, (float)Scaledtex_h); + D3D::context->RSSetViewports(1, &vp); + D3D11_RECT destrect = CD3D11_RECT(0, 0, Scaledtex_w, Scaledtex_h); + + // set transformation + if (efbcopycbuf[cbufid] == NULL) + { + D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]); + } + D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]); + + TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect); + D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); + + // TODO: Use linear filtering if (bScaleByHalf), else use point filtering + + D3D::context->OMSetBlendState(efbcopyblendstate, NULL, 0xffffffff); + D3D::context->RSSetState(efbcopyraststate); + D3D::context->OMSetDepthStencilState(efbcopydepthstate, 0); + D3D::context->OMSetRenderTargets(1, &tex->GetRTV(), NULL); + D3D::drawShadedTexQuad( + (bFromZBuffer) ? FBManager.GetEFBDepthTexture()->GetSRV() : FBManager.GetEFBColorTexture()->GetSRV(), + &sourcerect, + Renderer::GetFullTargetWidth(), + Renderer::GetFullTargetHeight(), + PixelShaderCache::GetColorMatrixProgram(), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); + + D3D::context->OMSetRenderTargets(1, &FBManager.GetEFBColorTexture()->GetRTV(), FBManager.GetEFBDepthTexture()->GetDSV()); + Renderer::RestoreAPIState(); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h new file mode 100644 index 0000000000..118b0cd7cc --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h @@ -0,0 +1,70 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include + +#include "D3DBase.h" +#include "D3DTexture.h" +#include "VideoCommon.h" +#include "BPMemory.h" + +class TextureCache +{ +public: + struct TCacheEntry + { + D3DTexture2D* texture; + + u32 addr; + u32 size_in_bytes; + u64 hash; + u32 paletteHash; + u32 oldpixel; + + int frameCount; + unsigned int w, h, fmt, MipLevels; + int Scaledw, Scaledh; + + float scaleX, scaleY; // for hires texutres + + bool isRenderTarget; + bool isNonPow2; + + TCacheEntry() : texture(NULL), addr(0), size_in_bytes(0), hash(0), paletteHash(0), oldpixel(0), + frameCount(0), w(0), h(0), fmt(0), MipLevels(0), Scaledw(0), Scaledh(0), + scaleX(1.f), scaleY(1.f), isRenderTarget(false), isNonPow2(true) {} + + void Destroy(bool shutdown); + bool IntersectsMemoryRange(u32 range_address, u32 range_size); + }; + + static void Init(); + static void Cleanup(); + static void Shutdown(); + static void Invalidate(bool shutdown); + static void InvalidateRange(u32 start_address, u32 size); + static TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel); + static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect); + +private: + typedef std::map TexCache; + + static u8* temp; + static TexCache textures; +}; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp new file mode 100644 index 0000000000..8f879dfe49 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp @@ -0,0 +1,383 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" + +#include "D3DBase.h" +#include "D3DShader.h" +#include "D3DUtil.h" + +#include "Statistics.h" +#include "Profiler.h" +#include "FBManager.h" +#include "VertexManager.h" +#include "OpcodeDecoding.h" +#include "IndexGenerator.h" +#include "VertexShaderManager.h" +#include "VertexShaderCache.h" +#include "PixelShaderManager.h" +#include "PixelShaderCache.h" +#include "NativeVertexFormat.h" +#include "NativeVertexWriter.h" +#include "TextureCache.h" + +#include "BPStructs.h" +#include "XFStructs.h" + +#include "Globals.h" + +#include +using std::string; + +using namespace D3D; + +extern NativeVertexFormat* g_nativeVertexFmt; + +namespace VertexManager +{ + +#define MAXVBUFFERSIZE 0x50000 +#define MAXIBUFFERSIZE 0xFFFF + +// TODO: find sensible values for these two +#define NUM_VERTEXBUFFERS 6 +#define NUM_INDEXBUFFERS 4 + +int lastPrimitive; + +u8* LocalVBuffer = NULL; +u16* TIBuffer = NULL; +u16* LIBuffer = NULL; +u16* PIBuffer = NULL; +bool Flushed=false; + +ID3D11Buffer* indexbuffers[NUM_INDEXBUFFERS] = {NULL}; +ID3D11Buffer* vertexbuffers[NUM_VERTEXBUFFERS] = {NULL}; +ID3D11Buffer* lineindexbuffer = NULL; +ID3D11Buffer* pointindexbuffer = NULL; + +inline ID3D11Buffer* GetSuitableIndexBuffer(const unsigned int minsize) +{ + for (unsigned int k = 0;k < NUM_INDEXBUFFERS-1;k++) + if (minsize > 2*(unsigned int)MAXIBUFFERSIZE>>(k+1)) + return indexbuffers[k]; + + return indexbuffers[NUM_INDEXBUFFERS-1]; +} + +inline ID3D11Buffer* GetSuitableVertexBuffer(const unsigned int minsize) +{ + for (unsigned int k = 0;k < NUM_VERTEXBUFFERS-1;++k) + if (minsize > (unsigned int)MAXVBUFFERSIZE>>(k+1)) + return vertexbuffers[k]; + + return vertexbuffers[NUM_VERTEXBUFFERS-1]; +} + +void CreateDeviceObjects() +{ + D3D11_BUFFER_DESC bufdesc; + HRESULT hr; + for (unsigned int k = 0;k < NUM_INDEXBUFFERS;++k) + { + bufdesc = CD3D11_BUFFER_DESC(2*MAXIBUFFERSIZE >> k, D3D11_BIND_INDEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + hr = D3D::device->CreateBuffer(&bufdesc, NULL, &indexbuffers[k]); + if (FAILED(hr)) PanicAlert("Failed to create index buffer, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)indexbuffers[k], "an index buffer of VertexManager"); + } + + bufdesc = CD3D11_BUFFER_DESC(2*MAXIBUFFERSIZE, D3D11_BIND_INDEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + hr = D3D::device->CreateBuffer(&bufdesc, NULL, &lineindexbuffer); + if (FAILED(hr)) PanicAlert("Failed to create index buffer, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)lineindexbuffer, "line index buffer of VertexManager"); + hr = D3D::device->CreateBuffer(&bufdesc, NULL, &pointindexbuffer); + if (FAILED(hr)) PanicAlert("Failed to create index buffer, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)pointindexbuffer, "point index buffer of VertexManager"); + + for (unsigned int k = 0;k < NUM_VERTEXBUFFERS;++k) + { + bufdesc = CD3D11_BUFFER_DESC(MAXVBUFFERSIZE >> k, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + hr = D3D::device->CreateBuffer(&bufdesc, NULL, &vertexbuffers[k]); + if (FAILED(hr)) PanicAlert("Failed to create vertex buffer, %s %d\n", __FILE__, __LINE__); + D3D::SetDebugObjectName((ID3D11DeviceChild*)vertexbuffers[k], "a vertex buffer of VertexManager"); + } +} + +void DestroyDeviceObjects() +{ + SAFE_RELEASE(lineindexbuffer); + SAFE_RELEASE(pointindexbuffer); + for (unsigned int k = 0;k < NUM_INDEXBUFFERS;++k) + SAFE_RELEASE(indexbuffers[k]); + + for (unsigned int k = 0;k < NUM_VERTEXBUFFERS;++k) + SAFE_RELEASE(vertexbuffers[k]); +} + +bool Init() +{ + LocalVBuffer = new u8[MAXVBUFFERSIZE]; + TIBuffer = new u16[MAXIBUFFERSIZE]; + LIBuffer = new u16[MAXIBUFFERSIZE]; + PIBuffer = new u16[MAXIBUFFERSIZE]; + s_pCurBufferPointer = LocalVBuffer; + Flushed=false; + + CreateDeviceObjects(); + + IndexGenerator::Start(TIBuffer,LIBuffer,PIBuffer); + return true; +} + +void ResetBuffer() +{ + s_pCurBufferPointer = LocalVBuffer; +} + +void Shutdown() +{ + DestroyDeviceObjects(); + if (LocalVBuffer) delete[] LocalVBuffer; + if (TIBuffer) delete[] TIBuffer; + if (LIBuffer) delete[] LIBuffer; + if (PIBuffer) delete[] PIBuffer; + LocalVBuffer = NULL; + TIBuffer = NULL; + LIBuffer = NULL; + PIBuffer = NULL; + + ResetBuffer(); +} + +void AddIndices(int _primitive, int _numVertices) +{ + switch (_primitive) + { + case GX_DRAW_QUADS: IndexGenerator::AddQuads(_numVertices); break; + case GX_DRAW_TRIANGLES: IndexGenerator::AddList(_numVertices); break; + case GX_DRAW_TRIANGLE_STRIP: IndexGenerator::AddStrip(_numVertices); break; + case GX_DRAW_TRIANGLE_FAN: IndexGenerator::AddFan(_numVertices); break; + case GX_DRAW_LINE_STRIP: IndexGenerator::AddLineStrip(_numVertices); break; + case GX_DRAW_LINES: IndexGenerator::AddLineList(_numVertices); break; + case GX_DRAW_POINTS: IndexGenerator::AddPoints(_numVertices); break; + } +} + +int GetRemainingSize() +{ + return MAXVBUFFERSIZE - (int)(s_pCurBufferPointer - LocalVBuffer); +} + +int GetRemainingVertices(int primitive) +{ + switch (primitive) + { + case GX_DRAW_QUADS: + case GX_DRAW_TRIANGLES: + case GX_DRAW_TRIANGLE_STRIP: + case GX_DRAW_TRIANGLE_FAN: + return (MAXIBUFFERSIZE - IndexGenerator::GetTriangleindexLen())/3; + case GX_DRAW_LINE_STRIP: + case GX_DRAW_LINES: + return (MAXIBUFFERSIZE - IndexGenerator::GetLineindexLen())/2; + case GX_DRAW_POINTS: + return (MAXIBUFFERSIZE - IndexGenerator::GetPointindexLen()); + default: return 0; + } +} + +void AddVertices(int _primitive, int _numVertices) +{ + if (_numVertices <= 0) return; + + switch (_primitive) + { + case GX_DRAW_QUADS: + case GX_DRAW_TRIANGLES: + case GX_DRAW_TRIANGLE_STRIP: + case GX_DRAW_TRIANGLE_FAN: + if (MAXIBUFFERSIZE - IndexGenerator::GetTriangleindexLen() < 3 * _numVertices) + Flush(); + break; + case GX_DRAW_LINE_STRIP: + case GX_DRAW_LINES: + if (MAXIBUFFERSIZE - IndexGenerator::GetLineindexLen() < 2 * _numVertices) + Flush(); + break; + case GX_DRAW_POINTS: + if (MAXIBUFFERSIZE - IndexGenerator::GetPointindexLen() < _numVertices) + Flush(); + break; + default: return; + } + if (Flushed) + { + IndexGenerator::Start(TIBuffer,LIBuffer,PIBuffer); + Flushed=false; + } + lastPrimitive = _primitive; + ADDSTAT(stats.thisFrame.numPrims, _numVertices); + INCSTAT(stats.thisFrame.numPrimitiveJoins); + AddIndices(_primitive, _numVertices); +} + +inline void Draw(unsigned int stride, bool alphapass) +{ + D3D11_MAPPED_SUBRESOURCE map; + ID3D11Buffer* vertexbuffer = GetSuitableVertexBuffer(IndexGenerator::GetNumVerts() * stride); + + if (!alphapass) + { + context->Map(vertexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, LocalVBuffer, IndexGenerator::GetNumVerts() * stride); + context->Unmap(vertexbuffer, 0); + } + + UINT bufoffset = 0; + UINT bufstride = (UINT)stride; + + if (!alphapass) gfxstate->ApplyState(); + else gfxstate->AlphaPass(); + if (IndexGenerator::GetNumTriangles() > 0) + { + ID3D11Buffer* indexbuffer = GetSuitableIndexBuffer(2*3*IndexGenerator::GetNumTriangles()); + if (!alphapass) + { + D3D::context->Map(indexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, TIBuffer, 2*3*IndexGenerator::GetNumTriangles()); + D3D::context->Unmap(indexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + D3D::context->IASetVertexBuffers(0, 1, &vertexbuffer, &bufstride, &bufoffset); + D3D::context->IASetIndexBuffer(indexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(3*IndexGenerator::GetNumTriangles(), 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumLines() > 0) + { + if (!alphapass) + { + D3D::context->Map(lineindexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, LIBuffer, 2*2*IndexGenerator::GetNumTriangles()); + D3D::context->Unmap(lineindexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); + D3D::context->IASetVertexBuffers(0, 1, &vertexbuffer, &bufstride, &bufoffset); + D3D::context->IASetIndexBuffer(lineindexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(2*IndexGenerator::GetNumLines(), 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } + if (IndexGenerator::GetNumPoints() > 0) + { + if (!alphapass) + { + D3D::context->Map(pointindexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, PIBuffer, 2*IndexGenerator::GetNumTriangles()); + D3D::context->Unmap(pointindexbuffer, 0); + } + + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); + D3D::context->IASetVertexBuffers(0, 1, &vertexbuffer, &bufstride, &bufoffset); + D3D::context->IASetIndexBuffer(pointindexbuffer, DXGI_FORMAT_R16_UINT, 0); + + D3D::context->DrawIndexed(IndexGenerator::GetNumPoints(), 0, 0); + INCSTAT(stats.thisFrame.numIndexedDrawCalls); + } +} + +void Flush() +{ + if (LocalVBuffer == s_pCurBufferPointer) return; + if (Flushed) return; + Flushed=true; + + DVSTARTPROFILE(); + + u32 usedtextures = 0; + for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i) + { + if (bpmem.tevorders[i/2].getEnable(i & 1)) + usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1); + } + + if (bpmem.genMode.numindstages > 0) + for (unsigned int i = 0; i < bpmem.genMode.numtevstages + 1; ++i) + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt); + + for (unsigned int i = 0; i < 8; i++) + { + if (usedtextures & (1 << i)) + { + Renderer::SetSamplerState(i & 3, i >> 2); + FourTexUnits &tex = bpmem.tex[i >> 2]; + TextureCache::TCacheEntry* tentry = TextureCache::Load(i, + (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, + tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, + tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, + tex.texTlut[i&3].tlut_format, + (tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips, + (tex.texMode1[i&3].max_lod >> 4)); + + if (tentry) + { + PixelShaderManager::SetTexDims(i, tentry->w, tentry->h, 0, 0); + if (tentry->scaleX != 1.0f || tentry->scaleY != 1.0f) + PixelShaderManager::SetCustomTexScale(i, tentry->scaleX, tentry->scaleY); + } + else + { + ERROR_LOG(VIDEO, "error loading texture"); + } + } + } + PixelShaderManager::SetTexturesUsed(0); + + VertexShaderManager::SetConstants(); + PixelShaderManager::SetConstants(); + + if (!PixelShaderCache::SetShader(false)) + goto shader_fail; + if (!VertexShaderCache::SetShader(g_nativeVertexFmt->m_components)) + goto shader_fail; + + unsigned int stride = g_nativeVertexFmt->GetVertexStride(); + g_nativeVertexFmt->SetupVertexPointers(); + + Draw(stride, false); + + if (bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate) + { + DWORD write = 0; + if (!PixelShaderCache::SetShader(true)) + goto shader_fail; + + // update alpha only + Draw(stride, true); + } + +shader_fail: + ResetBuffer(); + gfxstate->ResetShaderResources(); +} + +} // namespace diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h new file mode 100644 index 0000000000..6215b9d547 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h @@ -0,0 +1,38 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _VERTEXMANAGER_H +#define _VERTEXMANAGER_H + +#include "CPMemory.h" +#include "VertexLoader.h" + +namespace VertexManager +{ + +bool Init(); +void Shutdown(); + +void AddVertices(int _primitive, int _numVertices); +void Flush(); + +void CreateDeviceObjects(); +void DestroyDeviceObjects(); + +} // namespace + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.cpp b/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.cpp new file mode 100644 index 0000000000..4f0286b1a5 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.cpp @@ -0,0 +1,268 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "FileUtil.h" +#include "LinearDiskCache.h" + +#include "Globals.h" +#include "D3DBase.h" +#include "D3DShader.h" +#include "Statistics.h" +#include "Profiler.h" +#include "VideoConfig.h" +#include "VertexShaderCache.h" +#include "VertexLoader.h" +#include "BPMemory.h" +#include "XFMemory.h" + +VertexShaderCache::VSCache VertexShaderCache::vshaders; +const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry; + +static ID3D11VertexShader* SimpleVertexShader = NULL; +static ID3D11VertexShader* ClearVertexShader = NULL; +static ID3D11InputLayout* SimpleLayout = NULL; +static ID3D11InputLayout* ClearLayout = NULL; + +LinearDiskCache g_vs_disk_cache; + +ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() { return SimpleVertexShader; } +ID3D11VertexShader* VertexShaderCache::GetClearVertexShader() { return ClearVertexShader; } +ID3D11InputLayout* VertexShaderCache::GetSimpleInputLayout() { return SimpleLayout; } +ID3D11InputLayout* VertexShaderCache::GetClearInputLayout() { return ClearLayout; } + +// maps the constant numbers to float indices in the constant buffer +unsigned int vs_constant_offset_table[238]; +void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4) +{ + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number] ] = f1; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+1] = f2; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+2] = f3; + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+3] = f4; + D3D::gfxstate->vscbufchanged = true; +} + +void SetVSConstant4fv(unsigned int const_number, const float* f) +{ + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4); + D3D::gfxstate->vscbufchanged = true; +} + +void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f) +{ + for (unsigned int i = 0; i < count; i++) + { + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]], f+3*i, sizeof(float)*3); + D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]+3] = 0.f; + } +} + +void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f) +{ + memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4*count); + D3D::gfxstate->vscbufchanged = true; +} + +// this class will load the precompiled shaders into our cache +class VertexShaderCacheInserter : public LinearDiskCacheReader { +public: + void Read(const u8* key, int key_size, const u8* value, int value_size) + { + VERTEXSHADERUID uid; + if (key_size != sizeof(uid)) + { + ERROR_LOG(VIDEO, "Wrong key size in vertex shader cache"); + return; + } + memcpy(&uid, key, key_size); + + ID3D10Blob* blob; + D3D10CreateBlob(value_size, &blob); + memcpy(blob->GetBufferPointer(), value, value_size); + VertexShaderCache::InsertByteCode(uid, blob); + blob->Release(); + } +}; + +const char simple_shader_code[] = { + "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float2 vTexCoord : TEXCOORD0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vTexCoord = inTEX0;\n" + "return OUT;\n" + "}\n" +}; + +const char clear_shader_code[] = { + "struct VSOUTPUT\n" + "{\n" + "float4 vPosition : POSITION;\n" + "float4 vColor0 : COLOR0;\n" + "};\n" + "VSOUTPUT main(float4 inPosition : POSITION,float4 inColor0: COLOR0)\n" + "{\n" + "VSOUTPUT OUT;\n" + "OUT.vPosition = inPosition;\n" + "OUT.vColor0 = inColor0;\n" + "return OUT;\n" + "}\n" +}; + +void VertexShaderCache::Init() +{ + const D3D11_INPUT_ELEMENT_DESC simpleelems[2] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + const D3D11_INPUT_ELEMENT_DESC clearelems[2] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + ID3D10Blob* blob; + D3D::CompileVertexShader(simple_shader_code, strlen(simple_shader_code), &blob); + D3D::device->CreateInputLayout(simpleelems, 2, blob->GetBufferPointer(), blob->GetBufferSize(), &SimpleLayout); + SimpleVertexShader = D3D::CreateVertexShaderFromByteCode(blob); + if (SimpleLayout == NULL || SimpleVertexShader == NULL) PanicAlert("Failed to create simple vertex shader or input layout at %s %d\n", __FILE__, __LINE__); + blob->Release(); + + D3D::CompileVertexShader(clear_shader_code, (int)strlen(clear_shader_code), &blob); + D3D::device->CreateInputLayout(clearelems, 2, blob->GetBufferPointer(), blob->GetBufferSize(), &ClearLayout); + ClearVertexShader = D3D::CreateVertexShaderFromByteCode(blob); + if (ClearLayout == NULL || ClearVertexShader == NULL) PanicAlert("Failed to create clear vertex shader or input layout at %s %d\n", __FILE__, __LINE__); + blob->Release(); + + Clear(); + + // these values are hardcoded, they depend on internal D3DCompile behavior + // TODO: Do this with D3DReflect or something instead + unsigned int k; + for (k = 0;k < 64;k++) vs_constant_offset_table[C_TRANSFORMMATRICES+k] = 312+4*k; + for (k = 0;k < 24;k++) vs_constant_offset_table[C_TEXMATRICES+k] = 216+4*k; + for (k = 0;k < 32;k++) vs_constant_offset_table[C_NORMALMATRICES+k] = 568+4*k; + for (k = 0;k < 6;k++) vs_constant_offset_table[C_POSNORMALMATRIX+k] = 0+4*k; + for (k = 0;k < 64;k++) vs_constant_offset_table[C_POSTTRANSFORMMATRICES+k] = 696+4*k; + for (k = 0;k < 40;k++) vs_constant_offset_table[C_LIGHTS+k] = 56+4*k; + for (k = 0;k < 4;k++) vs_constant_offset_table[C_MATERIALS+k] = 40+4*k; + for (k = 0;k < 4;k++) vs_constant_offset_table[C_PROJECTION+k] = 24+4*k; + + if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + char cache_filename[MAX_PATH]; + sprintf(cache_filename, "%sdx11-%s-vs.cache", File::GetUserPath(D_SHADERCACHE_IDX), globals->unique_id); + VertexShaderCacheInserter inserter; + g_vs_disk_cache.OpenAndRead(cache_filename, &inserter); +} + +void VertexShaderCache::Clear() +{ + VSCache::iterator iter = vshaders.begin(); + for (; iter != vshaders.end(); ++iter) + iter->second.Destroy(); + vshaders.clear(); +} + +void VertexShaderCache::Shutdown() +{ + SAFE_RELEASE(SimpleVertexShader); + SAFE_RELEASE(ClearVertexShader); + + SAFE_RELEASE(SimpleLayout); + SAFE_RELEASE(ClearLayout); + + Clear(); + g_vs_disk_cache.Sync(); + g_vs_disk_cache.Close(); +} + +bool VertexShaderCache::SetShader(u32 components) +{ + DVSTARTPROFILE(); + + VERTEXSHADERUID uid; + GetVertexShaderId(&uid, components); + if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount) + return (vshaders[uid].shader != NULL); + + memcpy(&last_vertex_shader_uid, &uid, sizeof(VERTEXSHADERUID)); + + VSCache::iterator iter; + iter = vshaders.find(uid); + if (iter != vshaders.end()) + { + iter->second.frameCount = frameCount; + const VSCacheEntry &entry = iter->second; + last_entry = &entry; + + if (entry.shader) D3D::gfxstate->SetVShader(entry.shader, iter->second.bytecode); + return (entry.shader != NULL); + } + + const char* code = GenerateVertexShaderCode(components, API_D3D11); + + ID3D10Blob* pbytecode = NULL; + D3D::CompileVertexShader(code, (int)strlen(code), &pbytecode); + + if (pbytecode == NULL) + { + PanicAlert("Failed to compile Vertex Shader %s %d:\n\n%s", __FILE__, __LINE__, code); + return false; + } + g_vs_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->GetBufferPointer(), pbytecode->GetBufferSize()); + g_vs_disk_cache.Sync(); + + bool result = InsertByteCode(uid, pbytecode); + D3D::gfxstate->SetVShader(last_entry->shader, last_entry->bytecode); + pbytecode->Release(); + return result; +} + +bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, ID3D10Blob* bcodeblob) +{ + ID3D11VertexShader* shader = D3D::CreateVertexShaderFromByteCode(bcodeblob); + if (shader == NULL) + { + PanicAlert("Failed to create vertex shader from %p size %d at %s %d\n", bcodeblob->GetBufferPointer(), bcodeblob->GetBufferSize(), __FILE__, __LINE__); + return false; + } + + // Make an entry in the table + VSCacheEntry entry; + entry.shader = shader; + entry.frameCount = frameCount; + entry.SetByteCode(bcodeblob); + + vshaders[uid] = entry; + last_entry = &vshaders[uid]; + + INCSTAT(stats.numVertexShadersCreated); + SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size()); + + return true; +} + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.h b/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.h new file mode 100644 index 0000000000..7005f822cd --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.h @@ -0,0 +1,73 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _VERTEXSHADERCACHE_H +#define _VERTEXSHADERCACHE_H + +#include "D3DBase.h" + +#include +#include + +#include "D3DBase.h" +#include "VertexShaderGen.h" + +#include "d3dcompiler.h" + +class VertexShaderCache +{ +public: + static void Init(); + static void Clear(); + static void Shutdown(); + static bool SetShader(u32 components); + + static ID3D11VertexShader* GetSimpleVertexShader(); + static ID3D11VertexShader* GetClearVertexShader(); + static ID3D11InputLayout* GetSimpleInputLayout(); + static ID3D11InputLayout* GetClearInputLayout(); + + static bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, ID3D10Blob* bcodeblob); + +private: + struct VSCacheEntry + { + ID3D11VertexShader* shader; + ID3D10Blob* bytecode; // needed to initialize the input layout + int frameCount; + + VSCacheEntry() : shader(NULL), bytecode(NULL), frameCount(0) {} + void SetByteCode(ID3D10Blob* blob) + { + if (bytecode) bytecode->Release(); + bytecode = blob; + blob->AddRef(); + } + void Destroy() + { + SAFE_RELEASE(shader); + SAFE_RELEASE(bytecode); + } + }; + + typedef std::map VSCache; + + static VSCache vshaders; + static const VSCacheEntry* last_entry; +}; + +#endif // _VERTEXSHADERCACHE_H diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.cpp new file mode 100644 index 0000000000..86c41d5cda --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.cpp @@ -0,0 +1,29 @@ +#include +#include +#include "DialogManager.h" + +typedef std::vector WindowList; +WindowList dialogs; + +void DialogManager::AddDlg(HWND hDialog) +{ + dialogs.push_back(hDialog); +} + +bool DialogManager::IsDialogMessage(LPMSG message) +{ + WindowList::iterator iter; + for (iter=dialogs.begin(); iter!=dialogs.end(); iter++) + { + if (::IsDialogMessage(*iter,message)) + return true; + } + return false; +} + +void DialogManager::EnableAll(BOOL enable) +{ + WindowList::iterator iter; + for (iter=dialogs.begin(); iter!=dialogs.end(); iter++) + EnableWindow(*iter,enable); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.h b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.h new file mode 100644 index 0000000000..1ce828a640 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/DialogManager.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class DialogManager +{ +public: + static void AddDlg(HWND hDialog); + static bool IsDialogMessage(LPMSG message); + static void EnableAll(BOOL enable); +}; + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.cpp b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.cpp new file mode 100644 index 0000000000..6470d23cfb --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "Misc.h" + +namespace W32Util +{ + void CenterWindow(HWND hwnd) + { + HWND hwndParent; + RECT rect, rectP; + int width, height; + int screenwidth, screenheight; + int x, y; + + // make the window relative to its parent + hwndParent = GetParent(hwnd); + if (!hwndParent) + return; + + GetWindowRect(hwnd, &rect); + GetWindowRect(hwndParent, &rectP); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + x = ((rectP.right-rectP.left) - width) / 2 + rectP.left; + y = ((rectP.bottom-rectP.top) - height) / 2 + rectP.top; + + screenwidth = GetSystemMetrics(SM_CXSCREEN); + screenheight = GetSystemMetrics(SM_CYSCREEN); + + // make sure that the dialog box never moves outside of + // the screen + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x + width > screenwidth) x = screenwidth - width; + if (y + height > screenheight) y = screenheight - height; + + MoveWindow(hwnd, x, y, width, height, FALSE); + } +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.h b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.h new file mode 100644 index 0000000000..2ea70fedd8 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Misc.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace W32Util +{ + void CenterWindow(HWND hwnd); +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.cpp b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.cpp new file mode 100644 index 0000000000..b25a516adb --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.cpp @@ -0,0 +1,223 @@ +#include "Misc.h" +#include "PropertySheet.h" + +#include + +namespace W32Util +{ + bool centered; + + PropSheet::PropSheet() + { + watermark = 0; + header = 0; + icon = 0; + } + + int CALLBACK PropSheet::Callback(HWND hwndDlg, UINT uMsg, LPARAM lParam) + { + switch (uMsg) { + case PSCB_PRECREATE: + { + if (uMsg == PSCB_PRECREATE) + { + /* + if (lParam) + { + DLGTEMPLATE* pDlgTemplate; + DLGTEMPLATEEX* pDlgTemplateEx; + + pDlgTemplateEx = (DLGTEMPLATEEX*)lParam; + if (pDlgTemplateEx->signature == 0xFFFF) + { + // pDlgTemplateEx points to an extended + // dialog template structure. + + //pDlgTemplate->style |= DS_SETFONT; + u8* tmp1 = (u8*)&pDlgTemplateEx + sizeof(DLGTEMPLATEEX); + u16* tmp = (u16*)tmp1; + tmp++; //skip menu + tmp++; //skip dlg class + //Crash(); + //Here we should bash in Segoe UI + //It turns out to be way complicated though + //Not worth it + } + else + { + // This is a standard dialog template + // structure. + pDlgTemplate = (DLGTEMPLATE*)lParam; + } + } */ + } + + } + break; + case PSCB_INITIALIZED: + { + } + return 0; + } + return 0; + } + + void PropSheet::Show(HINSTANCE hInstance, HWND hParent, LPCTSTR title, int startpage, bool floating, bool wizard) + { + HPROPSHEETPAGE* pages = new HPROPSHEETPAGE[list.size()]; + PROPSHEETPAGE page; + //common settings + memset((void*)&page,0,sizeof(PROPSHEETPAGE)); + page.dwSize = sizeof(PROPSHEETPAGE); + page.hInstance = hInstance; + + int i=0; + for (DlgList::iterator iter = list.begin(); iter != list.end(); iter++, i++) + { + if (wizard) + { + if (i == 0 || i == list.size()-1) + page.dwFlags = PSP_HIDEHEADER; + else + page.dwFlags = PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; + } + else + { + page.dwFlags = PSP_USETITLE; + } + page.pszTemplate = iter->resource; + page.pfnDlgProc = Tab::TabDlgProc; + page.pszTitle = iter->title; + page.pszHeaderTitle = wizard?iter->title:0; + page.pszHeaderSubTitle = wizard?iter->hdrSubTitle:0; + page.lParam = (LPARAM)iter->tab; + pages[i] = CreatePropertySheetPage(&page); + } + + PROPSHEETHEADER sheet; + memset(&sheet,0,sizeof(sheet)); + sheet.dwSize = sizeof(PROPSHEETHEADER); + sheet.hInstance = hInstance; + sheet.hwndParent = hParent; + sheet.pszbmWatermark = watermark; + sheet.pszbmHeader = header; + sheet.pszCaption = title; + sheet.nPages = (UINT)list.size(); + sheet.phpage = pages; + sheet.nStartPage = startpage; + sheet.pfnCallback = (PFNPROPSHEETCALLBACK)Callback; + + NONCLIENTMETRICS ncm = {0}; + ncm.cbSize = sizeof(ncm); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); + hDialogFont = CreateFontIndirect(&ncm.lfMessageFont); + + if (wizard) + { + sheet.dwFlags = PSH_USECALLBACK | PSH_WIZARD97 | (watermark?PSH_WATERMARK:0) | (header?PSH_HEADER:0); + + //Create the intro/end title font + LOGFONT TitleLogFont = ncm.lfMessageFont; + TitleLogFont.lfWeight = FW_BOLD; + lstrcpy(TitleLogFont.lfFaceName, TEXT("Verdana Bold")); + //StringCchCopy(TitleLogFont.lfFaceName, 32, TEXT("Verdana Bold")); + + HDC hdc = GetDC(NULL); //gets the screen DC + int FontSize = 12; + TitleLogFont.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * FontSize / 72; + hTitleFont = CreateFontIndirect(&TitleLogFont); + ReleaseDC(NULL, hdc); + } else { + sheet.dwFlags = PSH_USECALLBACK | PSH_PROPTITLE; + hTitleFont = 0; + } + + if (icon) { + sheet.dwFlags |= PSH_USEHICON; + sheet.hIcon = icon; + } + + sheet.dwFlags |= PSH_NOCONTEXTHELP; + + if (floating) + sheet.dwFlags |= PSH_MODELESS; + //else + // sheet.dwFlags |= PSH_NOAPPLYNOW; + + centered=false; + PropertySheet(&sheet); + if (!floating) + { + for (DlgList::iterator iter = list.begin(); iter != list.end(); iter++) + { + delete iter->tab; + } + DeleteObject(hTitleFont); + } + DeleteObject(hDialogFont); + delete [] pages; + } + void PropSheet::Add(Tab* tab, LPCTSTR resource, LPCTSTR title, LPCTSTR subtitle) + { + tab->sheet = this; + list.push_back(Page(tab,resource,title,subtitle)); + } + + + void WizExteriorPage::Init(HWND hDlg) + { + HWND hwndControl = GetDlgItem(hDlg, captionID); + //SetWindowFont(hwndControl, sheet->GetTitleFont(), TRUE); + SendMessage(hwndControl,WM_SETFONT,(WPARAM)sheet->GetTitleFont(),0); + } + + INT_PTR Tab::TabDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) + { + Tab* tab = (Tab*)GetWindowLongPtr(hDlg, GWLP_USERDATA); + switch(message) + { + case WM_INITDIALOG: + { + if (!centered) //HACK + { + CenterWindow(GetParent(hDlg)); + centered=true; + } + LPARAM l = ((LPPROPSHEETPAGE)lParam)->lParam; + tab = (Tab*)l; + SetWindowLongPtr(hDlg, GWLP_USERDATA, (DWORD_PTR)l); + tab->Init(hDlg); + } + break; + + case WM_COMMAND: + tab->Command(hDlg,wParam); + PropSheet_Changed(GetParent(hDlg), hDlg); + break; + case WM_NOTIFY: + { + LPPSHNOTIFY lppsn = (LPPSHNOTIFY) lParam; + HWND sheet = lppsn->hdr.hwndFrom; + switch(lppsn->hdr.code) { + case PSN_APPLY: + tab->Apply(hDlg); + break; + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hDlg), + (tab->HasPrev()?PSWIZB_BACK:0) | + (tab->HasNext()?PSWIZB_NEXT:0) | + (tab->HasFinish()?PSWIZB_FINISH:0)); + break; + case PSN_WIZNEXT: + tab->Apply(hDlg); //maybe not always good + break; + case PSN_WIZBACK: + case PSN_RESET: //cancel + break; + } + } + break; + } + return 0; + } +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.h b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.h new file mode 100644 index 0000000000..83646790f1 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/PropertySheet.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +namespace W32Util +{ + class PropSheet; + + class Tab + { + public: + PropSheet* sheet; //back pointer .. + virtual void Init(HWND hDlg) {} + virtual void Command(HWND hDlg, WPARAM wParam) {} + virtual void Apply(HWND hDlg) {} + virtual bool HasPrev() {return true;} + virtual bool HasFinish() {return false;} + virtual bool HasNext() {return true;} + static INT_PTR __stdcall TabDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + }; + + + class WizExteriorPage : public Tab + { + int captionID; + public: + WizExteriorPage(int caption) {captionID = caption;} + void Init(HWND hDlg); + }; + + + class WizFirstPage : public WizExteriorPage + { + public: + WizFirstPage(int caption) : WizExteriorPage(caption) {} + bool HasPrev() {return false;} + }; + + + class WizLastPage : public WizExteriorPage + { + public: + WizLastPage(int caption) : WizExteriorPage(caption) {} + bool HasNext() {return false;} + bool HasFinish() {return true;} + }; + + + class WizInteriorPage : public Tab + { + public: + }; + + class PropSheet + { + LPCTSTR watermark; + LPCTSTR header; + HFONT hTitleFont; + HFONT hDialogFont; + HICON icon; + struct Page + { + Page(Tab* _tab, LPCTSTR _resource, LPCTSTR _title, LPCTSTR _subtitle = 0) + : tab(_tab), resource(_resource), title(_title), hdrSubTitle(_subtitle) {} + Tab* tab; + LPCTSTR resource; + LPCTSTR title; + LPCTSTR hdrSubTitle; + }; + public: + PropSheet(); + typedef std::vector DlgList; + DlgList list; + void SetWaterMark(LPCTSTR _watermark) {watermark=_watermark;} + void SetHeader(LPCTSTR _header) {header=_header;} + void SetIcon(HICON _icon) {icon = _icon;} + void Add(Tab* tab, LPCTSTR resource, LPCTSTR title, LPCTSTR subtitle = 0); + void Show(HINSTANCE hInstance, HWND hParent, LPCTSTR title, int startpage=0, bool floating = false, bool wizard = false); + HFONT GetTitleFont() {return hTitleFont;} + HFONT GetFont() {return hDialogFont;} + static int CALLBACK Callback(HWND hwndDlg, UINT uMsg, LPARAM lParam); + }; + + + +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.cpp b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.cpp new file mode 100644 index 0000000000..5fc1d7554c --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.cpp @@ -0,0 +1,95 @@ +#include +#include + +#include "TabControl.h" + +namespace W32Util +{ + // __________________________________________________________________________________________________ + // constructor + // + TabControl::TabControl(HINSTANCE _hInstance, HWND _hTabCtrl,DLGPROC _lpDialogFunc) : + m_hInstance(_hInstance), + m_hTabCtrl(_hTabCtrl), + m_numDialogs(0) + { + for (int i=0; ihwndFrom == m_hTabCtrl) + { + int iPage = TabCtrl_GetCurSel (m_hTabCtrl); + SelectDialog (iPage); + } + } + } + +} + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.h b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.h new file mode 100644 index 0000000000..1e51b53017 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/TabControl.h @@ -0,0 +1,36 @@ +#pragma once + +namespace W32Util +{ +#define MAX_WIN_DIALOGS 32 + + + class TabControl + { + private: + + HINSTANCE m_hInstance; + HWND m_hWndParent; + HWND m_hTabCtrl; + + HWND m_WinDialogs[MAX_WIN_DIALOGS]; + int m_numDialogs; + + public: + + TabControl(HINSTANCE _hInstance, HWND _hTabCtrl,DLGPROC _lpDialogFunc); + + ~TabControl(void); + + // + // --- tools --- + // + + HWND AddItem (char* _szText,int _iResource,DLGPROC _lpDialogFunc); + + void SelectDialog (int _nDialogId); + + void MessageHandler(UINT message, WPARAM wParam, LPARAM lParam); + }; + +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.cpp b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.cpp new file mode 100644 index 0000000000..51177f9c18 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.cpp @@ -0,0 +1,82 @@ +#include "Thread.h" + +namespace W32Util +{ + // __________________________________________________________________________________________________ + // Constructor + // + Thread::Thread ( DWORD (WINAPI * pFun) (void* arg), void* pArg) + { + _handle = CreateThread ( + 0, // Security attributes + 0, // Stack size + pFun, + pArg, + CREATE_SUSPENDED, + &_tid); + } + // __________________________________________________________________________________________________ + // Destructor + // + Thread::~Thread (void) + { + if (_handle != NULL) + { + if (CloseHandle (_handle) == FALSE) + { + Terminate(); + } + } + } + + // __________________________________________________________________________________________________ + // Resume + // + void + Thread::Resume (void) + { + if (_handle != NULL) + ResumeThread (_handle); + } + + // __________________________________________________________________________________________________ + // WaitForDeath + // + void + Thread::WaitForDeath (void) + { + if (_handle != NULL) + WaitForSingleObject (_handle, 100); + } + + // __________________________________________________________________________________________________ + // Terminate + // + void + Thread::Terminate (void) + { + if (_handle != NULL) + TerminateThread (_handle, 0); + _handle = NULL; + } + + // __________________________________________________________________________________________________ + // SetPriority + // + void + Thread::SetPriority (int _nPriority) + { + if (_handle != NULL) + SetThreadPriority(_handle, _nPriority); + } + + // __________________________________________________________________________________________________ + // Suspend + // + void + Thread::Suspend (void) + { + if (_handle != NULL) + SuspendThread(_handle); + } +} \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.h b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.h new file mode 100644 index 0000000000..ab583fb5bd --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/W32Util/Thread.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace W32Util +{ + class Thread + { + private: + HANDLE _handle; + DWORD _tid; // thread id + + public: + Thread ( DWORD (WINAPI * pFun) (void* arg), void* pArg); + ~Thread () ; + + // + // --- tools --- + // + + void Resume(void); + + void Suspend(void); + + void WaitForDeath(void); + + void Terminate(void); + + void SetPriority(int _nPriority); + + bool IsActive (void); + + HANDLE GetHandle(void) {return _handle;} + + }; + +} + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp new file mode 100644 index 0000000000..c045aeb2a0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp @@ -0,0 +1,439 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include +#include + +#include "Common.h" +#include "Atomic.h" +#include "Thread.h" +#include "LogManager.h" + +#include "svnrev.h" +#include "resource.h" +#include "main.h" +#include "VideoConfig.h" +#include "Fifo.h" +#include "OpcodeDecoding.h" +#include "TextureCache.h" +#include "BPStructs.h" +#include "VertexManager.h" +#include "VertexLoaderManager.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "VertexShaderCache.h" +#include "PixelShaderCache.h" +#include "CommandProcessor.h" +#include "PixelEngine.h" +#include "OnScreenDisplay.h" +#include "DlgSettings.h" +#include "D3DTexture.h" +#include "D3DUtil.h" +#include "W32Util/Misc.h" +#include "EmuWindow.h" +#include "VideoState.h" +#include "XFBConvert.h" +#include "render.h" + + +#if defined(DEBUGFAST) +#define DLL_PLUGIN_NAME "Dolphin Direct3D 11 (DebugFast)" +#elif defined(_DEBUG) +#define DLL_PLUGIN_NAME "Dolphin Direct3D 11 (Debug)" +#else +#define DLL_PLUGIN_NAME "Dolphin Direct3D 11" +#endif + + +HINSTANCE g_hInstance = NULL; +SVideoInitialize g_VideoInitialize; +PLUGIN_GLOBALS* globals = NULL; +static bool s_PluginInitialized = false; + +volatile u32 s_swapRequested = FALSE; +static u32 s_efbAccessRequested = FALSE; +static volatile u32 s_FifoShuttingDown = FALSE; +static bool ForceSwap = true; + +static volatile struct +{ + u32 xfbAddr; + FieldType field; + u32 fbWidth; + u32 fbHeight; +} s_beginFieldArgs; + +static volatile EFBAccessType s_AccessEFBType; + +bool HandleDisplayList(u32 address, u32 size) +{ + return false; +} + +bool IsD3D() +{ + return true; +} + +bool IsD3D9() +{ + return false; +} + +bool IsD3D11() +{ + return true; +} + +// This is used for the functions right below here which use wxwidgets +#if defined(HAVE_WX) && HAVE_WX +#ifdef _WIN32 + WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst); +#endif + +wxWindow* GetParentedWxWindow(HWND Parent) +{ +#ifdef _WIN32 + wxSetInstance((HINSTANCE)g_hInstance); +#endif + wxWindow* win = new wxWindow(); +#ifdef _WIN32 + win->SetHWND((WXHWND)Parent); + win->AdoptAttributesFromHWND(); +#endif + return win; +} +#endif + +void DllDebugger(HWND _hParent, bool Show) +{ +} + +#if defined(HAVE_WX) && HAVE_WX + class wxDLLApp : public wxApp + { + bool OnInit() + { + return true; + } + }; + IMPLEMENT_APP_NO_MAIN(wxDLLApp) + WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst); +#endif + +BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { +#if defined(HAVE_WX) && HAVE_WX + wxSetInstance((HINSTANCE)hinstDLL); + wxInitialize(); +#endif + } + break; + case DLL_PROCESS_DETACH: +#if defined(HAVE_WX) && HAVE_WX + wxUninitialize(); +#endif + break; + } + + g_hInstance = hinstDLL; + return TRUE; +} + +unsigned int Callback_PeekMessages() +{ + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + return FALSE; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return TRUE; +} + + +void UpdateFPSDisplay(const char* text) +{ + char temp[512]; + sprintf_s(temp, 512, "SVN R%i: DX11: %s", SVN_REV, text); + SetWindowTextA(EmuWindow::GetWnd(), temp); +} + +void GetDllInfo (PLUGIN_INFO* _PluginInfo) +{ + _PluginInfo->Version = 0x0100; + _PluginInfo->Type = PLUGIN_TYPE_VIDEO; + sprintf_s(_PluginInfo->Name, 100, DLL_PLUGIN_NAME); +} + +void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals) +{ + globals = _pPluginGlobals; + LogManager::SetInstance((LogManager*)globals->logManager); +} + +void DllAbout(HWND _hParent) +{ + MessageBoxA(NULL, "DllAbout not implemented, how did you come here? Anyway, report this to the devs.", "Error!", MB_OK); +} + +void DllConfig(HWND _hParent) +{ + DlgSettings_Show(g_hInstance, _hParent); +} + +void Initialize(void* init) +{ + frameCount = 0; + SVideoInitialize* _pVideoInitialize = (SVideoInitialize*)init; + g_VideoInitialize = *_pVideoInitialize; + InitXFBConvTables(); + + g_Config.Load((std::string(File::GetUserPath(D_CONFIG_IDX)) + "gfx_dx11.ini").c_str()); + g_Config.GameIniLoad(globals->game_ini); + UpdateActiveConfig(); + + g_VideoInitialize.pWindowHandle = (void*)EmuWindow::Create((HWND)g_VideoInitialize.pWindowHandle, g_hInstance, _T("Loading - Please wait.")); + if (g_VideoInitialize.pWindowHandle == NULL) + { + ERROR_LOG(VIDEO, "An error has occurred while trying to create the window."); + return; + } + + g_VideoInitialize.pPeekMessages = &Callback_PeekMessages; + g_VideoInitialize.pUpdateFPSDisplay = &UpdateFPSDisplay; + + _pVideoInitialize->pPeekMessages = g_VideoInitialize.pPeekMessages; + _pVideoInitialize->pUpdateFPSDisplay = g_VideoInitialize.pUpdateFPSDisplay; + _pVideoInitialize->pWindowHandle = g_VideoInitialize.pWindowHandle; + + OSD::AddMessage("Dolphin Direct3D 11 Video Plugin.", 5000); + s_PluginInitialized = true; +} + +void Video_Prepare() +{ + s_efbAccessRequested = FALSE; + s_FifoShuttingDown = FALSE; + s_swapRequested = FALSE; + ForceSwap = true; + Renderer::Init(); + TextureCache::Init(); + BPInit(); + VertexManager::Init(); + Fifo_Init(); + VertexLoaderManager::Init(); + OpcodeDecoder_Init(); + VertexShaderCache::Init(); + VertexShaderManager::Init(); + PixelShaderCache::Init(); + PixelShaderManager::Init(); + CommandProcessor::Init(); + PixelEngine::Init(); + D3D::InitUtils(); + + // tell the host that the window is ready + g_VideoInitialize.pCoreMessage(WM_USER_CREATE); +} + +void Shutdown() +{ + s_efbAccessRequested = FALSE; + s_FifoShuttingDown = FALSE; + s_swapRequested = FALSE; + D3D::ShutdownUtils(); + CommandProcessor::Shutdown(); + PixelShaderManager::Shutdown(); + PixelShaderCache::Shutdown(); + VertexShaderManager::Shutdown(); + VertexShaderCache::Shutdown(); + OpcodeDecoder_Shutdown(); + VertexLoaderManager::Shutdown(); + Fifo_Shutdown(); + VertexManager::Shutdown(); + TextureCache::Shutdown(); + Renderer::Shutdown(); + EmuWindow::Close(); + s_PluginInitialized = false; +} + +void DoState(unsigned char** ptr, int mode) +{ + // clear texture cache because it might have written to RAM + CommandProcessor::FifoCriticalEnter(); + TextureCache::Invalidate(false); + CommandProcessor::FifoCriticalLeave(); + // no need to clear shader caches + PointerWrap p(ptr, mode); + VideoCommon_DoState(p); +} + +void EmuStateChange(PLUGIN_EMUSTATE newState) +{ + Fifo_RunLoop((newState == PLUGIN_EMUSTATE_PLAY) ? true : false); +} + +void Video_EnterLoop() +{ + Fifo_EnterLoop(g_VideoInitialize); +} + +void Video_ExitLoop() +{ + Fifo_ExitLoop(); + s_FifoShuttingDown = TRUE; +} + +void Video_SetRendering(bool bEnabled) +{ + Fifo_SetRendering(bEnabled); +} + +// run from the graphics thread +void VideoFifo_CheckSwapRequest() +{ + if (Common::AtomicLoadAcquire(s_swapRequested)) + { + if (ForceSwap) + Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.field, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbHeight); + + Common::AtomicStoreRelease(s_swapRequested, FALSE); + } +} + +inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper) +{ + return !((aLower >= bUpper) || (bLower >= aUpper)); +} + +// Run from the graphics thread +void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight) +{ + ForceSwap = false; +} + +// Run from the CPU thread (from VideoInterface.cpp) +void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight) +{ +} + +void Video_EndField() +{ +} + +void Video_AddMessage(const char* pstr, u32 milliseconds) +{ + OSD::AddMessage(pstr, milliseconds); +} + +HRESULT ScreenShot(const char* filename) +{ + return S_OK; +} + +void Video_Screenshot(const char* _szFilename) +{ +} + +static struct +{ + EFBAccessType type; + u32 x; + u32 y; +} s_accessEFBArgs; + +static u32 s_AccessEFBResult = 0; + +void VideoFifo_CheckEFBAccess() +{ + if (Common::AtomicLoadAcquire(s_efbAccessRequested)) + { + s_AccessEFBResult = Renderer::AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y); + + Common::AtomicStoreRelease(s_efbAccessRequested, FALSE); + } +} + +u32 Video_AccessEFB(EFBAccessType type, u32 x, u32 y) +{ + if (s_PluginInitialized) + { + s_accessEFBArgs.type = type; + s_accessEFBArgs.x = x; + s_accessEFBArgs.y = y; + + Common::AtomicStoreRelease(s_efbAccessRequested, TRUE); + + if (g_VideoInitialize.bOnThread) + { + while (Common::AtomicLoadAcquire(s_efbAccessRequested) && !s_FifoShuttingDown) + //Common::SleepCurrentThread(1); + Common::YieldCPU(); + } + else + VideoFifo_CheckEFBAccess(); + + return s_AccessEFBResult; + } + return 0; +} + + +void Video_CommandProcessorRead16(u16& _rReturnValue, const u32 _Address) +{ + CommandProcessor::Read16(_rReturnValue, _Address); +} + +void Video_CommandProcessorWrite16(const u16 _Data, const u32 _Address) +{ + CommandProcessor::Write16(_Data, _Address); +} + +void Video_PixelEngineRead16(u16& _rReturnValue, const u32 _Address) +{ + PixelEngine::Read16(_rReturnValue, _Address); +} + +void Video_PixelEngineWrite16(const u16 _Data, const u32 _Address) +{ + PixelEngine::Write16(_Data, _Address); +} + +void Video_PixelEngineWrite32(const u32 _Data, const u32 _Address) +{ + PixelEngine::Write32(_Data, _Address); +} + +inline void Video_GatherPipeBursted(void) +{ + CommandProcessor::GatherPipeBursted(); +} + +void Video_WaitForFrameFinish(void) +{ + CommandProcessor::WaitForFrameFinish(); +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.h b/Source/Plugins/Plugin_VideoDX11/Src/main.h new file mode 100644 index 0000000000..fb3775ab7e --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.h @@ -0,0 +1,27 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef MAIN_H +#define MAIN_H + +#include "PluginSpecs_Video.h" +#include "Render.h" + +extern SVideoInitialize g_VideoInitialize; +extern volatile u32 s_swapRequested; + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/resource.h b/Source/Plugins/Plugin_VideoDX11/Src/resource.h new file mode 100644 index 0000000000..cb6b999cf0 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/resource.h @@ -0,0 +1,52 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc +// +#define IDD_ABOUT 102 +#define IDD_SETTINGS 103 +#define IDD_ADVANCED 105 +//#define IDC_ADAPTER 1001 +//#define IDC_ANTIALIASMODE 1002 +//#define IDC_RESOLUTION 1003 +#define IDC_VSYNC 1006 +#define IDC_ASPECT_16_9 1008 +#define IDC_ASPECT_4_3 1009 +#define IDC_WIDESCREEN_HACK 1010 +#define IDC_SAFE_TEXTURE_CACHE 1011 +#define IDC_EFB_ACCESS_ENABLE 1012 +#define IDC_WIREFRAME 1013 +#define IDC_DISABLEFOG 1014 +#define IDC_OVERLAYFPS 1015 +#define IDC_OVERLAYSTATS 1016 +#define IDC_OVERLAYPROJSTATS 1017 +#define IDC_ENABLEEFBCOPY 1018 +//#define IDC_EFBTORAM 1019 +//#define IDC_EFBTOTEX 1020 +#define IDC_TEXFMT_OVERLAY 1024 +#define IDC_TEXFMT_CENTER 1025 +#define IDC_ENABLEXFB 1026 +#define IDC_FORCEANISOTROPY 1027 +//#define IDC_ENABLEXFB2 1027 +//#define IDC_ENABLEREALXFB 1027 +//#define IDC_LOADHIRESTEXTURE 1028 +#define IDC_EFBSCALEDCOPY 1029 +#define IDC_OSDHOTKEY 1030 +#define IDC_COMBO2 1040 +#define IDC_ASPECTRATIO 1040 +#define IDC_SAFE_TEXTURE_CACHE_SAFE 1041 +#define IDC_SAFE_TEXTURE_CACHE_NORMAL 1042 +#define IDC_RADIO3 1043 +#define IDC_SAFE_TEXTURE_CACHE_FAST 1043 +//#define IDC_DISABLEMIPS 1100 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1044 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/resource.rc b/Source/Plugins/Plugin_VideoDX11/Src/resource.rc new file mode 100644 index 0000000000..539bfb2945 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/resource.rc @@ -0,0 +1,167 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOGEX 0, 0, 188, 81 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_BORDER | WS_SYSMENU +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Created by neobrain.",IDC_STATIC,7,7,85,9 + LTEXT "Licensed under the terms of the GNU General Public License.",IDC_STATIC,7,17,85,9 + LTEXT "Hardware requirements:",IDC_STATIC,7,37,174,26 + LTEXT "Any GPU which supports at least DX10.",IDC_STATIC,15,47,174,26 + LTEXT "DX9 hardware might work as well though.",IDC_STATIC,15,57,170,8 +END + +IDD_SETTINGS DIALOGEX 0, 0, 244, 183 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_BORDER | WS_SYSMENU +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "&V-Sync",IDC_VSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61,10,36,8 + CONTROL "&Widescreen Hack",IDC_WIDESCREEN_HACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,161,10,67,10 + LTEXT "&Aspect Ratio:",IDC_STATIC,9,25,48,8 + COMBOBOX IDC_ASPECTRATIO,60,23,89,57,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "&Enable CPU->EFB access ",IDC_EFB_ACCESS_ENABLE,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,7,45,94,11 + CONTROL "Enable &Safe Texture Cache",IDC_SAFE_TEXTURE_CACHE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,59,108,11 + CONTROL "Safe",IDC_SAFE_TEXTURE_CACHE_SAFE,"Button",BS_AUTORADIOBUTTON,20,72,32,10 + CONTROL "Normal",IDC_SAFE_TEXTURE_CACHE_NORMAL,"Button",BS_AUTORADIOBUTTON,52,72,40,10 + CONTROL "Fast",IDC_SAFE_TEXTURE_CACHE_FAST,"Button",BS_AUTORADIOBUTTON,92,72,32,10 +END + +IDD_ADVANCED DIALOGEX 0, 0, 244, 200 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_BORDER | WS_SYSMENU +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "&Settings",IDC_STATIC,6,7,228,74 + CONTROL "Overlay FPS counter",IDC_OVERLAYFPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,18,82,8 + CONTROL "Disable Fog",IDC_DISABLEFOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,78,8 + CONTROL "Enable Hotkey",IDC_OSDHOTKEY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,33,87,8 + CONTROL "Enable EFB copy",IDC_ENABLEEFBCOPY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,33,81,8 + CONTROL "EFB Scaled Copy",IDC_EFBSCALEDCOPY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,48,64,8 + CONTROL "Enable &Wireframe",IDC_WIREFRAME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,48,87,8 + CONTROL "Enable 16x &anisotropy filtering",IDC_FORCEANISOTROPY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,63,110,8 + + GROUPBOX "Debugging Tools",IDC_STATIC,7,148,228,46 + CONTROL "&Overlay some statistics",IDC_OVERLAYSTATS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,159,90,8 + CONTROL "Enable TexFmt Overlay",IDC_TEXFMT_OVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,174,92,10 + CONTROL "Centered",IDC_TEXFMT_CENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,174,52,10 +END + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 74 + END + + IDD_SETTINGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 237 + VERTGUIDE, 7 + VERTGUIDE, 68 + VERTGUIDE, 81 + VERTGUIDE, 87 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_ADVANCED, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 237 + VERTGUIDE, 14 + VERTGUIDE, 29 + VERTGUIDE, 114 + TOPMARGIN, 7 + BOTTOMMARGIN, 195 + HORZGUIDE, 18 + HORZGUIDE, 33 + HORZGUIDE, 49 + HORZGUIDE, 156 + END + + IDD_ENHANCEMENTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 217 + VERTGUIDE, 16 + VERTGUIDE, 74 + TOPMARGIN, 7 + BOTTOMMARGIN, 168 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include +#include