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:
Stenzek
2018-04-29 18:52:30 +10:00
parent a192a3bb30
commit 9e798eec94
41 changed files with 526 additions and 236 deletions

View File

@ -31,7 +31,11 @@ struct EFBEncodeParams
u32 DestWidth;
u32 ScaleFactor;
float y_scale;
u32 padding[3];
float gamma_rcp;
float clamp_top;
float clamp_bottom;
s32 filter_coefficients[3];
u32 padding;
};
PSTextureEncoder::PSTextureEncoder()
@ -66,9 +70,11 @@ void PSTextureEncoder::Shutdown()
SAFE_RELEASE(m_encode_params);
}
void PSTextureEncoder::Encode(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)
void PSTextureEncoder::Encode(
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, float y_scale, float gamma,
bool clamp_top, bool clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients)
{
// Resolve MSAA targets before copying.
// FIXME: Instead of resolving EFB, it would be better to pick out a
@ -101,7 +107,13 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
encode_params.SrcTop = src_rect.top;
encode_params.DestWidth = native_width;
encode_params.ScaleFactor = scale_by_half ? 2 : 1;
encode_params.y_scale = params.y_scale;
encode_params.y_scale = y_scale;
encode_params.gamma_rcp = 1.0f / gamma;
encode_params.clamp_top = clamp_top ? src_rect.top / float(EFB_HEIGHT) : 0.0f;
encode_params.clamp_bottom = clamp_bottom ? src_rect.bottom / float(EFB_HEIGHT) : 0.0f;
for (size_t i = 0; i < filter_coefficients.size(); i++)
encode_params.filter_coefficients[i] = filter_coefficients[i];
D3D::context->UpdateSubresource(m_encode_params, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encode_params);
@ -109,7 +121,7 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
// Also, box filtering won't be correct for anything other than 1x IR
if (scale_by_half || g_renderer->GetEFBScale() != 1 || params.y_scale > 1.0f)
if (scale_by_half || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f)
D3D::SetLinearCopySampler();
else
D3D::SetPointCopySampler();

View File

@ -8,6 +8,7 @@
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/VideoCommon.h"
@ -38,8 +39,9 @@ public:
void Init();
void Shutdown();
void Encode(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);
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half,
float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients);
private:
ID3D11PixelShader* GetEncodingPixelShader(const EFBCopyParams& params);

View File

@ -627,8 +627,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
}
// This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma)
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks)
{
ResetAPIState();
@ -650,7 +649,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
auto* xfb_texture = static_cast<DXTexture*>(texture);
BlitScreen(xfb_region, targetRc, xfb_texture->GetRawTexIdentifier(),
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height, Gamma);
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height);
// Reset viewport for drawing text
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast<float>(m_backbuffer_width),
@ -854,7 +853,7 @@ void Renderer::BBoxWrite(int index, u16 _value)
}
void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
u32 src_width, u32 src_height, float Gamma)
u32 src_width, u32 src_height)
{
if (g_ActiveConfig.stereo_mode == StereoMode::SBS ||
g_ActiveConfig.stereo_mode == StereoMode::TAB)
@ -871,13 +870,13 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height,
PixelShaderCache::GetColorCopyProgram(false),
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), nullptr, Gamma, 0);
VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0f, 0);
D3D::context->RSSetViewports(1, &rightVp);
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height,
PixelShaderCache::GetColorCopyProgram(false),
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), nullptr, Gamma, 1);
VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0f, 1);
}
else if (g_ActiveConfig.stereo_mode == StereoMode::Nvidia3DVision)
{
@ -896,13 +895,13 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height,
PixelShaderCache::GetColorCopyProgram(false),
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), nullptr, Gamma, 0);
VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0f, 0);
D3D::context->RSSetViewports(1, &rightVp);
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height,
PixelShaderCache::GetColorCopyProgram(false),
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), nullptr, Gamma, 1);
VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0f, 1);
// Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should
// recognize the signature and automatically include the right eye frame.
@ -927,7 +926,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
nullptr;
D3D::drawShadedTexQuad(src_texture->GetSRV(), src.AsRECT(), src_width, src_height, pixelShader,
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), geomShader, Gamma);
VertexShaderCache::GetSimpleInputLayout(), geomShader, 1.0f);
}
}

