Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement scaled partial texture updates #2679

Merged
merged 1 commit into from Jul 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions Data/Sys/GameSettings/SMN.ini
Expand Up @@ -5,7 +5,6 @@

[EmuState]
# The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationIssues = If "Store EFB Copies to Texture Only" is enabled, "Scaled EFB Copy" needs to be disabled for the coins to spin.
EmulationStateId = 4

[OnLoad]
Expand All @@ -20,5 +19,4 @@ EmulationStateId = 4
[Video_Settings]
SafeTextureCacheColorSamples = 512

[Video_Hacks]
EFBScaledCopy = False
[Video_Hacks]
62 changes: 59 additions & 3 deletions Source/Core/VideoBackends/D3D/TextureCache.cpp
Expand Up @@ -77,11 +77,67 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
return saved_png;
}

void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y)
void TextureCache::TCacheEntry::CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int> &srcrect,
const MathUtil::Rectangle<int> &dstrect)
{
TCacheEntry* entry = (TCacheEntry*)entry_;
TCacheEntry* srcentry = (TCacheEntry*)source;
if (srcrect.GetWidth() == dstrect.GetWidth()
&& srcrect.GetHeight() == dstrect.GetHeight())
{
const D3D11_BOX *psrcbox = nullptr;
D3D11_BOX srcbox;
if (srcrect.left != 0 || srcrect.top != 0)
{
srcbox.left = srcrect.left;
srcbox.top = srcrect.top;
srcbox.right = srcrect.right;
srcbox.bottom = srcrect.bottom;
psrcbox = &srcbox;
}
D3D::context->CopySubresourceRegion(
texture->GetTex(),
0,
dstrect.left,
dstrect.top,
0,
srcentry->texture->GetTex(),
0,
psrcbox);
return;
}
else if (!config.rendertarget)
{
return;
}
g_renderer->ResetAPIState(); // reset any game specific settings

D3D::context->CopySubresourceRegion(texture->GetTex(), 0, x , y , 0, entry->texture->GetTex(), 0, NULL);
const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(
float(dstrect.left),
float(dstrect.top),
float(dstrect.GetWidth()),
float(dstrect.GetHeight()));

D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), nullptr);
D3D::context->RSSetViewports(1, &vp);
D3D::SetLinearCopySampler();
D3D11_RECT srcRC;
srcRC.left = srcrect.left;
srcRC.right = srcrect.right;
srcRC.top = srcrect.top;
srcRC.bottom = srcrect.bottom;
D3D::drawShadedTexQuad(srcentry->texture->GetSRV(), &srcRC,
srcentry->config.width, srcentry->config.height,
PixelShaderCache::GetColorCopyProgram(false),
VertexShaderCache::GetSimpleVertexShader(),
VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0, 0);

D3D::context->OMSetRenderTargets(1,
&FramebufferManager::GetEFBColorTexture()->GetRTV(),
FramebufferManager::GetEFBDepthTexture()->GetDSV());

g_renderer->RestoreAPIState();
}

void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
Expand Down
5 changes: 4 additions & 1 deletion Source/Core/VideoBackends/D3D/TextureCache.h
Expand Up @@ -26,7 +26,10 @@ class TextureCache : public ::TextureCache
TCacheEntry(const TCacheEntryConfig& config, D3DTexture2D *_tex) : TCacheEntryBase(config), texture(_tex) {}
~TCacheEntry();

void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) override;
void CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int> &srcrect,
const MathUtil::Rectangle<int> &dstrect) override;

void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int levels) override;
Expand Down
69 changes: 62 additions & 7 deletions Source/Core/VideoBackends/OGL/TextureCache.cpp
Expand Up @@ -33,11 +33,13 @@
namespace OGL
{

static SHADER s_ColorCopyProgram;
static SHADER s_ColorMatrixProgram;
static SHADER s_DepthMatrixProgram;
static GLuint s_ColorMatrixUniform;
static GLuint s_DepthMatrixUniform;
static GLuint s_ColorCopyPositionUniform;
static GLuint s_ColorMatrixPositionUniform;
static GLuint s_DepthCopyPositionUniform;
static u32 s_ColorCbufid;
static u32 s_DepthCbufid;
Expand Down Expand Up @@ -137,12 +139,53 @@ TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConf
return entry;
}

