391 lines
9.6 KiB
C++
391 lines
9.6 KiB
C++
|
|
#include <math.h>
|
|
|
|
#include "Common.h"
|
|
#include "Timer.h"
|
|
|
|
#include "Render.h"
|
|
#include "BPMemory.h"
|
|
#include "Atomic.h"
|
|
#include "VideoConfig.h"
|
|
#include "FramebufferManager.h"
|
|
|
|
#include "Fifo.h"
|
|
#include "VertexShaderManager.h"
|
|
#include "DLCache.h"
|
|
#include "OnScreenDisplay.h"
|
|
#include "Statistics.h"
|
|
|
|
#include "Renderer.h"
|
|
|
|
#include "Main.h"
|
|
|
|
int RendererBase::s_target_width;
|
|
int RendererBase::s_target_height;
|
|
|
|
int RendererBase::s_Fulltarget_width;
|
|
int RendererBase::s_Fulltarget_height;
|
|
|
|
int RendererBase::s_backbuffer_width;
|
|
int RendererBase::s_backbuffer_height;
|
|
|
|
int RendererBase::s_XFB_width;
|
|
int RendererBase::s_XFB_height;
|
|
|
|
float RendererBase::xScale;
|
|
float RendererBase::yScale;
|
|
|
|
int RendererBase::s_fps;
|
|
|
|
u32 RendererBase::s_blendMode;
|
|
bool RendererBase::XFBWrited;
|
|
|
|
float RendererBase::EFBxScale;
|
|
float RendererBase::EFByScale;
|
|
|
|
volatile u32 RendererBase::s_swapRequested;
|
|
|
|
//void VideoConfig::UpdateProjectionHack()
|
|
//{
|
|
// return;
|
|
// //::UpdateProjectionHack(g_Config.iPhackvalue);
|
|
//}
|
|
|
|
RendererBase::RendererBase()
|
|
{
|
|
UpdateActiveConfig();
|
|
s_blendMode = 0;
|
|
|
|
s_XFB_width = MAX_XFB_WIDTH;
|
|
s_XFB_height = MAX_XFB_HEIGHT;
|
|
}
|
|
|
|
// can maybe reuse this func in Renderer::Swap to eliminate redundant code
|
|
void RendererBase::FramebufferSize(int w, int h)
|
|
{
|
|
TargetRectangle dst_rect;
|
|
ComputeDrawRectangle(w, h, false, &dst_rect);
|
|
|
|
xScale = (float)(dst_rect.right - dst_rect.left) / (float)s_XFB_width;
|
|
yScale = (float)(dst_rect.bottom - dst_rect.top) / (float)s_XFB_height;
|
|
|
|
// TODO: why these, prolly can remove them
|
|
const int s_LastAA = g_ActiveConfig.iMultisampleMode;
|
|
const int s_LastEFBScale = g_ActiveConfig.iEFBScale;
|
|
|
|
switch (s_LastEFBScale)
|
|
{
|
|
case 0:
|
|
EFBxScale = xScale;
|
|
EFByScale = yScale;
|
|
break;
|
|
|
|
case 1:
|
|
EFBxScale = ceilf(xScale);
|
|
EFByScale = ceilf(yScale);
|
|
break;
|
|
|
|
default:
|
|
EFByScale = EFBxScale = (g_ActiveConfig.iEFBScale - 1);
|
|
break;
|
|
};
|
|
|
|
const float SupersampleCoeficient = s_LastAA + 1;
|
|
EFBxScale *= SupersampleCoeficient;
|
|
EFByScale *= SupersampleCoeficient;
|
|
|
|
s_target_width = (int)(EFB_WIDTH * EFBxScale);
|
|
s_target_height = (int)(EFB_HEIGHT * EFByScale);
|
|
|
|
// TODO: set anything else?
|
|
s_Fulltarget_width = s_target_width;
|
|
s_Fulltarget_height = s_target_height;
|
|
}
|
|
|
|
void UpdateViewport()
|
|
{
|
|
g_renderer->UpdateViewport();
|
|
}
|
|
|
|
void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
|
|
{
|
|
RendererBase::RenderToXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
|
|
}
|
|
|
|
// whats this for?
|
|
bool IsD3D()
|
|
{
|
|
//PanicAlert("IsD3D!");
|
|
// TODO: temporary
|
|
return true;
|
|
}
|
|
|
|
void Renderer::RenderText(const char *pstr, int left, int top, u32 color)
|
|
{
|
|
|
|
}
|
|
|
|
void RendererBase::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
|
|
{
|
|
if (!fbWidth || !fbHeight)
|
|
return;
|
|
|
|
VideoFifo_CheckEFBAccess();
|
|
VideoFifo_CheckSwapRequestAt(xfbAddr, fbWidth, fbHeight);
|
|
XFBWrited = true;
|
|
|
|
// XXX: Without the VI, how would we know what kind of field this is? So
|
|
// just use progressive.
|
|
|
|
if (g_ActiveConfig.bUseXFB)
|
|
{
|
|
g_framebuffer_manager->CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
|
|
}
|
|
else
|
|
{
|
|
g_renderer->Swap(xfbAddr, FIELD_PROGRESSIVE, fbWidth, fbHeight, sourceRc);
|
|
Common::AtomicStoreRelease(s_swapRequested, FALSE);
|
|
}
|
|
}
|
|
|
|
TargetRectangle RendererBase::ConvertEFBRectangle(const EFBRectangle& rc)
|
|
{
|
|
int Xstride = (s_Fulltarget_width - s_target_width) / 2;
|
|
int Ystride = (s_Fulltarget_height - s_target_height) / 2;
|
|
TargetRectangle result;
|
|
result.left = (int)(rc.left * EFBxScale) + Xstride;
|
|
result.top = (int)(rc.top * EFByScale) + Ystride;
|
|
result.right = (int)(rc.right * EFBxScale) + Xstride;
|
|
result.bottom = (int)(rc.bottom * EFByScale) + Ystride;
|
|
return result;
|
|
}
|
|
|
|
bool RendererBase::SetScissorRect(EFBRectangle &rc)
|
|
{
|
|
int xoff = bpmem.scissorOffset.x * 2 - 342;
|
|
int yoff = bpmem.scissorOffset.y * 2 - 342;
|
|
|
|
rc.left = bpmem.scissorTL.x - xoff - 342;
|
|
rc.top = bpmem.scissorTL.y - yoff - 342;
|
|
rc.right = bpmem.scissorBR.x - xoff - 341;
|
|
rc.bottom = bpmem.scissorBR.y - yoff - 341;
|
|
|
|
int Xstride = (s_Fulltarget_width - s_target_width) / 2;
|
|
int Ystride = (s_Fulltarget_height - s_target_height) / 2;
|
|
|
|
rc.left = (int)(rc.left * EFBxScale);
|
|
rc.top = (int)(rc.top * EFByScale);
|
|
rc.right = (int)(rc.right * EFBxScale);
|
|
rc.bottom = (int)(rc.bottom * EFByScale);
|
|
|
|
if (rc.left < 0) rc.left = 0;
|
|
if (rc.right < 0) rc.right = 0;
|
|
if (rc.left > s_target_width) rc.left = s_target_width;
|
|
if (rc.right > s_target_width) rc.right = s_target_width;
|
|
if (rc.top < 0) rc.top = 0;
|
|
if (rc.bottom < 0) rc.bottom = 0;
|
|
if (rc.top > s_target_height) rc.top = s_target_height;
|
|
if (rc.bottom > s_target_height) rc.bottom = s_target_height;
|
|
|
|
rc.left += Xstride;
|
|
rc.top += Ystride;
|
|
rc.right += Xstride;
|
|
rc.bottom += Ystride;
|
|
|
|
if (rc.left > rc.right)
|
|
{
|
|
int temp = rc.right;
|
|
rc.right = rc.left;
|
|
rc.left = temp;
|
|
}
|
|
if (rc.top > rc.bottom)
|
|
{
|
|
int temp = rc.bottom;
|
|
rc.bottom = rc.top;
|
|
rc.top = temp;
|
|
}
|
|
|
|
return (rc.right >= rc.left && rc.bottom >= rc.top);
|
|
}
|
|
|
|
void RendererBase::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight, const EFBRectangle& rc)
|
|
{
|
|
if (g_bSkipCurrentFrame || (!XFBWrited && !g_ActiveConfig.bUseRealXFB) || !fbWidth || !fbHeight)
|
|
{
|
|
g_VideoInitialize.pCopiedToXFB(false);
|
|
return;
|
|
}
|
|
|
|
// this function is called after the XFB field is changed, not after
|
|
// EFB is copied to XFB. In this way, flickering is reduced in games
|
|
// and seems to also give more FPS in ZTP
|
|
|
|
if (field == FIELD_LOWER)
|
|
xfbAddr -= fbWidth * 2;
|
|
u32 xfbCount = 0;
|
|
const XFBSourceBase** xfbSourceList = g_framebuffer_manager->GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount);
|
|
|
|
if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
|
|
{
|
|
g_VideoInitialize.pCopiedToXFB(false);
|
|
return;
|
|
}
|
|
|
|
g_renderer->ResetAPIState();
|
|
|
|
// prepare copying the XFBs to our backbuffer
|
|
TargetRectangle dst_rect;
|
|
ComputeDrawRectangle(s_backbuffer_width, s_backbuffer_height, false, &dst_rect);
|
|
|
|
g_renderer->PrepareXFBCopy(dst_rect);
|
|
|
|
if (g_ActiveConfig.bUseXFB)
|
|
{
|
|
const XFBSourceBase* xfbSource;
|
|
|
|
// draw each xfb source
|
|
for (u32 i = 0; i < xfbCount; ++i)
|
|
{
|
|
xfbSource = xfbSourceList[i];
|
|
TargetRectangle sourceRc;
|
|
|
|
//if (g_ActiveConfig.bAutoScale)
|
|
//{
|
|
// sourceRc = xfbSource->sourceRc;
|
|
//}
|
|
//else
|
|
//{
|
|
sourceRc.left = 0;
|
|
sourceRc.top = 0;
|
|
sourceRc.right = xfbSource->texWidth;
|
|
sourceRc.bottom = xfbSource->texHeight;
|
|
//}
|
|
|
|
MathUtil::Rectangle<float> drawRc;
|
|
|
|
if (g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
|
|
{
|
|
// use virtual xfb with offset
|
|
int xfbHeight = xfbSource->srcHeight;
|
|
int xfbWidth = xfbSource->srcWidth;
|
|
int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2);
|
|
|
|
drawRc.bottom = 1.0f - 2.0f * ((hOffset) / (float)fbHeight);
|
|
drawRc.top = 1.0f - 2.0f * ((hOffset + xfbHeight) / (float)fbHeight);
|
|
drawRc.left = -(xfbWidth / (float)fbWidth);
|
|
drawRc.right = (xfbWidth / (float)fbWidth);
|
|
|
|
if (!g_ActiveConfig.bAutoScale)
|
|
{
|
|
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
|
float vScale = (float)fbHeight / (float)s_backbuffer_height;
|
|
float hScale = (float)fbWidth / (float)s_backbuffer_width;
|
|
|
|
drawRc.top *= vScale;
|
|
drawRc.bottom *= vScale;
|
|
drawRc.left *= hScale;
|
|
drawRc.right *= hScale;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
drawRc.top = -1;
|
|
drawRc.bottom = 1;
|
|
drawRc.left = -1;
|
|
drawRc.right = 1;
|
|
}
|
|
|
|
g_renderer->Draw(xfbSource, sourceRc, drawRc, rc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: organize drawRc stuff
|
|
MathUtil::Rectangle<float> drawRc;
|
|
drawRc.top = -1;
|
|
drawRc.bottom = 1;
|
|
drawRc.left = -1;
|
|
drawRc.right = 1;
|
|
|
|
const TargetRectangle targetRc = ConvertEFBRectangle(rc);
|
|
g_renderer->Draw(NULL, targetRc, drawRc, rc);
|
|
}
|
|
|
|
// done with drawing the game stuff, good moment to save a screenshot
|
|
// TODO: screenshot code
|
|
//
|
|
|
|
// finally present some information
|
|
// TODO: debug text, fps, etc...
|
|
//
|
|
|
|
OSD::DrawMessages();
|
|
|
|
g_renderer->EndFrame();
|
|
|
|
++frameCount;
|
|
|
|
DLCache::ProgressiveCleanup();
|
|
g_texture_cache->Cleanup();
|
|
|
|
// check for configuration changes
|
|
const int last_efbscale = g_ActiveConfig.iEFBScale;
|
|
//const int last_aa = g_ActiveConfig.iMultisampleMode;
|
|
|
|
UpdateActiveConfig();
|
|
|
|
|
|
bool window_resized = g_renderer->CheckForResize();
|
|
|
|
bool xfbchanged = false;
|
|
if (s_XFB_width != fbWidth || s_XFB_height != fbHeight)
|
|
{
|
|
xfbchanged = true;
|
|
s_XFB_width = fbWidth;
|
|
s_XFB_height = fbHeight;
|
|
if (s_XFB_width < 1) s_XFB_width = MAX_XFB_WIDTH;
|
|
if (s_XFB_width > MAX_XFB_WIDTH) s_XFB_width = MAX_XFB_WIDTH;
|
|
if (s_XFB_height < 1) s_XFB_height = MAX_XFB_HEIGHT;
|
|
if (s_XFB_height > MAX_XFB_HEIGHT) s_XFB_height = MAX_XFB_HEIGHT;
|
|
}
|
|
|
|
// update FPS counter
|
|
// TODO: make this better
|
|
static int fpscount = 0;
|
|
static unsigned long lasttime = 0;
|
|
if (Common::Timer::GetTimeMs() - lasttime >= 1000)
|
|
{
|
|
lasttime = Common::Timer::GetTimeMs();
|
|
s_fps = fpscount;
|
|
fpscount = 0;
|
|
}
|
|
if (XFBWrited)
|
|
++fpscount;
|
|
|
|
// set default viewport and scissor, for the clear to work correctly
|
|
stats.ResetFrame();
|
|
|
|
// done. Show our work ;)
|
|
g_renderer->Present();
|
|
|
|
// resize the back buffers NOW to avoid flickering when resizing windows
|
|
if (xfbchanged || window_resized || last_efbscale != g_ActiveConfig.iEFBScale)
|
|
{
|
|
g_renderer->GetBackBufferSize(&s_backbuffer_width, &s_backbuffer_height);
|
|
|
|
FramebufferSize(s_backbuffer_width, s_backbuffer_height);
|
|
|
|
g_renderer->RecreateFramebufferManger();
|
|
}
|
|
|
|
// begin next frame
|
|
g_renderer->RestoreAPIState();
|
|
|
|
g_renderer->BeginFrame();
|
|
|
|
g_renderer->UpdateViewport();
|
|
VertexShaderManager::SetViewportChanged();
|
|
g_VideoInitialize.pCopiedToXFB(XFBWrited || g_ActiveConfig.bUseRealXFB);
|
|
XFBWrited = false;
|
|
}
|