View File

@ -63,7 +63,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override;
@ -84,7 +84,7 @@ private:
void UpdateBackbufferSize();
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
u32 src_width, u32 src_height, float Gamma);
u32 src_width, u32 src_height);
void UpdateUtilityUniformBuffer(const void* uniforms, u32 uniforms_size);
void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices);

View File

@ -33,10 +33,12 @@ static std::unique_ptr<PSTextureEncoder> g_encoder;
void TextureCache::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)
const EFBRectangle& src_rect, bool scale_by_half, float y_scale,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
g_encoder->Encode(dst, params, native_width, bytes_per_row, num_blocks_y, memory_stride, src_rect,
scale_by_half);
scale_by_half, y_scale, gamma, clamp_top, clamp_bottom, filter_coefficients);
}
const char palette_shader[] =
@ -137,9 +139,9 @@ void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source,
D3D::stateman->SetTexture(1, palette_buf_srv);
// TODO: Add support for C14X2 format. (Different multiplier, more palette entries.)
float params[4] = {source->format == TextureFormat::I4 ? 15.f : 255.f};
D3D::context->UpdateSubresource(palette_uniform, 0, nullptr, &params, 0, 0);
D3D::stateman->SetPixelConstants(palette_uniform);
float params[8] = {source->format == TextureFormat::I4 ? 15.f : 255.f};
D3D::context->UpdateSubresource(uniform_buffer, 0, nullptr, &params, 0, 0);
D3D::stateman->SetPixelConstants(uniform_buffer);
const D3D11_RECT sourcerect = CD3D11_RECT(0, 0, source->GetWidth(), source->GetHeight());
@ -180,7 +182,7 @@ TextureCache::TextureCache()
palette_buf = nullptr;
palette_buf_srv = nullptr;
palette_uniform = nullptr;
uniform_buffer = nullptr;
palette_pixel_shader[static_cast<int>(TLUTFormat::IA8)] = GetConvertShader("IA8");
palette_pixel_shader[static_cast<int>(TLUTFormat::RGB565)] = GetConvertShader("RGB565");
palette_pixel_shader[static_cast<int>(TLUTFormat::RGB5A3)] = GetConvertShader("RGB5A3");
@ -195,10 +197,10 @@ TextureCache::TextureCache()
CHECK(SUCCEEDED(hr), "create palette decoder lut srv");
D3D::SetDebugObjectName(palette_buf_srv, "texture decoder lut srv");
const D3D11_BUFFER_DESC cbdesc =
CD3D11_BUFFER_DESC(16, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
hr = D3D::device->CreateBuffer(&cbdesc, nullptr, &palette_uniform);
CD3D11_BUFFER_DESC(sizeof(float) * 8, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
hr = D3D::device->CreateBuffer(&cbdesc, nullptr, &uniform_buffer);
CHECK(SUCCEEDED(hr), "Create palette decoder constant buffer");
D3D::SetDebugObjectName(palette_uniform,
D3D::SetDebugObjectName(uniform_buffer,
"a constant buffer used in TextureCache::CopyRenderTargetToTexture");
}
@ -209,7 +211,7 @@ TextureCache::~TextureCache()
SAFE_RELEASE(palette_buf);
SAFE_RELEASE(palette_buf_srv);
SAFE_RELEASE(palette_uniform);
SAFE_RELEASE(uniform_buffer);
for (auto*& shader : palette_pixel_shader)
SAFE_RELEASE(shader);
for (auto& iter : m_efb_to_tex_pixel_shaders)
@ -218,7 +220,9 @@ TextureCache::~TextureCache()
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
EFBCopyFormat dst_format, bool is_intensity)
EFBCopyFormat dst_format, bool is_intensity, float gamma,
bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
auto* destination_texture = static_cast<DXTexture*>(entry->texture.get());
@ -260,6 +264,27 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
else
D3D::SetPointCopySampler();
struct PixelConstants
{
float filter_coefficients[3];
float gamma_rcp;
float clamp_top;
float clamp_bottom;
float pixel_height;
u32 padding;
};
PixelConstants constants;
for (size_t i = 0; i < filter_coefficients.size(); i++)
constants.filter_coefficients[i] = filter_coefficients[i] / 64.0f;
constants.gamma_rcp = 1.0f / gamma;
constants.clamp_top = clamp_top ? src_rect.top / float(EFB_HEIGHT) : 0.0f;
constants.clamp_bottom = clamp_bottom ? src_rect.bottom / float(EFB_HEIGHT) : 1.0f;
constants.pixel_height =
g_ActiveConfig.bCopyEFBScaled ? 1.0f / g_renderer->GetTargetHeight() : 1.0f / EFB_HEIGHT;
constants.padding = 0;
D3D::context->UpdateSubresource(uniform_buffer, 0, nullptr, &constants, 0, 0);
D3D::stateman->SetPixelConstants(uniform_buffer);
// Make sure we don't draw with the texture set as both a source and target.
// (This can happen because we don't unbind textures when we free them.)
D3D::stateman->UnsetTexture(destination_texture->GetRawTexIdentifier()->GetSRV());

View File

@ -34,11 +34,13 @@ private:
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) override;
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format,
bool is_intensity) override;
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
bool CompileShaders() override { return true; }
void DeleteShaders() override {}
@ -46,7 +48,7 @@ private:
ID3D11Buffer* palette_buf;
ID3D11ShaderResourceView* palette_buf_srv;
ID3D11Buffer* palette_uniform;
ID3D11Buffer* uniform_buffer;
ID3D11PixelShader* palette_pixel_shader[3];
std::map<TextureConversionShaderGen::TCShaderUid, ID3D11PixelShader*> m_efb_to_tex_pixel_shaders;

