Skip to content
Permalink
Browse files
Merge pull request #7539 from stenzek/batched-efb-copies
TextureCache: Deferred/batched EFB copies
  • Loading branch information
delroth committed Nov 7, 2018
2 parents 710b893 + a45f977 commit dac58a8
Show file tree
Hide file tree
Showing 36 changed files with 351 additions and 147 deletions.
@@ -559,6 +559,7 @@ private void addHackSettings(ArrayList<SettingsItem> sl)
new BooleanSetting(SettingsFile.KEY_IGNORE_FORMAT, Settings.SECTION_GFX_HACKS,
ignoreFormatValue);
Setting efbToTexture = hacksSection.getSetting(SettingsFile.KEY_EFB_TEXTURE);
Setting deferEfbCopies = hacksSection.getSetting(SettingsFile.KEY_DEFER_EFB_COPIES);
Setting texCacheAccuracy = gfxSection.getSetting(SettingsFile.KEY_TEXCACHE_ACCURACY);
Setting gpuTextureDecoding = gfxSection.getSetting(SettingsFile.KEY_GPU_TEXTURE_DECODING);
Setting xfbToTexture = hacksSection.getSetting(SettingsFile.KEY_XFB_TEXTURE);
@@ -573,6 +574,9 @@ private void addHackSettings(ArrayList<SettingsItem> sl)
ignoreFormat));
sl.add(new CheckBoxSetting(SettingsFile.KEY_EFB_TEXTURE, Settings.SECTION_GFX_HACKS,
R.string.efb_copy_method, R.string.efb_copy_method_description, true, efbToTexture));
sl.add(new CheckBoxSetting(SettingsFile.KEY_DEFER_EFB_COPIES, Settings.SECTION_GFX_HACKS,
R.string.defer_efb_copies, R.string.defer_efb_copies_description, true,
deferEfbCopies));

