summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2018-04-29 18:52:30 +1000
committerStenzek <stenzek@gmail.com>2018-04-29 19:05:20 +1000
commit9e798eec94a8ebe94c1ef8270b504b5698b2a0f5 (patch)
tree4265b19082fd65555dd795703e09abb94811c65a /Source/Core/VideoCommon
parenta192a3bb3043c921ec897186c2a927b33050dffc (diff)
Implement EFB copy filter and gamma in hardware backends
Also makes y_scale a dynamic parameter for EFB copies, as it doesn't make sense to keep it as part of the uid, otherwise we're generating redundant shaders.
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/BPMemory.h4
-rw-r--r--Source/Core/VideoCommon/BPStructs.cpp12
-rw-r--r--Source/Core/VideoCommon/RenderBase.cpp2
-rw-r--r--Source/Core/VideoCommon/RenderBase.h3
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.cpp49
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.h31
-rw-r--r--Source/Core/VideoCommon/TextureConversionShader.cpp245
-rw-r--r--Source/Core/VideoCommon/TextureConverterShaderGen.cpp62
-rw-r--r--Source/Core/VideoCommon/VideoConfig.cpp1
-rw-r--r--Source/Core/VideoCommon/VideoConfig.h1
10 files changed, 273 insertions, 137 deletions
diff --git a/Source/Core/VideoCommon/BPMemory.h b/Source/Core/VideoCommon/BPMemory.h
index 4a1cea9d48..0f287b4f99 100644
--- a/Source/Core/VideoCommon/BPMemory.h
+++ b/Source/Core/VideoCommon/BPMemory.h
@@ -970,6 +970,8 @@ union UPE_Copy
union CopyFilterCoefficients
{
+ using Values = std::array<u8, 7>;
+
u64 Hex;
BitField<0, 6, u64> w0;
@@ -980,7 +982,7 @@ union CopyFilterCoefficients
BitField<38, 6, u64> w5;
BitField<44, 6, u64> w6;
- std::array<u8, 7> GetCoefficients() const
+ Values GetCoefficients() const
{
return {
static_cast<u8>(w0), static_cast<u8>(w1), static_cast<u8>(w2), static_cast<u8>(w3),
diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp
index 143132ecaf..0e0d639a63 100644
--- a/Source/Core/VideoCommon/BPStructs.cpp
+++ b/Source/Core/VideoCommon/BPStructs.cpp
@@ -229,10 +229,13 @@ static void BPWritten(const BPCmd& bp)
{
// bpmem.zcontrol.pixel_format to PEControl::Z24 is when the game wants to copy from ZBuffer
// (Zbuffer uses 24-bit Format)
+ static constexpr CopyFilterCoefficients::Values filter_coefficients = {
+ {0, 0, 21, 22, 21, 0, 0}};
bool is_depth_copy = bpmem.zcontrol.pixel_format == PEControl::Z24;
g_texture_cache->CopyRenderTargetToTexture(
destAddr, PE_copy.tp_realFormat(), srcRect.GetWidth(), srcRect.GetHeight(), destStride,
- is_depth_copy, srcRect, !!PE_copy.intensity_fmt, !!PE_copy.half_scale, 1.0f, 1.0f);
+ is_depth_copy, srcRect, !!PE_copy.intensity_fmt, !!PE_copy.half_scale, 1.0f, 1.0f,
+ bpmem.triggerEFBCopy.clamp_top, bpmem.triggerEFBCopy.clamp_bottom, filter_coefficients);
}
else
{
@@ -260,9 +263,10 @@ static void BPWritten(const BPCmd& bp)
bpmem.copyTexSrcWH.x + 1, destStride, height, yScale);
bool is_depth_copy = bpmem.zcontrol.pixel_format == PEControl::Z24;
- g_texture_cache->CopyRenderTargetToTexture(destAddr, EFBCopyFormat::XFB, srcRect.GetWidth(),
- height, destStride, is_depth_copy, srcRect, false,
- false, yScale, s_gammaLUT[PE_copy.gamma]);
+ g_texture_cache->CopyRenderTargetToTexture(
+ destAddr, EFBCopyFormat::XFB, srcRect.GetWidth(), height, destStride, is_depth_copy,
+ srcRect, false, false, yScale, s_gammaLUT[PE_copy.gamma], bpmem.triggerEFBCopy.clamp_top,
+ bpmem.triggerEFBCopy.clamp_bottom, bpmem.copyfilter.GetCoefficients());
// This stays in to signal end of a "frame"
g_renderer->RenderToXFB(destAddr, srcRect, destStride, height, s_gammaLUT[PE_copy.gamma]);
diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp
index 1598fe0a00..76d40f5aa3 100644
--- a/Source/Core/VideoCommon/RenderBase.cpp
+++ b/Source/Core/VideoCommon/RenderBase.cpp
@@ -680,7 +680,7 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
// TODO: merge more generic parts into VideoCommon
{
std::lock_guard<std::mutex> guard(m_swap_mutex);
- g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks, xfb_entry->gamma);
+ g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks);
}
// Update the window size based on the frame that was just rendered.
diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h
index e7f7668f22..7e4f4e00f7 100644
--- a/Source/Core/VideoCommon/RenderBase.h
+++ b/Source/Core/VideoCommon/RenderBase.h
@@ -175,8 +175,7 @@ public:
// Finish up the current frame, print some stats
void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
u64 ticks);
- virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks,
- float Gamma = 1.0f) = 0;
+ virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) = 0;
PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; }
void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; }
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 9ac75d31f0..f0b1392531 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -1499,10 +1499,39 @@ void TextureCacheBase::LoadTextureLevelZeroFromMemory(TCacheEntry* entry_to_upda
}
}
-void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 width,
- u32 height, u32 dstStride, bool is_depth_copy,
- const EFBRectangle& srcRect, bool isIntensity,
- bool scaleByHalf, float y_scale, float gamma)
+TextureCacheBase::CopyFilterCoefficientArray
+TextureCacheBase::GetRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients)
+{
+ // To simplify the backend, we precalculate the three coefficients in common. Coefficients 0, 1
+ // are for the row above, 2, 3, 4 are for the current pixel, and 5, 6 are for the row below.
+ return {static_cast<u32>(coefficients[0]) + static_cast<u32>(coefficients[1]),
+ static_cast<u32>(coefficients[2]) + static_cast<u32>(coefficients[3]) +
+ static_cast<u32>(coefficients[4]),
+ static_cast<u32>(coefficients[5]) + static_cast<u32>(coefficients[6])};
+}
+
+TextureCacheBase::CopyFilterCoefficientArray
+TextureCacheBase::GetVRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients)
+{
+ // If the user disables the copy filter, only apply it to the VRAM copy.
+ // This way games which are sensitive to changes to the RAM copy of the XFB will be unaffected.
+ CopyFilterCoefficientArray res = GetRAMCopyFilterCoefficients(coefficients);
+ if (!g_ActiveConfig.bDisableCopyFilter)
+ return res;
+
+ // Disabling the copy filter in options should not ignore the values the game sets completely,
+ // as some games use the filter coefficients to control the brightness of the screen. Instead,
+ // add all coefficients to the middle sample, so the deflicker/vertical filter has no effect.
+ res[1] += res[0] + res[2];
+ res[0] = 0;
+ res[2] = 0;
+ return res;
+}
+
+void TextureCacheBase::CopyRenderTargetToTexture(
+ u32 dstAddr, EFBCopyFormat dstFormat, u32 width, u32 height, u32 dstStride, bool is_depth_copy,
+ const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf, float y_scale, float gamma,
+ bool clamp_top, bool clamp_bottom, const CopyFilterCoefficients::Values& filter_coefficients)
{
// Emulation methods:
//
@@ -1622,8 +1651,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
if (copy_to_ram)
{
PEControl::PixelFormat srcFormat = bpmem.zcontrol.pixel_format;
- EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity, y_scale);
- CopyEFB(dst, format, tex_w, bytes_per_row, num_blocks_y, dstStride, srcRect, scaleByHalf);
+ EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity);
+ CopyEFB(dst, format, tex_w, bytes_per_row, num_blocks_y, dstStride, srcRect, scaleByHalf,
+ y_scale, gamma, clamp_top, clamp_bottom,
+ GetRAMCopyFilterCoefficients(filter_coefficients));
}
else
{
@@ -1742,8 +1773,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
{
entry->SetGeneralParameters(dstAddr, 0, baseFormat, is_xfb_copy);
entry->SetDimensions(tex_w, tex_h, 1);
- entry->gamma = gamma;
-
entry->frameCount = FRAMECOUNT_INVALID;
if (is_xfb_copy)
{
@@ -1757,7 +1786,9 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
entry->may_have_overlapping_textures = false;
entry->is_custom_tex = false;
- CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, dstFormat, isIntensity);
+ CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, dstFormat, isIntensity, gamma,
+ clamp_top, clamp_bottom,
+ GetVRAMCopyFilterCoefficients(filter_coefficients));
u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash);
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 6dce346c24..720e95b470 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -47,23 +47,21 @@ struct TextureAndTLUTFormat
struct EFBCopyParams
{
EFBCopyParams(PEControl::PixelFormat efb_format_, EFBCopyFormat copy_format_, bool depth_,
- bool yuv_, float y_scale_)
- : efb_format(efb_format_), copy_format(copy_format_), depth(depth_), yuv(yuv_),
- y_scale(y_scale_)
+ bool yuv_)
+ : efb_format(efb_format_), copy_format(copy_format_), depth(depth_), yuv(yuv_)
{
}
bool operator<(const EFBCopyParams& rhs) const
{
- return std::tie(efb_format, copy_format, depth, yuv, y_scale) <
- std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv, rhs.y_scale);
+ return std::tie(efb_format, copy_format, depth, yuv) <
+ std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv);
}
PEControl::PixelFormat efb_format;
EFBCopyFormat copy_format;
bool depth;
bool yuv;
- float y_scale;
};
struct TextureLookupInformation
@@ -108,6 +106,8 @@ private:
static const int FRAMECOUNT_INVALID = 0;
public:
+ using CopyFilterCoefficientArray = std::array<u32, 3>;
+
struct TCacheEntry
{
// common members
@@ -126,7 +126,6 @@ public:
// content, aren't just downscaled
bool should_force_safe_hashing = false; // for XFB
bool is_xfb_copy = false;
- float gamma = 1.0f;
u64 id;
bool reference_changed = false; // used by xfb to determine when a reference xfb changed
@@ -216,7 +215,9 @@ public:
virtual void CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row,
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect,
- bool scale_by_half) = 0;
+ bool scale_by_half, float y_scale, float gamma, bool clamp_top,
+ bool clamp_bottom,
+ const CopyFilterCoefficientArray& filter_coefficients) = 0;
virtual bool CompileShaders() = 0;
virtual void DeleteShaders() = 0;
@@ -248,7 +249,9 @@ public:
virtual void BindTextures();
void CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 width, u32 height,
u32 dstStride, bool is_depth_copy, const EFBRectangle& srcRect,
- bool isIntensity, bool scaleByHalf, float y_scale, float gamma);
+ bool isIntensity, bool scaleByHalf, float y_scale, float gamma,
+ bool clamp_top, bool clamp_bottom,
+ const CopyFilterCoefficients::Values& filter_coefficients);
virtual void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, const void* palette,
TLUTFormat format) = 0;
@@ -315,13 +318,21 @@ private:
virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
- EFBCopyFormat dst_format, bool is_intensity) = 0;
+ EFBCopyFormat dst_format, bool is_intensity, float gamma,
+ bool clamp_top, bool clamp_bottom,
+ const CopyFilterCoefficientArray& filter_coefficients) = 0;
// Removes and unlinks texture from texture cache and returns it to the pool
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter);
void UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_row, u32 num_blocks_y);
+ // Precomputing the coefficients for the previous, current, and next lines for the copy filter.
+ CopyFilterCoefficientArray
+ GetRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients);
+ CopyFilterCoefficientArray
+ GetVRAMCopyFilterCoefficients(const CopyFilterCoefficients::Values& coefficients);
+
TexAddrCache textures_by_address;
TexHashCache textures_by_hash;
TexPool texture_pool;
diff --git a/Source/Core/VideoCommon/TextureConversionShader.cpp b/Source/Core/VideoCommon/TextureConversionShader.cpp
index 4d1539572c..1912f2aee5 100644
--- a/Source/Core/VideoCommon/TextureConversionShader.cpp
+++ b/Source/Core/VideoCommon/TextureConversionShader.cpp
@@ -57,19 +57,44 @@ u16 GetEncodedSampleCount(EFBCopyFormat format)
}
}
-// block dimensions : widthStride, heightStride
-// texture dims : width, height, x offset, y offset
-static void WriteSwizzler(char*& p, EFBCopyFormat format, APIType ApiType)
+static void WriteHeader(char*& p, APIType ApiType)
{
- // left, top, of source rectangle within source texture
- // width of the destination rectangle, scale_factor (1 or 2)
- if (ApiType == APIType::Vulkan)
- WRITE(p,
- "layout(std140, push_constant) uniform PCBlock { int4 position; float y_scale; } PC;\n");
- else
+ if (ApiType == APIType::OpenGL)
{
+ // left, top, of source rectangle within source texture
+ // width of the destination rectangle, scale_factor (1 or 2)
WRITE(p, "uniform int4 position;\n");
WRITE(p, "uniform float y_scale;\n");
+ WRITE(p, "uniform float gamma_rcp;\n");
+ WRITE(p, "uniform float2 clamp_tb;\n");
+ WRITE(p, "uniform int3 filter_coefficients;\n");
+ WRITE(p, "#define samp0 samp9\n");
+ WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n");
+ WRITE(p, "FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
+ }
+ else if (ApiType == APIType::Vulkan)
+ {
+ WRITE(p, "UBO_BINDING(std140, 1) uniform PSBlock {\n");
+ WRITE(p, " int4 position;\n");
+ WRITE(p, " float y_scale;\n");
+ WRITE(p, " float gamma_rcp;\n");
+ WRITE(p, " float2 clamp_tb;\n");
+ WRITE(p, " int3 filter_coefficients;\n");
+ WRITE(p, "};\n");
+ WRITE(p, "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
+ WRITE(p, "FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
+ }
+ else // D3D
+ {
+ WRITE(p, "cbuffer PSBlock : register(b0) {\n");
+ WRITE(p, " int4 position;\n");
+ WRITE(p, " float y_scale;\n");
+ WRITE(p, " float gamma_rcp;\n");
+ WRITE(p, " float2 clamp_tb;\n");
+ WRITE(p, " int3 filter_coefficients;\n");
+ WRITE(p, "};\n");
+ WRITE(p, "sampler samp0 : register(s0);\n");
+ WRITE(p, "Texture2DArray Tex0 : register(t0);\n");
}
// D3D does not have roundEven(), only round(), which is specified "to the nearest integer".
@@ -96,39 +121,100 @@ static void WriteSwizzler(char*& p, EFBCopyFormat format, APIType ApiType)
WRITE(p, " val = int4(val.r >> 3, val.g >> 2, val.b >> 3, 1);\n");
WRITE(p, " return float4(val) / float4(31.0, 63.0, 31.0, 1.0);\n");
WRITE(p, "}\n");
+}
- int blkW = TexDecoder_GetEFBCopyBlockWidthInTexels(format);
- int blkH = TexDecoder_GetEFBCopyBlockHeightInTexels(format);
- int samples = GetEncodedSampleCount(format);
+static void WriteSampleFunction(char*& p, const EFBCopyParams& params, APIType ApiType)
+{
+ auto WriteSampleOp = [&](int yoffset) {
+ if (!params.depth)
+ {
+ switch (params.efb_format)
+ {
+ case PEControl::RGB8_Z24:
+ WRITE(p, "RGBA8ToRGB8(");
+ break;
+ case PEControl::RGBA6_Z24:
+ WRITE(p, "RGBA8ToRGBA6(");
+ break;
+ case PEControl::RGB565_Z16:
+ WRITE(p, "RGBA8ToRGB565(");
+ break;
+ default:
+ WRITE(p, "(");
+ break;
+ }
+ }
+ else
+ {
+ // Handle D3D depth inversion.
+ if (ApiType == APIType::D3D || ApiType == APIType::Vulkan)
+ WRITE(p, "1.0 - (");
+ else
+ WRITE(p, "(");
+ }
- if (ApiType == APIType::OpenGL)
- {
- WRITE(p, "#define samp0 samp9\n");
- WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n");
+ if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
+ WRITE(p, "texture(samp0, float3(");
+ else
+ WRITE(p, "Tex0.Sample(samp0, float3(");
- WRITE(p, "FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
- WRITE(p, "void main()\n");
- WRITE(p, "{\n"
- " int2 sampleUv;\n"
- " int2 uv1 = int2(gl_FragCoord.xy);\n");
- }
- else if (ApiType == APIType::Vulkan)
- {
- WRITE(p, "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
- WRITE(p, "FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
+ WRITE(p, "uv.x + xoffset * pixel_size.x, ");
+
+ // Reverse the direction for OpenGL, since positive numbers are distance from the bottom row.
+ if (yoffset != 0)
+ {
+ if (ApiType == APIType::OpenGL)
+ WRITE(p, "clamp(uv.y - float(%d) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset);
+ else
+ WRITE(p, "clamp(uv.y + float(%d) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset);
+ }
+ else
+ {
+ WRITE(p, "uv.y");
+ }
+
+ WRITE(p, ", 0.0)))");
+ };
+ // The copy filter applies to both color and depth copies. This has been verified on hardware.
+ // The filter is only applied to the RGB channels, the alpha channel is left intact.
+ WRITE(p, "float4 SampleEFB(float2 uv, float2 pixel_size, int xoffset)\n");
+ WRITE(p, "{\n");
+ WRITE(p, " float4 prev_row = ");
+ WriteSampleOp(-1);
+ WRITE(p, ";\n");
+ WRITE(p, " float4 current_row = ");
+ WriteSampleOp(0);
+ WRITE(p, ";\n");
+ WRITE(p, " float4 next_row = ");
+ WriteSampleOp(1);
+ WRITE(p, ";\n");
+ WRITE(p,
+ " float3 col = float3(clamp((int3(prev_row.rgb * 255.0) * filter_coefficients[0] +\n"
+ " int3(current_row.rgb * 255.0) * filter_coefficients[1] +\n"
+ " int3(next_row.rgb * 255.0) * filter_coefficients[2]) >> 6,\n"
+ " int3(0, 0, 0), int3(255, 255, 255))) / 255.0;\n");
+ WRITE(p, " return float4(col, current_row.a);\n");
+ WRITE(p, "}\n");
+}
+
+// block dimensions : widthStride, heightStride
+// texture dims : width, height, x offset, y offset
+static void WriteSwizzler(char*& p, const EFBCopyParams& params, EFBCopyFormat format,
+ APIType ApiType)
+{
+ WriteHeader(p, ApiType);
+ WriteSampleFunction(p, params, ApiType);
+
+ if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
+ {
WRITE(p, "void main()\n");
WRITE(p, "{\n"
" int2 sampleUv;\n"
- " int2 uv1 = int2(gl_FragCoord.xy);\n"
- " int4 position = PC.position;\n"
- " float y_scale = PC.y_scale;\n");
+ " int2 uv1 = int2(gl_FragCoord.xy);\n");
}
else // D3D
{
- WRITE(p, "sampler samp0 : register(s0);\n");
- WRITE(p, "Texture2DArray Tex0 : register(t0);\n");
-
WRITE(p, "void main(\n");
WRITE(p, " out float4 ocol0 : SV_Target, in float4 rawpos : SV_Position)\n");
WRITE(p, "{\n"
@@ -136,6 +222,10 @@ static void WriteSwizzler(char*& p, EFBCopyFormat format, APIType ApiType)
" int2 uv1 = int2(rawpos.xy);\n");
}
+ int blkW = TexDecoder_GetEFBCopyBlockWidthInTexels(format);
+ int blkH = TexDecoder_GetEFBCopyBlockHeightInTexels(format);
+ int samples = GetEncodedSampleCount(format);
+
WRITE(p, " int x_block_position = (uv1.x >> %d) << %d;\n", IntLog2(blkH * blkW / samples),
IntLog2(blkW));
WRITE(p, " int y_block_position = uv1.y << %d;\n", IntLog2(blkH));
@@ -167,51 +257,13 @@ static void WriteSwizzler(char*& p, EFBCopyFormat format, APIType ApiType)
WRITE(p, " uv0.y = 1.0-uv0.y;\n");
}
- WRITE(p, " float sample_offset = float(position.w) / float(%d);\n", EFB_WIDTH);
+ WRITE(p, " float2 pixel_size = position.ww / float2(%d, %d);\n", EFB_WIDTH, EFB_HEIGHT);
}
static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset,
APIType ApiType, const EFBCopyParams& params)
{
- WRITE(p, " %s = ", dest);
-
- if (!params.depth)
- {
- switch (params.efb_format)
- {
- case PEControl::RGB8_Z24:
- WRITE(p, "RGBA8ToRGB8(");
- break;
- case PEControl::RGBA6_Z24:
- WRITE(p, "RGBA8ToRGBA6(");
- break;
- case PEControl::RGB565_Z16:
- WRITE(p, "RGBA8ToRGB565(");
- break;
- default:
- WRITE(p, "(");
- break;
- }
- }
- else
- {
- // Handle D3D depth inversion.
- if (ApiType == APIType::D3D || ApiType == APIType::Vulkan)
- WRITE(p, "1.0 - (");
- else
- WRITE(p, "(");
- }
-
- if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
- {
- WRITE(p, "texture(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0))).%s;\n", xoffset,
- colorComp);
- }
- else
- {
- WRITE(p, "Tex0.Sample(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0))).%s;\n", xoffset,
- colorComp);
- }
+ WRITE(p, " %s = SampleEFB(uv0, pixel_size, %d).%s;\n", dest, xoffset, colorComp);
}
static void WriteColorToIntensity(char*& p, const char* src, const char* dest)
@@ -239,7 +291,7 @@ static void WriteEncoderEnd(char*& p)
static void WriteI8Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::R8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::R8, ApiType);
WRITE(p, " float3 texSample;\n");
WriteSampleColor(p, "rgb", "texSample", 0, ApiType, params);
@@ -261,7 +313,7 @@ static void WriteI8Encoder(char*& p, APIType ApiType, const EFBCopyParams& param
static void WriteI4Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::R4, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::R4, ApiType);
WRITE(p, " float3 texSample;\n");
WRITE(p, " float4 color0;\n");
WRITE(p, " float4 color1;\n");
@@ -302,7 +354,7 @@ static void WriteI4Encoder(char*& p, APIType ApiType, const EFBCopyParams& param
static void WriteIA8Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RA8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RA8, ApiType);
WRITE(p, " float4 texSample;\n");
WriteSampleColor(p, "rgba", "texSample", 0, ApiType, params);
@@ -320,7 +372,7 @@ static void WriteIA8Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
static void WriteIA4Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RA4, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RA4, ApiType);
WRITE(p, " float4 texSample;\n");
WRITE(p, " float4 color0;\n");
WRITE(p, " float4 color1;\n");
@@ -352,7 +404,7 @@ static void WriteIA4Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
static void WriteRGB565Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RGB565, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RGB565, ApiType);
WRITE(p, " float3 texSample0;\n");
WRITE(p, " float3 texSample1;\n");
@@ -377,7 +429,7 @@ static void WriteRGB565Encoder(char*& p, APIType ApiType, const EFBCopyParams& p
static void WriteRGB5A3Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RGB5A3, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RGB5A3, ApiType);
WRITE(p, " float4 texSample;\n");
WRITE(p, " float color0;\n");
@@ -441,7 +493,7 @@ static void WriteRGB5A3Encoder(char*& p, APIType ApiType, const EFBCopyParams& p
static void WriteRGBA8Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RGBA8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RGBA8, ApiType);
WRITE(p, " float4 texSample;\n");
WRITE(p, " float4 color0;\n");
@@ -466,7 +518,7 @@ static void WriteRGBA8Encoder(char*& p, APIType ApiType, const EFBCopyParams& pa
static void WriteC4Encoder(char*& p, const char* comp, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::R4, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::R4, ApiType);
WRITE(p, " float4 color0;\n");
WRITE(p, " float4 color1;\n");
@@ -488,7 +540,7 @@ static void WriteC4Encoder(char*& p, const char* comp, APIType ApiType, const EF
static void WriteC8Encoder(char*& p, const char* comp, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::R8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::R8, ApiType);
WriteSampleColor(p, comp, "ocol0.b", 0, ApiType, params);
WriteSampleColor(p, comp, "ocol0.g", 1, ApiType, params);
@@ -501,7 +553,7 @@ static void WriteC8Encoder(char*& p, const char* comp, APIType ApiType, const EF
static void WriteCC4Encoder(char*& p, const char* comp, APIType ApiType,
const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RA4, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RA4, ApiType);
WRITE(p, " float2 texSample;\n");
WRITE(p, " float4 color0;\n");
WRITE(p, " float4 color1;\n");
@@ -532,7 +584,7 @@ static void WriteCC4Encoder(char*& p, const char* comp, APIType ApiType,
static void WriteCC8Encoder(char*& p, const char* comp, APIType ApiType,
const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RA8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RA8, ApiType);
WriteSampleColor(p, comp, "ocol0.bg", 0, ApiType, params);
WriteSampleColor(p, comp, "ocol0.ra", 1, ApiType, params);
@@ -543,7 +595,7 @@ static void WriteCC8Encoder(char*& p, const char* comp, APIType ApiType,
static void WriteZ8Encoder(char*& p, const char* multiplier, APIType ApiType,
const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::G8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::G8, ApiType);
WRITE(p, " float depth;\n");
@@ -564,7 +616,7 @@ static void WriteZ8Encoder(char*& p, const char* multiplier, APIType ApiType,
static void WriteZ16Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RA8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RA8, ApiType);
WRITE(p, " float depth;\n");
WRITE(p, " float3 expanded;\n");
@@ -596,7 +648,7 @@ static void WriteZ16Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
static void WriteZ16LEncoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::GB8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::GB8, ApiType);
WRITE(p, " float depth;\n");
WRITE(p, " float3 expanded;\n");
@@ -632,7 +684,7 @@ static void WriteZ16LEncoder(char*& p, APIType ApiType, const EFBCopyParams& par
static void WriteZ24Encoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::RGBA8, ApiType);
+ WriteSwizzler(p, params, EFBCopyFormat::RGBA8, ApiType);
WRITE(p, " float depth0;\n");
WRITE(p, " float depth1;\n");
@@ -672,18 +724,21 @@ static void WriteZ24Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
static void WriteXFBEncoder(char*& p, APIType ApiType, const EFBCopyParams& params)
{
- WriteSwizzler(p, EFBCopyFormat::XFB, ApiType);
-
- WRITE(p, " float3 y_const = float3(0.257, 0.504, 0.098);\n");
- WRITE(p, " float3 u_const = float3(-0.148, -0.291, 0.439);\n");
- WRITE(p, " float3 v_const = float3(0.439, -0.368, -0.071);\n");
- WRITE(p, " float3 color0;\n");
- WRITE(p, " float3 color1;\n");
+ WriteSwizzler(p, params, EFBCopyFormat::XFB, ApiType);
+ WRITE(p, "float3 color0, color1;\n");
WriteSampleColor(p, "rgb", "color0", 0, ApiType, params);
WriteSampleColor(p, "rgb", "color1", 1, ApiType, params);
- WRITE(p, " float3 average = (color0 + color1) * 0.5;\n");
+ // Gamma is only applied to XFB copies.
+ WRITE(p, " color0 = pow(color0, gamma_rcp.xxx);\n");
+ WRITE(p, " color1 = pow(color1, gamma_rcp.xxx);\n");
+
+ // Convert to YUV.
+ WRITE(p, " const float3 y_const = float3(0.257, 0.504, 0.098);\n");
+ WRITE(p, " const float3 u_const = float3(-0.148, -0.291, 0.439);\n");
+ WRITE(p, " const float3 v_const = float3(0.439, -0.368, -0.071);\n");
+ WRITE(p, " float3 average = (color0 + color1) * 0.5;\n");
WRITE(p, " ocol0.b = dot(color0, y_const) + 0.0625;\n");
WRITE(p, " ocol0.g = dot(average, u_const) + 0.5;\n");
WRITE(p, " ocol0.r = dot(color1, y_const) + 0.0625;\n");
diff --git a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp
index 74ab4b38d2..dce823ba5a 100644
--- a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp
+++ b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp
@@ -38,34 +38,66 @@ ShaderCode GenerateShader(APIType api_type, const UidData* uid_data)
if (api_type == APIType::OpenGL)
{
out.Write("SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
- "#define samp0 samp9\n"
- "#define uv0 f_uv0\n"
+ "uniform float3 filter_coefficients;\n"
+ "uniform float gamma_rcp;\n"
+ "uniform float2 clamp_tb;\n"
+ "uniform float pixel_height;\n");
+ out.Write("float4 SampleEFB(float3 uv, float y_offset) {\n"
+ " return texture(samp9, float3(uv.x, clamp(uv.y - (y_offset * pixel_height), "
+ "clamp_tb.x, clamp_tb.y), %s));\n"
+ "}\n",
+ mono_depth ? "0.0" : "uv.z");
+ out.Write("#define uv0 f_uv0\n"
"in vec3 uv0;\n"
"out vec4 ocol0;\n"
- "void main(){\n"
- " vec4 texcol = texture(samp0, %s);\n",
- mono_depth ? "vec3(uv0.xy, 0.0)" : "uv0");
+ "void main(){\n");
}
else if (api_type == APIType::Vulkan)
{
- out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"
- "layout(location = 0) in vec3 uv0;\n"
+ out.Write("UBO_BINDING(std140, 1) uniform PSBlock {\n"
+ " float3 filter_coefficients;\n"
+ " float gamma_rcp;\n"
+ " float2 clamp_tb;\n"
+ " float pixel_height;\n"
+ "};\n");
+ out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
+ out.Write("float4 SampleEFB(float3 uv, float y_offset) {\n"
+ " return texture(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), "
+ "clamp_tb.x, clamp_tb.y), %s));\n"
+ "}\n",
+ mono_depth ? "0.0" : "uv.z");
+ out.Write("layout(location = 0) in vec3 uv0;\n"
"layout(location = 1) in vec4 col0;\n"
"layout(location = 0) out vec4 ocol0;"
- "void main(){\n"
- " vec4 texcol = texture(samp0, %s);\n",
- mono_depth ? "vec3(uv0.xy, 0.0)" : "uv0");
+ "void main(){\n");
}
else if (api_type == APIType::D3D)
{
out.Write("Texture2DArray tex0 : register(t0);\n"
"SamplerState samp0 : register(s0);\n"
- "void main(out float4 ocol0 : SV_Target,\n"
+ "uniform float3 filter_coefficients;\n"
+ "uniform float gamma_rcp;\n"
+ "uniform float2 clamp_tb;\n"
+ "uniform float pixel_height;\n\n");
+ out.Write("float4 SampleEFB(float3 uv, float y_offset) {\n"
+ " return tex0.Sample(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), "
+ "clamp_tb.x, clamp_tb.y), %s));\n"
+ "}\n",
+ mono_depth ? "0.0" : "uv.z");
+ out.Write("void main(out float4 ocol0 : SV_Target,\n"
" in float4 pos : SV_Position,\n"
- " in float3 uv0 : TEXCOORD0) {\n"
- " float4 texcol = tex0.Sample(samp0, uv0);\n");
+ " in float3 uv0 : TEXCOORD0) {\n");
}
+ // The copy filter applies to both color and depth copies. This has been verified on hardware.
+ // The filter is only applied to the RGB channels, the alpha channel is left intact.
+ out.Write(" float4 prev_row = SampleEFB(uv0, -1.0f);\n"
+ " float4 current_row = SampleEFB(uv0, 0.0f);\n"
+ " float4 next_row = SampleEFB(uv0, 1.0f);\n"
+ " float4 texcol = float4(prev_row.rgb * filter_coefficients[0] +\n"
+ " current_row.rgb * filter_coefficients[1] +\n"
+ " next_row.rgb * filter_coefficients[2], current_row.a);\n");
+
if (uid_data->is_depth_copy)
{
if (api_type == APIType::D3D || api_type == APIType::Vulkan)
@@ -223,8 +255,8 @@ ShaderCode GenerateShader(APIType api_type, const UidData* uid_data)
out.Write(" ocol0 = texcol;\n");
break;
- case EFBCopyFormat::XFB: // XFB copy, we just pretend it's an RGBX copy
- out.Write(" ocol0 = float4(texcol.rgb, 1.0);\n");
+ case EFBCopyFormat::XFB:
+ out.Write(" ocol0 = float4(pow(texcol.rgb, gamma_rcp.xxx), texcol.a);\n");
break;
default:
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index d08d5b793a..abdf27dcd1 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -120,6 +120,7 @@ void VideoConfig::Refresh()
iMaxAnisotropy = Config::Get(Config::GFX_ENHANCE_MAX_ANISOTROPY);
sPostProcessingShader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
bForceTrueColor = Config::Get(Config::GFX_ENHANCE_FORCE_TRUE_COLOR);
+ bDisableCopyFilter = Config::Get(Config::GFX_ENHANCE_DISABLE_COPY_FILTER);
stereo_mode = static_cast<StereoMode>(Config::Get(Config::GFX_STEREO_MODE));
iStereoDepth = Config::Get(Config::GFX_STEREO_DEPTH);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index cc8e4cde88..03ccfca3ba 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -73,6 +73,7 @@ struct VideoConfig final
int iMaxAnisotropy;
std::string sPostProcessingShader;
bool bForceTrueColor;
+ bool bDisableCopyFilter;
// Information
bool bShowFPS;