From 6e348a27315347870791851522bf04562cddee9f Mon Sep 17 00:00:00 2001 From: Nolan Check Date: Tue, 8 Mar 2011 07:39:36 +0000 Subject: [PATCH] DX11: Add real XFB mode support. May not work perfectly yet. I'm working on a set of fairly big VI-related changes. When I'm done, it should improve animation smoothness for all the backends, especially when virtual or real XFB mode is enabled. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7319 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Common/Src/MathUtil.h | 9 +- .../Plugin_VideoDX11/Plugin_VideoDX11.vcxproj | 4 + .../Plugin_VideoDX11.vcxproj.filters | 12 + .../Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp | 8 +- .../Src/FramebufferManager.cpp | 16 +- .../Plugins/Plugin_VideoDX11/Src/Render.cpp | 25 +- .../Plugin_VideoDX11/Src/Television.cpp | 154 +++++++ .../Plugins/Plugin_VideoDX11/Src/Television.h | 65 +++ .../Plugin_VideoDX11/Src/XFBEncoder.cpp | 387 ++++++++++++++++++ .../Plugins/Plugin_VideoDX11/Src/XFBEncoder.h | 68 +++ Source/Plugins/Plugin_VideoDX11/Src/main.cpp | 2 +- 11 files changed, 734 insertions(+), 16 deletions(-) create mode 100644 Source/Plugins/Plugin_VideoDX11/Src/Television.cpp create mode 100644 Source/Plugins/Plugin_VideoDX11/Src/Television.h create mode 100644 Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.cpp create mode 100644 Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.h diff --git a/Source/Core/Common/Src/MathUtil.h b/Source/Core/Common/Src/MathUtil.h index da56becc42..d10540d45f 100644 --- a/Source/Core/Common/Src/MathUtil.h +++ b/Source/Core/Common/Src/MathUtil.h @@ -111,6 +111,13 @@ struct Rectangle T right; T bottom; + Rectangle() + { } + + Rectangle(T theLeft, T theTop, T theRight, T theBottom) + : left(theLeft), top(theTop), right(theRight), bottom(theBottom) + { } + T GetWidth() const { return abs(right - left); } T GetHeight() const { return abs(bottom - top); } @@ -124,7 +131,7 @@ struct Rectangle if (bottom < y2) bottom = y2; } - // If the rectangle is in an coordinate system with an upper-left origin, + // If the rectangle is in a coordinate system with an upper-left origin, // use this Clamp. void ClampUL(T x1, T y1, T x2, T y2) { diff --git a/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj index b45cc18cb5..82601ff250 100644 --- a/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj +++ b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj @@ -210,9 +210,11 @@ Create Create + + @@ -228,11 +230,13 @@ + + diff --git a/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj.filters b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj.filters index e78f8e53dc..0feb3c7dd0 100644 --- a/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj.filters +++ b/Source/Plugins/Plugin_VideoDX11/Plugin_VideoDX11.vcxproj.filters @@ -45,6 +45,12 @@ Render + + Render + + + Render + @@ -93,6 +99,12 @@ Render + + Render + + + Render + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp index 995a9e9a02..81a4cf96a6 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/D3DUtil.cpp @@ -587,10 +587,10 @@ void drawShadedTexSubQuad(ID3D11ShaderResourceView* texture, float G = 1.0f / Gamma; STSQVertex coords[4] = { - { rDest->left , rDest->bottom, 0.0f, u1, v1, G}, - { rDest->right, rDest->bottom, 0.0f, u2, v1, G}, - { rDest->left , rDest->top , 0.0f, u1, v2, G}, - { rDest->right, rDest->top , 0.0f, u2, v2, G}, + { rDest->left , rDest->bottom, 0.0f, u1, v2, G}, + { rDest->right, rDest->bottom, 0.0f, u2, v2, G}, + { rDest->left , rDest->top , 0.0f, u1, v1, G}, + { rDest->right, rDest->top , 0.0f, u2, v1, G}, }; // only upload the data to VRAM if it changed diff --git a/Source/Plugins/Plugin_VideoDX11/Src/FramebufferManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/FramebufferManager.cpp index 35f6387fa0..ca00d9e74a 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/FramebufferManager.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/FramebufferManager.cpp @@ -23,9 +23,13 @@ #include "PixelShaderCache.h" #include "Render.h" #include "VertexShaderCache.h" +#include "XFBEncoder.h" +#include "HW/Memmap.h" namespace DX11 { +static XFBEncoder s_xfbEncoder; + FramebufferManager::Efb FramebufferManager::m_efb; D3DTexture2D* &FramebufferManager::GetEFBColorTexture() { return m_efb.color_tex; } @@ -144,10 +148,14 @@ FramebufferManager::FramebufferManager() m_efb.resolved_color_tex = NULL; m_efb.resolved_depth_tex = NULL; } + + s_xfbEncoder.Init(); } FramebufferManager::~FramebufferManager() { + s_xfbEncoder.Shutdown(); + SAFE_RELEASE(m_efb.color_tex); SAFE_RELEASE(m_efb.color_temp_tex); SAFE_RELEASE(m_efb.color_staging_buf); @@ -160,8 +168,8 @@ FramebufferManager::~FramebufferManager() void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) { - // TODO - PanicAlert("CopyToRealXFB not implemented, yet\n"); + u8* dst = Memory::GetPointer(xfbAddr); + s_xfbEncoder.Encode(dst, fbWidth, fbHeight, sourceRc, Gamma); } XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height) @@ -197,8 +205,8 @@ void XFBSource::Draw(const MathUtil::Rectangle &sourcerc, void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) { - // TODO: - PanicAlert("RealXFB not implemented, yet\n"); + // DX11's XFB decoder does not use this function. + // YUYV data is decoded in Render::Swap. } void XFBSource::CopyEFB(float Gamma) diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp index e642527bd9..e2d990e87c 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp @@ -40,6 +40,7 @@ #include "VertexShaderCache.h" #include "Core.h" #include "OnFrame.h" +#include "Television.h" namespace DX11 { @@ -50,6 +51,8 @@ static u32 s_LastAA = 0; static u32 s_blendMode; +static Television s_television; + ID3D11Buffer* access_efb_cbuf = NULL; ID3D11BlendState* clearblendstates[4] = {NULL}; ID3D11DepthStencilState* cleardepthstates[3] = {NULL}; @@ -213,6 +216,8 @@ static const D3D11_TEXTURE_ADDRESS_MODE d3dClamps[4] = void SetupDeviceObjects() { + s_television.Init(); + g_framebuffer_manager = new FramebufferManager; HRESULT hr; @@ -307,6 +312,8 @@ void TeardownDeviceObjects() SAFE_RELEASE(resetblendstate); SAFE_RELEASE(resetdepthstate); SAFE_RELEASE(resetraststate); + + s_television.Shutdown(); } Renderer::Renderer() @@ -930,7 +937,6 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons // 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 XFBSourceBase* const* xfbSourceList = FramebufferManager::GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB) @@ -969,7 +975,13 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons // activate linear filtering for the buffer copies D3D::SetLinearCopySampler(); - if(g_ActiveConfig.bUseXFB) + if (g_ActiveConfig.bUseXFB && g_ActiveConfig.bUseRealXFB) + { + // TODO: Television should be used to render Virtual XFB mode as well. + s_television.Submit(xfbAddr, fbWidth, fbHeight); + s_television.Render(); + } + else if(g_ActiveConfig.bUseXFB) { const XFBSourceBase* xfbSource; @@ -993,8 +1005,8 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons int xfbWidth = xfbSource->srcWidth; int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2); - drawRc.bottom = 1.0f - (2.0f * (hOffset) / (float)fbHeight); - drawRc.top = 1.0f - (2.0f * (hOffset + xfbHeight) / (float)fbHeight); + drawRc.top = 1.0f - (2.0f * (hOffset) / (float)fbHeight); + drawRc.bottom = 1.0f - (2.0f * (hOffset + xfbHeight) / (float)fbHeight); drawRc.left = -(xfbWidth / (float)fbWidth); drawRc.right = (xfbWidth / (float)fbWidth); @@ -1009,8 +1021,8 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons } else { - drawRc.top = -1; - drawRc.bottom = 1; + drawRc.top = 1; + drawRc.bottom = -1; drawRc.left = -1; drawRc.right = 1; } @@ -1026,6 +1038,7 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons D3DTexture2D* read_texture = FramebufferManager::GetResolvedEFBColorTexture(); D3D::drawShadedTexQuad(read_texture->GetSRV(), targetRc.AsRECT(), Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), PixelShaderCache::GetColorCopyProgram(false),VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(), Gamma); } + // done with drawing the game stuff, good moment to save a screenshot if (s_bScreenshot) { diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Television.cpp b/Source/Plugins/Plugin_VideoDX11/Src/Television.cpp new file mode 100644 index 0000000000..4fbc3be3d4 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/Television.cpp @@ -0,0 +1,154 @@ +// 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 "Television.h" + +#include "VideoConfig.h" +#include "D3DBase.h" +#include "D3DShader.h" +#include "D3DUtil.h" +#include "VertexShaderCache.h" +#include "HW/Memmap.h" + +namespace DX11 +{ + +static const char YUYV_DECODER_PS[] = +"// dolphin-emu YUYV decoder pixel shader\n" + +"Texture2D Tex0 : register(t0);\n" +"sampler Samp0 : register(s0);\n" + +"static const float3x3 YCBCR_TO_RGB = float3x3(\n" + "1.164, 0.000, 1.596,\n" + "1.164, -0.392, -0.813,\n" + "1.164, 2.017, 0.000\n" + ");\n" + +"void main(out float4 ocol0 : SV_Target, in float4 pos : SV_Position, in float2 uv0 : TEXCOORD0)\n" +"{\n" + "float3 sample = Tex0.Sample(Samp0, uv0).rgb;\n" + + // GameCube/Wii XFB data is in YUYV format with ITU-R Rec. BT.601 color + // primaries, compressed to the range Y in 16..235, U and V in 16..240. + // We want to convert it to RGB format with sRGB color primaries, with + // range 0..255. + + // Recover RGB components + "float3 yuv_601_sub = sample.grb - float3(16.0/255.0, 128.0/255.0, 128.0/255.0);\n" + "float3 rgb_601 = mul(YCBCR_TO_RGB, yuv_601_sub);\n" + + // If we were really obsessed with accuracy, we would correct for the + // differing color primaries between BT.601 and sRGB. However, this may not + // be worth the trouble because: + // - BT.601 defines two sets of primaries: one for NTSC and one for PAL. + // - sRGB's color primaries are actually an intermediate between BT.601's + // NTSC and PAL primaries. + // - If users even noticed any difference at all, they would be confused by + // the slightly-different colors in the NTSC and PAL versions of the same + // game. + // - Even the game designers probably don't pay close attention to this + // stuff. + // Still, instructions on how to do it can be found at + // + + "ocol0 = float4(rgb_601, 1);\n" +"}\n" +; + +Television::Television() + : m_yuyvTexture(NULL), m_yuyvTextureSRV(NULL), m_pShader(NULL) +{ } + +void Television::Init() +{ + HRESULT hr; + + // Create YUYV texture for real XFB mode + + // This texture format is designed for YUYV data. + D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC( + DXGI_FORMAT_G8R8_G8B8_UNORM, MAX_XFB_WIDTH, MAX_XFB_HEIGHT, 1, 1); + hr = D3D::device->CreateTexture2D(&t2dd, NULL, &m_yuyvTexture); + CHECK(SUCCEEDED(hr), "create tv yuyv texture"); + D3D::SetDebugObjectName(m_yuyvTexture, "tv yuyv texture"); + + // Create shader resource view for YUYV texture + + D3D11_SHADER_RESOURCE_VIEW_DESC srvd = CD3D11_SHADER_RESOURCE_VIEW_DESC( + m_yuyvTexture, D3D11_SRV_DIMENSION_TEXTURE2D, + DXGI_FORMAT_G8R8_G8B8_UNORM); + hr = D3D::device->CreateShaderResourceView(m_yuyvTexture, &srvd, &m_yuyvTextureSRV); + CHECK(SUCCEEDED(hr), "create tv yuyv texture srv"); + D3D::SetDebugObjectName(m_yuyvTextureSRV, "tv yuyv texture srv"); + + // Create YUYV-decoding pixel shader + + m_pShader = D3D::CompileAndCreatePixelShader(YUYV_DECODER_PS, sizeof(YUYV_DECODER_PS)); + CHECK(m_pShader != NULL, "compile and create yuyv decoder pixel shader"); + D3D::SetDebugObjectName(m_pShader, "yuyv decoder pixel shader"); +} + +void Television::Shutdown() +{ + SAFE_RELEASE(m_pShader); + SAFE_RELEASE(m_yuyvTextureSRV); + SAFE_RELEASE(m_yuyvTexture); +} + +void Television::Submit(u32 xfbAddr, u32 width, u32 height) +{ + m_curAddr = xfbAddr; + m_curWidth = width; + m_curHeight = height; + + // Load data from GameCube RAM to YUYV texture + u8* yuyvSrc = Memory::GetPointer(xfbAddr); + D3D11_BOX box = CD3D11_BOX(0, 0, 0, width, height, 1); + D3D::context->UpdateSubresource(m_yuyvTexture, 0, &box, yuyvSrc, 2*width, 2*width*height); +} + +void Television::Render() +{ + if (g_ActiveConfig.bUseRealXFB && g_ActiveConfig.bUseXFB) + { + // Use real XFB mode + // TODO: If this is the lower field, render at a vertical offset of 1 + // line down. We could even consider implementing a deinterlacing + // algorithm. + + MathUtil::Rectangle sourceRc(0.f, 0.f, float(m_curWidth), float(m_curHeight)); + MathUtil::Rectangle destRc(-1.f, 1.f, 1.f, -1.f); + + D3D::drawShadedTexSubQuad( + m_yuyvTextureSRV, &sourceRc, + MAX_XFB_WIDTH, MAX_XFB_HEIGHT, + &destRc, + m_pShader, + VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout()); + } + else if (g_ActiveConfig.bUseXFB) + { + // Use virtual XFB mode + + // TODO: Eventually, Television should render the Virtual XFB mode + // display as well. + } +} + +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Television.h b/Source/Plugins/Plugin_VideoDX11/Src/Television.h new file mode 100644 index 0000000000..4b921223ec --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/Television.h @@ -0,0 +1,65 @@ +// 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 _TELEVISION_H +#define _TELEVISION_H + +#include "VideoCommon.h" + +struct ID3D11Texture2D; +struct ID3D11ShaderResourceView; +struct ID3D11PixelShader; + +namespace DX11 +{ + +class Television +{ + +public: + + Television(); + + void Init(); + void Shutdown(); + + // Submit video data to be drawn. This will change the current state of the + // TV. xfbAddr points to YUYV data stored in GameCube/Wii RAM, but the XFB + // may be virtualized when rendering so the RAM may not actually be read. + void Submit(u32 xfbAddr, u32 width, u32 height); + + // Render the current state of the TV. + void Render(); + +private: + + // Properties of last Submit call + u32 m_curAddr; + u32 m_curWidth; + u32 m_curHeight; + + // Used for real XFB mode + + ID3D11Texture2D* m_yuyvTexture; + ID3D11ShaderResourceView* m_yuyvTextureSRV; + ID3D11PixelShader* m_pShader; + +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.cpp b/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.cpp new file mode 100644 index 0000000000..cedad0a880 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.cpp @@ -0,0 +1,387 @@ +// 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 "XFBEncoder.h" + +#include "D3DBase.h" +#include "D3DBlob.h" +#include "D3DShader.h" +#include "Render.h" +#include "GfxState.h" +#include "FramebufferManager.h" + +namespace DX11 +{ + +union XFBEncodeParams +{ + struct + { + FLOAT Width; // Width and height of encoded XFB in luma pixels + FLOAT Height; + FLOAT TexLeft; // Normalized tex coordinates of XFB source area in EFB texture + FLOAT TexTop; + FLOAT TexRight; + FLOAT TexBottom; + FLOAT Gamma; + }; + // Constant buffers must be a multiple of 16 bytes in size + u8 pad[32]; // Pad to the next multiple of 16 +}; + +static const char XFB_ENCODE_VS[] = +"// dolphin-emu XFB encoder vertex shader\n" + +"cbuffer cbParams : register(b0)\n" +"{\n" + "struct\n" // Should match XFBEncodeParams above + "{\n" + "float Width;\n" + "float Height;\n" + "float TexLeft;\n" + "float TexTop;\n" + "float TexRight;\n" + "float TexBottom;\n" + "float Gamma;\n" + "} Params;\n" +"}\n" + +"struct Output\n" +"{\n" + "float4 Pos : SV_Position;\n" + "float2 Coord : ENCODECOORD;\n" +"};\n" + +"Output main(in float2 Pos : POSITION)\n" +"{\n" + "Output result;\n" + "result.Pos = float4(2*Pos.x-1, -2*Pos.y+1, 0, 1);\n" + "result.Coord = Pos * float2(floor(Params.Width/2), Params.Height);\n" + "return result;\n" +"}\n" +; + +static const char XFB_ENCODE_PS[] = +"// dolphin-emu XFB encoder pixel shader\n" + +"cbuffer cbParams : register(b0)\n" +"{\n" + "struct\n" // Should match XFBEncodeParams above + "{\n" + "float Width;\n" + "float Height;\n" + "float TexLeft;\n" + "float TexTop;\n" + "float TexRight;\n" + "float TexBottom;\n" + "float Gamma;\n" + "} Params;\n" +"}\n" + +"Texture2D EFBTexture : register(t0);\n" +"sampler EFBSampler : register(s0);\n" + +// GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see +// +"static const float3x4 RGB_TO_YCBCR = float3x4(\n" + "0.257, 0.504, 0.098, 16.0/255.0,\n" + "-0.148, -0.291, 0.439, 128.0/255.0,\n" + "0.439, -0.368, -0.071, 128.0/255.0\n" + ");\n" + +"float3 SampleEFB(float2 coord)\n" +"{\n" + "float2 texCoord = lerp(float2(Params.TexLeft,Params.TexTop), float2(Params.TexRight,Params.TexBottom), coord / float2(Params.Width,Params.Height));\n" + "return EFBTexture.Sample(EFBSampler, texCoord).rgb;\n" +"}\n" + +"void main(out float4 ocol0 : SV_Target, in float4 Pos : SV_Position, in float2 Coord : ENCODECOORD)\n" +"{\n" + "float2 baseCoord = Coord * float2(2,1);\n" + // FIXME: Shall we apply gamma here, or apply it below to the Y components? + // Be careful if you apply it to Y! The Y components are in the range (16..235) / 255. + "float3 sampleL = pow(abs(SampleEFB(baseCoord+float2(-1,0))), Params.Gamma);\n" // Left + "float3 sampleM = pow(abs(SampleEFB(baseCoord)), Params.Gamma);\n" // Middle + "float3 sampleR = pow(abs(SampleEFB(baseCoord+float2(1,0))), Params.Gamma);\n" // Right + + "float3 yuvL = mul(RGB_TO_YCBCR, float4(sampleL,1));\n" + "float3 yuvM = mul(RGB_TO_YCBCR, float4(sampleM,1));\n" + "float3 yuvR = mul(RGB_TO_YCBCR, float4(sampleR,1));\n" + + // The Y components correspond to two EFB pixels, while the U and V are + // made from a blend of three EFB pixels. + "float y0 = yuvM.r;\n" + "float y1 = yuvR.r;\n" + "float u0 = 0.25*yuvL.g + 0.5*yuvM.g + 0.25*yuvR.g;\n" + "float v0 = 0.25*yuvL.b + 0.5*yuvM.b + 0.25*yuvR.b;\n" + + "ocol0 = float4(y0, u0, y1, v0);\n" +"}\n" +; + +static const D3D11_INPUT_ELEMENT_DESC QUAD_LAYOUT_DESC[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 } +}; + +static const struct QuadVertex +{ + float posX; + float posY; +} QUAD_VERTS[4] = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }; + +XFBEncoder::XFBEncoder() + : m_out(NULL), m_outRTV(NULL), m_outStage(NULL), m_encodeParams(NULL), + m_quad(NULL), m_vShader(NULL), m_quadLayout(NULL), m_pShader(NULL), + m_xfbEncodeBlendState(NULL), m_xfbEncodeDepthState(NULL), + m_xfbEncodeRastState(NULL), m_efbSampler(NULL) +{ } + +void XFBEncoder::Init() +{ + HRESULT hr; + + // Create output texture + + // The pixel shader can generate one YUYV entry per pixel. One YUYV entry + // is created for every two EFB pixels. + D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC( + DXGI_FORMAT_R8G8B8A8_UNORM, MAX_XFB_WIDTH/2, MAX_XFB_HEIGHT, 1, 1, + D3D11_BIND_RENDER_TARGET); + hr = D3D::device->CreateTexture2D(&t2dd, NULL, &m_out); + CHECK(SUCCEEDED(hr), "create xfb encoder output texture"); + D3D::SetDebugObjectName(m_out, "xfb encoder output texture"); + + // Create output render target view + + D3D11_RENDER_TARGET_VIEW_DESC rtvd = CD3D11_RENDER_TARGET_VIEW_DESC(m_out, + D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM); + hr = D3D::device->CreateRenderTargetView(m_out, &rtvd, &m_outRTV); + CHECK(SUCCEEDED(hr), "create xfb encoder output texture rtv"); + D3D::SetDebugObjectName(m_outRTV, "xfb encoder output rtv"); + + // Create output staging buffer + + t2dd.Usage = D3D11_USAGE_STAGING; + t2dd.BindFlags = 0; + t2dd.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + hr = D3D::device->CreateTexture2D(&t2dd, NULL, &m_outStage); + CHECK(SUCCEEDED(hr), "create xfb encoder output staging buffer"); + D3D::SetDebugObjectName(m_outStage, "xfb encoder output staging buffer"); + + // Create constant buffer for uploading params to shaders + + D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(XFBEncodeParams), + D3D11_BIND_CONSTANT_BUFFER); + hr = D3D::device->CreateBuffer(&bd, NULL, &m_encodeParams); + CHECK(SUCCEEDED(hr), "create xfb encode params buffer"); + D3D::SetDebugObjectName(m_encodeParams, "xfb encoder params buffer"); + + // Create vertex quad + + bd = CD3D11_BUFFER_DESC(sizeof(QUAD_VERTS), D3D11_BIND_VERTEX_BUFFER, + D3D11_USAGE_IMMUTABLE); + D3D11_SUBRESOURCE_DATA srd = { QUAD_VERTS, 0, 0 }; + + hr = D3D::device->CreateBuffer(&bd, &srd, &m_quad); + CHECK(SUCCEEDED(hr), "create xfb encode quad vertex buffer"); + D3D::SetDebugObjectName(m_quad, "xfb encoder quad vertex buffer"); + + // Create vertex shader + + D3DBlob* bytecode = NULL; + if (!D3D::CompileVertexShader(XFB_ENCODE_VS, sizeof(XFB_ENCODE_VS), &bytecode)) + { + ERROR_LOG(VIDEO, "XFB encode vertex shader failed to compile"); + return; + } + + hr = D3D::device->CreateVertexShader(bytecode->Data(), bytecode->Size(), NULL, &m_vShader); + CHECK(SUCCEEDED(hr), "create xfb encode vertex shader"); + D3D::SetDebugObjectName(m_vShader, "xfb encoder vertex shader"); + + // Create input layout for vertex quad using bytecode from vertex shader + + hr = D3D::device->CreateInputLayout(QUAD_LAYOUT_DESC, + sizeof(QUAD_LAYOUT_DESC)/sizeof(D3D11_INPUT_ELEMENT_DESC), + bytecode->Data(), bytecode->Size(), &m_quadLayout); + CHECK(SUCCEEDED(hr), "create xfb encode quad vertex layout"); + D3D::SetDebugObjectName(m_quadLayout, "xfb encoder quad layout"); + + bytecode->Release(); + + // Create pixel shader + + m_pShader = D3D::CompileAndCreatePixelShader(XFB_ENCODE_PS, sizeof(XFB_ENCODE_PS)); + if (!m_pShader) + { + ERROR_LOG(VIDEO, "XFB encode pixel shader failed to compile"); + return; + } + D3D::SetDebugObjectName(m_pShader, "xfb encoder pixel shader"); + + // Create blend state + + D3D11_BLEND_DESC bld = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + hr = D3D::device->CreateBlendState(&bld, &m_xfbEncodeBlendState); + CHECK(SUCCEEDED(hr), "create xfb encode blend state"); + D3D::SetDebugObjectName(m_xfbEncodeBlendState, "xfb encoder blend state"); + + // Create depth state + + D3D11_DEPTH_STENCIL_DESC dsd = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT()); + dsd.DepthEnable = FALSE; + hr = D3D::device->CreateDepthStencilState(&dsd, &m_xfbEncodeDepthState); + CHECK(SUCCEEDED(hr), "create xfb encode depth state"); + D3D::SetDebugObjectName(m_xfbEncodeDepthState, "xfb encoder depth state"); + + // Create rasterizer state + + D3D11_RASTERIZER_DESC rd = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); + rd.CullMode = D3D11_CULL_NONE; + rd.DepthClipEnable = FALSE; + hr = D3D::device->CreateRasterizerState(&rd, &m_xfbEncodeRastState); + CHECK(SUCCEEDED(hr), "create xfb encode rasterizer state"); + D3D::SetDebugObjectName(m_xfbEncodeRastState, "xfb encoder rast state"); + + // Create EFB texture sampler + + D3D11_SAMPLER_DESC sd = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + // FIXME: Should we really use point sampling here? + sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + hr = D3D::device->CreateSamplerState(&sd, &m_efbSampler); + CHECK(SUCCEEDED(hr), "create xfb encode texture sampler"); + D3D::SetDebugObjectName(m_efbSampler, "xfb encoder texture sampler"); +} + +void XFBEncoder::Shutdown() +{ + SAFE_RELEASE(m_efbSampler); + SAFE_RELEASE(m_xfbEncodeRastState); + SAFE_RELEASE(m_xfbEncodeDepthState); + SAFE_RELEASE(m_xfbEncodeBlendState); + SAFE_RELEASE(m_pShader); + SAFE_RELEASE(m_quadLayout); + SAFE_RELEASE(m_vShader); + SAFE_RELEASE(m_quad); + SAFE_RELEASE(m_encodeParams); + SAFE_RELEASE(m_outStage); + SAFE_RELEASE(m_outRTV); + SAFE_RELEASE(m_out); +} + +void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma) +{ + HRESULT hr; + + // Reset API + + g_renderer->ResetAPIState(); + + // Set up all the state for XFB encoding + + D3D::context->PSSetShader(m_pShader, NULL, 0); + D3D::context->VSSetShader(m_vShader, NULL, 0); + + D3D::stateman->PushBlendState(m_xfbEncodeBlendState); + D3D::stateman->PushDepthState(m_xfbEncodeDepthState); + D3D::stateman->PushRasterizerState(m_xfbEncodeRastState); + D3D::stateman->Apply(); + + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(width/2), FLOAT(height)); + D3D::context->RSSetViewports(1, &vp); + + D3D::context->IASetInputLayout(m_quadLayout); + D3D::context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + UINT stride = sizeof(QuadVertex); + UINT offset = 0; + D3D::context->IASetVertexBuffers(0, 1, &m_quad, &stride, &offset); + + TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(srcRect); + + XFBEncodeParams params = { 0 }; + params.Width = FLOAT(width); + params.Height = FLOAT(height); + params.TexLeft = FLOAT(targetRect.left) / g_renderer->GetFullTargetWidth(); + params.TexTop = FLOAT(targetRect.top) / g_renderer->GetFullTargetHeight(); + params.TexRight = FLOAT(targetRect.right) / g_renderer->GetFullTargetWidth(); + params.TexBottom = FLOAT(targetRect.bottom) / g_renderer->GetFullTargetHeight(); + params.Gamma = gamma; + D3D::context->UpdateSubresource(m_encodeParams, 0, NULL, ¶ms, 0, 0); + + D3D::context->VSSetConstantBuffers(0, 1, &m_encodeParams); + + D3D::context->OMSetRenderTargets(1, &m_outRTV, NULL); + + ID3D11ShaderResourceView* pEFB = FramebufferManager::GetEFBColorTexture()->GetSRV(); + + D3D::context->PSSetConstantBuffers(0, 1, &m_encodeParams); + D3D::context->PSSetShaderResources(0, 1, &pEFB); + D3D::context->PSSetSamplers(0, 1, &m_efbSampler); + + // Encode! + + D3D::context->Draw(4, 0); + + // Copy to staging buffer + + D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, width/2, height, 1); + D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox); + + // Clean up state + + IUnknown* nullDummy = NULL; + + D3D::context->PSSetSamplers(0, 1, (ID3D11SamplerState**)&nullDummy); + D3D::context->PSSetShaderResources(0, 1, (ID3D11ShaderResourceView**)&nullDummy); + D3D::context->PSSetConstantBuffers(0, 1, (ID3D11Buffer**)&nullDummy); + + D3D::context->OMSetRenderTargets(0, NULL, NULL); + + D3D::context->VSSetConstantBuffers(0, 1, (ID3D11Buffer**)&nullDummy); + + D3D::stateman->PopRasterizerState(); + D3D::stateman->PopDepthState(); + D3D::stateman->PopBlendState(); + + D3D::context->PSSetShader(NULL, NULL, 0); + D3D::context->VSSetShader(NULL, NULL, 0); + + // Transfer staging buffer to GameCube/Wii RAM + + D3D11_MAPPED_SUBRESOURCE map = { 0 }; + hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map); + CHECK(SUCCEEDED(hr), "map staging buffer"); + + u8* src = (u8*)map.pData; + for (unsigned int y = 0; y < height; ++y) + { + memcpy(dst, src, 2*width); + dst += bpmem.copyMipMapStrideChannels*32; + src += map.RowPitch; + } + + D3D::context->Unmap(m_outStage, 0); + + // Restore API + + g_renderer->RestoreAPIState(); + D3D::context->OMSetRenderTargets(1, + &FramebufferManager::GetEFBColorTexture()->GetRTV(), + FramebufferManager::GetEFBDepthTexture()->GetDSV()); +} + +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.h b/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.h new file mode 100644 index 0000000000..dba1fd20e9 --- /dev/null +++ b/Source/Plugins/Plugin_VideoDX11/Src/XFBEncoder.h @@ -0,0 +1,68 @@ +// 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 _XFBENCODER_H +#define _XFBENCODER_H + +#include "VideoCommon.h" + +struct ID3D11Texture2D; +struct ID3D11RenderTargetView; +struct ID3D11Buffer; +struct ID3D11VertexShader; +struct ID3D11PixelShader; +struct ID3D11InputLayout; +struct ID3D11BlendState; +struct ID3D11DepthStencilState; +struct ID3D11RasterizerState; +struct ID3D11SamplerState; + +namespace DX11 +{ + +class XFBEncoder +{ + +public: + + XFBEncoder(); + + void Init(); + void Shutdown(); + + void Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma); + +private: + + ID3D11Texture2D* m_out; + ID3D11RenderTargetView* m_outRTV; + ID3D11Texture2D* m_outStage; + ID3D11Buffer* m_encodeParams; + ID3D11Buffer* m_quad; + ID3D11VertexShader* m_vShader; + ID3D11InputLayout* m_quadLayout; + ID3D11PixelShader* m_pShader; + ID3D11BlendState* m_xfbEncodeBlendState; + ID3D11DepthStencilState* m_xfbEncodeDepthState; + ID3D11RasterizerState* m_xfbEncodeRastState; + ID3D11SamplerState* m_efbSampler; + +}; + +} + +#endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp index d77836f4e5..ebd40d8782 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp @@ -82,7 +82,7 @@ void InitBackendInfo() g_Config.backend_info.APIType = API_D3D11; g_Config.backend_info.bUseRGBATextures = true; // the GX formats barely match any D3D11 formats g_Config.backend_info.bSupportsEFBToRAM = true; - g_Config.backend_info.bSupportsRealXFB = false; + g_Config.backend_info.bSupportsRealXFB = true; g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.bAllowSignedBytes = true; g_Config.backend_info.bSupportsDualSourceBlend = true;