First version of MSAA code added. No UI yet. No CSAA yet. Doesn't work in Zeldas and Metroids for unknown reason.

Automatic texture invalidation when using copy efb to ram (fixes weird flickery scanner in metroid 2). 

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2617 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2009-03-08 19:19:51 +00:00
parent c7a45ecf95
commit c6ffcec991
12 changed files with 346 additions and 148 deletions

View File

@ -104,6 +104,30 @@ struct TRectangle
int GetWidth() const { return right - left; }
int GetHeight() const { return bottom - top; }
void FlipYPosition(int y_height, TRectangle *dest) const
{
int offset = y_height - (bottom - top);
dest->left = left;
dest->top = top + offset;
dest->right = right;
dest->bottom = bottom + offset;
}
void FlipY(int y_height, TRectangle *dest) const {
dest->left = left;
dest->right = right;
dest->bottom = y_height - bottom;
dest->top = y_height - top;
}
void Scale(float factor_x, float factor_y, TRectangle *dest) const
{
dest->left = (int)(factor_x * left);
dest->right = (int)(factor_x * right);
dest->top = (int)(factor_y * top);
dest->bottom = (int)(factor_y * bottom);
}
};
// Logging

View File

@ -451,7 +451,7 @@ void BPWritten(int addr, int changes, int newval)
// Clear color
Renderer::SetRenderMode(Renderer::RM_Normal);
// Clear Z-Buffer target
bool bRestoreZBufferTarget = Renderer::GetFakeZTarget() != 0;
bool bRestoreZBufferTarget = Renderer::UseFakeZTarget();
// Update the view port for clearing the picture
glViewport(0, 0, Renderer::GetTargetWidth(), Renderer::GetTargetHeight());

View File

@ -93,6 +93,27 @@ void OpenGL_SetWindowText(const char *text)
#endif
}
bool OpenGL_CheckFBOStatus()
{
unsigned int fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
std::string error = "error creating fbo, framebufferstatus is not complete:\n";
switch (fbo_status)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: error += "INCOMPLETE_ATTACHMENT_EXT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "INCOMPLETE_MISSING_ATTACHMENT_EXT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "INCOMPLETE_DIMENSIONS_EXT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "INCOMPLETE_FORMATS_EXT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "INCOMPLETE_DRAW_BUFFER_EXT"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "INCOMPLETE_READ_BUFFER_EXT"; break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT: error += "UNSUPPORTED_EXT"; break;
}
PanicAlert(error.c_str());
return false;
}
return true;
}
// =======================================================================================
// Draw messages on top of the screen

View File

@ -119,14 +119,20 @@ extern GLWindow GLWin;
#endif
// Initialization / upkeep
bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _width, int _height);
void OpenGL_Shutdown();
void OpenGL_Update();
bool OpenGL_MakeCurrent();
void OpenGL_SwapBuffers();
// Get status
bool OpenGL_CheckFBOStatus();
u32 OpenGL_GetBackbufferWidth();
u32 OpenGL_GetBackbufferHeight();
bool OpenGL_Create(SVideoInitialize &_VideoInitialize, int _width, int _height);
bool OpenGL_MakeCurrent();
void OpenGL_SwapBuffers();
// Set things
void OpenGL_SetWindowText(const char *text);
void OpenGL_Shutdown();
void OpenGL_Update();
#endif
#endif

View File