void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y)
void TextureCache::TCacheEntry::CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int> &srcrect,
const MathUtil::Rectangle<int> &dstrect)
{

TCacheEntry* entry = (TCacheEntry*)entry_;

glCopyImageSubData(entry->texture, GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, texture, GL_TEXTURE_2D_ARRAY, 0, x, y, 0, entry->native_width, entry->native_height, 1);
TCacheEntry* srcentry = (TCacheEntry*)source;
if (srcrect.GetWidth() == dstrect.GetWidth()
&& srcrect.GetHeight() == dstrect.GetHeight()
&& g_ActiveConfig.backend_info.bSupportsCopySubImage)
{
glCopyImageSubData(
srcentry->texture,
GL_TEXTURE_2D_ARRAY,
0,
srcrect.left,
srcrect.top,
0,
texture,
GL_TEXTURE_2D_ARRAY,
0,
dstrect.left,
dstrect.top,
0,
dstrect.GetWidth(),
dstrect.GetHeight(),
1);
return;
}
else if (!config.rendertarget)

This comment was marked as off-topic.

{
return;
}
g_renderer->ResetAPIState();
FramebufferManager::SetFramebuffer(framebuffer);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, srcentry->texture);
g_sampler_cache->BindLinearSampler(9);
glViewport(dstrect.left, dstrect.top, dstrect.GetWidth(), dstrect.GetHeight());
s_ColorCopyProgram.Bind();
glUniform4f(s_ColorCopyPositionUniform,
float(srcrect.left),
float(srcrect.top),
float(srcrect.GetWidth()),
float(srcrect.GetHeight()));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
}

void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
Expand Down Expand Up @@ -208,7 +251,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
if (s_ColorCbufid != cbufid)
glUniform4fv(s_ColorMatrixUniform, 7, colmat);
s_ColorCbufid = cbufid;
uniform_location = s_ColorCopyPositionUniform;
uniform_location = s_ColorMatrixPositionUniform;
}

TargetRectangle R = g_renderer->ConvertEFBRectangle(srcRect);
Expand Down Expand Up @@ -286,6 +329,16 @@ void TextureCache::SetStage()

void TextureCache::CompileShaders()
{
const char *pColorCopyProg =
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"in vec3 f_uv0;\n"
"out vec4 ocol0;\n"
"\n"
"void main(){\n"
" vec4 texcol = texture(samp9, f_uv0);\n"
" ocol0 = texcol;\n"
"}\n";

const char *pColorMatrixProg =
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 colmat[7];\n"
Expand Down Expand Up @@ -357,6 +410,7 @@ void TextureCache::CompileShaders()
const char* prefix = (GProgram == nullptr) ? "f" : "v";
const char* depth_layer = (g_ActiveConfig.bStereoEFBMonoDepth) ? "0.0" : "f_uv0.z";

ProgramShaderCache::CompileShader(s_ColorCopyProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorCopyProg, GProgram);
ProgramShaderCache::CompileShader(s_ColorMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorMatrixProg, GProgram);
ProgramShaderCache::CompileShader(s_DepthMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), StringFromFormat(pDepthMatrixProg, depth_layer).c_str(), GProgram);

Expand All @@ -365,7 +419,8 @@ void TextureCache::CompileShaders()
s_ColorCbufid = -1;
s_DepthCbufid = -1;

s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position");
s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorCopyProgram.glprogid, "copy_position");
s_ColorMatrixPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position");
s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position");

std::string palette_shader =
Expand Down
5 changes: 4 additions & 1 deletion Source/Core/VideoBackends/OGL/TextureCache.h
Expand Up @@ -33,7 +33,10 @@ class TextureCache : public ::TextureCache
TCacheEntry(const TCacheEntryConfig& config);
~TCacheEntry();