View File

@ -92,7 +92,7 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
return result;
}
void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64, float)
void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64)
{
UpdateActiveConfig();
}

View File

@ -34,7 +34,7 @@ public:
void BBoxWrite(int index, u16 value) override {}
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override

View File

@ -27,12 +27,15 @@ public:
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) override
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override
{
}
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity) override
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override
{
}
};

View File

@ -1365,8 +1365,7 @@ void Renderer::ApplyBlendingState(const BlendingState state, bool force)
}
// This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma)
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks)
{
if (g_ogl_config.bSupportsDebug)
{

View File

@ -126,7 +126,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override;

View File

@ -68,10 +68,18 @@ constexpr const char* geometry_program = "layout(triangles) in;\n"
void TextureCache::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)
const EFBRectangle& src_rect, bool scale_by_half, float y_scale,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
// Flip top/bottom due to lower-left coordinate system.
float clamp_top_val =
clamp_bottom ? (1.0f - src_rect.bottom / static_cast<float>(EFB_HEIGHT)) : 0.0f;
float clamp_bottom_val =
clamp_top ? (1.0f - src_rect.top / static_cast<float>(EFB_HEIGHT)) : 0.0f;
TextureConverter::EncodeToRamFromTexture(dst, params, native_width, bytes_per_row, num_blocks_y,
memory_stride, src_rect, scale_by_half);
memory_stride, src_rect, scale_by_half, y_scale, gamma,
clamp_top_val, clamp_bottom_val, filter_coefficients);
}
TextureCache::TextureCache()
@ -483,7 +491,9 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
EFBCopyFormat dst_format, bool is_intensity)
EFBCopyFormat dst_format, bool is_intensity, float gamma,
bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
auto* destination_texture = static_cast<OGLTexture*>(entry->texture.get());
g_renderer->ResetAPIState(); // reset any game specific settings
@ -528,6 +538,11 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
code.GetBuffer(), geo_program);
shader.position_uniform = glGetUniformLocation(shader.shader.glprogid, "copy_position");
shader.pixel_height_uniform = glGetUniformLocation(shader.shader.glprogid, "pixel_height");
shader.gamma_rcp_uniform = glGetUniformLocation(shader.shader.glprogid, "gamma_rcp");
shader.clamp_tb_uniform = glGetUniformLocation(shader.shader.glprogid, "clamp_tb");
shader.filter_coefficients_uniform =
glGetUniformLocation(shader.shader.glprogid, "filter_coefficients");
}
shader.shader.Bind();
@ -535,6 +550,15 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
TargetRectangle R = g_renderer->ConvertEFBRectangle(src_rect);
glUniform4f(shader.position_uniform, static_cast<float>(R.left), static_cast<float>(R.top),
static_cast<float>(R.right), static_cast<float>(R.bottom));
glUniform1f(shader.pixel_height_uniform, g_ActiveConfig.bCopyEFBScaled ?
1.0f / g_renderer->GetTargetHeight() :
1.0f / EFB_HEIGHT);
glUniform1f(shader.gamma_rcp_uniform, 1.0f / gamma);
glUniform2f(shader.clamp_tb_uniform,
clamp_bottom ? (1.0f - src_rect.bottom / static_cast<float>(EFB_HEIGHT)) : 0.0f,
clamp_top ? (1.0f - src_rect.top / static_cast<float>(EFB_HEIGHT)) : 0.0f);
glUniform3f(shader.filter_coefficients_uniform, filter_coefficients[0] / 64.0f,
filter_coefficients[1] / 64.0f, filter_coefficients[2] / 64.0f);
ProgramShaderCache::BindVertexFormat(nullptr);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

