Various ClearScreen fixes.

Will fix some games and break some others.
Assist trophies in SSBB work fine now, Super Mario Sunshine is a little broken again.
Still needs some more work...

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6627 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
NeoBrainX 2010-12-20 16:57:29 +00:00
parent 83868566ea
commit 9ebf507a55
4 changed files with 92 additions and 29 deletions

View File

@ -86,27 +86,73 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
}
}
/* Explanation of the magic behind ClearScreen:
There's numerous possible formats for the pixel data in the EFB.
However, in the HW accelerated plugins we're always using RGBA8
for the EFB format, which implicates some problems:
- We're using an alpha channel although the game doesn't (1)
- If the actual EFB format is PIXELFMT_RGBA6_Z24, we are using more bits per channel than the native HW (2)
- When doing a z copy (EFB copy target format GX_TF_Z24X8 (and possibly others?)), the native HW assumes that the EFB format is RGB8 when clearing.
Thus the RGBA6 values get overwritten with plain RGB8 data without any kind of conversion. (3)
- When changing EFB formats, the EFB contents will NOT get converted to the new format;
this currently isn't implemented in any HW accelerated plugin and might cause issues. (4)
- Possible other oddities should be noted here as well
To properly emulate the above points, we're doing the following:
(1)
- disable alpha channel writing in any kind of rendering if the actual EFB format doesn't use an alpha channel
- when clearing:
- if the actual EFB format uses an alpha channel, enable alpha channel writing if alphaupdate is set
- if the actual EFB format doesn't use an alpha channel, set the alpha channel to 0xFF
(2)
- just scale down the RGBA8 color to RGBA6 and upscale it to RGBA8 again
(3)
- more tricky, doing some bit magic here to properly reinterpret the data
(4) TODO
- generally delay ClearScreen calls as long as possible (until any other EFB access)
- when the pixel format changes:
- call ClearScreen if it's still being delayed, reinterpret the color for the new format though
- otherwise convert EFB contents to the new pixel format
*/
void ClearScreen(const BPCmd &bp, const EFBRectangle &rc)
{
bool colorEnable = bpmem.blendmode.colorupdate;
bool alphaEnable = (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24 && bpmem.blendmode.alphaupdate);
bool alphaEnable = bpmem.blendmode.alphaupdate || (bpmem.zcontrol.pixel_format != PIXELFMT_RGBA6_Z24); // (1)
bool zEnable = bpmem.zmode.updateenable;
if (colorEnable || alphaEnable || zEnable)
{
u32 color = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB;
u32 z = bpmem.clearZValue;
/*
// texture formats logic transposition from "EFB Copy to Texture" to "Copy Clear Screen" concepts.
// this it's a deduction without assurance. Ref. (p.12(Nintendo Co., Ltd. US 2010/0073394 A1))
UPE_Copy EFB_copy = bpmem.triggerEFBCopy;
// since this is an early implementation and we can't be sure, forward clauses are fairly restrictive.
if (EFB_copy.tp_realFormat() == 6) // RGBA8
color |= (!EFB_copy.intensity_fmt && z > 0) ? 0xFF000000 : 0x0;
else if (EFB_copy.tp_realFormat() == 7) // A8
color |= ((!EFB_copy.intensity_fmt && bpmem.zcontrol.pixel_format > 3) || z > 0) ? 0xFF000000 : 0x0;
*/
if (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24)
{
UPE_Copy PE_copy = bpmem.triggerEFBCopy;
// TODO: Not sure whether there's more formats to check for here - maybe GX_TF_Z8 and GX_TF_Z16?
if (PE_copy.tp_realFormat() == GX_TF_Z24X8) // (3): Reinterpret RGB8 color as RGBA6
{
u32 srcr8 = (color & 0xFF0000) >> 16;
u32 srcg8 = (color & 0xFF00) >> 8;
u32 srcb8 = color & 0xFF;
u32 dstr6 = srcr8 >> 2;
u32 dstg6 = ((srcr8 & 0xFF) << 4) | (srcg8 >> 4);
u32 dstb6 = ((srcg8 & 0xFFFF) << 2) | (srcb8 >> 6);
u32 dsta6 = srcb8 & 0xFFFFFF;
u32 dstr8 = (dstr6 << 2) | (dstr6>>4);
u32 dstg8 = (dstg6 << 2) | (dstg6>>4);
u32 dstb8 = (dstb6 << 2) | (dstb6>>4);
u32 dsta8 = (dsta6 << 2) | (dsta6>>4);
color = (dsta8 << 24) | (dstr8 << 16) | (dstg8 << 8) | dstb8;
}
else // (2): convert RGBA8 color to RGBA6
{
color = ((color & 0xFCFCFCFC) >> 2) << 2;
color |= (color >> 6) & 0x3030303;
}
}
else // (1): Clear alpha channel to 0xFF if no alpha channel is supposed to be there
{
color |= 0xFF000000;
}
g_renderer->ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z);
}
}