sl.add(new HeaderSetting(null, null, R.string.texture_cache, 0));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_TEXCACHE_ACCURACY,
@@ -80,6 +80,7 @@
public static final String KEY_SKIP_EFB = "EFBAccessEnable";
public static final String KEY_IGNORE_FORMAT = "EFBEmulateFormatChanges";
public static final String KEY_EFB_TEXTURE = "EFBToTextureEnable";
public static final String KEY_DEFER_EFB_COPIES = "DeferEFBCopies";
public static final String KEY_TEXCACHE_ACCURACY = "SafeTextureCacheColorSamples";
public static final String KEY_GPU_TEXTURE_DECODING = "EnableGPUTextureDecoding";
public static final String KEY_XFB_TEXTURE = "XFBToTextureEnable";
@@ -207,6 +207,8 @@
<string name="ignore_format_changes_description">Ignore any changes to the EFB format.</string>
<string name="efb_copy_method">Store EFB Copies to Texture Only</string>
<string name="efb_copy_method_description">Stores EFB Copies exclusively on the GPU, bypassing system memory. Causes graphical defects in a small number of games. If unsure, leave this checked.</string>
<string name="defer_efb_copies">Defer EFB Copies to RAM</string>
<string name="defer_efb_copies_description">Waits until the game synchronizes with the emulated GPU before writing the contents of EFB copies to RAM. May result in faster performance. If unsure, leave this unchecked.</string>
<string name="texture_cache">Texture Cache</string>
<string name="texture_cache_accuracy">Texture Cache Accuracy</string>
<string name="texture_cache_accuracy_description">The safer the selection, the less likely the emulator will be missing any texture updates from RAM.</string>
@@ -317,6 +317,7 @@ void DolphinAnalytics::MakePerGameBuilder()
builder.AddData("cfg-gfx-efb-copy-format-changes", g_Config.bEFBEmulateFormatChanges);
builder.AddData("cfg-gfx-efb-copy-ram", !g_Config.bSkipEFBCopyToRam);
builder.AddData("cfg-gfx-xfb-copy-ram", !g_Config.bSkipXFBCopyToRam);
builder.AddData("cfg-gfx-defer-efb-copies", g_Config.bDeferEFBCopies);
builder.AddData("cfg-gfx-immediate-xfb", !g_Config.bImmediateXFB);
builder.AddData("cfg-gfx-efb-copy-scaled", g_Config.bCopyEFBScaled);
builder.AddData("cfg-gfx-internal-resolution", g_Config.iEFBScale);
@@ -139,6 +139,7 @@ const ConfigInfo<bool> GFX_HACK_SKIP_XFB_COPY_TO_RAM{{System::GFX, "Hacks", "XFB
true};
const ConfigInfo<bool> GFX_HACK_DISABLE_COPY_TO_VRAM{{System::GFX, "Hacks", "DisableCopyToVRAM"},
false};
const ConfigInfo<bool> GFX_HACK_DEFER_EFB_COPIES{{System::GFX, "Hacks", "DeferEFBCopies"}, true};
const ConfigInfo<bool> GFX_HACK_IMMEDIATE_XFB{{System::GFX, "Hacks", "ImmediateXFBEnable"}, false};
const ConfigInfo<bool> GFX_HACK_COPY_EFB_SCALED{{System::GFX, "Hacks", "EFBScaledCopy"}, true};
const ConfigInfo<bool> GFX_HACK_EFB_EMULATE_FORMAT_CHANGES{
@@ -107,6 +107,7 @@ extern const ConfigInfo<bool> GFX_HACK_FORCE_PROGRESSIVE;
extern const ConfigInfo<bool> GFX_HACK_SKIP_EFB_COPY_TO_RAM;
extern const ConfigInfo<bool> GFX_HACK_SKIP_XFB_COPY_TO_RAM;
extern const ConfigInfo<bool> GFX_HACK_DISABLE_COPY_TO_VRAM;
extern const ConfigInfo<bool> GFX_HACK_DEFER_EFB_COPIES;
extern const ConfigInfo<bool> GFX_HACK_IMMEDIATE_XFB;
extern const ConfigInfo<bool> GFX_HACK_COPY_EFB_SCALED;
extern const ConfigInfo<bool> GFX_HACK_EFB_EMULATE_FORMAT_CHANGES;
@@ -114,6 +114,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location)
Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM.location,
Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM.location,
Config::GFX_HACK_DISABLE_COPY_TO_VRAM.location,
Config::GFX_HACK_DEFER_EFB_COPIES.location,
Config::GFX_HACK_IMMEDIATE_XFB.location,
Config::GFX_HACK_COPY_EFB_SCALED.location,
Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES.location,
@@ -46,10 +46,13 @@ void HacksWidget::CreateWidgets()
Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, true);
m_store_efb_copies = new GraphicsBool(tr("Store EFB Copies to Texture Only"),
Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM);
m_defer_efb_copies =
new GraphicsBool(tr("Defer EFB Copies to RAM"), Config::GFX_HACK_DEFER_EFB_COPIES);

efb_layout->addWidget(m_skip_efb_cpu, 0, 0);
efb_layout->addWidget(m_ignore_format_changes, 0, 1);
efb_layout->addWidget(m_store_efb_copies, 1, 0);
efb_layout->addWidget(m_defer_efb_copies, 1, 1);

// Texture Cache
auto* texture_cache_box = new QGroupBox(tr("Texture Cache"));
@@ -109,6 +112,8 @@ void HacksWidget::CreateWidgets()
main_layout->addStretch();

setLayout(main_layout);

UpdateDeferEFBCopiesEnabled();
}

void HacksWidget::OnBackendChanged(const QString& backend_name)
@@ -129,6 +134,10 @@ void HacksWidget::OnBackendChanged(const QString& backend_name)
void HacksWidget::ConnectWidgets()
{
connect(m_accuracy, &QSlider::valueChanged, [this](int) { SaveSettings(); });
connect(m_store_efb_copies, &QCheckBox::stateChanged,
[this](int) { UpdateDeferEFBCopiesEnabled(); });
connect(m_store_xfb_copies, &QCheckBox::stateChanged,
[this](int) { UpdateDeferEFBCopiesEnabled(); });
}