View File

@ -65,11 +65,13 @@ private:
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) override;
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format,
bool is_intensity) override;
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
bool CompileShaders() override;
void DeleteShaders() override;
@ -84,6 +86,10 @@ private:
{
SHADER shader;
GLuint position_uniform;
GLuint pixel_height_uniform;
GLuint gamma_rcp_uniform;
GLuint clamp_tb_uniform;
GLuint filter_coefficients_uniform;
};
std::map<TextureConversionShaderGen::TCShaderUid, EFBCopyShader> m_efb_copy_programs;

View File

@ -39,6 +39,9 @@ struct EncodingProgram
SHADER program;
GLint copy_position_uniform;
GLint y_scale_uniform;
GLint gamma_rcp_uniform;
GLint clamp_tb_uniform;
GLint filter_coefficients_uniform;
};
std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
@ -81,6 +84,10 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
program.copy_position_uniform = glGetUniformLocation(program.program.glprogid, "position");
program.y_scale_uniform = glGetUniformLocation(program.program.glprogid, "y_scale");
program.gamma_rcp_uniform = glGetUniformLocation(program.program.glprogid, "gamma_rcp");
program.clamp_tb_uniform = glGetUniformLocation(program.program.glprogid, "clamp_tb");
program.filter_coefficients_uniform =
glGetUniformLocation(program.program.glprogid, "filter_coefficients");
return s_encoding_programs.emplace(params, program).first->second;
}
@ -137,7 +144,9 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line
void EncodeToRamFromTexture(u8* dest_ptr, 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)
const EFBRectangle& src_rect, bool scale_by_half, float y_scale,
float gamma, float clamp_top, float clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients)
{
g_renderer->ResetAPIState();
@ -146,14 +155,18 @@ void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 nativ
texconv_shader.program.Bind();
glUniform4i(texconv_shader.copy_position_uniform, src_rect.left, src_rect.top, native_width,
scale_by_half ? 2 : 1);
glUniform1f(texconv_shader.y_scale_uniform, params.y_scale);
glUniform1f(texconv_shader.y_scale_uniform, y_scale);
glUniform1f(texconv_shader.gamma_rcp_uniform, 1.0f / gamma);
glUniform2f(texconv_shader.clamp_tb_uniform, clamp_top, clamp_bottom);
glUniform3i(texconv_shader.filter_coefficients_uniform, filter_coefficients[0],
filter_coefficients[1], filter_coefficients[2]);
const GLuint read_texture = params.depth ?
FramebufferManager::ResolveAndGetDepthTarget(src_rect) :
FramebufferManager::ResolveAndGetRenderTarget(src_rect);
EncodeToRamUsingShader(read_texture, dest_ptr, bytes_per_row, num_blocks_y, memory_stride,
scale_by_half && !params.depth, params.y_scale);
scale_by_half && !params.depth, y_scale);
g_renderer->RestoreAPIState();
}

