summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Software/EfbInterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoBackends/Software/EfbInterface.cpp')
-rw-r--r--Source/Core/VideoBackends/Software/EfbInterface.cpp1239
1 files changed, 621 insertions, 618 deletions
diff --git a/Source/Core/VideoBackends/Software/EfbInterface.cpp b/Source/Core/VideoBackends/Software/EfbInterface.cpp
index 9d8af4f88b..abfb9edc79 100644
--- a/Source/Core/VideoBackends/Software/EfbInterface.cpp
+++ b/Source/Core/VideoBackends/Software/EfbInterface.cpp
@@ -12,625 +12,628 @@
#include "VideoCommon/LookUpTables.h"
#include "VideoCommon/PerfQueryBase.h"
-
-static u8 efb[EFB_WIDTH*EFB_HEIGHT*6];
+static u8 efb[EFB_WIDTH * EFB_HEIGHT * 6];
namespace EfbInterface
{
- u32 perf_values[PQ_NUM_MEMBERS];
-
- static inline u32 GetColorOffset(u16 x, u16 y)
- {
- return (x + y * EFB_WIDTH) * 3;
- }
-
- static inline u32 GetDepthOffset(u16 x, u16 y)
- {
- return (x + y * EFB_WIDTH) * 3 + DEPTH_BUFFER_START;
- }
-
- static void SetPixelAlphaOnly(u32 offset, u8 a)
- {
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::Z24:
- case PEControl::RGB565_Z16:
- // do nothing
- break;
- case PEControl::RGBA6_Z24:
- {
- u32 a32 = a;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xffffffc0;
- val |= (a32 >> 2) & 0x0000003f;
- *dst = val;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
- }
-
- static void SetPixelColorOnly(u32 offset, u8 *rgb)
- {
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::Z24:
- {
- u32 src = *(u32*)rgb;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= src >> 8;
- *dst = val;
- }
- break;
- case PEControl::RGBA6_Z24:
- {
- u32 src = *(u32*)rgb;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff00003f;
- val |= (src >> 4) & 0x00000fc0; // blue
- val |= (src >> 6) & 0x0003f000; // green
- val |= (src >> 8) & 0x00fc0000; // red
- *dst = val;
- }
- break;
- case PEControl::RGB565_Z16:
- {
- INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
- u32 src = *(u32*)rgb;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= src >> 8;
- *dst = val;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
- }
-
- static void SetPixelAlphaColor(u32 offset, u8 *color)
- {
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::Z24:
- {
- u32 src = *(u32*)color;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= src >> 8;
- *dst = val;
- }
- break;
- case PEControl::RGBA6_Z24:
- {
- u32 src = *(u32*)color;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= (src >> 2) & 0x0000003f; // alpha
- val |= (src >> 4) & 0x00000fc0; // blue
- val |= (src >> 6) & 0x0003f000; // green
- val |= (src >> 8) & 0x00fc0000; // red
- *dst = val;
- }
- break;
- case PEControl::RGB565_Z16:
- {
- INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
- u32 src = *(u32*)color;
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= src >> 8;
- *dst = val;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
- }
-
- static void GetPixelColor(u32 offset, u8 *color)
- {
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::Z24:
- {
- u32 src = *(u32*)&efb[offset];
- u32 *dst = (u32*)color;
- u32 val = 0xff | ((src & 0x00ffffff) << 8);
- *dst = val;
- }
- break;
- case PEControl::RGBA6_Z24:
- {
- u32 src = *(u32*)&efb[offset];
- color[ALP_C] = Convert6To8(src & 0x3f);
- color[BLU_C] = Convert6To8((src >> 6) & 0x3f);
- color[GRN_C] = Convert6To8((src >> 12) & 0x3f);
- color[RED_C] = Convert6To8((src >> 18) & 0x3f);
- }
- break;
- case PEControl::RGB565_Z16:
- {
- INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
- u32 src = *(u32*)&efb[offset];
- u32 *dst = (u32*)color;
- u32 val = 0xff | ((src & 0x00ffffff) << 8);
- *dst = val;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
- }
-
- static void SetPixelDepth(u32 offset, u32 depth)
- {
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::RGBA6_Z24:
- case PEControl::Z24:
- {
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= depth & 0x00ffffff;
- *dst = val;
- }
- break;
- case PEControl::RGB565_Z16:
- {
- INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
- u32 *dst = (u32*)&efb[offset];
- u32 val = *dst & 0xff000000;
- val |= depth & 0x00ffffff;
- *dst = val;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
- }
-
- static u32 GetPixelDepth(u32 offset)
- {
- u32 depth = 0;
-
- switch (bpmem.zcontrol.pixel_format)
- {
- case PEControl::RGB8_Z24:
- case PEControl::RGBA6_Z24:
- case PEControl::Z24:
- {
- depth = (*(u32*)&efb[offset]) & 0x00ffffff;
- }
- break;
- case PEControl::RGB565_Z16:
- {
- INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
- depth = (*(u32*)&efb[offset]) & 0x00ffffff;
- }
- break;
- default:
- ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
- }
-
- return depth;
- }
-
- static u32 GetSourceFactor(u8 *srcClr, u8 *dstClr, BlendMode::BlendFactor mode)
- {
- switch (mode)
- {
- case BlendMode::ZERO:
- return 0;
- case BlendMode::ONE:
- return 0xffffffff;
- case BlendMode::DSTCLR:
- return *(u32*)dstClr;
- case BlendMode::INVDSTCLR:
- return 0xffffffff - *(u32*)dstClr;
- case BlendMode::SRCALPHA:
- {
- u8 alpha = srcClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::INVSRCALPHA:
- {
- u8 alpha = 0xff - srcClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::DSTALPHA:
- {
- u8 alpha = dstClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::INVDSTALPHA:
- {
- u8 alpha = 0xff - dstClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- }
-
- return 0;
- }
-
- static u32 GetDestinationFactor(u8 *srcClr, u8 *dstClr, BlendMode::BlendFactor mode)
- {
- switch (mode)
- {
- case BlendMode::ZERO:
- return 0;
- case BlendMode::ONE:
- return 0xffffffff;
- case BlendMode::SRCCLR:
- return *(u32*)srcClr;
- case BlendMode::INVSRCCLR:
- return 0xffffffff - *(u32*)srcClr;
- case BlendMode::SRCALPHA:
- {
- u8 alpha = srcClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::INVSRCALPHA:
- {
- u8 alpha = 0xff - srcClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::DSTALPHA:
- {
- u8 alpha = dstClr[ALP_C] & 0xff;
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- case BlendMode::INVDSTALPHA:
- {
- u8 alpha = 0xff - dstClr[ALP_C];
- u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
- return factor;
- }
- }
-
- return 0;
- }
-
- static void BlendColor(u8 *srcClr, u8 *dstClr)
- {
- u32 srcFactor = GetSourceFactor(srcClr, dstClr, bpmem.blendmode.srcfactor);
- u32 dstFactor = GetDestinationFactor(srcClr, dstClr, bpmem.blendmode.dstfactor);
-
- for (int i = 0; i < 4; i++)
- {
- // add MSB of factors to make their range 0 -> 256
- u32 sf = (srcFactor & 0xff);
- sf += sf >> 7;
-
- u32 df = (dstFactor & 0xff);
- df += df >> 7;
-
- u32 color = (srcClr[i] * sf + dstClr[i] * df) >> 8;
- dstClr[i] = (color>255)?255:color;
-
- dstFactor >>= 8;
- srcFactor >>= 8;
- }
- }
-
- static void LogicBlend(u32 srcClr, u32* dstClr, BlendMode::LogicOp op)
- {
- switch (op)
- {
- case BlendMode::CLEAR:
- *dstClr = 0;
- break;
- case BlendMode::AND:
- *dstClr = srcClr & *dstClr;
- break;
- case BlendMode::AND_REVERSE:
- *dstClr = srcClr & (~*dstClr);
- break;
- case BlendMode::COPY:
- *dstClr = srcClr;
- break;
- case BlendMode::AND_INVERTED:
- *dstClr = (~srcClr) & *dstClr;
- break;
- case BlendMode::NOOP:
- // Do nothing
- break;
- case BlendMode::XOR:
- *dstClr = srcClr ^ *dstClr;
- break;
- case BlendMode::OR:
- *dstClr = srcClr | *dstClr;
- break;
- case BlendMode::NOR:
- *dstClr = ~(srcClr | *dstClr);
- break;
- case BlendMode::EQUIV:
- *dstClr = ~(srcClr ^ *dstClr);
- break;
- case BlendMode::INVERT:
- *dstClr = ~*dstClr;
- break;
- case BlendMode::OR_REVERSE:
- *dstClr = srcClr | (~*dstClr);
- break;
- case BlendMode::COPY_INVERTED:
- *dstClr = ~srcClr;
- break;
- case BlendMode::OR_INVERTED:
- *dstClr = (~srcClr) | *dstClr;
- break;
- case BlendMode::NAND:
- *dstClr = ~(srcClr & *dstClr);
- break;
- case BlendMode::SET:
- *dstClr = 0xffffffff;
- break;
- }
- }
-
- static void SubtractBlend(u8 *srcClr, u8 *dstClr)
- {
- for (int i = 0; i < 4; i++)
- {
- int c = (int)dstClr[i] - (int)srcClr[i];
- dstClr[i] = (c < 0)?0:c;
- }
- }
-
- static void Dither(u16 x, u16 y, u8 *color)
- {
- // No blending for RGB8 mode
- if (!bpmem.blendmode.dither || bpmem.zcontrol.pixel_format != PEControl::PixelFormat::RGBA6_Z24)
- return;
-
- // Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering
- static const u8 dither[2][2] = {{0, 2}, {3, 1}};
-
- // Only the color channels are dithered?
- for (int i = BLU_C; i <= RED_C; i++)
- color[i] = ((color[i] - (color[i] >> 6)) + dither[y & 1][x & 1]) & 0xfc;
- }
-
- void BlendTev(u16 x, u16 y, u8 *color)
- {
- u32 dstClr;
- u32 offset = GetColorOffset(x, y);
-
- u8 *dstClrPtr = (u8*)&dstClr;
-
- GetPixelColor(offset, dstClrPtr);
-
- if (bpmem.blendmode.blendenable)
- {
- if (bpmem.blendmode.subtract)
- SubtractBlend(color, dstClrPtr);
- else
- BlendColor(color, dstClrPtr);
- }
- else if (bpmem.blendmode.logicopenable)
- {
- LogicBlend(*((u32*)color), &dstClr, bpmem.blendmode.logicmode);
- }
- else
- {
- dstClrPtr = color;
- }
-
- if (bpmem.dstalpha.enable)
- dstClrPtr[ALP_C] = bpmem.dstalpha.alpha;
-
- if (bpmem.blendmode.colorupdate)
- {
- Dither(x, y, dstClrPtr);
- if (bpmem.blendmode.alphaupdate)
- SetPixelAlphaColor(offset, dstClrPtr);
- else
- SetPixelColorOnly(offset, dstClrPtr);
- }
- else if (bpmem.blendmode.alphaupdate)
- {
- SetPixelAlphaOnly(offset, dstClrPtr[ALP_C]);
- }
- }
-
- void SetColor(u16 x, u16 y, u8 *color)
- {
- u32 offset = GetColorOffset(x, y);
- if (bpmem.blendmode.colorupdate)
- {
- if (bpmem.blendmode.alphaupdate)
- SetPixelAlphaColor(offset, color);
- else
- SetPixelColorOnly(offset, color);
- }
- else if (bpmem.blendmode.alphaupdate)
- {
- SetPixelAlphaOnly(offset, color[ALP_C]);
- }
- }
-
- void SetDepth(u16 x, u16 y, u32 depth)
- {
- if (bpmem.zmode.updateenable)
- SetPixelDepth(GetDepthOffset(x, y), depth);
- }
-
- void GetColor(u16 x, u16 y, u8 *color)
- {
- u32 offset = GetColorOffset(x, y);
- GetPixelColor(offset, color);
- }
-
- // For internal used only, return a non-normalized value, which saves work later.
- void GetColorYUV(u16 x, u16 y, yuv444 *out)
- {
- u8 color[4];
- GetColor(x, y, color);
-
- // GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see
- // http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
- out->Y = (u8)( 0.257f * color[RED_C] + 0.504f * color[GRN_C] + 0.098f * color[BLU_C]);
- out->U = (u8)(-0.148f * color[RED_C] + -0.291f * color[GRN_C] + 0.439f * color[BLU_C]);
- out->V = (u8)( 0.439f * color[RED_C] + -0.368f * color[GRN_C] + -0.071f * color[BLU_C]);
- }
-
- u32 GetDepth(u16 x, u16 y)
- {
- u32 offset = GetDepthOffset(x, y);
- return GetPixelDepth(offset);
- }
-
- u8 *GetPixelPointer(u16 x, u16 y, bool depth)
- {
- if (depth)
- return &efb[GetDepthOffset(x, y)];
- return &efb[GetColorOffset(x, y)];
- }
-
- void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma)
- {
- // FIXME: We should do Gamma correction
-
- if (!xfb_in_ram)
- {
- WARN_LOG(VIDEO, "Tried to copy to invalid XFB address");
- return;
- }
-
- int left = sourceRc.left;
- int right = sourceRc.right;
-
- // this assumes copies will always start on an even (YU) pixel and the
- // copy always has an even width, which might not be true.
- if (left & 1 || right & 1)
- {
- WARN_LOG(VIDEO, "Trying to copy XFB to from unaligned EFB source");
- // this will show up as wrongly encoded
- }
-
- // Scanline buffer, leave room for borders
- yuv444 scanline[EFB_WIDTH+2];
-
- // our internal yuv444 type is not normalized, so black is {0, 0, 0} instead of {16, 128, 128}
- yuv444 black;
- black.Y = 0;
- black.U = 0;
- black.V = 0;
-
- scanline[0] = black; // black border at start
- scanline[right+1] = black; // black border at end
-
- for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
- {
- // Get a scanline of YUV pixels in 4:4:4 format
-
- for (int i = 1, x = left; x < right; i++, x++)
- {
- GetColorYUV(x, y, &scanline[i]);
- }
-
- // And Downsample them to 4:2:2
- for (int i = 1, x = left; x < right; i+=2, x+=2)
- {
- // YU pixel
- xfb_in_ram[x].Y = scanline[i].Y + 16;
- // we mix our color differences in 10 bit space so it will round more accurately
- // U[i] = 1/4 * U[i-1] + 1/2 * U[i] + 1/4 * U[i+1]
- xfb_in_ram[x].UV = 128 + ((scanline[i-1].U + (scanline[i].U << 1) + scanline[i+1].U) >> 2);
-
- // YV pixel
- xfb_in_ram[x+1].Y = scanline[i+1].Y + 16;
- // V[i] = 1/4 * V[i-1] + 1/2 * V[i] + 1/4 * V[i+1]
- xfb_in_ram[x+1].UV = 128 + ((scanline[i].V + (scanline[i+1].V << 1) + scanline[i+2].V) >> 2);
- }
- xfb_in_ram += fbWidth;
- }
- }
-
- // Like CopyToXFB, but we copy directly into the OpenGL color texture without going via GameCube main memory or doing a yuyv conversion
- void BypassXFB(u8* texture, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma)
- {
- if (fbWidth*fbHeight > MAX_XFB_WIDTH*MAX_XFB_HEIGHT)
- {
- ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
- return;
- }
-
- u32 color;
- u8* colorPtr = (u8*)&color;
- u32* texturePtr = (u32*)texture;
- u32 textureAddress = 0;
-
- int left = sourceRc.left;
- int right = sourceRc.right;
-
- for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
- {
- for (u16 x = left; x < right; x++)
- {
- GetColor(x, y, colorPtr);
- texturePtr[textureAddress++] = Common::swap32(color | 0xFF);
- }
- }
- }
-
- bool ZCompare(u16 x, u16 y, u32 z)
- {
- u32 offset = GetDepthOffset(x, y);
- u32 depth = GetPixelDepth(offset);
-
- bool pass;
-
- switch (bpmem.zmode.func)
- {
- case ZMode::NEVER:
- pass = false;
- break;
- case ZMode::LESS:
- pass = z < depth;
- break;
- case ZMode::EQUAL:
- pass = z == depth;
- break;
- case ZMode::LEQUAL:
- pass = z <= depth;
- break;
- case ZMode::GREATER:
- pass = z > depth;
- break;
- case ZMode::NEQUAL:
- pass = z != depth;
- break;
- case ZMode::GEQUAL:
- pass = z >= depth;
- break;
- case ZMode::ALWAYS:
- pass = true;
- break;
- default:
- pass = false;
- ERROR_LOG(VIDEO, "Bad Z compare mode %i", (int)bpmem.zmode.func);
- }
-
- if (pass && bpmem.zmode.updateenable)
- {
- SetPixelDepth(offset, z);
- }
-
- return pass;
- }
+u32 perf_values[PQ_NUM_MEMBERS];
+
+static inline u32 GetColorOffset(u16 x, u16 y)
+{
+ return (x + y * EFB_WIDTH) * 3;
+}
+
+static inline u32 GetDepthOffset(u16 x, u16 y)
+{
+ return (x + y * EFB_WIDTH) * 3 + DEPTH_BUFFER_START;
+}
+
+static void SetPixelAlphaOnly(u32 offset, u8 a)
+{
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::Z24:
+ case PEControl::RGB565_Z16:
+ // do nothing
+ break;
+ case PEControl::RGBA6_Z24:
+ {
+ u32 a32 = a;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xffffffc0;
+ val |= (a32 >> 2) & 0x0000003f;
+ *dst = val;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+}
+
+static void SetPixelColorOnly(u32 offset, u8* rgb)
+{
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::Z24:
+ {
+ u32 src = *(u32*)rgb;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= src >> 8;
+ *dst = val;
+ }
+ break;
+ case PEControl::RGBA6_Z24:
+ {
+ u32 src = *(u32*)rgb;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff00003f;
+ val |= (src >> 4) & 0x00000fc0; // blue
+ val |= (src >> 6) & 0x0003f000; // green
+ val |= (src >> 8) & 0x00fc0000; // red
+ *dst = val;
+ }
+ break;
+ case PEControl::RGB565_Z16:
+ {
+ INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
+ u32 src = *(u32*)rgb;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= src >> 8;
+ *dst = val;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+}
+
+static void SetPixelAlphaColor(u32 offset, u8* color)
+{
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::Z24:
+ {
+ u32 src = *(u32*)color;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= src >> 8;
+ *dst = val;
+ }
+ break;
+ case PEControl::RGBA6_Z24:
+ {
+ u32 src = *(u32*)color;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= (src >> 2) & 0x0000003f; // alpha
+ val |= (src >> 4) & 0x00000fc0; // blue
+ val |= (src >> 6) & 0x0003f000; // green
+ val |= (src >> 8) & 0x00fc0000; // red
+ *dst = val;
+ }
+ break;
+ case PEControl::RGB565_Z16:
+ {
+ INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
+ u32 src = *(u32*)color;
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= src >> 8;
+ *dst = val;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+}
+
+static void GetPixelColor(u32 offset, u8* color)
+{
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::Z24:
+ {
+ u32 src = *(u32*)&efb[offset];
+ u32* dst = (u32*)color;
+ u32 val = 0xff | ((src & 0x00ffffff) << 8);
+ *dst = val;
+ }
+ break;
+ case PEControl::RGBA6_Z24:
+ {
+ u32 src = *(u32*)&efb[offset];
+ color[ALP_C] = Convert6To8(src & 0x3f);
+ color[BLU_C] = Convert6To8((src >> 6) & 0x3f);
+ color[GRN_C] = Convert6To8((src >> 12) & 0x3f);
+ color[RED_C] = Convert6To8((src >> 18) & 0x3f);
+ }
+ break;
+ case PEControl::RGB565_Z16:
+ {
+ INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
+ u32 src = *(u32*)&efb[offset];
+ u32* dst = (u32*)color;
+ u32 val = 0xff | ((src & 0x00ffffff) << 8);
+ *dst = val;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+}
+
+static void SetPixelDepth(u32 offset, u32 depth)
+{
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::RGBA6_Z24:
+ case PEControl::Z24:
+ {
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= depth & 0x00ffffff;
+ *dst = val;
+ }
+ break;
+ case PEControl::RGB565_Z16:
+ {
+ INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
+ u32* dst = (u32*)&efb[offset];
+ u32 val = *dst & 0xff000000;
+ val |= depth & 0x00ffffff;
+ *dst = val;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+}
+
+static u32 GetPixelDepth(u32 offset)
+{
+ u32 depth = 0;
+
+ switch (bpmem.zcontrol.pixel_format)
+ {
+ case PEControl::RGB8_Z24:
+ case PEControl::RGBA6_Z24:
+ case PEControl::Z24:
+ {
+ depth = (*(u32*)&efb[offset]) & 0x00ffffff;
+ }
+ break;
+ case PEControl::RGB565_Z16:
+ {
+ INFO_LOG(VIDEO, "RGB565_Z16 is not supported correctly yet");
+ depth = (*(u32*)&efb[offset]) & 0x00ffffff;
+ }
+ break;
+ default:
+ ERROR_LOG(VIDEO, "Unsupported pixel format: %i", static_cast<int>(bpmem.zcontrol.pixel_format));
+ }
+
+ return depth;
+}
+
+static u32 GetSourceFactor(u8* srcClr, u8* dstClr, BlendMode::BlendFactor mode)
+{
+ switch (mode)
+ {
+ case BlendMode::ZERO:
+ return 0;
+ case BlendMode::ONE:
+ return 0xffffffff;
+ case BlendMode::DSTCLR:
+ return *(u32*)dstClr;
+ case BlendMode::INVDSTCLR:
+ return 0xffffffff - *(u32*)dstClr;
+ case BlendMode::SRCALPHA:
+ {
+ u8 alpha = srcClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::INVSRCALPHA:
+ {
+ u8 alpha = 0xff - srcClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::DSTALPHA:
+ {
+ u8 alpha = dstClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::INVDSTALPHA:
+ {
+ u8 alpha = 0xff - dstClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ }
+
+ return 0;
+}
+
+static u32 GetDestinationFactor(u8* srcClr, u8* dstClr, BlendMode::BlendFactor mode)
+{
+ switch (mode)
+ {
+ case BlendMode::ZERO:
+ return 0;
+ case BlendMode::ONE:
+ return 0xffffffff;
+ case BlendMode::SRCCLR:
+ return *(u32*)srcClr;
+ case BlendMode::INVSRCCLR:
+ return 0xffffffff - *(u32*)srcClr;
+ case BlendMode::SRCALPHA:
+ {
+ u8 alpha = srcClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::INVSRCALPHA:
+ {
+ u8 alpha = 0xff - srcClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::DSTALPHA:
+ {
+ u8 alpha = dstClr[ALP_C] & 0xff;
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ case BlendMode::INVDSTALPHA:
+ {
+ u8 alpha = 0xff - dstClr[ALP_C];
+ u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
+ return factor;
+ }
+ }
+
+ return 0;
+}
+
+static void BlendColor(u8* srcClr, u8* dstClr)
+{
+ u32 srcFactor = GetSourceFactor(srcClr, dstClr, bpmem.blendmode.srcfactor);
+ u32 dstFactor = GetDestinationFactor(srcClr, dstClr, bpmem.blendmode.dstfactor);
+
+ for (int i = 0; i < 4; i++)
+ {
+ // add MSB of factors to make their range 0 -> 256
+ u32 sf = (srcFactor & 0xff);
+ sf += sf >> 7;
+
+ u32 df = (dstFactor & 0xff);
+ df += df >> 7;
+
+ u32 color = (srcClr[i] * sf + dstClr[i] * df) >> 8;
+ dstClr[i] = (color > 255) ? 255 : color;
+
+ dstFactor >>= 8;
+ srcFactor >>= 8;
+ }
+}
+
+static void LogicBlend(u32 srcClr, u32* dstClr, BlendMode::LogicOp op)
+{
+ switch (op)
+ {
+ case BlendMode::CLEAR:
+ *dstClr = 0;
+ break;
+ case BlendMode::AND:
+ *dstClr = srcClr & *dstClr;
+ break;
+ case BlendMode::AND_REVERSE:
+ *dstClr = srcClr & (~*dstClr);
+ break;
+ case BlendMode::COPY:
+ *dstClr = srcClr;
+ break;
+ case BlendMode::AND_INVERTED:
+ *dstClr = (~srcClr) & *dstClr;
+ break;
+ case BlendMode::NOOP:
+ // Do nothing
+ break;
+ case BlendMode::XOR:
+ *dstClr = srcClr ^ *dstClr;
+ break;
+ case BlendMode::OR:
+ *dstClr = srcClr | *dstClr;
+ break;
+ case BlendMode::NOR:
+ *dstClr = ~(srcClr | *dstClr);
+ break;
+ case BlendMode::EQUIV:
+ *dstClr = ~(srcClr ^ *dstClr);
+ break;
+ case BlendMode::INVERT:
+ *dstClr = ~*dstClr;
+ break;
+ case BlendMode::OR_REVERSE:
+ *dstClr = srcClr | (~*dstClr);
+ break;
+ case BlendMode::COPY_INVERTED:
+ *dstClr = ~srcClr;
+ break;
+ case BlendMode::OR_INVERTED:
+ *dstClr = (~srcClr) | *dstClr;
+ break;
+ case BlendMode::NAND:
+ *dstClr = ~(srcClr & *dstClr);
+ break;
+ case BlendMode::SET:
+ *dstClr = 0xffffffff;
+ break;
+ }
+}
+
+static void SubtractBlend(u8* srcClr, u8* dstClr)
+{
+ for (int i = 0; i < 4; i++)
+ {
+ int c = (int)dstClr[i] - (int)srcClr[i];
+ dstClr[i] = (c < 0) ? 0 : c;
+ }
+}
+
+static void Dither(u16 x, u16 y, u8* color)
+{
+ // No blending for RGB8 mode
+ if (!bpmem.blendmode.dither || bpmem.zcontrol.pixel_format != PEControl::PixelFormat::RGBA6_Z24)
+ return;
+
+ // Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering
+ static const u8 dither[2][2] = {{0, 2}, {3, 1}};
+
+ // Only the color channels are dithered?
+ for (int i = BLU_C; i <= RED_C; i++)
+ color[i] = ((color[i] - (color[i] >> 6)) + dither[y & 1][x & 1]) & 0xfc;
+}
+
+void BlendTev(u16 x, u16 y, u8* color)
+{
+ u32 dstClr;
+ u32 offset = GetColorOffset(x, y);
+
+ u8* dstClrPtr = (u8*)&dstClr;
+
+ GetPixelColor(offset, dstClrPtr);
+
+ if (bpmem.blendmode.blendenable)
+ {
+ if (bpmem.blendmode.subtract)
+ SubtractBlend(color, dstClrPtr);
+ else
+ BlendColor(color, dstClrPtr);
+ }
+ else if (bpmem.blendmode.logicopenable)
+ {
+ LogicBlend(*((u32*)color), &dstClr, bpmem.blendmode.logicmode);
+ }
+ else
+ {
+ dstClrPtr = color;
+ }
+
+ if (bpmem.dstalpha.enable)
+ dstClrPtr[ALP_C] = bpmem.dstalpha.alpha;
+
+ if (bpmem.blendmode.colorupdate)
+ {
+ Dither(x, y, dstClrPtr);
+ if (bpmem.blendmode.alphaupdate)
+ SetPixelAlphaColor(offset, dstClrPtr);
+ else
+ SetPixelColorOnly(offset, dstClrPtr);
+ }
+ else if (bpmem.blendmode.alphaupdate)
+ {
+ SetPixelAlphaOnly(offset, dstClrPtr[ALP_C]);
+ }
+}
+
+void SetColor(u16 x, u16 y, u8* color)
+{
+ u32 offset = GetColorOffset(x, y);
+ if (bpmem.blendmode.colorupdate)
+ {
+ if (bpmem.blendmode.alphaupdate)
+ SetPixelAlphaColor(offset, color);
+ else
+ SetPixelColorOnly(offset, color);
+ }
+ else if (bpmem.blendmode.alphaupdate)
+ {
+ SetPixelAlphaOnly(offset, color[ALP_C]);
+ }
+}
+
+void SetDepth(u16 x, u16 y, u32 depth)
+{
+ if (bpmem.zmode.updateenable)
+ SetPixelDepth(GetDepthOffset(x, y), depth);
+}
+
+void GetColor(u16 x, u16 y, u8* color)
+{
+ u32 offset = GetColorOffset(x, y);
+ GetPixelColor(offset, color);
+}
+
+// For internal used only, return a non-normalized value, which saves work later.
+void GetColorYUV(u16 x, u16 y, yuv444* out)
+{
+ u8 color[4];
+ GetColor(x, y, color);
+
+ // GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see
+ // http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
+ out->Y = (u8)(0.257f * color[RED_C] + 0.504f * color[GRN_C] + 0.098f * color[BLU_C]);
+ out->U = (u8)(-0.148f * color[RED_C] + -0.291f * color[GRN_C] + 0.439f * color[BLU_C]);
+ out->V = (u8)(0.439f * color[RED_C] + -0.368f * color[GRN_C] + -0.071f * color[BLU_C]);
+}
+
+u32 GetDepth(u16 x, u16 y)
+{
+ u32 offset = GetDepthOffset(x, y);
+ return GetPixelDepth(offset);
+}
+
+u8* GetPixelPointer(u16 x, u16 y, bool depth)
+{
+ if (depth)
+ return &efb[GetDepthOffset(x, y)];
+ return &efb[GetColorOffset(x, y)];
+}
+
+void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,
+ float Gamma)
+{
+ // FIXME: We should do Gamma correction
+
+ if (!xfb_in_ram)
+ {
+ WARN_LOG(VIDEO, "Tried to copy to invalid XFB address");
+ return;
+ }
+
+ int left = sourceRc.left;
+ int right = sourceRc.right;
+
+ // this assumes copies will always start on an even (YU) pixel and the
+ // copy always has an even width, which might not be true.
+ if (left & 1 || right & 1)
+ {
+ WARN_LOG(VIDEO, "Trying to copy XFB to from unaligned EFB source");
+ // this will show up as wrongly encoded
+ }
+
+ // Scanline buffer, leave room for borders
+ yuv444 scanline[EFB_WIDTH + 2];
+
+ // our internal yuv444 type is not normalized, so black is {0, 0, 0} instead of {16, 128, 128}
+ yuv444 black;
+ black.Y = 0;
+ black.U = 0;
+ black.V = 0;
+
+ scanline[0] = black; // black border at start
+ scanline[right + 1] = black; // black border at end
+
+ for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
+ {
+ // Get a scanline of YUV pixels in 4:4:4 format
+
+ for (int i = 1, x = left; x < right; i++, x++)
+ {
+ GetColorYUV(x, y, &scanline[i]);
+ }
+
+ // And Downsample them to 4:2:2
+ for (int i = 1, x = left; x < right; i += 2, x += 2)
+ {
+ // YU pixel
+ xfb_in_ram[x].Y = scanline[i].Y + 16;
+ // we mix our color differences in 10 bit space so it will round more accurately
+ // U[i] = 1/4 * U[i-1] + 1/2 * U[i] + 1/4 * U[i+1]
+ xfb_in_ram[x].UV =
+ 128 + ((scanline[i - 1].U + (scanline[i].U << 1) + scanline[i + 1].U) >> 2);
+
+ // YV pixel
+ xfb_in_ram[x + 1].Y = scanline[i + 1].Y + 16;
+ // V[i] = 1/4 * V[i-1] + 1/2 * V[i] + 1/4 * V[i+1]
+ xfb_in_ram[x + 1].UV =
+ 128 + ((scanline[i].V + (scanline[i + 1].V << 1) + scanline[i + 2].V) >> 2);
+ }
+ xfb_in_ram += fbWidth;
+ }
+}
+
+// Like CopyToXFB, but we copy directly into the OpenGL color texture without going via GameCube
+// main memory or doing a yuyv conversion
+void BypassXFB(u8* texture, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma)
+{
+ if (fbWidth * fbHeight > MAX_XFB_WIDTH * MAX_XFB_HEIGHT)
+ {
+ ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
+ return;
+ }
+
+ u32 color;
+ u8* colorPtr = (u8*)&color;
+ u32* texturePtr = (u32*)texture;
+ u32 textureAddress = 0;
+
+ int left = sourceRc.left;
+ int right = sourceRc.right;
+
+ for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
+ {
+ for (u16 x = left; x < right; x++)
+ {
+ GetColor(x, y, colorPtr);
+ texturePtr[textureAddress++] = Common::swap32(color | 0xFF);
+ }
+ }
+}
+
+bool ZCompare(u16 x, u16 y, u32 z)
+{
+ u32 offset = GetDepthOffset(x, y);
+ u32 depth = GetPixelDepth(offset);
+
+ bool pass;
+
+ switch (bpmem.zmode.func)
+ {
+ case ZMode::NEVER:
+ pass = false;
+ break;
+ case ZMode::LESS:
+ pass = z < depth;
+ break;
+ case ZMode::EQUAL:
+ pass = z == depth;
+ break;
+ case ZMode::LEQUAL:
+ pass = z <= depth;
+ break;
+ case ZMode::GREATER:
+ pass = z > depth;
+ break;
+ case ZMode::NEQUAL:
+ pass = z != depth;
+ break;
+ case ZMode::GEQUAL:
+ pass = z >= depth;
+ break;
+ case ZMode::ALWAYS:
+ pass = true;
+ break;
+ default:
+ pass = false;
+ ERROR_LOG(VIDEO, "Bad Z compare mode %i", (int)bpmem.zmode.func);
+ }
+
+ if (pass && bpmem.zmode.updateenable)
+ {
+ SetPixelDepth(offset, z);
+ }
+
+ return pass;
+}
}