dolphin/Source/Plugins/Plugin_VideoMerge/Src/Renderer.cpp

391 lines
10 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;
}