diff options
| author | Stenzek <stenzek@gmail.com> | 2018-04-29 18:52:30 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2018-04-29 19:05:20 +1000 |
| commit | 9e798eec94a8ebe94c1ef8270b504b5698b2a0f5 (patch) | |
| tree | 4265b19082fd65555dd795703e09abb94811c65a /Source/Core/VideoCommon | |
| parent | a192a3bb3043c921ec897186c2a927b33050dffc (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.h | 4 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/BPStructs.cpp | 12 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/RenderBase.cpp | 2 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/RenderBase.h | 3 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureCacheBase.cpp | 49 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureCacheBase.h | 31 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureConversionShader.cpp | 245 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureConverterShaderGen.cpp | 62 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VideoConfig.cpp | 1 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VideoConfig.h | 1 |
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; |
