2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 03:29:41 +00:00
|
|
|
// Refer to the license.txt file included.
|
2010-06-13 19:50:06 +00:00
|
|
|
|
2017-02-01 15:56:13 +00:00
|
|
|
#include "VideoBackends/D3D/TextureCache.h"
|
|
|
|
|
2016-05-24 11:59:47 +00:00
|
|
|
#include <algorithm>
|
2015-12-21 02:49:49 +00:00
|
|
|
#include <memory>
|
|
|
|
|
2017-02-01 15:56:13 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
#include "Common/Logging/Log.h"
|
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoBackends/D3D/D3DBase.h"
|
2015-01-26 23:33:23 +00:00
|
|
|
#include "VideoBackends/D3D/D3DShader.h"
|
2014-10-29 00:19:09 +00:00
|
|
|
#include "VideoBackends/D3D/D3DState.h"
|
2017-04-23 04:44:34 +00:00
|
|
|
#include "VideoBackends/D3D/D3DTexture.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoBackends/D3D/D3DUtil.h"
|
2017-04-23 04:44:34 +00:00
|
|
|
#include "VideoBackends/D3D/DXTexture.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoBackends/D3D/FramebufferManager.h"
|
2014-11-29 20:31:30 +00:00
|
|
|
#include "VideoBackends/D3D/GeometryShaderCache.h"
|
2014-10-29 08:52:08 +00:00
|
|
|
#include "VideoBackends/D3D/PSTextureEncoder.h"
|
2016-06-24 08:43:46 +00:00
|
|
|
#include "VideoBackends/D3D/PixelShaderCache.h"
|
2014-02-19 11:14:09 +00:00
|
|
|
#include "VideoBackends/D3D/VertexShaderCache.h"
|
2017-04-23 04:44:34 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/ImageWrite.h"
|
|
|
|
#include "VideoCommon/RenderBase.h"
|
2017-04-23 04:44:34 +00:00
|
|
|
#include "VideoCommon/TextureConfig.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/VideoConfig.h"
|
2010-06-13 19:50:06 +00:00
|
|
|
|
2010-10-19 22:24:27 +00:00
|
|
|
namespace DX11
|
|
|
|
{
|
2017-04-23 04:44:34 +00:00
|
|
|
static const size_t MAX_COPY_BUFFERS = 32;
|
|
|
|
static ID3D11Buffer* s_efbcopycbuf[MAX_COPY_BUFFERS] = {0};
|
2017-04-04 12:52:48 +00:00
|
|
|
static std::unique_ptr<PSTextureEncoder> g_encoder;
|
2015-06-13 13:51:58 +00:00
|
|
|
|
2017-04-23 04:44:34 +00:00
|
|
|
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
|
2010-10-19 22:24:27 +00:00
|
|
|
{
|
2017-04-23 04:44:34 +00:00
|
|
|
return std::make_unique<DXTexture>(config);
|
2015-10-29 16:48:35 +00:00
|
|
|
}
|
2010-11-27 11:11:05 +00:00
|
|
|
|
2017-04-04 13:55:36 +00:00
|
|
|
void TextureCache::CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width,
|
|
|
|
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
|
|
|
|
bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half)
|
2015-10-29 16:48:35 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
g_encoder->Encode(dst, format, native_width, bytes_per_row, num_blocks_y, memory_stride,
|
2017-04-04 13:55:36 +00:00
|
|
|
is_depth_copy, src_rect, scale_by_half);
|
2010-10-19 22:24:27 +00:00
|
|
|
}
|
|
|
|
|
2015-01-26 23:33:23 +00:00
|
|
|
const char palette_shader[] =
|
2016-06-24 08:43:46 +00:00
|
|
|
R"HLSL(
|
2015-01-26 23:33:23 +00:00
|
|
|
sampler samp0 : register(s0);
|
|
|
|
Texture2DArray Tex0 : register(t0);
|
|
|
|
Buffer<uint> Tex1 : register(t1);
|
|
|
|
uniform float Multiply;
|
|
|
|
|
|
|
|
uint Convert3To8(uint v)
|
|
|
|
{
|
|
|
|
// Swizzle bits: 00000123 -> 12312312
|
|
|
|
return (v << 5) | (v << 2) | (v >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint Convert4To8(uint v)
|
|
|
|
{
|
|
|
|
// Swizzle bits: 00001234 -> 12341234
|
|
|
|
return (v << 4) | v;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint Convert5To8(uint v)
|
|
|
|
{
|
|
|
|
// Swizzle bits: 00012345 -> 12345123
|
|
|
|
return (v << 3) | (v >> 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint Convert6To8(uint v)
|
|
|
|
{
|
|
|
|
// Swizzle bits: 00123456 -> 12345612
|
|
|
|
return (v << 2) | (v >> 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
float4 DecodePixel_RGB5A3(uint val)
|
|
|
|
{
|
|
|
|
int r,g,b,a;
|
|
|
|
if ((val&0x8000))
|
|
|
|
{
|
|
|
|
r=Convert5To8((val>>10) & 0x1f);
|
|
|
|
g=Convert5To8((val>>5 ) & 0x1f);
|
|
|
|
b=Convert5To8((val ) & 0x1f);
|
|
|
|
a=0xFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
a=Convert3To8((val>>12) & 0x7);
|
|
|
|
r=Convert4To8((val>>8 ) & 0xf);
|
|
|
|
g=Convert4To8((val>>4 ) & 0xf);
|
|
|
|
b=Convert4To8((val ) & 0xf);
|
|
|
|
}
|
|
|
|
return float4(r, g, b, a) / 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
float4 DecodePixel_RGB565(uint val)
|
|
|
|
{
|
|
|
|
int r, g, b, a;
|
|
|
|
r = Convert5To8((val >> 11) & 0x1f);
|
|
|
|
g = Convert6To8((val >> 5) & 0x3f);
|
|
|
|
b = Convert5To8((val) & 0x1f);
|
|
|
|
a = 0xFF;
|
|
|
|
return float4(r, g, b, a) / 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
float4 DecodePixel_IA8(uint val)
|
|
|
|
{
|
|
|
|
int i = val & 0xFF;
|
|
|
|
int a = val >> 8;
|
|
|
|
return float4(i, i, i, a) / 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
void main(
|
|
|
|
out float4 ocol0 : SV_Target,
|
|
|
|
in float4 pos : SV_Position,
|
|
|
|
in float3 uv0 : TEXCOORD0)
|
|
|
|
{
|
|
|
|
uint src = round(Tex0.Sample(samp0,uv0) * Multiply).r;
|
|
|
|
src = Tex1.Load(src);
|
|
|
|
src = ((src << 8) & 0xFF00) | (src >> 8);
|
|
|
|
ocol0 = DECODE(src);
|
|
|
|
}
|
|
|
|
)HLSL";
|
|
|
|
|
2017-04-23 04:44:34 +00:00
|
|
|
void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source, void* palette,
|
|
|
|
TlutFormat format)
|
2015-01-26 23:33:23 +00:00
|
|
|
{
|
2017-04-23 04:44:34 +00:00
|
|
|
DXTexture* source_texture = static_cast<DXTexture*>(source->texture.get());
|
|
|
|
DXTexture* destination_texture = static_cast<DXTexture*>(destination->texture.get());
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->ResetAPIState();
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// stretch picture with increased internal resolution
|
2017-04-23 04:44:34 +00:00
|
|
|
const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, static_cast<float>(source->GetWidth()),
|
|
|
|
static_cast<float>(source->GetHeight()));
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::context->RSSetViewports(1, &vp);
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D11_BOX box{0, 0, 0, 512, 1, 1};
|
|
|
|
D3D::context->UpdateSubresource(palette_buf, 0, &box, palette, 0, 0);
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::stateman->SetTexture(1, palette_buf_srv);
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// TODO: Add support for C14X2 format. (Different multiplier, more palette entries.)
|
2017-04-23 04:44:34 +00:00
|
|
|
float params[4] = {(source->format & 0xf) == GX_TF_I4 ? 15.f : 255.f};
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::context->UpdateSubresource(palette_uniform, 0, nullptr, ¶ms, 0, 0);
|
|
|
|
D3D::stateman->SetPixelConstants(palette_uniform);
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2017-04-23 04:44:34 +00:00
|
|
|
const D3D11_RECT sourcerect = CD3D11_RECT(0, 0, source->GetWidth(), source->GetHeight());
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::SetPointCopySampler();
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// Make sure we don't draw with the texture set as both a source and target.
|
|
|
|
// (This can happen because we don't unbind textures when we free them.)
|
2017-04-23 04:44:34 +00:00
|
|
|
D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV());
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::stateman->Apply();
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2017-04-23 04:44:34 +00:00
|
|
|
D3D::context->OMSetRenderTargets(1, &destination_texture->GetRawTexIdentifier()->GetRTV(),
|
2016-06-24 08:43:46 +00:00
|
|
|
nullptr);
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// Create texture copy
|
2017-04-23 04:44:34 +00:00
|
|
|
D3D::drawShadedTexQuad(
|
|
|
|
source_texture->GetRawTexIdentifier()->GetSRV(), &sourcerect, source->GetWidth(),
|
|
|
|
source->GetHeight(), palette_pixel_shader[format], VertexShaderCache::GetSimpleVertexShader(),
|
|
|
|
VertexShaderCache::GetSimpleInputLayout(), GeometryShaderCache::GetCopyGeometryShader());
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
|
|
|
FramebufferManager::GetEFBDepthTexture()->GetDSV());
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->RestoreAPIState();
|
2015-01-26 23:33:23 +00:00
|
|
|
}
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
ID3D11PixelShader* GetConvertShader(const char* Type)
|
2015-01-26 23:33:23 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::string shader = "#define DECODE DecodePixel_";
|
|
|
|
shader.append(Type);
|
|
|
|
shader.append("\n");
|
|
|
|
shader.append(palette_shader);
|
|
|
|
return D3D::CompileAndCreatePixelShader(shader);
|
2015-01-26 23:33:23 +00:00
|
|
|
}
|
|
|
|
|
2010-10-19 22:24:27 +00:00
|
|
|
TextureCache::TextureCache()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// FIXME: Is it safe here?
|
|
|
|
g_encoder = std::make_unique<PSTextureEncoder>();
|
|
|
|
g_encoder->Init();
|
|
|
|
|
|
|
|
palette_buf = nullptr;
|
|
|
|
palette_buf_srv = nullptr;
|
|
|
|
palette_uniform = nullptr;
|
|
|
|
palette_pixel_shader[GX_TL_IA8] = GetConvertShader("IA8");
|
|
|
|
palette_pixel_shader[GX_TL_RGB565] = GetConvertShader("RGB565");
|
|
|
|
palette_pixel_shader[GX_TL_RGB5A3] = GetConvertShader("RGB5A3");
|
|
|
|
auto lutBd = CD3D11_BUFFER_DESC(sizeof(u16) * 256, D3D11_BIND_SHADER_RESOURCE);
|
|
|
|
HRESULT hr = D3D::device->CreateBuffer(&lutBd, nullptr, &palette_buf);
|
|
|
|
CHECK(SUCCEEDED(hr), "create palette decoder lut buffer");
|
|
|
|
D3D::SetDebugObjectName(palette_buf, "texture decoder lut buffer");
|
|
|
|
// TODO: C14X2 format.
|
|
|
|
auto outlutUavDesc =
|
|
|
|
CD3D11_SHADER_RESOURCE_VIEW_DESC(palette_buf, DXGI_FORMAT_R16_UINT, 0, 256, 0);
|
|
|
|
hr = D3D::device->CreateShaderResourceView(palette_buf, &outlutUavDesc, &palette_buf_srv);
|
|
|
|
CHECK(SUCCEEDED(hr), "create palette decoder lut srv");
|
|
|
|
D3D::SetDebugObjectName(palette_buf_srv, "texture decoder lut srv");
|
|
|
|
const D3D11_BUFFER_DESC cbdesc =
|
|
|
|
CD3D11_BUFFER_DESC(16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
|
|
|
|
hr = D3D::device->CreateBuffer(&cbdesc, nullptr, &palette_uniform);
|
|
|
|
CHECK(SUCCEEDED(hr), "Create palette decoder constant buffer");
|
|
|
|
D3D::SetDebugObjectName((ID3D11DeviceChild*)palette_uniform,
|
|
|
|
"a constant buffer used in TextureCache::CopyRenderTargetToTexture");
|
2010-06-13 19:50:06 +00:00
|
|
|
}
|
|
|
|
|
2010-10-19 22:24:27 +00:00
|
|
|
TextureCache::~TextureCache()
|
2010-06-13 19:50:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
for (unsigned int k = 0; k < MAX_COPY_BUFFERS; ++k)
|
2017-04-23 04:44:34 +00:00
|
|
|
SAFE_RELEASE(s_efbcopycbuf[k]);
|
2011-06-11 19:37:21 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
g_encoder->Shutdown();
|
|
|
|
g_encoder.reset();
|
2015-01-26 23:33:23 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
SAFE_RELEASE(palette_buf);
|
|
|
|
SAFE_RELEASE(palette_buf_srv);
|
|
|
|
SAFE_RELEASE(palette_uniform);
|
|
|
|
for (ID3D11PixelShader*& shader : palette_pixel_shader)
|
|
|
|
SAFE_RELEASE(shader);
|
2010-06-13 19:50:06 +00:00
|
|
|
}
|
2017-04-23 04:44:34 +00:00
|
|
|
|
|
|
|
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
|
|
|
|
const EFBRectangle& src_rect, bool scale_by_half,
|
|
|
|
unsigned int cbuf_id, const float* colmat)
|
|
|
|
{
|
|
|
|
auto* destination_texture = static_cast<DXTexture*>(entry->texture.get());
|
|
|
|
|
|
|
|
// When copying at half size, in multisampled mode, resolve the color/depth buffer first.
|
|
|
|
// This is because multisampled texture reads go through Load, not Sample, and the linear
|
|
|
|
// filter is ignored.
|
|
|
|
bool multisampled = (g_ActiveConfig.iMultisamples > 1);
|
|
|
|
ID3D11ShaderResourceView* efbTexSRV = is_depth_copy ?
|
|
|
|
FramebufferManager::GetEFBDepthTexture()->GetSRV() :
|
|
|
|
FramebufferManager::GetEFBColorTexture()->GetSRV();
|
|
|
|
if (multisampled && scale_by_half)
|
|
|
|
{
|
|
|
|
multisampled = false;
|
|
|
|
efbTexSRV = is_depth_copy ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() :
|
|
|
|
FramebufferManager::GetResolvedEFBColorTexture()->GetSRV();
|
|
|
|
}
|
|
|
|
|
|
|
|
g_renderer->ResetAPIState();
|
|
|
|
|
|
|
|
// stretch picture with increased internal resolution
|
|
|
|
const D3D11_VIEWPORT vp =
|
|
|
|
CD3D11_VIEWPORT(0.f, 0.f, static_cast<float>(destination_texture->GetConfig().width),
|
|
|
|
static_cast<float>(destination_texture->GetConfig().height));
|
|
|
|
D3D::context->RSSetViewports(1, &vp);
|
|
|
|
|
|
|
|
// set transformation
|
|
|
|
if (nullptr == s_efbcopycbuf[cbuf_id])
|
|
|
|
{
|
|
|
|
const D3D11_BUFFER_DESC cbdesc =
|
|
|
|
CD3D11_BUFFER_DESC(28 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
|
|
|
|
D3D11_SUBRESOURCE_DATA data;
|
|
|
|
data.pSysMem = colmat;
|
|
|
|
HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &s_efbcopycbuf[cbuf_id]);
|
|
|
|
CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbuf_id);
|
|
|
|
D3D::SetDebugObjectName((ID3D11DeviceChild*)s_efbcopycbuf[cbuf_id],
|
|
|
|
"a constant buffer used in TextureCache::CopyRenderTargetToTexture");
|
|
|
|
}
|
|
|
|
D3D::stateman->SetPixelConstants(s_efbcopycbuf[cbuf_id]);
|
|
|
|
|
|
|
|
const TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(src_rect);
|
|
|
|
// TODO: try targetSource.asRECT();
|
|
|
|
const D3D11_RECT sourcerect =
|
|
|
|
CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom);
|
|
|
|
|
|
|
|
// Use linear filtering if (bScaleByHalf), use point filtering otherwise
|
|
|
|
if (scale_by_half)
|
|
|
|
D3D::SetLinearCopySampler();
|
|
|
|
else
|
|
|
|
D3D::SetPointCopySampler();
|
|
|
|
|
|
|
|
// Make sure we don't draw with the texture set as both a source and target.
|
|
|
|
// (This can happen because we don't unbind textures when we free them.)
|
|
|
|
D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV());
|
|
|
|
D3D::stateman->Apply();
|
|
|
|
|
|
|
|
D3D::context->OMSetRenderTargets(1, &destination_texture->GetRawTexIdentifier()->GetRTV(),
|
|
|
|
nullptr);
|
|
|
|
|
|
|
|
// Create texture copy
|
|
|
|
D3D::drawShadedTexQuad(
|
|
|
|
efbTexSRV, &sourcerect, g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(),
|
|
|
|
is_depth_copy ? PixelShaderCache::GetDepthMatrixProgram(multisampled) :
|
|
|
|
PixelShaderCache::GetColorMatrixProgram(multisampled),
|
|
|
|
VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(),
|
|
|
|
GeometryShaderCache::GetCopyGeometryShader());
|
|
|
|
|
|
|
|
D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
|
|
|
FramebufferManager::GetEFBDepthTexture()->GetDSV());
|
|
|
|
|
|
|
|
g_renderer->RestoreAPIState();
|
|
|
|
}
|
2010-06-13 19:50:06 +00:00
|
|
|
}
|