View File

@ -7,10 +7,9 @@
#include "Common/CommonTypes.h"
#include "Common/GL/GLUtil.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoCommon.h"
struct EFBCopyParams;
namespace OGL
{
// Converts textures between formats using shaders
@ -21,9 +20,11 @@ void Init();
void Shutdown();
// returns size of the encoded data (in bytes)
void EncodeToRamFromTexture(u8* dest_ptr, 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);
void EncodeToRamFromTexture(
u8* dest_ptr, 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,
float y_scale, float gamma, float clamp_top, float clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients);
}
} // namespace OGL

View File

@ -502,6 +502,23 @@ static u32 VerticalFilter(const std::array<u32, 3>& colors,
return out_color32;
}
static u32 GammaCorrection(u32 color, const float gamma_rcp)
{
u8 in_colors[4];
std::memcpy(&in_colors, &color, sizeof(in_colors));
u8 out_color[4];
for (int i = BLU_C; i <= RED_C; i++)
{
out_color[i] = static_cast<u8>(
MathUtil::Clamp(std::pow(in_colors[i] / 255.0f, gamma_rcp) * 255.0f, 0.0f, 255.0f));
}
u32 out_color32;
std::memcpy(&out_color32, out_color, sizeof(out_color32));
return out_color32;
}
// For internal used only, return a non-normalized value, which saves work later.
static yuv444 ConvertColorToYUV(u32 color)
{
@ -530,8 +547,7 @@ u8* GetPixelPointer(u16 x, u16 y, bool depth)
}
void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rect, float y_scale,
bool clamp_top, bool clamp_bottom, float Gamma,
const std::array<u8, 7>& filterCoefficients)
float gamma)
{
if (!xfb_in_ram)
{
@ -539,8 +555,12 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec
return;
}
int left = source_rect.left;
int right = source_rect.right;
const int left = source_rect.left;
const int right = source_rect.right;
const bool clamp_top = bpmem.triggerEFBCopy.clamp_top;
const bool clamp_bottom = bpmem.triggerEFBCopy.clamp_bottom;
const float gamma_rcp = 1.0f / gamma;
const auto filter_coefficients = bpmem.copyfilter.GetCoefficients();
// this assumes copies will always start on an even (YU) pixel and the
// copy always has an even width, which might not be true.
@ -575,9 +595,10 @@ void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rec
std::array<u32, 3> colors = {{GetColor(x, y_prev), GetColor(x, y), GetColor(x, y_next)}};
// Vertical Filter (Multisampling resolve, deflicker, brightness)
u32 filtered = VerticalFilter(colors, filterCoefficients);
u32 filtered = VerticalFilter(colors, filter_coefficients);
// TODO: Gamma correction happens here.
// Gamma correction happens here.
filtered = GammaCorrection(filtered, gamma_rcp);
scanline[i] = ConvertColorToYUV(filtered);
}

View File

@ -59,8 +59,7 @@ u32 GetDepth(u16 x, u16 y);
u8* GetPixelPointer(u16 x, u16 y, bool depth);
void EncodeXFB(u8* xfb_in_ram, u32 memory_stride, const EFBRectangle& source_rect, float y_scale,
bool clamp_top, bool clamp_bottom, float Gamma,
const std::array<u8, 7>& filterCoefficients);
float gamma);
extern u32 perf_values[PQ_NUM_MEMBERS];
inline void IncPerfCounterQuadCount(PerfQueryType type)

View File