void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) override;
void CopyRectangleFromTexture(
const TCacheEntryBase* source,
const MathUtil::Rectangle<int> &srcrect,
const MathUtil::Rectangle<int> &dstrect) override;

void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level) override;
Expand Down
100 changes: 76 additions & 24 deletions Source/Core/VideoCommon/TextureCacheBase.cpp
Expand Up @@ -211,46 +211,98 @@ bool TextureCache::TCacheEntryBase::OverlapsMemoryRange(u32 range_address, u32 r
return true;
}

void TextureCache::TCacheEntryBase::DoPartialTextureUpdates()
TextureCache::TCacheEntryBase* TextureCache::DoPartialTextureUpdates(TexCache::iterator iter_t)
{
const bool isPaletteTexture = (format== GX_TF_C4 || format == GX_TF_C8 || format == GX_TF_C14X2 || format >= 0x10000);
TCacheEntryBase* entry_to_update = iter_t->second;
const bool isPaletteTexture = (entry_to_update->format == GX_TF_C4
|| entry_to_update->format == GX_TF_C8
|| entry_to_update->format == GX_TF_C14X2
|| entry_to_update->format >= 0x10000);

// Efb copies and paletted textures are excluded from these updates, until there's an example where a game would
// benefit from this. Both would require more work to be done.
// TODO: Implement upscaling support for normal textures, and then remove the efb to ram and the scaled efb restrictions
if (!g_ActiveConfig.backend_info.bSupportsCopySubImage || !g_ActiveConfig.bSkipEFBCopyToRam || IsEfbCopy()
|| isPaletteTexture || (g_ActiveConfig.bCopyEFBScaled && g_ActiveConfig.iEFBScale != SCALE_1X))
return;

u32 block_width = TexDecoder_GetBlockWidthInTexels(format);
u32 block_height = TexDecoder_GetBlockHeightInTexels(format);
u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(format) / 2;
if (entry_to_update->IsEfbCopy()
|| isPaletteTexture)
return entry_to_update;

u32 numBlocksX = (native_width + block_width - 1) / block_width;
u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format);
u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format);
u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(entry_to_update->format) / 2;

TexCache::iterator iter = textures_by_address.lower_bound(addr);
TexCache::iterator iterend = textures_by_address.upper_bound(addr + size_in_bytes);
u32 numBlocksX = (entry_to_update->native_width + block_width - 1) / block_width;

