mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
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.
This commit is contained in:
@ -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),
|
||||
|
@ -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]);
|
||||
|
@ -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.
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
|
||||
WRITE(p, "texture(samp0, float3(");
|
||||
else
|
||||
WRITE(p, "Tex0.Sample(samp0, float3(");
|
||||
|
||||
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, "#define samp0 samp9\n");
|
||||
WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n");
|
||||
|
||||
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, "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");
|
||||
}
|
||||
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");
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -73,6 +73,7 @@ struct VideoConfig final
|
||||
int iMaxAnisotropy;
|
||||
std::string sPostProcessingShader;
|
||||
bool bForceTrueColor;
|
||||
bool bDisableCopyFilter;
|
||||
|
||||
// Information
|
||||
bool bShowFPS;
|
||||
|
Reference in New Issue
Block a user