View File

@ -462,9 +462,12 @@ bool Renderer::SetScissorRect()
void Renderer::SetColorMask()
{
// Only enable alpha channel if it's supported by the current EFB format
UINT8 color_mask = 0;
if (bpmem.blendmode.alphaupdate) color_mask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
if (bpmem.blendmode.colorupdate) color_mask |= D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE;
if (bpmem.blendmode.alphaupdate && (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24))
color_mask = D3D11_COLOR_WRITE_ENABLE_ALPHA;
if (bpmem.blendmode.colorupdate)
color_mask |= D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE;
D3D::gfxstate->SetRenderTargetWriteMask(color_mask);
}
@ -688,14 +691,15 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE
{
ResetAPIState();
if (bpmem.blendmode.colorupdate && bpmem.blendmode.alphaupdate) D3D::stateman->PushBlendState(clearblendstates[0]);
else if (bpmem.blendmode.colorupdate) D3D::stateman->PushBlendState(clearblendstates[1]);
else if (bpmem.blendmode.alphaupdate) D3D::stateman->PushBlendState(clearblendstates[2]);
if (colorEnable && alphaEnable) D3D::stateman->PushBlendState(clearblendstates[0]);
else if (colorEnable) D3D::stateman->PushBlendState(clearblendstates[1]);
else if (alphaEnable) D3D::stateman->PushBlendState(clearblendstates[2]);
else D3D::stateman->PushBlendState(clearblendstates[3]);
if (!bpmem.zmode.testenable) D3D::stateman->PushDepthState(cleardepthstates[0]);
else if (bpmem.zmode.updateenable) D3D::stateman->PushDepthState(cleardepthstates[1]);
else /*if (!bpmem.zmode.updateenable)*/ D3D::stateman->PushDepthState(cleardepthstates[2]);
// TODO: Should we enable Z testing here?
/*if (!bpmem.zmode.testenable) D3D::stateman->PushDepthState(cleardepthstates[0]);
else */if (zEnable) D3D::stateman->PushDepthState(cleardepthstates[1]);
else /*if (!zEnable)*/ D3D::stateman->PushDepthState(cleardepthstates[2]);
// Update the view port for clearing the picture
TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc);

View File

@ -473,8 +473,9 @@ bool Renderer::SetScissorRect()
void Renderer::SetColorMask()
{
// Only enable alpha channel if it's supported by the current EFB format
DWORD color_mask = 0;
if (bpmem.blendmode.alphaupdate)
if (bpmem.blendmode.alphaupdate && (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24))
color_mask = D3DCOLORWRITEENABLE_ALPHA;
if (bpmem.blendmode.colorupdate)
color_mask |= D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE;
@ -789,11 +790,22 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE
{
// Reset rendering pipeline while keeping color masks and depth buffer settings
ResetAPIState();
SetDepthMode();
SetColorMask();
if (zEnable) // other depth functions don't make sense here
DWORD color_mask = 0;
if (alphaEnable)
color_mask = D3DCOLORWRITEENABLE_ALPHA;
if (colorEnable)
color_mask |= D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE;
D3D::ChangeRenderState(D3DRS_COLORWRITEENABLE, color_mask);
if (zEnable)
{
D3D::ChangeRenderState(D3DRS_ZENABLE, TRUE);
D3D::ChangeRenderState(D3DRS_ZWRITEENABLE, TRUE);
D3D::ChangeRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
}
else
D3D::ChangeRenderState(D3DRS_ZENABLE, FALSE);
// Update the viewport for clearing the target EFB rect
TargetRectangle targetRc = ConvertEFBRectangle(rc);

View File

@ -661,8 +661,9 @@ bool Renderer::SetScissorRect()
void Renderer::SetColorMask()
{
// Only enable alpha channel if it's supported by the current EFB format
GLenum ColorMask = (bpmem.blendmode.colorupdate) ? GL_TRUE : GL_FALSE;
GLenum AlphaMask = (bpmem.blendmode.alphaupdate) ? GL_TRUE : GL_FALSE;
GLenum AlphaMask = (bpmem.blendmode.alphaupdate && (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24)) ? GL_TRUE : GL_FALSE;
glColorMask(ColorMask, ColorMask, ColorMask, AlphaMask);
}
@ -807,13 +808,13 @@ void Renderer::UpdateViewport()
glDepthRange(GLNear, GLFar);
}
// TODO: Clearing RGB or alpha only isn't implemented, yet!
void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z)
{
// Update the view port for clearing the picture
TargetRectangle targetRc = ConvertEFBRectangle(rc);
glViewport(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
// Always set the scissor in case it was set by the game and has not been reset
glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());