@ -87,8 +87,7 @@ std::unique_ptr<AbstractPipeline> SWRenderer::CreatePipeline(const AbstractPipel
}
// Called on the GPU thread
void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma)
void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks)
{
OSD::DoCallbacks(OSD::CallbackType::OnFrame);

View File

@ -34,7 +34,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override;

View File

@ -18,15 +18,18 @@ public:
}
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) override
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override
{
TextureEncoder::Encode(dst, params, native_width, bytes_per_row, num_blocks_y, memory_stride,
src_rect, scale_by_half);
src_rect, scale_by_half, y_scale, gamma);
}
private:
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity) override
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override
{
// TODO: If we ever want to "fake" vram textures, we would need to implement this
}

View File

@ -1469,15 +1469,12 @@ void EncodeEfbCopy(u8* dst, const EFBCopyParams& params, u32 native_width, u32 b
}
void Encode(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)
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half,
float y_scale, float gamma)
{
if (params.copy_format == EFBCopyFormat::XFB)
{
static constexpr std::array<float, 4> gamma_LUT = {1.0f, 1.7f, 2.2f, 1.0f};
EfbInterface::EncodeXFB(dst, native_width, src_rect, params.y_scale,
!!bpmem.triggerEFBCopy.clamp_top, !!bpmem.triggerEFBCopy.clamp_bottom,
gamma_LUT[bpmem.triggerEFBCopy.gamma],
bpmem.copyfilter.GetCoefficients());
EfbInterface::EncodeXFB(dst, native_width, src_rect, y_scale, gamma);
}
else
{

View File

@ -5,12 +5,12 @@
#pragma once
#include "Common/CommonTypes.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoCommon.h"
struct EFBCopyParams;
namespace TextureEncoder
{
void Encode(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);
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half,
float y_scale, float gamma);
}

View File

@ -697,8 +697,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
BindEFBToStateTracker();
}
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma)
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks)
{
// Pending/batched EFB pokes should be included in the final image.
FramebufferManager::GetInstance()->FlushEFBPokes();

View File

@ -59,7 +59,7 @@ public:
void BBoxWrite(int index, u16 value) override;
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks) override;
void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable,
u32 color, u32 z) override;

View File

@ -100,7 +100,9 @@ void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source,
void TextureCache::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)
const EFBRectangle& src_rect, bool scale_by_half, float y_scale,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
// Flush EFB pokes first, as they're expected to be included.
FramebufferManager::GetInstance()->FlushEFBPokes();
@ -131,9 +133,9 @@ void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_widt
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_texture_converter->EncodeTextureToMemory(src_texture->GetView(), dst, params, native_width,
bytes_per_row, num_blocks_y, memory_stride, src_rect,
scale_by_half);
m_texture_converter->EncodeTextureToMemory(
src_texture->GetView(), dst, params, native_width, bytes_per_row, num_blocks_y, memory_stride,
src_rect, scale_by_half, y_scale, gamma, clamp_top, clamp_bottom, filter_coefficients);
// Transition back to original state
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout);
@ -209,7 +211,9 @@ void TextureCache::DeleteShaders()
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
EFBCopyFormat dst_format, bool is_intensity)
EFBCopyFormat dst_format, bool is_intensity, float gamma,
bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients)
{
VKTexture* texture = static_cast<VKTexture*>(entry->texture.get());
@ -228,6 +232,26 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
StateTracker::GetInstance()->EndRenderPass();
// Fill uniform buffer.
struct PixelUniforms
{
float filter_coefficients[3];
float gamma_rcp;
float clamp_top;
float clamp_bottom;
float pixel_height;
u32 padding;
};
PixelUniforms uniforms;
for (size_t i = 0; i < filter_coefficients.size(); i++)
uniforms.filter_coefficients[i] = filter_coefficients[i] / 64.0f;
uniforms.gamma_rcp = 1.0f / gamma;
uniforms.clamp_top = clamp_top ? src_rect.top / float(EFB_HEIGHT) : 0.0f;
uniforms.clamp_bottom = clamp_bottom ? src_rect.bottom / float(EFB_HEIGHT) : 1.0f;
uniforms.pixel_height =
g_ActiveConfig.bCopyEFBScaled ? 1.0f / g_renderer->GetTargetHeight() : 1.0f / EFB_HEIGHT;
uniforms.padding = 0;
// Transition EFB to shader resource before binding.
// An out-of-bounds source region is valid here, and fine for the draw (since it is converted
// to texture coordinates), but it's not valid to resolve an out-of-range rectangle.
@ -274,6 +298,10 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
g_shader_cache->GetPassthroughVertexShader(),
g_shader_cache->GetPassthroughGeometryShader(), shader);
u8* ubo_ptr = draw.AllocatePSUniforms(sizeof(PixelUniforms));
std::memcpy(ubo_ptr, &uniforms, sizeof(PixelUniforms));
draw.CommitPSUniforms(sizeof(PixelUniforms));
draw.SetPSSampler(0, src_texture->GetView(), src_sampler);
VkRect2D dest_region = {{0, 0}, {texture->GetConfig().width, texture->GetConfig().height}};

