diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index d8cfb12e59..5c5e253888 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -453,7 +453,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con { wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, 5, 5); - const wxString stereo_choices[] = { "Off", "Side-by-Side", "Top-and-Bottom", "Anaglyph" }; + const wxString stereo_choices[] = { "Off", "Side-by-Side", "Top-and-Bottom", vconfig.backend_info.bSupports3DVision ? "Nvidia 3D Vision" : "Anaglyph" }; szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Stereoscopic 3D Mode:")), 1, wxALIGN_CENTER_VERTICAL, 0); szr_stereo->Add(CreateChoice(page_enh, vconfig.iStereoMode, wxGetTranslation(stereo_3d_desc), 4, stereo_choices)); diff --git a/Source/Core/VideoBackends/D3D/D3DTexture.cpp b/Source/Core/VideoBackends/D3D/D3DTexture.cpp index a24a54bc2a..b60738c286 100644 --- a/Source/Core/VideoBackends/D3D/D3DTexture.cpp +++ b/Source/Core/VideoBackends/D3D/D3DTexture.cpp @@ -37,7 +37,7 @@ void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned } // namespace -D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt, unsigned int levels, unsigned int slices) +D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT fmt, unsigned int levels, unsigned int slices, D3D11_SUBRESOURCE_DATA* data) { ID3D11Texture2D* pTexture = nullptr; HRESULT hr; @@ -50,7 +50,7 @@ D3DTexture2D* D3DTexture2D::Create(unsigned int width, unsigned int height, D3D1 else cpuflags = (D3D11_CPU_ACCESS_FLAG)0; D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(fmt, width, height, slices, levels, bind, usage, cpuflags); - hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); + hr = D3D::device->CreateTexture2D(&texdesc, data, &pTexture); if (FAILED(hr)) { PanicAlert("Failed to create texture at %s, line %d: hr=%#x\n", __FILE__, __LINE__, hr); diff --git a/Source/Core/VideoBackends/D3D/D3DTexture.h b/Source/Core/VideoBackends/D3D/D3DTexture.h index e865b0451d..6be15b6102 100644 --- a/Source/Core/VideoBackends/D3D/D3DTexture.h +++ b/Source/Core/VideoBackends/D3D/D3DTexture.h @@ -22,7 +22,7 @@ public: // 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, bool multisampled = false); - static D3DTexture2D* Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT, unsigned int levels = 1, unsigned int slices = 1); + static D3DTexture2D* Create(unsigned int width, unsigned int height, D3D11_BIND_FLAG bind, D3D11_USAGE usage, DXGI_FORMAT, unsigned int levels = 1, unsigned int slices = 1, D3D11_SUBRESOURCE_DATA* data = nullptr); // reference counting, use AddRef() when creating a new reference and Release() it when you don't need it anymore void AddRef(); diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 7593d2810f..a76b14e29c 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -55,6 +55,18 @@ ID3D11DepthStencilState* resetdepthstate = nullptr; ID3D11RasterizerState* resetraststate = nullptr; static ID3D11Texture2D* s_screenshot_texture = nullptr; +static D3DTexture2D* s_3d_vision_texture = nullptr; + +typedef struct _Nv_Stereo_Image_Header +{ + unsigned int dwSignature; + unsigned int dwWidth; + unsigned int dwHeight; + unsigned int dwBPP; + unsigned int dwFlags; +} NVSTEREOIMAGEHEADER, *LPNVSTEREOIMAGEHEADER; + +#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e // GX pipeline state struct @@ -183,6 +195,24 @@ void CreateScreenshotTexture(const TargetRectangle& rc) D3D::SetDebugObjectName((ID3D11DeviceChild*)s_screenshot_texture, "staging screenshot texture"); } +void Create3DVisionTexture(int width, int height) +{ + // Create a staging texture for 3D vision with signature information in the last row. + // Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process. + D3D11_SUBRESOURCE_DATA sysData; + sysData.SysMemPitch = 4 * width * 2; + sysData.pSysMem = new u8[(height + 1) * sysData.SysMemPitch]; + LPNVSTEREOIMAGEHEADER header = (LPNVSTEREOIMAGEHEADER)((u8*)sysData.pSysMem + height * sysData.SysMemPitch); + header->dwSignature = NVSTEREO_IMAGE_SIGNATURE; + header->dwWidth = width * 2; + header->dwHeight = height + 1; + header->dwBPP = 32; + header->dwFlags = 0; + + s_3d_vision_texture = D3DTexture2D::Create(width * 2, height + 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sysData); + delete[] sysData.pSysMem; +} + Renderer::Renderer(void *&window_handle) { D3D::Create((HWND)window_handle); @@ -776,6 +806,30 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co D3D::context->RSSetViewports(1, &rightVp); D3D::drawShadedTexQuad(read_texture->GetSRV(), targetRc.AsRECT(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), Gamma, 1); } + else if (g_ActiveConfig.iStereoMode == STEREO_3DVISION) + { + if (!s_3d_vision_texture) + Create3DVisionTexture(s_backbuffer_width, s_backbuffer_height); + + D3D11_VIEWPORT leftVp = CD3D11_VIEWPORT((float)X, (float)Y, (float)Width, (float)Height); + D3D11_VIEWPORT rightVp = CD3D11_VIEWPORT((float)(X + s_backbuffer_width), (float)Y, (float)Width, (float)Height); + + // Render to staging texture which is double the width of the backbuffer + D3D::context->OMSetRenderTargets(1, &s_3d_vision_texture->GetRTV(), nullptr); + + D3D::context->RSSetViewports(1, &leftVp); + D3D::drawShadedTexQuad(read_texture->GetSRV(), targetRc.AsRECT(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), Gamma, 0); + + D3D::context->RSSetViewports(1, &rightVp); + D3D::drawShadedTexQuad(read_texture->GetSRV(), targetRc.AsRECT(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), Gamma, 1); + + // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should + // recognize the signature and automatically include the right eye frame. + D3D11_BOX box = CD3D11_BOX(0, 0, 0, s_backbuffer_width, s_backbuffer_height, 1); + D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0, s_3d_vision_texture->GetTex(), 0, &box); + + D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr); + } else { D3D::drawShadedTexQuad(read_texture->GetSRV(), targetRc.AsRECT(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight(), PixelShaderCache::GetColorCopyProgram(false), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), Gamma); @@ -972,6 +1026,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co // TODO: Aren't we still holding a reference to the back buffer right now? D3D::Reset(); SAFE_RELEASE(s_screenshot_texture); + SAFE_RELEASE(s_3d_vision_texture); s_backbuffer_width = D3D::GetBackBufferWidth(); s_backbuffer_height = D3D::GetBackBufferHeight(); } diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index d5ce0f8dab..5a371396d2 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -80,6 +80,7 @@ void InitBackendInfo() g_Config.backend_info.bSupportsPrimitiveRestart = true; g_Config.backend_info.bSupportsOversizedViewports = false; g_Config.backend_info.bSupportsStereoscopy = true; + g_Config.backend_info.bSupports3DVision = true; g_Config.backend_info.bSupportsGSInstancing = false; IDXGIFactory* factory; diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 0fb36ab8d1..eb486c811b 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -139,6 +139,7 @@ static void InitBackendInfo() //g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer g_Config.backend_info.bSupportsOversizedViewports = true; g_Config.backend_info.bSupportsStereoscopy = true; + g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.Adapters.clear(); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index c825f8ec53..879c0726a9 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -49,7 +49,8 @@ enum StereoMode STEREO_OFF = 0, STEREO_SBS, STEREO_TAB, - STEREO_ANAGLYPH + STEREO_ANAGLYPH, + STEREO_3DVISION = STEREO_ANAGLYPH }; // NEVER inherit from this class. @@ -151,6 +152,7 @@ struct VideoConfig final bool bSupportsPrimitiveRestart; bool bSupportsOversizedViewports; bool bSupportsStereoscopy; + bool bSupports3DVision; bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon bool bSupportsBBox;