mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
D3D12: Implement XFB encoding/decoding (support Real XFB)
This commit is contained in:
parent
3372bfa6ab
commit
6f3573dda8
@ -67,7 +67,6 @@
|
||||
<ClCompile Include="ShaderCache.cpp" />
|
||||
<ClCompile Include="ShaderConstantsManager.cpp" />
|
||||
<ClCompile Include="StaticShaderCache.cpp" />
|
||||
<ClCompile Include="Television.cpp" />
|
||||
<ClCompile Include="TextureCache.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
<ClCompile Include="XFBEncoder.cpp" />
|
||||
@ -91,7 +90,6 @@
|
||||
<ClInclude Include="ShaderCache.h" />
|
||||
<ClInclude Include="ShaderConstantsManager.h" />
|
||||
<ClInclude Include="StaticShaderCache.h" />
|
||||
<ClInclude Include="Television.h" />
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="TextureEncoder.h" />
|
||||
<ClInclude Include="VertexManager.h" />
|
||||
|
@ -39,9 +39,6 @@
|
||||
<ClCompile Include="Render.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Television.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureCache.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
@ -105,9 +102,6 @@
|
||||
<ClInclude Include="Render.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Television.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureCache.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
|
@ -26,11 +26,6 @@ namespace DX12
|
||||
namespace D3D
|
||||
{
|
||||
|
||||
unsigned int AlignValue(unsigned int value, unsigned int alignment)
|
||||
{
|
||||
return (value + (alignment - 1)) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
void ResourceBarrier(ID3D12GraphicsCommandList* command_list, ID3D12Resource* resource, D3D12_RESOURCE_STATES state_before, D3D12_RESOURCE_STATES state_after, UINT subresource)
|
||||
{
|
||||
if (state_before == state_after)
|
||||
|
@ -22,7 +22,11 @@ extern StateCache gx_state_cache;
|
||||
namespace D3D
|
||||
{
|
||||
|
||||
unsigned int AlignValue(unsigned int value, unsigned int alignment);
|
||||
constexpr unsigned int AlignValue(unsigned int value, unsigned int alignment)
|
||||
{
|
||||
return (value + (alignment - 1)) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
void ResourceBarrier(ID3D12GraphicsCommandList* command_list, ID3D12Resource* resource, D3D12_RESOURCE_STATES state_before, D3D12_RESOURCE_STATES state_after, UINT subresource);
|
||||
|
||||
// Font creation flags
|
||||
|
@ -15,8 +15,6 @@
|
||||
namespace DX12
|
||||
{
|
||||
|
||||
static XFBEncoder s_xfbEncoder;
|
||||
|
||||
FramebufferManager::Efb FramebufferManager::m_efb;
|
||||
unsigned int FramebufferManager::m_target_width;
|
||||
unsigned int FramebufferManager::m_target_height;
|
||||
@ -133,14 +131,10 @@ FramebufferManager::FramebufferManager()
|
||||
}
|
||||
|
||||
InitializeEFBAccessCopies();
|
||||
|
||||
s_xfbEncoder.Init();
|
||||
}
|
||||
|
||||
FramebufferManager::~FramebufferManager()
|
||||
{
|
||||
s_xfbEncoder.Shutdown();
|
||||
|
||||
DestroyEFBAccessCopies();
|
||||
|
||||
SAFE_RELEASE(m_efb.color_tex);
|
||||
@ -153,7 +147,9 @@ FramebufferManager::~FramebufferManager()
|
||||
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float gamma)
|
||||
{
|
||||
u8* dst = Memory::GetPointer(xfbAddr);
|
||||
s_xfbEncoder.Encode(dst, fbStride/2, fbHeight, sourceRc, gamma);
|
||||
D3DTexture2D* src_texture = GetResolvedEFBColorTexture();
|
||||
TargetRectangle scaled_rect = g_renderer->ConvertEFBRectangle(sourceRc);
|
||||
g_xfb_encoder->EncodeTextureToRam(dst, fbStride, fbHeight, src_texture, scaled_rect, m_target_width, m_target_height, gamma);
|
||||
}
|
||||
|
||||
std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers)
|
||||
@ -412,8 +408,8 @@ void FramebufferManager::DestroyEFBAccessCopies()
|
||||
|
||||
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
// DX12's XFB decoder does not use this function.
|
||||
// YUYV data is decoded in Render::Swap.
|
||||
u8* src = Memory::GetPointer(xfbAddr);
|
||||
g_xfb_encoder->DecodeToTexture(m_tex, src, fbWidth, fbHeight);
|
||||
}
|
||||
|
||||
void XFBSource::CopyEFB(float gamma)
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "VideoBackends/D3D12/ShaderCache.h"
|
||||
#include "VideoBackends/D3D12/ShaderConstantsManager.h"
|
||||
#include "VideoBackends/D3D12/StaticShaderCache.h"
|
||||
#include "VideoBackends/D3D12/Television.h"
|
||||
#include "VideoBackends/D3D12/TextureCache.h"
|
||||
|
||||
#include "VideoCommon/AVIDump.h"
|
||||
@ -50,8 +49,6 @@ static u32 s_last_multisamples = 1;
|
||||
static bool s_last_stereo_mode = false;
|
||||
static bool s_last_xfb_mode = false;
|
||||
|
||||
static Television s_television;
|
||||
|
||||
enum CLEAR_BLEND_DESC
|
||||
{
|
||||
CLEAR_BLEND_DESC_ALL_CHANNELS_ENABLED = 0,
|
||||
@ -104,8 +101,6 @@ StateCache gx_state_cache;
|
||||
|
||||
static void SetupDeviceObjects()
|
||||
{
|
||||
s_television.Init();
|
||||
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>();
|
||||
|
||||
D3D12_DEPTH_STENCIL_DESC depth_desc;
|
||||
@ -175,8 +170,6 @@ static void TeardownDeviceObjects()
|
||||
s_screenshot_texture = nullptr;
|
||||
}
|
||||
|
||||
s_television.Shutdown();
|
||||
|
||||
gx_state_cache.Clear();
|
||||
}
|
||||
|
||||
@ -750,15 +743,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
||||
// activate linear filtering for the buffer copies
|
||||
D3D::SetLinearCopySampler();
|
||||
|
||||
if (g_ActiveConfig.bUseXFB && g_ActiveConfig.bUseRealXFB)
|
||||
{
|
||||
// EXISTINGD3D11TODO: Television should be used to render Virtual XFB mode as well.
|
||||
D3D::SetViewportAndScissor(target_rc.left, target_rc.top, target_rc.GetWidth(), target_rc.GetHeight());
|
||||
|
||||
s_television.Submit(xfb_addr, fb_stride, fb_width, fb_height);
|
||||
s_television.Render();
|
||||
}
|
||||
else if (g_ActiveConfig.bUseXFB)
|
||||
if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
const XFBSource* xfb_source;
|
||||
|
||||
@ -768,33 +753,40 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
||||
xfb_source = static_cast<const XFBSource*>(xfb_source_list[i]);
|
||||
|
||||
TargetRectangle drawRc;
|
||||
|
||||
// use virtual xfb with offset
|
||||
int xfb_height = xfb_source->srcHeight;
|
||||
int xfb_width = xfb_source->srcWidth;
|
||||
int hOffset = (static_cast<s32>(xfb_source->srcAddr) - static_cast<s32>(xfb_addr)) / (static_cast<s32>(fb_stride) * 2);
|
||||
|
||||
drawRc.top = target_rc.top + hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
drawRc.bottom = target_rc.top + (hOffset + xfb_height) * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
drawRc.left = target_rc.left + (target_rc.GetWidth() - xfb_width * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
drawRc.right = target_rc.left + (target_rc.GetWidth() + xfb_width * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
|
||||
// The following code disables auto stretch. Kept for reference.
|
||||
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
||||
//float vScale = static_cast<float>(fbHeight) / static_cast<float>(s_backbuffer_height);
|
||||
//float hScale = static_cast<float>(fbWidth) / static_cast<float>(s_backbuffer_width);
|
||||
//drawRc.top *= vScale;
|
||||
//drawRc.bottom *= vScale;
|
||||
//drawRc.left *= hScale;
|
||||
//drawRc.right *= hScale;
|
||||
|
||||
TargetRectangle source_rc;
|
||||
source_rc.left = xfb_source->sourceRc.left;
|
||||
source_rc.top = xfb_source->sourceRc.top;
|
||||
source_rc.right = xfb_source->sourceRc.right;
|
||||
source_rc.bottom = xfb_source->sourceRc.bottom;
|
||||
|
||||
source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
|
||||
// use virtual xfb with offset
|
||||
int xfb_height = xfb_source->srcHeight;
|
||||
int xfb_width = xfb_source->srcWidth;
|
||||
int hOffset = (static_cast<s32>(xfb_source->srcAddr) - static_cast<s32>(xfb_addr)) / (static_cast<s32>(fb_stride) * 2);
|
||||
|
||||
if (g_ActiveConfig.bUseRealXFB)
|
||||
{
|
||||
drawRc = target_rc;
|
||||
source_rc.right -= fb_stride - fb_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawRc.top = target_rc.top + hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
drawRc.bottom = target_rc.top + (hOffset + xfb_height) * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
drawRc.left = target_rc.left + (target_rc.GetWidth() - xfb_width * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
drawRc.right = target_rc.left + (target_rc.GetWidth() + xfb_width * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
|
||||
// The following code disables auto stretch. Kept for reference.
|
||||
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
||||
//float vScale = static_cast<float>(fbHeight) / static_cast<float>(s_backbuffer_height);
|
||||
//float hScale = static_cast<float>(fbWidth) / static_cast<float>(s_backbuffer_width);
|
||||
//drawRc.top *= vScale;
|
||||
//drawRc.bottom *= vScale;
|
||||
//drawRc.left *= hScale;
|
||||
//drawRc.right *= hScale;
|
||||
|
||||
source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
|
||||
}
|
||||
|
||||
BlitScreen(source_rc, drawRc, xfb_source->m_tex, xfb_source->texWidth, xfb_source->texHeight, gamma);
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ static ID3DBlob* s_depth_matrix_program_blob[2] = {};
|
||||
static ID3DBlob* s_depth_resolve_to_color_program_blob = {};
|
||||
static ID3DBlob* s_clear_program_blob = {};
|
||||
static ID3DBlob* s_anaglyph_program_blob = {};
|
||||
static ID3DBlob* s_xfb_encode_shader_blob = {};
|
||||
static ID3DBlob* s_xfb_decode_shader_blob = {};
|
||||
static ID3DBlob* s_rgba6_to_rgb8_program_blob[2] = {};
|
||||
static ID3DBlob* s_rgb8_to_rgba6_program_blob[2] = {};
|
||||
|
||||
@ -411,6 +413,93 @@ static constexpr const char s_copy_geometry_shader_hlsl[] = {
|
||||
"}\n"
|
||||
};
|
||||
|
||||
static const char s_xfb_encode_shader_hlsl[] = R"(
|
||||
|
||||
Texture2DArray tex0 : register(t0);
|
||||
SamplerState samp0 : register(s0);
|
||||
|
||||
cbuffer EncodeParams : register(b0)
|
||||
{
|
||||
float4 srcRect;
|
||||
float2 texelSize;
|
||||
}
|
||||
|
||||
// GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see
|
||||
// <http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion>
|
||||
static const float3x4 RGB_TO_YCBCR = float3x4(
|
||||
0.257, 0.504, 0.098, 16.0/255.0,
|
||||
-0.148, -0.291, 0.439, 128.0/255.0,
|
||||
0.439, -0.368, -0.071, 128.0/255.0
|
||||
);
|
||||
|
||||
void main(
|
||||
out float4 ocol0 : SV_Target,
|
||||
in float4 pos : SV_Position,
|
||||
in float3 uv0 : TEXCOORD0,
|
||||
in float gamma : TEXCOORD1)
|
||||
{
|
||||
// Load three input pixels, emulate clamp sampler by clamping to the source rectangle.
|
||||
// Subtract 0.5 from the x coordinate because we're doubling the width, and want the pixel center shifted back to 0.5.
|
||||
// The native resolution is used as a reference here so bilinear filtering works as expected.
|
||||
float2 baseCoords = lerp(srcRect.xy, srcRect.zw, float2(uv0.x - 0.5 * texelSize.x, uv0.y));
|
||||
float3 sampleL = tex0.Sample(samp0, float3(max(srcRect.xy, baseCoords - float2(texelSize.x, 0)), 0)).rgb;
|
||||
float3 sampleM = tex0.Sample(samp0, float3(baseCoords, 0)).rgb;
|
||||
float3 sampleR = tex0.Sample(samp0, float3(min(srcRect.zw, baseCoords + float2(texelSize.x, 0)), 0)).rgb;
|
||||
|
||||
// Gamma correction (gamma is already rcp(gamma))
|
||||
// abs() here because the HLSL compiler throws a warning otherwise.
|
||||
sampleL = pow(abs(sampleL), gamma);
|
||||
sampleM = pow(abs(sampleM), gamma);
|
||||
sampleR = pow(abs(sampleR), gamma);
|
||||
|
||||
// RGB -> YUV
|
||||
float3 yuvL = mul(RGB_TO_YCBCR, float4(sampleL,1));
|
||||
float3 yuvM = mul(RGB_TO_YCBCR, float4(sampleM,1));
|
||||
float3 yuvR = mul(RGB_TO_YCBCR, float4(sampleR,1));
|
||||
|
||||
// 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;
|
||||
float y1 = yuvR.r;
|
||||
float u0 = 0.25*yuvL.g + 0.5*yuvM.g + 0.25*yuvR.g;
|
||||
float v0 = 0.25*yuvL.b + 0.5*yuvM.b + 0.25*yuvR.b;
|
||||
ocol0 = float4(y0, u0, y1, v0);
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
static const char s_xfb_decode_shader_hlsl[] = R"(
|
||||
|
||||
Texture2DArray tex0 : register(t0);
|
||||
|
||||
static const float3x3 YCBCR_TO_RGB = float3x3(
|
||||
1.164, 0.000, 1.596,
|
||||
1.164, -0.392, -0.813,
|
||||
1.164, 2.017, 0.000
|
||||
);
|
||||
|
||||
void main(
|
||||
out float4 ocol0 : SV_Target,
|
||||
in float4 pos : SV_Position,
|
||||
in float3 uv0 : TEXCOORD0)
|
||||
{
|
||||
// Divide coordinates by 2 due to half-width YUYV texure.
|
||||
int2 ipos = int2(pos.xy);
|
||||
int2 texpos = int2(ipos.x >> 1, ipos.y);
|
||||
float4 yuyv = tex0.Load(int4(texpos, 0, 0));
|
||||
|
||||
// Select U for even pixels, V for odd pixels.
|
||||
float y = lerp(yuyv.r, yuyv.b, float(ipos.x & 1));
|
||||
|
||||
// Recover RGB components
|
||||
float3 yuv_601_sub = float3(y, yuyv.ga) - float3(16.0/255.0, 128.0/255.0, 128.0/255.0);
|
||||
float3 rgb_601 = mul(YCBCR_TO_RGB, yuv_601_sub);
|
||||
|
||||
ocol0 = float4(rgb_601, 1);
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
D3D12_SHADER_BYTECODE StaticShaderCache::GetReinterpRGBA6ToRGB8PixelShader(bool multisampled)
|
||||
{
|
||||
D3D12_SHADER_BYTECODE bytecode = {};
|
||||
@ -625,6 +714,28 @@ D3D12_SHADER_BYTECODE StaticShaderCache::GetCopyGeometryShader()
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
D3D12_SHADER_BYTECODE StaticShaderCache::GetXFBEncodePixelShader()
|
||||
{
|
||||
D3D12_SHADER_BYTECODE bytecode =
|
||||
{
|
||||
s_xfb_encode_shader_blob->GetBufferPointer(),
|
||||
s_xfb_encode_shader_blob->GetBufferSize()
|
||||
};
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
D3D12_SHADER_BYTECODE StaticShaderCache::GetXFBDecodePixelShader()
|
||||
{
|
||||
D3D12_SHADER_BYTECODE bytecode =
|
||||
{
|
||||
s_xfb_decode_shader_blob->GetBufferPointer(),
|
||||
s_xfb_decode_shader_blob->GetBufferSize()
|
||||
};
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
void StaticShaderCache::Init()
|
||||
{
|
||||
// Compile static pixel shaders
|
||||
@ -633,6 +744,8 @@ void StaticShaderCache::Init()
|
||||
D3D::CompilePixelShader(s_color_copy_program_hlsl, &s_color_copy_program_blob[0]);
|
||||
D3D::CompilePixelShader(s_color_matrix_program_hlsl, &s_color_matrix_program_blob[0]);
|
||||
D3D::CompilePixelShader(s_depth_matrix_program_hlsl, &s_depth_matrix_program_blob[0]);
|
||||
D3D::CompilePixelShader(s_xfb_encode_shader_hlsl, &s_xfb_encode_shader_blob);
|
||||
D3D::CompilePixelShader(s_xfb_decode_shader_hlsl, &s_xfb_decode_shader_blob);
|
||||
|
||||
// Compile static vertex shaders
|
||||
D3D::CompileVertexShader(s_simple_vertex_shader_hlsl, &s_simple_vertex_shader_blob);
|
||||
@ -657,7 +770,8 @@ void StaticShaderCache::InvalidateMSAAShaders()
|
||||
void StaticShaderCache::Shutdown()
|
||||
{
|
||||
// Free pixel shader blobs
|
||||
|
||||
SAFE_RELEASE(s_xfb_decode_shader_blob);
|
||||
SAFE_RELEASE(s_xfb_encode_shader_blob);
|
||||
SAFE_RELEASE(s_clear_program_blob);
|
||||
SAFE_RELEASE(s_anaglyph_program_blob);
|
||||
SAFE_RELEASE(s_depth_resolve_to_color_program_blob);
|
||||
|
@ -23,6 +23,8 @@ public:
|
||||
static D3D12_SHADER_BYTECODE GetAnaglyphPixelShader();
|
||||
static D3D12_SHADER_BYTECODE GetReinterpRGBA6ToRGB8PixelShader(bool multisampled);
|
||||
static D3D12_SHADER_BYTECODE GetReinterpRGB8ToRGBA6PixelShader(bool multisampled);
|
||||
static D3D12_SHADER_BYTECODE GetXFBEncodePixelShader();
|
||||
static D3D12_SHADER_BYTECODE GetXFBDecodePixelShader();
|
||||
|
||||
// Vertex shaders
|
||||
static D3D12_SHADER_BYTECODE GetSimpleVertexShader();
|
||||
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "VideoBackends/D3D12/D3DBase.h"
|
||||
#include "VideoBackends/D3D12/D3DShader.h"
|
||||
#include "VideoBackends/D3D12/D3DState.h"
|
||||
#include "VideoBackends/D3D12/D3DUtil.h"
|
||||
#include "VideoBackends/D3D12/Television.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
|
||||
namespace DX12
|
||||
{
|
||||
|
||||
Television::Television()
|
||||
{
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
}
|
||||
|
||||
void Television::Init()
|
||||
{
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
}
|
||||
|
||||
void Television::Shutdown()
|
||||
{
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
}
|
||||
|
||||
void Television::Submit(u32 xfb_address, u32 stride, u32 width, u32 height)
|
||||
{
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
}
|
||||
|
||||
void Television::Render()
|
||||
{
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
// D3D12TODO: Add DX12 path for this file.
|
||||
|
||||
namespace DX12
|
||||
{
|
||||
|
||||
class Television final
|
||||
{
|
||||
|
||||
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 xfb_address, u32 stride, u32 width, u32 height);
|
||||
|
||||
// Render the current state of the TV.
|
||||
void Render();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -6,33 +6,178 @@
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "VideoBackends/D3D12/D3DBase.h"
|
||||
#include "VideoBackends/D3D12/D3DCommandListManager.h"
|
||||
#include "VideoBackends/D3D12/D3DShader.h"
|
||||
#include "VideoBackends/D3D12/D3DState.h"
|
||||
#include "VideoBackends/D3D12/D3DUtil.h"
|
||||
#include "VideoBackends/D3D12/FramebufferManager.h"
|
||||
#include "VideoBackends/D3D12/Render.h"
|
||||
#include "VideoBackends/D3D12/StaticShaderCache.h"
|
||||
#include "VideoBackends/D3D12/XFBEncoder.h"
|
||||
|
||||
// D3D12TODO: Convert this file..
|
||||
|
||||
namespace DX12
|
||||
{
|
||||
|
||||
// YUYV data is packed into half-width RGBA, with Y values in (R,B) and UV in (G,A)
|
||||
constexpr size_t XFB_TEXTURE_WIDTH = MAX_XFB_WIDTH / 2;
|
||||
constexpr size_t XFB_TEXTURE_HEIGHT = MAX_XFB_HEIGHT;
|
||||
|
||||
// Buffer enough space for 2 XFB buffers (our frame latency)
|
||||
constexpr size_t XFB_UPLOAD_BUFFER_SIZE = D3D::AlignValue(XFB_TEXTURE_WIDTH * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * XFB_TEXTURE_HEIGHT * 2;
|
||||
constexpr size_t XFB_ENCODER_PARAMS_BUFFER_SIZE = 64 * 1024;
|
||||
|
||||
std::unique_ptr<XFBEncoder> g_xfb_encoder;
|
||||
|
||||
XFBEncoder::XFBEncoder()
|
||||
{ }
|
||||
|
||||
void XFBEncoder::Init()
|
||||
{
|
||||
// D3D12TODO: Convert this file..
|
||||
ID3D12Resource* texture;
|
||||
|
||||
CheckHR(D3D::device12->CreateCommittedResource(
|
||||
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, XFB_TEXTURE_WIDTH, XFB_TEXTURE_HEIGHT, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET),
|
||||
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&texture)));
|
||||
|
||||
m_yuyv_texture = new D3DTexture2D(texture,
|
||||
(D3D11_BIND_FLAG)(D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET),
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
SAFE_RELEASE(texture);
|
||||
|
||||
CheckHR(D3D::device12->CreateCommittedResource(
|
||||
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK),
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&CD3DX12_RESOURCE_DESC::Buffer(D3D::AlignValue(XFB_TEXTURE_WIDTH * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * MAX_XFB_HEIGHT),
|
||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&m_readback_buffer)));
|
||||
|
||||
m_upload_buffer = std::make_unique<D3DStreamBuffer>(XFB_UPLOAD_BUFFER_SIZE, XFB_UPLOAD_BUFFER_SIZE, nullptr);
|
||||
m_encode_params_buffer = std::make_unique<D3DStreamBuffer>(XFB_ENCODER_PARAMS_BUFFER_SIZE, XFB_ENCODER_PARAMS_BUFFER_SIZE, nullptr);
|
||||
}
|
||||
|
||||
void XFBEncoder::Shutdown()
|
||||
XFBEncoder::~XFBEncoder()
|
||||
{
|
||||
// D3D12TODO: Convert this file..
|
||||
SAFE_RELEASE(m_yuyv_texture);
|
||||
SAFE_RELEASE(m_readback_buffer);
|
||||
}
|
||||
|
||||
void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma)
|
||||
void XFBEncoder::EncodeTextureToRam(u8* dst, u32 dst_pitch, u32 dst_height,
|
||||
D3DTexture2D* src_texture, const TargetRectangle& src_rect,
|
||||
u32 src_width, u32 src_height, float gamma)
|
||||
{
|
||||
// D3D12TODO: Convert this file..
|
||||
// src_rect is in native coordinates
|
||||
// dst_pitch is in words
|
||||
u32 dst_width = dst_pitch / 2;
|
||||
u32 dst_texture_width = dst_width / 2;
|
||||
_assert_msg_(VIDEO, dst_width <= MAX_XFB_WIDTH && dst_height <= MAX_XFB_HEIGHT, "XFB destination does not exceed maximum size");
|
||||
|
||||
// Encode parameters constant buffer used by shader
|
||||
struct EncodeParameters
|
||||
{
|
||||
float srcRect[4];
|
||||
float texelSize[2];
|
||||
float pad[2];
|
||||
};
|
||||
EncodeParameters parameters =
|
||||
{
|
||||
{
|
||||
static_cast<float>(src_rect.left) / static_cast<float>(src_width),
|
||||
static_cast<float>(src_rect.top) / static_cast<float>(src_height),
|
||||
static_cast<float>(src_rect.right) / static_cast<float>(src_width),
|
||||
static_cast<float>(src_rect.bottom) / static_cast<float>(src_height)
|
||||
},
|
||||
{
|
||||
1.0f / EFB_WIDTH,
|
||||
1.0f / EFB_HEIGHT
|
||||
},
|
||||
{
|
||||
0.0f,
|
||||
0.0f
|
||||
}
|
||||
};
|
||||
m_encode_params_buffer->AllocateSpaceInBuffer(sizeof(parameters), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
memcpy(m_encode_params_buffer->GetCPUAddressOfCurrentAllocation(), ¶meters, sizeof(parameters));
|
||||
|
||||
// Convert RGBA texture to YUYV intermediate texture.
|
||||
// Performs downscaling through a linear filter. Probably not ideal, but it's not going to look perfect anyway.
|
||||
CD3DX12_RECT src_texture_rect(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom);
|
||||
D3D12_RESOURCE_STATES src_texture_state = src_texture->GetResourceUsageState();
|
||||
m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
D3D::current_command_list->OMSetRenderTargets(1, &m_yuyv_texture->GetRTV12(), FALSE, nullptr);
|
||||
D3D::current_command_list->SetGraphicsRootConstantBufferView(DESCRIPTOR_TABLE_PS_CBVONE, m_encode_params_buffer->GetGPUAddressOfCurrentAllocation());
|
||||
D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true);
|
||||
D3D::SetViewportAndScissor(0, 0, dst_texture_width, dst_height);
|
||||
D3D::SetLinearCopySampler();
|
||||
D3D::DrawShadedTexQuad(
|
||||
src_texture, &src_texture_rect, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||
StaticShaderCache::GetXFBEncodePixelShader(), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(),
|
||||
{}, gamma, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, false);
|
||||
|
||||
src_texture->TransitionToResourceState(D3D::current_command_list, src_texture_state);
|
||||
|
||||
// Copy from YUYV intermediate texture to readback buffer. It's likely the pitch here is going to be different to dst_pitch.
|
||||
u32 readback_pitch = D3D::AlignValue(dst_width * sizeof(u16), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT dst_footprint = { 0, { DXGI_FORMAT_R8G8B8A8_UNORM, dst_texture_width, dst_height, 1, readback_pitch } };
|
||||
CD3DX12_TEXTURE_COPY_LOCATION dst_location(m_readback_buffer, dst_footprint);
|
||||
CD3DX12_TEXTURE_COPY_LOCATION src_location(m_yuyv_texture->GetTex12(), 0);
|
||||
CD3DX12_BOX src_box(0, 0, dst_texture_width, dst_height);
|
||||
m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, &src_box);
|
||||
|
||||
// Wait until the GPU completes the copy. Resets back to known state automatically.
|
||||
D3D::command_list_mgr->ExecuteQueuedWork(true);
|
||||
|
||||
// Copy from the readback buffer to dst.
|
||||
// Can't be done as one memcpy due to pitch difference.
|
||||
void* readback_texture_map;
|
||||
CheckHR(m_readback_buffer->Map(0, nullptr, &readback_texture_map));
|
||||
|
||||
for (u32 row = 0; row < dst_height; row++)
|
||||
{
|
||||
const u8* row_src = reinterpret_cast<u8*>(readback_texture_map) + readback_pitch * row;
|
||||
u8* row_dst = dst + dst_pitch * row;
|
||||
memcpy(row_dst, row_src, std::min(dst_pitch, readback_pitch));
|
||||
}
|
||||
|
||||
m_readback_buffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void XFBEncoder::DecodeToTexture(D3DTexture2D* dst_texture, const u8* src, u32 src_width, u32 src_height)
|
||||
{
|
||||
_assert_msg_(VIDEO, src_width <= MAX_XFB_WIDTH && src_height <= MAX_XFB_HEIGHT, "XFB source does not exceed maximum size");
|
||||
|
||||
// Copy to XFB upload buffer. Each row has to be done separately due to pitch differences.
|
||||
u32 buffer_pitch = D3D::AlignValue(src_width / 2 * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
m_upload_buffer->AllocateSpaceInBuffer(buffer_pitch * src_height, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
|
||||
for (u32 row = 0; row < src_height; row++)
|
||||
{
|
||||
const u8* row_src = src + (src_width * 2) * row;
|
||||
u8* row_dst = reinterpret_cast<u8*>(m_upload_buffer->GetCPUAddressOfCurrentAllocation()) + buffer_pitch * row;
|
||||
memcpy(row_dst, row_src, src_width * 2);
|
||||
}
|
||||
|
||||
// Copy from upload buffer to intermediate YUYV texture.
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT src_footprint = { m_upload_buffer->GetOffsetOfCurrentAllocation(), { DXGI_FORMAT_R8G8B8A8_UNORM, src_width / 2, src_height, 1, buffer_pitch } };
|
||||
CD3DX12_TEXTURE_COPY_LOCATION src_location(m_upload_buffer->GetBuffer(), src_footprint);
|
||||
CD3DX12_TEXTURE_COPY_LOCATION dst_location(m_yuyv_texture->GetTex12(), 0);
|
||||
CD3DX12_BOX src_box(0, 0, src_width / 2, src_height);
|
||||
m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, &src_box);
|
||||
|
||||
// Convert YUYV texture to RGBA texture with pixel shader.
|
||||
CD3DX12_RECT src_texture_rect(0, 0, src_width / 2, src_height);
|
||||
dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
D3D::current_command_list->OMSetRenderTargets(1, &dst_texture->GetRTV12(), FALSE, nullptr);
|
||||
D3D::SetViewportAndScissor(0, 0, src_width, src_height);
|
||||
D3D::DrawShadedTexQuad(
|
||||
m_yuyv_texture, &src_texture_rect, XFB_TEXTURE_WIDTH, XFB_TEXTURE_HEIGHT,
|
||||
StaticShaderCache::GetXFBDecodePixelShader(), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(),
|
||||
{}, 1.0f, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, false);
|
||||
|
||||
// XFB source textures are expected to be in shader resource state.
|
||||
dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,28 +1,42 @@
|
||||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <memory>
|
||||
|
||||
#include "VideoBackends/D3D12/D3DStreamBuffer.h"
|
||||
#include "VideoBackends/D3D12/D3DTexture.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
namespace DX12
|
||||
{
|
||||
|
||||
class D3DTexture2D;
|
||||
|
||||
class XFBEncoder
|
||||
{
|
||||
|
||||
public:
|
||||
XFBEncoder();
|
||||
~XFBEncoder();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void EncodeTextureToRam(u8* dst, u32 dst_pitch, u32 dst_height,
|
||||
D3DTexture2D* src_texture, const TargetRectangle& src_rect,
|
||||
u32 src_width, u32 src_height, float gamma);
|
||||
|
||||
void Encode(u8* dst, u32 width, u32 height, const EFBRectangle& src_rect, float gamma);
|
||||
void DecodeToTexture(D3DTexture2D* dst_texture, const u8* src, u32 src_width, u32 src_height);
|
||||
|
||||
private:
|
||||
// D3D12TODO: Implement this class
|
||||
D3DTexture2D* m_yuyv_texture;
|
||||
|
||||
ID3D12Resource* m_readback_buffer;
|
||||
|
||||
std::unique_ptr<D3DStreamBuffer> m_upload_buffer;
|
||||
std::unique_ptr<D3DStreamBuffer> m_encode_params_buffer;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<XFBEncoder> g_xfb_encoder;
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "VideoBackends/D3D12/TextureCache.h"
|
||||
#include "VideoBackends/D3D12/VertexManager.h"
|
||||
#include "VideoBackends/D3D12/VideoBackend.h"
|
||||
#include "VideoBackends/D3D12/XFBEncoder.h"
|
||||
|
||||
#include "VideoCommon/BPStructs.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
@ -179,6 +180,7 @@ void VideoBackend::Video_Prepare()
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
g_xfb_encoder = std::make_unique<XFBEncoder>();
|
||||
ShaderCache::Init();
|
||||
ShaderConstantsManager::Init();
|
||||
StaticShaderCache::Init();
|
||||
@ -228,6 +230,7 @@ void VideoBackend::Shutdown()
|
||||
StaticShaderCache::Shutdown();
|
||||
BBox::Shutdown();
|
||||
|
||||
g_xfb_encoder.reset();
|
||||
g_perf_query.reset();
|
||||
g_vertex_manager.reset();
|
||||
g_texture_cache.reset();
|
||||
|
@ -1309,6 +1309,11 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
||||
{
|
||||
drawRc = flipped_trc;
|
||||
sourceRc.right -= fbStride - fbWidth;
|
||||
|
||||
// RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a top-left origin.
|
||||
// The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
|
||||
sourceRc.top = sourceRc.bottom;
|
||||
sourceRc.bottom = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -67,12 +67,10 @@ const XFBSourceBase* const* FramebufferManagerBase::GetRealXFBSource(u32 xfbAddr
|
||||
m_realXFBSource->texWidth = fbWidth;
|
||||
m_realXFBSource->texHeight = fbHeight;
|
||||
|
||||
// OpenGL texture coordinates originate at the lower left, which is why
|
||||
// sourceRc.top = fbHeight and sourceRc.bottom = 0.
|
||||
m_realXFBSource->sourceRc.left = 0;
|
||||
m_realXFBSource->sourceRc.top = fbHeight;
|
||||
m_realXFBSource->sourceRc.top = 0;
|
||||
m_realXFBSource->sourceRc.right = fbWidth;
|
||||
m_realXFBSource->sourceRc.bottom = 0;
|
||||
m_realXFBSource->sourceRc.bottom = fbHeight;
|
||||
|
||||
// Decode YUYV data from GameCube RAM
|
||||
m_realXFBSource->DecodeToTexture(xfbAddr, fbWidth, fbHeight);
|
||||
|
Loading…
Reference in New Issue
Block a user