View File

@ -38,7 +38,8 @@ public:
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) override;
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
bool SupportsGPUTextureDecode(TextureFormat format, TLUTFormat palette_format) override;
@ -52,8 +53,9 @@ public:
private:
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, EFBCopyFormat dst_format,
bool is_intensity) override;
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
float gamma, bool clamp_top, bool clamp_bottom,
const CopyFilterCoefficientArray& filter_coefficients) override;
std::unique_ptr<StreamBuffer> m_texture_upload_buffer;

View File

@ -38,6 +38,11 @@ struct EFBEncodeParams
{
std::array<s32, 4> position_uniform;
float y_scale;
float gamma_rcp;
float clamp_top;
float clamp_bottom;
s32 filter_coefficients[3];
u32 padding;
};
}
TextureConverter::TextureConverter()
@ -201,10 +206,11 @@ void TextureConverter::ConvertTexture(TextureCacheBase::TCacheEntry* dst_entry,
draw.EndRenderPass();
}
void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr,
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)
void TextureConverter::EncodeTextureToMemory(
VkImageView src_texture, u8* dest_ptr, 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, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients)
{
VkShaderModule shader = GetEncodingShader(params);
if (shader == VK_NULL_HANDLE)
@ -236,14 +242,21 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
encoder_params.position_uniform[1] = src_rect.top;
encoder_params.position_uniform[2] = static_cast<s32>(native_width);
encoder_params.position_uniform[3] = scale_by_half ? 2 : 1;
encoder_params.y_scale = params.y_scale;
draw.SetPushConstants(&encoder_params, sizeof(encoder_params));
encoder_params.y_scale = y_scale;
encoder_params.gamma_rcp = 1.0f / gamma;
encoder_params.clamp_top = clamp_top ? src_rect.top / float(EFB_HEIGHT) : 0.0f;
encoder_params.clamp_bottom = clamp_bottom ? src_rect.bottom / float(EFB_HEIGHT) : 0.0f;
for (size_t i = 0; i < filter_coefficients.size(); i++)
encoder_params.filter_coefficients[i] = filter_coefficients[i];
u8* ubo_ptr = draw.AllocatePSUniforms(sizeof(EFBEncodeParams));
std::memcpy(ubo_ptr, &encoder_params, sizeof(EFBEncodeParams));
draw.CommitPSUniforms(sizeof(EFBEncodeParams));
// We also linear filtering for both box filtering and downsampling higher resolutions to 1x
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
bool linear_filter =
(scale_by_half && !params.depth) || g_renderer->GetEFBScale() != 1 || params.y_scale > 1.0f;
(scale_by_half && !params.depth) || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f;
draw.SetPSSampler(0, src_texture,
linear_filter ? g_object_cache->GetLinearSampler() :
g_object_cache->GetPointSampler());

View File

@ -40,9 +40,12 @@ public:
// Uses an encoding shader to copy src_texture to dest_ptr.
// NOTE: Executes the current command buffer.
void EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr, 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);
void
EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr, 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, float y_scale,
float gamma, bool clamp_top, bool clamp_bottom,
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients);
bool SupportsTextureDecoding(TextureFormat format, TLUTFormat palette_format);
void DecodeTexture(VkCommandBuffer command_buffer, TextureCache::TCacheEntry* entry,