void HacksWidget::LoadSettings()
@@ -202,6 +211,11 @@ void HacksWidget::AddDescriptions()
"in a small number of games.\n\nEnabled = EFB Copies to Texture\nDisabled = EFB Copies to "
"RAM "
"(and Texture)\n\nIf unsure, leave this checked.");
static const char TR_DEFER_EFB_COPIES_DESCRIPTION[] = QT_TR_NOOP(
"Waits until the game synchronizes with the emulated GPU before writing the contents of EFB "
"copies to RAM. Reduces the overhead of EFB RAM copies, provides a performance boost in many "
"games, at the risk of breaking those which do not safely synchronize with the emulated "
"GPU.\n\nIf unsure, leave this checked.");
static const char TR_ACCUARCY_DESCRIPTION[] = QT_TR_NOOP(
"The \"Safe\" setting eliminates the likelihood of the GPU missing texture updates "
"from RAM.\nLower accuracies cause in-game text to appear garbled in certain "
@@ -240,6 +254,7 @@ void HacksWidget::AddDescriptions()
AddDescription(m_skip_efb_cpu, TR_SKIP_EFB_CPU_ACCESS_DESCRIPTION);
AddDescription(m_ignore_format_changes, TR_IGNORE_FORMAT_CHANGE_DESCRIPTION);
AddDescription(m_store_efb_copies, TR_STORE_EFB_TO_TEXTURE_DESCRIPTION);
AddDescription(m_defer_efb_copies, TR_DEFER_EFB_COPIES_DESCRIPTION);
AddDescription(m_accuracy, TR_ACCUARCY_DESCRIPTION);
AddDescription(m_store_xfb_copies, TR_STORE_XFB_TO_TEXTURE_DESCRIPTION);
AddDescription(m_immediate_xfb, TR_IMMEDIATE_XFB_DESCRIPTION);
@@ -248,3 +263,11 @@ void HacksWidget::AddDescriptions()
AddDescription(m_disable_bounding_box, TR_DISABLE_BOUNDINGBOX_DESCRIPTION);
AddDescription(m_vertex_rounding, TR_VERTEX_ROUNDING_DESCRIPTION);
}

void HacksWidget::UpdateDeferEFBCopiesEnabled()
{
// We disable the checkbox for defer EFB copies when both EFB and XFB copies to texture are
// enabled.
const bool can_defer = m_store_efb_copies->isChecked() && m_store_xfb_copies->isChecked();
m_defer_efb_copies->setEnabled(!can_defer);
}
@@ -42,8 +42,11 @@ class HacksWidget final : public GraphicsWidget
QCheckBox* m_fast_depth_calculation;
QCheckBox* m_disable_bounding_box;
QCheckBox* m_vertex_rounding;
QCheckBox* m_defer_efb_copies;

void CreateWidgets();
void ConnectWidgets();
void AddDescriptions();

void UpdateDeferEFBCopiesEnabled();
};
@@ -46,13 +46,8 @@ PSTextureEncoder::~PSTextureEncoder() = default;

void PSTextureEncoder::Init()
{
// TODO: Move this to a constant somewhere in common.
TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8,
true);
m_encoding_render_texture = g_renderer->CreateTexture(encoding_texture_config);
m_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, encoding_texture_config);
ASSERT(m_encoding_render_texture && m_encoding_readback_texture);
m_encoding_render_texture = g_renderer->CreateTexture(TextureCache::GetEncodingTextureConfig());
ASSERT(m_encoding_render_texture);

// Create constant buffer for uploading data to shaders
D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(EFBEncodeParams), D3D11_BIND_CONSTANT_BUFFER);
@@ -71,9 +66,9 @@ void PSTextureEncoder::Shutdown()
}

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,
AbstractStagingTexture* 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.
@@ -133,14 +128,7 @@ void PSTextureEncoder::Encode(

// Copy to staging buffer
MathUtil::Rectangle<int> copy_rect(0, 0, words_per_row, num_blocks_y);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->Flush();
if (m_encoding_readback_texture->Map())
{
m_encoding_readback_texture->ReadTexels(copy_rect, dst, memory_stride);
m_encoding_readback_texture->Unmap();
}
dst->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, copy_rect);
}

g_renderer->RestoreAPIState();
@@ -38,17 +38,16 @@ class PSTextureEncoder final

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,
float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
void Encode(AbstractStagingTexture* 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);