TexCache::iterator iter = textures_by_address.lower_bound(entry_to_update->addr);
TexCache::iterator iterend = textures_by_address.upper_bound(entry_to_update->addr + entry_to_update->size_in_bytes);
bool entry_need_scaling = true;
while (iter != iterend)
{
TCacheEntryBase* entry = iter->second;
if (entry->IsEfbCopy() && addr <= entry->addr && entry->addr + entry->size_in_bytes <= addr + size_in_bytes
&& entry->frameCount == FRAMECOUNT_INVALID && entry->copyMipMapStrideChannels * 32 == numBlocksX * block_size)
if (entry != entry_to_update
&& entry->IsEfbCopy()
&& entry_to_update->addr <= entry->addr
&& entry->addr + entry->size_in_bytes <= entry_to_update->addr + entry_to_update->size_in_bytes
&& entry->frameCount == FRAMECOUNT_INVALID
&& entry->copyMipMapStrideChannels * 32 == numBlocksX * block_size)
{
u32 block_offset = (entry->addr - addr) / block_size;
u32 block_offset = (entry->addr - entry_to_update->addr) / block_size;
u32 block_x = block_offset % numBlocksX;
u32 block_y = block_offset / numBlocksX;

u32 x = block_x * block_width;
u32 y = block_y * block_height;

DoPartialTextureUpdate(entry, x, y);

MathUtil::Rectangle<int> srcrect, dstrect;
srcrect.left = 0;
srcrect.top = 0;
dstrect.left = 0;
dstrect.top = 0;
if (entry_need_scaling)
{
entry_need_scaling = false;
u32 w = entry_to_update->native_width * entry->config.width / entry->native_width;
u32 h = entry_to_update->native_height * entry->config.height / entry->native_height;
u32 max = g_renderer->GetMaxTextureSize();
if (max < w || max < h)
{
iter++;
continue;
}
if (entry_to_update->config.width != w || entry_to_update->config.height != h)

This comment was marked as off-topic.

This comment was marked as off-topic.

{
TextureCache::TCacheEntryConfig newconfig;
newconfig.width = w;
newconfig.height = h;
newconfig.rendertarget = true;
TCacheEntryBase* newentry = AllocateTexture(newconfig);
newentry->SetGeneralParameters(entry_to_update->addr, entry_to_update->size_in_bytes, entry_to_update->format);
newentry->SetDimensions(entry_to_update->native_width, entry_to_update->native_height, 1);
newentry->SetHashes(entry_to_update->hash);
newentry->frameCount = frameCount;
newentry->is_efb_copy = false;
srcrect.right = entry_to_update->config.width;
srcrect.bottom = entry_to_update->config.height;
dstrect.right = w;
dstrect.bottom = h;
newentry->CopyRectangleFromTexture(entry_to_update, srcrect, dstrect);

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

entry_to_update = newentry;
u64 key = iter_t->first;
iter_t = FreeTexture(iter_t);
textures_by_address.emplace(key, entry_to_update);
}
}
srcrect.right = entry->config.width;
srcrect.bottom = entry->config.height;
dstrect.left = x * entry_to_update->config.width / entry_to_update->native_width;
dstrect.top = y * entry_to_update->config.height / entry_to_update->native_height;
dstrect.right = (x + entry->native_width) * entry_to_update->config.width / entry_to_update->native_width;
dstrect.bottom = (y + entry->native_height) * entry_to_update->config.height / entry_to_update->native_height;
entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect);
// Mark the texture update as used, so it isn't applied more than once
entry->frameCount = frameCount;
}
++iter;
}
return entry_to_update;
}

void TextureCache::DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level)
Expand Down Expand Up @@ -323,7 +375,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1;
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1;

unsigned int expandedWidth = (width + bsw) & (~bsw);
unsigned int expandedWidth = (width + bsw) & (~bsw);
unsigned int expandedHeight = (height + bsh) & (~bsh);
const unsigned int nativeW = width;
const unsigned int nativeH = height;
Expand Down Expand Up @@ -440,7 +492,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
if (entry->hash == full_hash && entry->format == full_format && entry->native_levels >= tex_levels &&
entry->native_width == nativeW && entry->native_height == nativeH)
{
entry->DoPartialTextureUpdates();
entry = DoPartialTextureUpdates(iter);

return ReturnEntry(stage, entry);
}
Expand Down Expand Up @@ -494,7 +546,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
if (entry->format == full_format && entry->native_levels >= tex_levels &&
entry->native_width == nativeW && entry->native_height == nativeH)
{
entry->DoPartialTextureUpdates();
entry = DoPartialTextureUpdates(iter);

return ReturnEntry(stage, entry);
}
Expand Down Expand Up @@ -539,7 +591,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
if (!(texformat == GX_TF_RGBA8 && from_tmem))
{
const u8* tlut = &texMem[tlutaddr];
TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat) tlutfmt);
TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat)tlutfmt);
}
else
{
Expand All @@ -560,7 +612,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
TCacheEntryBase* entry = AllocateTexture(config);
GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true);

textures_by_address.emplace((u64)address, entry);
iter = textures_by_address.emplace((u64)address, entry);
if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 ||
std::max(texture_size, palette_size) <= (u32)g_ActiveConfig.iSafeTextureCache_ColorSamples * 8)
{
Expand Down Expand Up @@ -636,7 +688,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
INCSTAT(stats.numTexturesUploaded);
SETSTAT(stats.numTexturesAlive, textures_by_address.size());

entry->DoPartialTextureUpdates();
entry = DoPartialTextureUpdates(iter);

return ReturnEntry(stage, entry);
}
Expand Down