@ -109,7 +109,7 @@ FRAGMENTSHADER* PixelShaderCache::GetShader()
{
DVSTARTPROFILE();
PIXELSHADERUID uid;
u32 zbufrender = (Renderer::GetFakeZTarget() && bpmem.zmode.updateenable) ? 1 : 0;
u32 zbufrender = (Renderer::UseFakeZTarget() && bpmem.zmode.updateenable) ? 1 : 0;
u32 zBufRenderToCol0 = Renderer::GetRenderMode() != Renderer::RM_Normal;
GetPixelShaderId(uid, PixelShaderManager::GetTextureMask(), zbufrender, zBufRenderToCol0);
@ -127,7 +127,7 @@ FRAGMENTSHADER* PixelShaderCache::GetShader()
PSCacheEntry& newentry = pshaders[uid];
const char *code = GeneratePixelShader(PixelShaderManager::GetTextureMask(),
Renderer::GetFakeZTarget() != 0,
Renderer::UseFakeZTarget(),
Renderer::GetRenderMode() != Renderer::RM_Normal);
#if defined(_DEBUG) || defined(DEBUGFAST)

View File

@ -73,7 +73,8 @@ static bool s_bFullscreen = false;
static int nZBufferRender = 0; // if > 0, then use zbuffer render, and count down.
static bool MSAA = false;
// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA.
static int s_MSAASamples = 1;
// Normal Mode
// s_RenderTarget is a texture_rect
@ -93,6 +94,7 @@ static bool MSAA = false;
// A framebuffer is a set of render targets: a color and a z buffer. They can be either RenderBuffers or Textures.
static GLuint s_uFramebuffer = 0;
static GLuint s_uResolvedFramebuffer = 0;
// The size of these should be a (not necessarily even) multiple of the EFB size, 640x528, but isn't.
// These are all texture IDs. Bind them as rect arb textures.
@ -107,23 +109,23 @@ static GLuint s_ResolvedDepthTarget = 0;
static bool s_bATIDrawBuffers = false;
static bool s_bHaveStencilBuffer = false;
static bool s_bHaveFramebufferBlit = false;
static bool g_bBlendSeparate = false;
static u32 s_blendMode;
static volatile bool s_bScreenshot = false;
static Common::CriticalSection s_criticalScreenshot;
static std::string s_sScreenshotName;
static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal;
bool g_bBlendSeparate = false;
int frameCount;
static int s_fps = 0;
// These STAY CONSTANT during execution, no matter how much you resize the game window.\
// These STAY CONSTANT during execution, no matter how much you resize the game window.
// TODO: Add functionality to reinit all the render targets when the window is resized.
static int s_targetwidth; // Size of render buffer FBO.
static int s_targetheight;
static u32 s_blendMode;
extern void HandleCgError(CGcontext ctx, CGerror err, void *appdata);
@ -173,8 +175,7 @@ bool Renderer::Init()
g_cgcontext = cgCreateContext();
cgGetError();
cgSetErrorHandler(HandleCgError, NULL);
cgSetErrorHandler(HandleCgError, NULL);
// Look for required extensions.
const char *ptoken = (const char*)glGetString(GL_EXTENSIONS);
@ -216,8 +217,12 @@ bool Renderer::Init()
ERROR_LOG(VIDEO, "*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\nDoes your video card support OpenGL 2.x?");
bSuccess = false;
}
s_bHaveFramebufferBlit = GLEW_EXT_framebuffer_blit ? true : false;
s_bHaveFramebufferBlit = strstr(ptoken, "GL_EXT_framebuffer_blit") != NULL;
if (!s_bHaveFramebufferBlit)
{
// MSAA ain't gonna work. turn it off if enabled.
s_MSAASamples = 1;
}
if (!bSuccess)
return false;
@ -246,14 +251,6 @@ bool Renderer::Init()
if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers)
glDrawBuffers = glDrawBuffersARB;
glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer);
if (s_uFramebuffer == 0) {
ERROR_LOG(VIDEO, "failed to create the renderbuffer\nDoes your video card support OpenGL 2.x?");
}
_assert_(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
// The size of the framebuffer targets should really NOT be the size of the OpenGL viewport.
// The EFB is larger than 640x480 - in fact, it's 640x528, give or take a couple of lines.
// So the below is wrong.
@ -271,14 +268,19 @@ bool Renderer::Init()
if (s_targetheight < EFB_HEIGHT)
s_targetheight = EFB_HEIGHT;
if (!MSAA) {
glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer);
if (s_uFramebuffer == 0) {
ERROR_LOG(VIDEO, "failed to create the renderbuffer\nDoes your video card support OpenGL 2.x?");
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
if (s_MSAASamples <= 1) {
// Create the framebuffer target texture
glGenTextures(1, (GLuint *)&s_RenderTarget);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
// Create our main color render target as a texture rectangle of the desired size.
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
SetDefaultRectTexParams();
GL_REPORT_ERROR();
GLint nMaxMRT = 0;
@ -318,11 +320,64 @@ bool Renderer::Init()
if (s_FakeZTarget == 0)
ERROR_LOG(VIDEO, "Disabling ztarget MRT feature (max MRT = %d)\n", nMaxMRT);
} else {
// TODO MSAA rendertarget init
}
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
else
{
// MSAA rendertarget init.
// First set up the boring multisampled rendertarget.
glGenRenderbuffersEXT(1, &s_RenderTarget);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_RenderTarget);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_RGBA, s_targetwidth, s_targetheight);
glGenRenderbuffersEXT(1, &s_FakeZTarget);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_FakeZTarget);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_RGBA, s_targetwidth, s_targetheight);
glGenRenderbuffersEXT(1, &s_DepthTarget);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_DepthTarget);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, s_MSAASamples, GL_DEPTH24_STENCIL8_EXT, s_targetwidth, s_targetheight);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
// Attach them to our multisampled FBO. The multisampled FBO is still bound here.
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, s_RenderTarget);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_RENDERBUFFER_EXT, s_FakeZTarget);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget);
OpenGL_CheckFBOStatus();
bool bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT;
if (bFailed) PanicAlert("Incomplete rt");
// Create our resolve FBO, and bind it.
glGenFramebuffersEXT(1, (GLuint *)&s_uResolvedFramebuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uResolvedFramebuffer);
// Generate the resolve targets.
glGenTextures(1, (GLuint *)&s_ResolvedRenderTarget);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ResolvedRenderTarget);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
SetDefaultRectTexParams();
// Generate the resolve targets.
glGenTextures(1, (GLuint *)&s_ResolvedFakeZTarget);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ResolvedFakeZTarget);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, s_targetwidth, s_targetheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
SetDefaultRectTexParams();
// Create the real depth/stencil buffer. It's a renderbuffer, not a texture.
glGenRenderbuffersEXT(1, &s_ResolvedDepthTarget);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_ResolvedDepthTarget);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, s_targetwidth, s_targetheight);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
// Attach our resolve targets to our resolved FBO.
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ResolvedRenderTarget, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ResolvedFakeZTarget, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_ResolvedDepthTarget);
OpenGL_CheckFBOStatus();
bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT;
if (bFailed) PanicAlert("Incomplete rt2");
}
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
nZBufferRender = 0; // Initialize the Z render shutoff countdown. We only render Z if it's desired, to save GPU power.
@ -473,7 +528,7 @@ float Renderer::GetTargetScaleY()
void Renderer::SetRenderTarget(GLuint targ)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,
targ != 0 ? targ : s_RenderTarget, 0);
}
@ -485,25 +540,64 @@ void Renderer::SetDepthTarget(GLuint targ)
void Renderer::SetFramebuffer(GLuint fb)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,
fb != 0 ? fb : s_uFramebuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb != 0 ? fb : s_uFramebuffer);
}
GLuint Renderer::ResolveAndGetRenderTarget(const TRectangle &source_rect)
{
return s_RenderTarget;
if (s_MSAASamples > 1)
{
// Flip the rectangle
TRectangle flipped_rect;
source_rect.FlipYPosition(GetTargetHeight(), &flipped_rect);
// Do the resolve.
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, s_uResolvedFramebuffer);
glBlitFramebufferEXT(flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom,
flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
// Return the resolved target.
return s_ResolvedRenderTarget;
}
else
{
return s_RenderTarget;
}
}
GLuint Renderer::ResolveAndGetFakeZTarget(const TRectangle &source_rect)
{
// This logic should be moved elsewhere.
return s_FakeZTarget;
if (s_MSAASamples > 1)
{
// Flip the rectangle
TRectangle flipped_rect;
source_rect.FlipYPosition(GetTargetHeight(), &flipped_rect);
// Do the resolve. We resolve both color channels, not very necessary.
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, s_uResolvedFramebuffer);
glBlitFramebufferEXT(flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom,
flipped_rect.left, flipped_rect.top, flipped_rect.right, flipped_rect.bottom,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
// Return the resolved target.
return s_ResolvedFakeZTarget;
}
else
{
return s_FakeZTarget;
}
}
GLuint Renderer::GetFakeZTarget()
bool Renderer::UseFakeZTarget()
{
// This logic should be moved elsewhere.
return nZBufferRender > 0 ? s_FakeZTarget : 0;
return nZBufferRender > 0;
}
void Renderer::ResetGLState()
@ -768,7 +862,7 @@ void Renderer::SetRenderMode(RenderMode mode)
}
else if (s_RenderMode == RM_Normal) {
// setup buffers
_assert_(GetFakeZTarget() && bpmem.zmode.updateenable);
_assert_(UseFakeZTarget() && bpmem.zmode.updateenable);
if (mode == RM_ZBufferAlpha) {
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
@ -782,7 +876,7 @@ void Renderer::SetRenderMode(RenderMode mode)
GL_REPORT_ERRORD();
}
else {
_assert_(GetFakeZTarget());
_assert_(UseFakeZTarget());
_assert_(s_bHaveStencilBuffer);
if (mode == RM_ZBufferOnly) {
@ -897,72 +991,87 @@ void Renderer::Swap(const TRectangle& rc)
TRectangle back_rc;
ComputeBackbufferRectangle(&back_rc);
// Disable all other stages.
for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i);
// Update GLViewPort
glViewport(back_rc.left, back_rc.top,
back_rc.right - back_rc.left, back_rc.bottom - back_rc.top);
GL_REPORT_ERRORD();
// Copy the framebuffer to screen.
// TODO: Use glBlitFramebufferEXT.
#if 0
// Use framebuffer blit to stretch screen. No messing around with annoying glBegin and viewports.
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
glBlitFramebufferEXT(0, GetTargetHeight() - (rc.bottom - rc.top), rc.GetWidth(), rc.GetHeight(),
back_rc.left, back_rc.top, back_rc.GetWidth(), back_rc.GetHeight(),
GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
//glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
#else
// Render to the real buffer now.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
// Texture map s_RenderTargets[s_curtarget] onto the main buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
// Use linear filtering.
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
TextureMngr::EnableTexRECT(0);
float u_max;
float v_min = 0.f;
float v_max;
if (g_Config.bAutoScale) {
if (g_Config.bAutoScale)
{
u_max = (rc.right - rc.left);
v_min = (float)GetTargetHeight() - (rc.bottom - rc.top);
v_max = (float)GetTargetHeight();
} else {
u_max = (float)GetTargetWidth();
v_max = (float)GetTargetHeight(); // TODO - when we change the target height to 528, this will have to change.
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBegin(GL_QUADS);
glTexCoord2f(0, v_min); glVertex2f(-1, -1);
glTexCoord2f(0, v_max); glVertex2f(-1, 1);
glTexCoord2f(u_max, v_max); glVertex2f( 1, 1);
glTexCoord2f(u_max, v_min); glVertex2f( 1, -1);
glEnd();
#endif
else
{
u_max = (float)GetTargetWidth();
v_max = (float)GetTargetHeight();
}
// Restore filtering.
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (s_bHaveFramebufferBlit)
{
// Use framebuffer blit to stretch screen.
// No messing around with annoying glBegin and viewports, plus can support multisampling.
if (s_MSAASamples > 1)
{
ResolveAndGetRenderTarget(rc);
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uResolvedFramebuffer);
}
else
{
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
}
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
glBlitFramebufferEXT(0, v_min, u_max, v_max,
back_rc.left, back_rc.top, back_rc.right, back_rc.bottom,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer, we'll draw debug text on top
}
else
{
// No framebuffer_blit extension - crappy gfx card! Fall back to plain texturing solution.
// Disable all other stages.
for (int i = 1; i < 8; ++i)
TextureMngr::DisableStage(i);
// Wireframe
if (g_Config.bWireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Update GLViewPort
glViewport(back_rc.left, back_rc.top,
back_rc.right - back_rc.left, back_rc.bottom - back_rc.top);
GL_REPORT_ERRORD();
// Copy the framebuffer to screen.
// TODO: Use glBlitFramebufferEXT.
// Render to the real buffer now.
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer
// Texture map s_RenderTargets[s_curtarget] onto the main buffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
// Use linear filtering.
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
TextureMngr::EnableTexRECT(0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBegin(GL_QUADS);
glTexCoord2f(0, v_min); glVertex2f(-1, -1);
glTexCoord2f(0, v_max); glVertex2f(-1, 1);
glTexCoord2f(u_max, v_max); glVertex2f( 1, 1);
glTexCoord2f(u_max, v_min); glVertex2f( 1, -1);
glEnd();
// Restore filtering.
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// Wireframe
if (g_Config.bWireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
// End of non-framebuffer_blit workaround.
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureMngr::DisableStage(0);
// -------------------------------------
// Take screenshot, if requested
if (s_bScreenshot) {
s_criticalScreenshot.Enter();
@ -995,6 +1104,8 @@ void Renderer::Swap(const TRectangle& rc)
void Renderer::DrawDebugText()
{
// Reset viewport for drawing text
glViewport(0, 0, OpenGL_GetBackbufferWidth(), OpenGL_GetBackbufferHeight());
// Draw various messages on the screen, like FPS, statistics, etc.
char debugtext_buffer[8192];
char *p = debugtext_buffer;

View File

@ -88,11 +88,13 @@ public:
// If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve target as a texture ID.
// Thus, this call may be expensive. Don't repeat it unnecessarily.
// If not in MSAA mode, will just return the render target texture ID.
// After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to.
static GLuint ResolveAndGetRenderTarget(const TRectangle &rect);
// Same as above but for the FakeZ Target.
// After calling this, before you render anything else, you MUST bind the framebuffer you want to draw to.
static GLuint ResolveAndGetFakeZTarget(const TRectangle &rect);
static GLuint GetFakeZTarget(); // This is used by some functions to check for Z target existence. Should be changed to a bool.
static bool UseFakeZTarget(); // This is used by some functions to check for Z target existence.
// Random utilities
static void RenderText(const char* pstr, int left, int top, u32 color);

View File

@ -99,7 +99,7 @@ void CreateYuyvToRgbProgram()
}
}
FRAGMENTSHADER& GetOrCreateEncodingShader(u32 format)
FRAGMENTSHADER &GetOrCreateEncodingShader(u32 format)
{
if (format > NUM_ENCODING_PROGRAMS)
{
@ -245,11 +245,17 @@ void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyf
if (texconv_shader.glprogid == 0)
return;
u8* ptr = Memory_GetPtr(address);
u8 *dest_ptr = Memory_GetPtr(address);
u32 source_texture = bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(source) : Renderer::ResolveAndGetRenderTarget(source);
s32 width = source.right - source.left;
s32 height = source.bottom - source.top;
int width = source.right - source.left;
int height = source.bottom - source.top;
int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format);
// Invalidate any existing texture covering this memory range.
// TODO - don't delete the texture if it already exists, just replace the contents.
TextureMngr::InvalidateRange(address, size_in_bytes);
if (bScaleByHalf)
{
@ -280,7 +286,7 @@ void EncodeToRam(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyf
scaledSource.left = 0;
scaledSource.right = expandedWidth / samples;
EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, ptr, expandedWidth / samples, expandedHeight, bScaleByHalf);
EncodeToRamUsingShader(texconv_shader, source_texture, scaledSource, dest_ptr, expandedWidth / samples, expandedHeight, bScaleByHalf);
if (bFromZBuffer)
Renderer::SetZBufferRender(); // notify for future settings

View File

@ -93,6 +93,15 @@ bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int he
return SaveTGA(filename, width, height, &data[0]);
}
bool TextureMngr::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size)
{
if (addr + size_in_bytes < range_address)
return false;
if (addr >= range_address + range_size)
return false;
return true;
}
void TextureMngr::TCacheEntry::SetTextureParameters(TexMode0 &newmode)
{
mode = newmode;
@ -179,6 +188,12 @@ void TextureMngr::Shutdown()
temp = NULL;
}
#ifdef _WIN32
#define ERASE_THROUGH_ITERATOR(container, iterator) iterator = container.erase(iterator)
#else
#define ERASE_THROUGH_ITERATOR(container, iterator) container.erase(iterator++)
#endif
void TextureMngr::ProgressiveCleanup()
{
TexCache::iterator iter = textures.begin();
@ -188,19 +203,11 @@ void TextureMngr::ProgressiveCleanup()
{
if (!iter->second.isRenderTarget) {
iter->second.Destroy(false);
#ifdef _WIN32
iter = textures.erase(iter);
#else
textures.erase(iter++);
#endif
ERASE_THROUGH_ITERATOR(textures, iter);
}
else {
iter->second.Destroy(false);
#ifdef _WIN32
iter = textures.erase(iter);
#else
textures.erase(iter++);
#endif
ERASE_THROUGH_ITERATOR(textures, iter);
}
}
else
@ -211,17 +218,28 @@ void TextureMngr::ProgressiveCleanup()
while (itdepth != mapDepthTargets.end())
{
if (frameCount > 20 + itdepth->second.framecount) {
#ifdef _WIN32
itdepth = mapDepthTargets.erase(itdepth);
#else
mapDepthTargets.erase(itdepth++);
#endif
ERASE_THROUGH_ITERATOR(mapDepthTargets, itdepth);
}
else ++itdepth;
}
}
TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt)
void TextureMngr::InvalidateRange(u32 start_address, u32 size) {
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
if (iter->second.IntersectsMemoryRange(start_address, size))
{
iter->second.Destroy(false);
ERASE_THROUGH_ITERATOR(textures, iter);
}
else {
++iter;
}
}
}
TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, int tex_format, int tlutaddr, int tlutfmt)
{
/* notes (about "UNsafe texture cache"):
* Have to be removed soon.
@ -232,7 +250,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
/* notes (about "safe texture cache"):
* Metroids text issue (character table):
* Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
* That's why we have to hash the TLUT too for TLUT format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
* That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
* And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but
* have to be a real unique ID.
* DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
@ -253,26 +271,26 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
TexMode0 &tm0 = bpmem.tex[texstage > 3].texMode0[texstage & 3];
u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
int bs = TexDecoder_GetBlockWidthInTexels(format) - 1;
int bs = TexDecoder_GetBlockWidthInTexels(tex_format) - 1;
int expandedWidth = (width + bs) & (~bs);
u32 hash_value;
u32 texID = address;
if (g_Config.bSafeTextureCache)
{
hash_value = TexDecoder_GetSafeTextureHash(ptr, expandedWidth, height, format, 0); // remove last arg
if ((format == GX_TF_C4) || (format == GX_TF_C8) || (format == GX_TF_C14X2))
hash_value = TexDecoder_GetSafeTextureHash(ptr, expandedWidth, height, tex_format, 0); // remove last arg
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
//texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], TexDecoder_GetPaletteSize(format));
//texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format));
// This trick (to change the texID depending on the TLUT addr) is a trick to get around
// an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
// each other stored in a single texture, and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that texture with different tluts get different IDs.
texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], (format == GX_TF_C4) ? 32 : 128);
texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], (tex_format == GX_TF_C4) ? 32 : 128);
//DebugLog("addr: %08x | texID: %08x | texHash: %08x", address, texID, hash_value);
}
}
@ -295,7 +313,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
if (entry.mode.hex != tm0.hex)
entry.SetTextureParameters(tm0);
//DebugLog("%cC addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U'
// , address, format, entry.hash, width, height);
// , address, tex_format, entry.hash, width, height);
return &entry;
}
else
@ -303,7 +321,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
// Let's reload the new texture data into the same texture,
// instead of destroying it and having to create a new one.
// Might speed up movie playback very, very slightly.
if (width == entry.w && height == entry.h && format == entry.fmt)
if (width == entry.w && height == entry.h && tex_format == entry.fmt)
{
glBindTexture(entry.isNonPow2 ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D, entry.texture);
if (entry.mode.hex != tm0.hex)
@ -318,7 +336,7 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
}
}
PC_TexFormat dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, height, format, tlutaddr, tlutfmt);
PC_TexFormat dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, height, tex_format, tlutaddr, tlutfmt);
//Make an entry in the table
TCacheEntry& entry = textures[texID];
@ -335,10 +353,11 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
((u32 *)ptr)[entry.hashoffset] = entry.hash;
}
//DebugLog("%c addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U'
// , address, format, entry.hash, width, height);
// , address, tex_format, entry.hash, width, height);
entry.addr = address;
entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, tex_format);
entry.isRenderTarget = false;
entry.isNonPow2 = ((width & (width - 1)) || (height & (height - 1)));
@ -354,7 +373,8 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
int gl_format;
int gl_iformat;
int gl_type;
switch (dfmt) {
switch (dfmt)
{
default:
case PC_TEX_FMT_NONE:
PanicAlert("Invalid PC texture format %i", dfmt);
@ -397,14 +417,14 @@ TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width
entry.frameCount = frameCount;
entry.w = width;
entry.h = height;
entry.fmt = format;
entry.fmt = tex_format;
entry.SetTextureParameters(tm0);
if (g_Config.bDumpTextures) // dump texture to file
{
static int counter = 0;
char szTemp[MAX_PATH];
sprintf(szTemp, "%s/txt_%04i_%i.tga", FULL_DUMP_TEXTURES_DIR, counter++, format);
sprintf(szTemp, "%s/txt_%04i_%i.tga", FULL_DUMP_TEXTURES_DIR, counter++, tex_format);
SaveTexture(szTemp,target, entry.texture, width, height);
}
@ -616,7 +636,16 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool
// GL_REPORT_ERRORD();
// return;
// }
TRectangle scaled_rect;
source_rect.Scale(Renderer::GetTargetScaleX(), Renderer::GetTargetScaleY(), &scaled_rect);
TRectangle flipped_rect;
scaled_rect.FlipY(Renderer::GetTargetHeight(), &flipped_rect);
// Make sure to resolve anything we need to read from.
// TODO - it seems that it sometimes doesn't resolve the entire area we are interested in. See shadows in Burnout 2.
GLuint read_texture = bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(scaled_rect) : Renderer::ResolveAndGetRenderTarget(scaled_rect);
Renderer::SetRenderMode(Renderer::RM_Normal); // set back to normal
GL_REPORT_ERRORD();
@ -633,7 +662,6 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool
// create and attach the render target
std::map<u32, DEPTHTARGET>::iterator itdepth = mapDepthTargets.find((h << 16) | w);
if (itdepth == mapDepthTargets.end())
{
DEPTHTARGET& depth = mapDepthTargets[(h << 16) | w];
@ -657,10 +685,10 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, (bFromZBuffer ? Renderer::ResolveAndGetFakeZTarget(source_rect) : Renderer::ResolveAndGetRenderTarget(source_rect)));
TextureMngr::EnableTexRECT(0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture);
glViewport(0, 0, w, h);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
@ -668,15 +696,11 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool
PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation
GL_REPORT_ERRORD();
// Get Target X, Y
float MValueX = Renderer::GetTargetScaleX();
float MValueY = Renderer::GetTargetScaleY();
glBegin(GL_QUADS);
glTexCoord2f((float)source_rect.left * MValueX, Renderer::GetTargetHeight()-(float)source_rect.bottom * MValueY); glVertex2f(-1, 1);
glTexCoord2f((float)source_rect.left * MValueX, Renderer::GetTargetHeight()-(float)source_rect.top * MValueY); glVertex2f(-1, -1);
glTexCoord2f((float)source_rect.right * MValueX, Renderer::GetTargetHeight()-(float)source_rect.top * MValueY); glVertex2f( 1, -1);
glTexCoord2f((float)source_rect.right * MValueX, Renderer::GetTargetHeight()-(float)source_rect.bottom * MValueY); glVertex2f( 1, 1);
glTexCoord2f(flipped_rect.left, flipped_rect.bottom); glVertex2f(-1, 1);
glTexCoord2f(flipped_rect.left, flipped_rect.top ); glVertex2f(-1, -1);
glTexCoord2f(flipped_rect.right, flipped_rect.top ); glVertex2f( 1, -1);
glTexCoord2f(flipped_rect.right, flipped_rect.bottom); glVertex2f( 1, 1);
glEnd();
GL_REPORT_ERRORD();
@ -691,7 +715,7 @@ void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool
GL_REPORT_ERRORD();
if(g_Config.bDumpEFBTarget)
if (g_Config.bDumpEFBTarget)
{
static int count = 0;
SaveTexture(StringFromFormat("%s/efb_frame_%i.tga", FULL_DUMP_TEXTURES_DIR, count++).c_str(), GL_TEXTURE_RECTANGLE_ARB, entry.texture, entry.w, entry.h);

View File

@ -29,10 +29,11 @@ class TextureMngr
public:
struct TCacheEntry
{
TCacheEntry() : texture(0), addr(0), hash(0), w(0), h(0), isRenderTarget(false), isUpsideDown(false), isNonPow2(true), bHaveMipMaps(false) { mode.hex = 0xFCFCFCFC; }
TCacheEntry() : texture(0), addr(0), size_in_bytes(0), hash(0), w(0), h(0), isRenderTarget(false), isUpsideDown(false), isNonPow2(true), bHaveMipMaps(false) { mode.hex = 0xFCFCFCFC; }
GLuint texture;
u32 addr;
u32 size_in_bytes;
u32 hash;
u32 paletteHash;
u32 hashoffset;
@ -40,7 +41,7 @@ public:
TexMode0 mode; // current filter and clamp modes that texture is set to
int frameCount;
int w,h,fmt;
int w, h, fmt;
bool isRenderTarget; // if render texture, then rendertex is filled with the direct copy of the render target
// later conversions would have to convert properly from rendertexfmt to texfmt
@ -51,6 +52,7 @@ public:
void SetTextureParameters(TexMode0& newmode);
void Destroy(bool shutdown);
void ConvertFromRenderTarget(u32 taddr, int twidth, int theight, int tformat, int tlutaddr, int tlutfmt);
bool IntersectsMemoryRange(u32 range_address, u32 range_size);
};
struct DEPTHTARGET
@ -73,6 +75,8 @@ public:
static void ProgressiveCleanup();
static void Shutdown();
static void Invalidate(bool shutdown);
static void InvalidateRange(u32 start_address, u32 size);
static TCacheEntry* Load(int texstage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt);
static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, const TRectangle &source);

View File

@ -242,7 +242,7 @@ void Flush()
VERTEXSHADER* vs = VertexShaderCache::GetShader(g_nativeVertexFmt->m_components);
bool bRestoreBuffers = false;
if (Renderer::GetFakeZTarget()) {
if (Renderer::UseFakeZTarget()) {
if (bpmem.zmode.updateenable) {
if (!bpmem.blendmode.colorupdate) {
Renderer::SetRenderMode(bpmem.blendmode.alphaupdate ? Renderer::RM_ZBufferAlpha : Renderer::RM_ZBufferOnly);

View File

@ -72,7 +72,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components)
{
DVSTARTPROFILE();
VERTEXSHADERUID uid;
u32 zbufrender = (bpmem.ztex2.op == ZTEXTURE_ADD) || Renderer::GetFakeZTarget() != 0;
u32 zbufrender = (bpmem.ztex2.op == ZTEXTURE_ADD) || Renderer::UseFakeZTarget();
GetVertexShaderId(uid, components, zbufrender);
VSCache::iterator iter = vshaders.find(uid);
@ -87,7 +87,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components)
}
VSCacheEntry& entry = vshaders[uid];
const char *code = GenerateVertexShader(components, Renderer::GetFakeZTarget() != 0);
const char *code = GenerateVertexShader(components, Renderer::UseFakeZTarget());
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_Config.iLog & CONF_SAVESHADERS && code) {