private:
ID3D11PixelShader* GetEncodingPixelShader(const EFBCopyParams& params);

ID3D11Buffer* m_encode_params = nullptr;
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
std::map<EFBCopyParams, ID3D11PixelShader*> m_encoding_shaders;
};
}
@@ -31,8 +31,8 @@ namespace DX11
{
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,
void TextureCache::CopyEFB(AbstractStagingTexture* 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 CopyFilterCoefficientArray& filter_coefficients)
@@ -22,18 +22,11 @@ class TextureCache : public TextureCacheBase
~TextureCache();

private:
u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH,
bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf,
const EFBRectangle& source)
{
return 0;
};

void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;

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,
void CopyEFB(AbstractStagingTexture* 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 CopyFilterCoefficientArray& filter_coefficients) override;

@@ -25,8 +25,8 @@ class TextureCache : public TextureCacheBase
{
}

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,
void CopyEFB(AbstractStagingTexture* 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 CopyFilterCoefficientArray& filter_coefficients) override
{
@@ -159,8 +159,8 @@ void main()

//#define TIME_TEXTURE_DECODING 1

void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
void TextureCache::CopyEFB(AbstractStagingTexture* 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 CopyFilterCoefficientArray& filter_coefficients)
@@ -63,8 +63,8 @@ class TextureCache : public TextureCacheBase
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;

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,
void CopyEFB(AbstractStagingTexture* 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 CopyFilterCoefficientArray& filter_coefficients) override;

@@ -46,7 +46,6 @@ struct EncodingProgram

std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
std::unique_ptr<AbstractTexture> s_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> s_encoding_readback_texture;

const int renderBufferWidth = EFB_WIDTH * 4;
const int renderBufferHeight = 1024;
@@ -93,16 +92,11 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)

void Init()
{
TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, 1, AbstractTextureFormat::BGRA8,
true);
s_encoding_render_texture = g_renderer->CreateTexture(config);
s_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
s_encoding_render_texture = g_renderer->CreateTexture(TextureCache::GetEncodingTextureConfig());
}

void Shutdown()
{
s_encoding_readback_texture.reset();
s_encoding_render_texture.reset();

for (auto& program : s_encoding_programs)
@@ -112,8 +106,9 @@ void Shutdown()

// dst_line_size, writeStride in bytes

static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size,
u32 dstHeight, u32 writeStride, bool linearFilter, float y_scale)
static void EncodeToRamUsingShader(GLuint srcTexture, AbstractStagingTexture* destAddr,
u32 dst_line_size, u32 dstHeight, u32 writeStride,
bool linearFilter, float y_scale)
{
FramebufferManager::SetFramebuffer(
static_cast<OGLTexture*>(s_encoding_render_texture.get())->GetFramebuffer());
@@ -137,15 +132,14 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

MathUtil::Rectangle<int> copy_rect(0, 0, dst_line_size / 4, dstHeight);
s_encoding_readback_texture->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
s_encoding_readback_texture->ReadTexels(copy_rect, destAddr, writeStride);

destAddr->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0, copy_rect);
}

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,
void EncodeToRamFromTexture(AbstractStagingTexture* dest, 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)
{
g_renderer->ResetAPIState();
@@ -165,7 +159,7 @@ void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 nativ
FramebufferManager::ResolveAndGetDepthTarget(src_rect) :
FramebufferManager::ResolveAndGetRenderTarget(src_rect);

EncodeToRamUsingShader(read_texture, dest_ptr, bytes_per_row, num_blocks_y, memory_stride,
EncodeToRamUsingShader(read_texture, dest, bytes_per_row, num_blocks_y, memory_stride,
scale_by_half && !params.depth, y_scale);

g_renderer->RestoreAPIState();
@@ -10,6 +10,9 @@
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoCommon.h"

struct EFBCopyParams;
class AbstractStagingTexture;

namespace OGL
{
// Converts textures between formats using shaders
@@ -21,7 +24,7 @@ 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,
AbstractStagingTexture* dest, 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);

0 comments on commit dac58a8

Please sign in to comment.