1336 lines
47 KiB
C++
1336 lines
47 KiB
C++
// Copyright (C) 2003-2008 Dolphin Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
#include "Globals.h"
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
#include "GLUtil.h"
|
|
|
|
#include <Cg/cg.h>
|
|
#include <Cg/cgGL.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <mmsystem.h>
|
|
#endif
|
|
|
|
#include "Config.h"
|
|
#include "Profiler.h"
|
|
#include "Statistics.h"
|
|
#include "ImageWrite.h"
|
|
#include "Render.h"
|
|
#include "OpcodeDecoding.h"
|
|
#include "BPStructs.h"
|
|
#include "TextureMngr.h"
|
|
#include "rasterfont.h"
|
|
#include "VertexShaderGen.h"
|
|
#include "PixelShaderCache.h"
|
|
#include "PixelShaderManager.h"
|
|
#include "VertexLoaderManager.h"
|
|
#include "VertexLoader.h"
|
|
#include "XFB.h"
|
|
#include "OnScreenDisplay.h"
|
|
#include "Timer.h"
|
|
|
|
#include "main.h" // Local
|
|
#ifdef _WIN32
|
|
#include "OS/Win32.h"
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include "Win32Window.h" // warning: crapcode
|
|
#else
|
|
#endif
|
|
|
|
|
|
CGcontext g_cgcontext;
|
|
CGprofile g_cgvProf;
|
|
CGprofile g_cgfProf;
|
|
|
|
RasterFont* s_pfont = NULL;
|
|
|
|
static bool s_bFullscreen = false;
|
|
|
|
static int nZBufferRender = 0; // if > 0, then use zbuffer render, and count down.
|
|
|
|
// 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;
|
|
|
|
// The size of these should be a (not necessarily even) multiple of the EFB size, 640x528, but isn'.t
|
|
static GLuint s_RenderTarget = 0;
|
|
static GLuint s_DepthTarget = 0;
|
|
static GLuint s_ZBufferTarget = 0;
|
|
|
|
static bool s_bATIDrawBuffers = false;
|
|
static bool s_bHaveStencilBuffer = false;
|
|
|
|
static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal;
|
|
bool g_bBlendSeparate = false;
|
|
int frameCount;
|
|
|
|
void HandleCgError(CGcontext ctx, CGerror err, void *appdata);
|
|
|
|
static const GLenum glSrcFactors[8] =
|
|
{
|
|
GL_ZERO,
|
|
GL_ONE,
|
|
GL_DST_COLOR,
|
|
GL_ONE_MINUS_DST_COLOR,
|
|
GL_SRC_ALPHA,
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
GL_DST_ALPHA,
|
|
GL_ONE_MINUS_DST_ALPHA
|
|
};
|
|
|
|
static const GLenum glDestFactors[8] = {
|
|
GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
|
|
GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA
|
|
};
|
|
|
|
static u32 s_blendMode;
|
|
|
|
bool Renderer::Init()
|
|
{
|
|
bool bSuccess = true;
|
|
int numvertexattribs = 0;
|
|
GLenum err = GL_NO_ERROR;
|
|
g_cgcontext = cgCreateContext();
|
|
|
|
cgGetError();
|
|
cgSetErrorHandler(HandleCgError, NULL);
|
|
|
|
// fill the opengl extension map
|
|
const char* ptoken = (const char*)glGetString(GL_EXTENSIONS);
|
|
if (ptoken == NULL) return false;
|
|
|
|
INFO_LOG("Supported OpenGL Extensions:\n");
|
|
INFO_LOG(ptoken); // write to the log file
|
|
INFO_LOG("\n");
|
|
|
|
if (GLEW_EXT_blend_func_separate && GLEW_EXT_blend_equation_separate)
|
|
g_bBlendSeparate = true;
|
|
|
|
//Checks if it ONLY has the ATI_draw_buffers extension, some have both
|
|
if (GLEW_ATI_draw_buffers && !GLEW_ARB_draw_buffers)
|
|
s_bATIDrawBuffers = true;
|
|
|
|
s_bFullscreen = g_Config.bFullscreen;
|
|
|
|
if (glewInit() != GLEW_OK) {
|
|
ERROR_LOG("glewInit() failed!\nDoes your video card support OpenGL 2.x?");
|
|
return false;
|
|
}
|
|
|
|
if (!GLEW_EXT_framebuffer_object) {
|
|
ERROR_LOG("*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********\nDoes your video card support OpenGL 2.x?");
|
|
bSuccess = false;
|
|
}
|
|
|
|
if (!GLEW_EXT_secondary_color) {
|
|
ERROR_LOG("*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\nDoes your video card support OpenGL 2.x?");
|
|
bSuccess = false;
|
|
}
|
|
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint *)&numvertexattribs);
|
|
|
|
if (numvertexattribs < 11) {
|
|
ERROR_LOG("*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********\nDoes your video card support OpenGL 2.x?", numvertexattribs);
|
|
bSuccess = false;
|
|
}
|
|
|
|
if (!bSuccess)
|
|
return false;
|
|
|
|
#ifdef _WIN32
|
|
if (WGLEW_EXT_swap_control)
|
|
wglSwapIntervalEXT(0);
|
|
else
|
|
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\nDoes your video card support OpenGL 2.x?");
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
|
if (glXSwapIntervalSGI)
|
|
glXSwapIntervalSGI(0);
|
|
else
|
|
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\n");
|
|
#else
|
|
|
|
//TODO
|
|
|
|
#endif
|
|
|
|
// check the max texture width and height
|
|
GLint max_texture_size;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&max_texture_size);
|
|
if (max_texture_size < 1024) {
|
|
ERROR_LOG("GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024", max_texture_size);
|
|
}
|
|
|
|
GL_REPORT_ERROR();
|
|
if (err != GL_NO_ERROR) bSuccess = false;
|
|
|
|
if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers)
|
|
glDrawBuffers = glDrawBuffersARB;
|
|
|
|
glGenFramebuffersEXT(1, (GLuint *)&s_uFramebuffer);
|
|
if (s_uFramebuffer == 0) {
|
|
ERROR_LOG("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.
|
|
int nBackbufferWidth = (int)OpenGL_GetWidth();
|
|
int nBackbufferHeight = (int)OpenGL_GetHeight();
|
|
|
|
// Create the framebuffer target
|
|
glGenTextures(1, (GLuint *)&s_RenderTarget);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
|
|
|
|
// Setup the texture params
|
|
// initialize to default
|
|
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, nBackbufferWidth, nBackbufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (glGetError() != GL_NO_ERROR) {
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
GL_REPORT_ERROR();
|
|
}
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
GL_REPORT_ERROR();
|
|
|
|
int nMaxMRT = 0;
|
|
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, (GLint *)&nMaxMRT);
|
|
|
|
if (nMaxMRT > 1)
|
|
{
|
|
// create zbuffer target
|
|
glGenTextures(1, (GLuint *)&s_ZBufferTarget);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget);
|
|
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, nBackbufferWidth, nBackbufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (glGetError() != GL_NO_ERROR) {
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
GL_REPORT_ERROR();
|
|
}
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
// create the depth buffer
|
|
glGenRenderbuffersEXT(1, (GLuint *)&s_DepthTarget);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, s_DepthTarget);
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, nBackbufferWidth, nBackbufferHeight);
|
|
|
|
if (glGetError() != GL_NO_ERROR)
|
|
{
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, nBackbufferWidth, nBackbufferHeight);
|
|
s_bHaveStencilBuffer = false;
|
|
}
|
|
else
|
|
{
|
|
s_bHaveStencilBuffer = true;
|
|
}
|
|
|
|
GL_REPORT_ERROR();
|
|
|
|
// set as render targets
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget, 0);
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget);
|
|
|
|
GL_REPORT_ERROR();
|
|
|
|
if (s_ZBufferTarget != 0) {
|
|
// test to make sure it works
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, 0);
|
|
bool bFailed = glGetError() != GL_NO_ERROR || glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT;
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
|
|
|
|
if (bFailed) {
|
|
glDeleteTextures(1, (GLuint *)&s_ZBufferTarget);
|
|
s_ZBufferTarget = 0;
|
|
}
|
|
}
|
|
|
|
if (s_ZBufferTarget == 0)
|
|
ERROR_LOG("disabling ztarget mrt feature (max mrt=%d)\n", nMaxMRT);
|
|
|
|
// Why is this left here?
|
|
//glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, s_DepthTarget);
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
nZBufferRender = 0;
|
|
|
|
GL_REPORT_ERROR();
|
|
if (err != GL_NO_ERROR)
|
|
bSuccess = false;
|
|
|
|
s_pfont = new RasterFont();
|
|
|
|
// load the effect, find the best profiles (if any)
|
|
if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE) {
|
|
ERROR_LOG("arbvp1 not supported\n");
|
|
return false;
|
|
}
|
|
|
|
if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE) {
|
|
ERROR_LOG("arbfp1 not supported\n");
|
|
return false;
|
|
}
|
|
|
|
g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX);
|
|
g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
|
|
cgGLSetOptimalOptions(g_cgvProf);
|
|
cgGLSetOptimalOptions(g_cgfProf);
|
|
|
|
//ERROR_LOG("max buffer sizes: %d %d\n", cgGetProgramBufferMaxSize(g_cgvProf), cgGetProgramBufferMaxSize(g_cgfProf));
|
|
int nenvvertparams, nenvfragparams, naddrregisters[2];
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, (GLint *)&nenvvertparams);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, (GLint *)&nenvfragparams);
|
|
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, (GLint *)&naddrregisters[0]);
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, (GLint *)&naddrregisters[1]);
|
|
DEBUG_LOG("max program env parameters: vert=%d, frag=%d\n", nenvvertparams, nenvfragparams);
|
|
DEBUG_LOG("max program address register parameters: vert=%d, frag=%d\n", naddrregisters[0], naddrregisters[1]);
|
|
|
|
if (nenvvertparams < 238)
|
|
ERROR_LOG("not enough vertex shader environment constants!!\n");
|
|
|
|
#ifndef _DEBUG
|
|
cgGLSetDebugMode(GL_FALSE);
|
|
#endif
|
|
|
|
if (cgGetError() != CG_NO_ERROR) {
|
|
ERROR_LOG("cg error\n");
|
|
return false;
|
|
}
|
|
|
|
s_RenderMode = Renderer::RM_Normal;
|
|
|
|
if (!InitializeGL())
|
|
return false;
|
|
|
|
XFB_Init();
|
|
return glGetError() == GL_NO_ERROR && bSuccess;
|
|
}
|
|
void Renderer::Shutdown(void)
|
|
{
|
|
delete s_pfont;
|
|
s_pfont = 0;
|
|
|
|
XFB_Shutdown();
|
|
|
|
if (g_cgcontext) {
|
|
cgDestroyContext(g_cgcontext);
|
|
g_cgcontext = 0;
|
|
}
|
|
if (s_RenderTarget) {
|
|
glDeleteTextures(1, &s_RenderTarget);
|
|
s_RenderTarget = 0;
|
|
}
|
|
if (s_DepthTarget) {
|
|
glDeleteRenderbuffersEXT(1, &s_DepthTarget);
|
|
s_DepthTarget = 0;
|
|
}
|
|
if (s_uFramebuffer) {
|
|
glDeleteFramebuffersEXT(1, &s_uFramebuffer);
|
|
s_uFramebuffer = 0;
|
|
}
|
|
}
|
|
|
|
bool Renderer::InitializeGL()
|
|
{
|
|
glStencilFunc(GL_ALWAYS, 0, 0);
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
|
|
glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
glShadeModel(GL_SMOOTH);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
glClearDepth(1.0f);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
|
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // perspective correct interpolation of colors and tex coords
|
|
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
|
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
// Do we really need to set this initial glScissor() value? Wont it be called all the time while the game is running?
|
|
//glScissor(0, 0, (int)OpenGL_GetWidth(), (int)OpenGL_GetHeight());
|
|
glBlendColorEXT(0, 0, 0, 0.5f);
|
|
glClearDepth(1.0f);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
// legacy multitexturing: select texture channel only.
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
|
|
GLenum err = GL_NO_ERROR;
|
|
GL_REPORT_ERROR();
|
|
|
|
return err == GL_NO_ERROR;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Return the rendering window width and height
|
|
// ------------------------
|
|
int Renderer::GetTargetWidth()
|
|
{
|
|
return (g_Config.bStretchToFit ? 640 : (int)OpenGL_GetWidth());
|
|
}
|
|
|
|
int Renderer::GetTargetHeight()
|
|
{
|
|
return (g_Config.bStretchToFit ? 480 : (int)OpenGL_GetHeight());
|
|
}
|
|
/////////////////////////////
|
|
|
|
|
|
void Renderer::SetRenderTarget(GLuint targ)
|
|
{
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,
|
|
targ != 0 ? targ : s_RenderTarget, 0);
|
|
}
|
|
|
|
void Renderer::SetDepthTarget(GLuint targ)
|
|
{
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT,
|
|
targ != 0 ? targ : s_DepthTarget);
|
|
}
|
|
|
|
void Renderer::SetFramebuffer(GLuint fb)
|
|
{
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,
|
|
fb != 0 ? fb : s_uFramebuffer);
|
|
}
|
|
|
|
GLuint Renderer::GetRenderTarget()
|
|
{
|
|
return s_RenderTarget;
|
|
}
|
|
GLuint Renderer::GetZBufferTarget()
|
|
{
|
|
return nZBufferRender > 0 ? s_ZBufferTarget : 0;
|
|
}
|
|
|
|
void Renderer::ResetGLState()
|
|
{
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_BLEND);
|
|
glDepthMask(GL_FALSE);
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
void Renderer::RestoreGLState()
|
|
{
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
if (bpmem.genMode.cullmode > 0) glEnable(GL_CULL_FACE);
|
|
if (bpmem.zmode.testenable) glEnable(GL_DEPTH_TEST);
|
|
if (bpmem.zmode.updateenable) glDepthMask(GL_TRUE);
|
|
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
SetColorMask();
|
|
SetBlendMode(true);
|
|
}
|
|
|
|
void Renderer::SetColorMask()
|
|
{
|
|
if (bpmem.blendmode.alphaupdate && bpmem.blendmode.colorupdate)
|
|
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
|
|
else if (bpmem.blendmode.alphaupdate)
|
|
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_TRUE);
|
|
else if (bpmem.blendmode.colorupdate)
|
|
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_FALSE);
|
|
}
|
|
|
|
void Renderer::SetBlendMode(bool forceUpdate)
|
|
{
|
|
// blend mode bit mask
|
|
// 0 - blend enable
|
|
// 1 - dst alpha enable
|
|
// 2 - reverse subtract enable (else add)
|
|
// 3-5 - srcRGB function
|
|
// 6-8 - dstRGB function
|
|
// 9-16 - dstAlpha
|
|
|
|
u32 newval = bpmem.blendmode.subtract << 2;
|
|
|
|
if (g_bBlendSeparate) {
|
|
newval |= bpmem.dstalpha.enable ? 3 : 0;
|
|
newval |= bpmem.dstalpha.alpha << 9;
|
|
}
|
|
|
|
if (bpmem.blendmode.blendenable) {
|
|
newval |= 1;
|
|
|
|
if (bpmem.blendmode.subtract) {
|
|
newval |= 0x0048; // src 1 dst 1
|
|
} else {
|
|
newval |= bpmem.blendmode.srcfactor << 3;
|
|
newval |= bpmem.blendmode.dstfactor << 6;
|
|
}
|
|
} else {
|
|
newval |= 0x0008; // src 1 dst 0
|
|
}
|
|
|
|
u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode;
|
|
|
|
if (changes & 1) {
|
|
newval & 1 ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
|
}
|
|
|
|
bool dstAlphaEnable = g_bBlendSeparate && newval & 2;
|
|
|
|
if (changes & 6) {
|
|
// dst alpha enable or subtract enable change
|
|
if (dstAlphaEnable)
|
|
glBlendEquationSeparate(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD, GL_FUNC_ADD);
|
|
else
|
|
glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD);
|
|
}
|
|
|
|
if (changes & 0x1FA) {
|
|
// dst alpha enable or blend RGB change
|
|
if (dstAlphaEnable)
|
|
glBlendFuncSeparate(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7], GL_CONSTANT_ALPHA, GL_ZERO);
|
|
else
|
|
glBlendFunc(glSrcFactors[(newval >> 3) & 7], glDestFactors[(newval >> 6) & 7]);
|
|
}
|
|
|
|
if (dstAlphaEnable && changes & 0x1FE00) {
|
|
// dst alpha color change
|
|
glBlendColor(0, 0, 0, (float)bpmem.dstalpha.alpha / 255.0f);
|
|
}
|
|
|
|
s_blendMode = newval;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
|
|
// case 0x52 > SetScissorRect()
|
|
// ---------------
|
|
// This function handles the OpenGL glScissor() function
|
|
// ---------------
|
|
// bpmem.scissorTL.x, y = 342x342
|
|
// bpmem.scissorBR.x, y = 981x821
|
|
// Renderer::GetTargetHeight() = the fixed ini file setting
|
|
// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box
|
|
// therefore the width and height are (scissorBR + 1) - scissorTL
|
|
// ---------------
|
|
bool Renderer::SetScissorRect()
|
|
{
|
|
int xoff = bpmem.scissorOffset.x * 2 - 342;
|
|
int yoff = bpmem.scissorOffset.y * 2 - 342;
|
|
float MValueX = OpenGL_GetXmax();
|
|
float MValueY = OpenGL_GetYmax();
|
|
float rc_left = (float)bpmem.scissorTL.x - xoff - 342; // left = 0
|
|
rc_left *= MValueX;
|
|
if (rc_left < 0) rc_left = 0;
|
|
|
|
float rc_top = (float)bpmem.scissorTL.y - yoff - 342; // right = 0
|
|
rc_top *= MValueY;
|
|
if (rc_top < 0) rc_top = 0;
|
|
|
|
float rc_right = (float)bpmem.scissorBR.x - xoff - 341; // right = 640
|
|
rc_right *= MValueX;
|
|
if (rc_right > 640 * MValueX) rc_right = 640 * MValueX;
|
|
|
|
float rc_bottom = (float)bpmem.scissorBR.y - yoff - 341; // bottom = 480
|
|
rc_bottom *= MValueY;
|
|
if (rc_bottom > 480 * MValueY) rc_bottom = 480 * MValueY;
|
|
|
|
/*LOG("Scissor: lt=(%d,%d), rb=(%d,%d,%i), off=(%d,%d)\n",
|
|
rc_left, rc_top,
|
|
rc_right, rc_bottom, Renderer::GetTargetHeight(),
|
|
xoff, yoff
|
|
);*/
|
|
|
|
// Check that the coordinates are good
|
|
if (rc_right >= rc_left && rc_bottom >= rc_top)
|
|
{
|
|
glScissor(
|
|
(int)rc_left, // x = 0 for example
|
|
Renderer::GetTargetHeight() - (int)(rc_bottom), // y = 0 for example
|
|
(int)(rc_right-rc_left), // width = 640 for example
|
|
(int)(rc_bottom-rc_top) // height = 480 for example
|
|
);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Renderer::IsUsingATIDrawBuffers()
|
|
{
|
|
return s_bATIDrawBuffers;
|
|
}
|
|
|
|
bool Renderer::HaveStencilBuffer()
|
|
{
|
|
return s_bHaveStencilBuffer;
|
|
}
|
|
|
|
void Renderer::SetZBufferRender()
|
|
{
|
|
nZBufferRender = 10; // give it 10 frames
|
|
GLenum s_drawbuffers[2] = {
|
|
GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_COLOR_ATTACHMENT1_EXT
|
|
};
|
|
glDrawBuffers(2, s_drawbuffers);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, 0);
|
|
_assert_(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT);
|
|
}
|
|
|
|
void Renderer::FlushZBufferAlphaToTarget()
|
|
{
|
|
ResetGLState();
|
|
|
|
SetRenderTarget(0);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
|
|
|
glViewport(0, 0, GetTargetWidth(), GetTargetHeight());
|
|
|
|
// texture map s_RenderTargets[s_curtarget] onto the main buffer
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget);
|
|
TextureMngr::EnableTexRECT(0);
|
|
// disable all other stages
|
|
for (int i = 1; i < 8; ++i)
|
|
TextureMngr::DisableStage(i);
|
|
GL_REPORT_ERRORD();
|
|
|
|
// setup the stencil to only accept pixels that have been written
|
|
glStencilFunc(GL_EQUAL, 1, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
// TODO: This code should not have to bother with stretchtofit checking -
|
|
// all necessary scale initialization should be done elsewhere.
|
|
// TODO: Investigate BlitFramebufferEXT.
|
|
if (g_Config.bStretchToFit)
|
|
{
|
|
//TODO: Do Correctly in a bit
|
|
float FactorW = 640.f / (float)OpenGL_GetWidth();
|
|
float FactorH = 480.f / (float)OpenGL_GetHeight();
|
|
|
|
float Max = (FactorW < FactorH) ? FactorH : FactorW;
|
|
float Temp = 1.0f / Max;
|
|
FactorW *= Temp;
|
|
FactorH *= Temp;
|
|
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0, 0); glVertex2f(-FactorW,-FactorH);
|
|
glTexCoord2f(0, (float)GetTargetHeight()); glVertex2f(-FactorW,FactorH);
|
|
glTexCoord2f((float)GetTargetWidth(), (float)GetTargetHeight()); glVertex2f(FactorW,FactorH);
|
|
glTexCoord2f((float)GetTargetWidth(), 0); glVertex2f(FactorW,-FactorH);
|
|
glEnd();
|
|
}
|
|
else
|
|
{
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0, 0); glVertex2f(-1,-1);
|
|
glTexCoord2f(0, (float)(GetTargetHeight())); glVertex2f(-1,1);
|
|
glTexCoord2f((float)(GetTargetWidth()), (float)(GetTargetHeight())); glVertex2f(1,1);
|
|
glTexCoord2f((float)(GetTargetWidth()), 0); glVertex2f(1,-1);
|
|
glEnd();
|
|
}
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
|
RestoreGLState();
|
|
}
|
|
|
|
void Renderer::SetRenderMode(RenderMode mode)
|
|
{
|
|
if (!s_bHaveStencilBuffer && mode == RM_ZBufferAlpha)
|
|
mode = RM_ZBufferOnly;
|
|
|
|
if (s_RenderMode == mode)
|
|
return;
|
|
|
|
if (mode == RM_Normal) {
|
|
// flush buffers
|
|
if (s_RenderMode == RM_ZBufferAlpha) {
|
|
FlushZBufferAlphaToTarget();
|
|
glDisable(GL_STENCIL_TEST);
|
|
}
|
|
SetColorMask();
|
|
SetRenderTarget(0);
|
|
SetZBufferRender();
|
|
GL_REPORT_ERRORD();
|
|
}
|
|
else if (s_RenderMode == RM_Normal) {
|
|
// setup buffers
|
|
_assert_(GetZBufferTarget() && bpmem.zmode.updateenable);
|
|
|
|
if (mode == RM_ZBufferAlpha) {
|
|
glEnable(GL_STENCIL_TEST);
|
|
glClearStencil(0);
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
}
|
|
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
GL_REPORT_ERRORD();
|
|
}
|
|
else {
|
|
_assert_(GetZBufferTarget());
|
|
_assert_(s_bHaveStencilBuffer);
|
|
|
|
if (mode == RM_ZBufferOnly) {
|
|
// flush and remove stencil
|
|
_assert_(s_RenderMode==RM_ZBufferAlpha);
|
|
FlushZBufferAlphaToTarget();
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
SetRenderTarget(s_ZBufferTarget);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
GL_REPORT_ERRORD();
|
|
}
|
|
else {
|
|
_assert_(mode == RM_ZBufferAlpha && s_RenderMode == RM_ZBufferOnly);
|
|
|
|
// setup stencil
|
|
glEnable(GL_STENCIL_TEST);
|
|
glClearStencil(0);
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
}
|
|
}
|
|
|
|
s_RenderMode = mode;
|
|
}
|
|
|
|
Renderer::RenderMode Renderer::GetRenderMode()
|
|
{
|
|
return s_RenderMode;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// This function has the final picture if the XFB functions are not used. We adjust the aspect ratio here.
|
|
// ----------------------
|
|
void Renderer::Swap(const TRectangle& rc)
|
|
{
|
|
OpenGL_Update(); // just updates the render window position and the backbuffer size
|
|
|
|
DVSTARTPROFILE();
|
|
|
|
Renderer::SetRenderMode(Renderer::RM_Normal);
|
|
|
|
#if 0
|
|
// Blit the FBO to do Anti-Aliasing
|
|
// Not working?
|
|
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, s_uFramebuffer);
|
|
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
|
|
glBlitFramebufferEXT(0, 0, 640, 480, 0, 0, OpenGL_GetWidth(), OpenGL_GetHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
|
|
|
|
#else
|
|
// render to the real buffer now
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the backbuffer
|
|
|
|
// -----------------------------------------------------------------------
|
|
// GLViewPort variables
|
|
// ------------------
|
|
/* Work with float values for the XFB supplement and aspect ratio functions. These are default
|
|
values that are used if the XFB supplement and the keep aspect ratio function are unused */
|
|
float FloatGLWidth = (float)OpenGL_GetWidth();
|
|
float FloatGLHeight = (float)OpenGL_GetHeight();
|
|
float FloatXOffset = 0, FloatYOffset = 0;
|
|
// -------------------------------------
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// XFB supplement, fix the black borders problem
|
|
// ------------------
|
|
/* I'm limiting it to the stretch to fit option because I don't know how the other mode works. The reason
|
|
I don't allow this option together with UseXFB is that they are supplements and the XFB function
|
|
should be able to produce the same result */
|
|
if(g_Config.bStretchToFit && !g_Config.bUseXFB)
|
|
{
|
|
// The rendering window size
|
|
float WinWidth = (float)OpenGL_GetWidth();
|
|
float WinHeight = (float)OpenGL_GetHeight();
|
|
|
|
// The fraction of the screen that the image occupies
|
|
// Rc.right and rc.bottom is the original picture pixel size
|
|
/* There is a +1 in Rc (earlier called multirc, the input to this function), but these
|
|
adjustments seems to work better without it. */
|
|
float WidthRatio = (float)(rc.right - 1) / 640.0f;
|
|
float HeightRatio = (float)(rc.bottom - 1) / 480.0f;
|
|
|
|
// The pixel size of the image on the screen, adjusted for the actual window size
|
|
float OldWidth = WidthRatio * (float)WinWidth;
|
|
float OldHeight = HeightRatio * (float)WinHeight;
|
|
|
|
// The adjusted width and height
|
|
FloatGLWidth = ceil((float)WinWidth / WidthRatio);
|
|
FloatGLHeight = ceil((float)WinHeight / HeightRatio);
|
|
|
|
// The width and height deficit in actual pixels
|
|
float WidthDeficit = (float)WinWidth - OldWidth;
|
|
float HeightDeficit = (float)WinHeight - OldHeight;
|
|
|
|
// The picture will be drawn from the bottom so we need this YOffset
|
|
// The X-axis needs no adjustment because the picture begins from the left
|
|
FloatYOffset = -HeightDeficit / HeightRatio;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Logging
|
|
// ------------------
|
|
/*
|
|
Console::ClearScreen();
|
|
Console::Print("Bpmem L:%i T:%i X:%i Y:%i\n", bpmem.copyTexSrcXY.x, bpmem.copyTexSrcXY.y, bpmem.copyTexSrcWH.x, bpmem.copyTexSrcWH.y);
|
|
Console::Print("Input Left:%i Top:%i Right:%i Bottom:%i\n", rc.left, rc.top, rc.right, rc.bottom);
|
|
Console::Print("Old picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f\n", WidthRatio, OldWidth, HeightRatio, OldHeight);
|
|
Console::Print("New picture: Width[%1.2f]:%4.0f Height[%1.2f]:%4.0f YOffset:%4.0f YDeficit:%4.0f\n", WidthRatio, WinWidth, HeightRatio, WinHeight, FloatYOffset, HeightDeficit);
|
|
Console::Print("----------------------------------------------------------------\n");
|
|
*/
|
|
// ------------------------------
|
|
}
|
|
// ------------------------------
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
/* Keep aspect ratio at 4:3. This may be interesting if you for example have a 5:4 screen but don't like
|
|
the stretching, and would rather have a letterbox. */
|
|
// Output: GLWidth, GLHeight, XOffset, YOffset
|
|
// ------------------
|
|
|
|
// The rendering window size
|
|
float WinWidth = (float)OpenGL_GetWidth();
|
|
float WinHeight = (float)OpenGL_GetHeight();
|
|
// The rendering window aspect ratio as a fraction of the 4:3 ratio
|
|
float Ratio = WinWidth / WinHeight / (4.0f / 3.0f);
|
|
float wAdj, hAdj;
|
|
// Actual pixel size of the picture after adjustment
|
|
float PictureWidth = WinWidth, PictureHeight = WinHeight;
|
|
|
|
// This function currently only works together with the Stretch To Fit option
|
|
if (g_Config.bKeepAR && g_Config.bStretchToFit)
|
|
{
|
|
// Check if height or width is the limiting factor. If ratio > 1 the picture is to wide and have to limit the width.
|
|
if (Ratio > 1)
|
|
{
|
|
// ------------------------------------------------
|
|
// Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen
|
|
// ----------------
|
|
wAdj = Ratio;
|
|
hAdj = 1.0;
|
|
FloatGLWidth = FloatGLWidth / wAdj;
|
|
FloatGLHeight = FloatGLHeight / hAdj;
|
|
// --------------------
|
|
|
|
// ------------------------------------------------
|
|
// Calculate the new X offset
|
|
// ----------------
|
|
// The picture width
|
|
PictureWidth = WinWidth / Ratio;
|
|
// Move the left of the picture to the middle of the screen
|
|
FloatXOffset = FloatXOffset + WinWidth / 2.0f;
|
|
// Then remove half the picture height to move it to the horizontal center
|
|
FloatXOffset = FloatXOffset - PictureWidth / 2.0f;
|
|
// --------------------
|
|
}
|
|
// The window is to high, we have to limit the height
|
|
else
|
|
{
|
|
// ------------------------------------------------
|
|
// Calculate the new width and height for glViewport, this is not the actual size of either the picture or the screen
|
|
// ----------------
|
|
// Invert the ratio to make it > 1
|
|
Ratio = 1.0f / Ratio;
|
|
wAdj = 1.0f;
|
|
hAdj = Ratio;
|
|
FloatGLWidth = FloatGLWidth / wAdj;
|
|
FloatGLHeight = FloatGLHeight / hAdj;
|
|
// --------------------
|
|
|
|
// ------------------------------------------------
|
|
// Calculate the new Y offset
|
|
// ----------------
|
|
// The picture height
|
|
PictureHeight = WinHeight / Ratio;
|
|
// Keep the picture on the bottom of the screen, this is needed because YOffset may not be 0 here
|
|
FloatYOffset = FloatYOffset / hAdj;
|
|
// Move the bottom of the picture to the middle of the screen
|
|
FloatYOffset = FloatYOffset + WinHeight / 2.0f;
|
|
// Then remove half the picture height to move it to the vertical center
|
|
FloatYOffset = FloatYOffset - PictureHeight / 2.0f;
|
|
// --------------------
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Logging
|
|
// ------------------
|
|
/*
|
|
Console::Print("Screen Width:%4.0f Height:%4.0f Ratio:%1.2f\n", WinWidth, WinHeight, Ratio);
|
|
Console::Print("GL Width:%4.1f Height:%4.1f\n", FloatGLWidth, FloatGLHeight);
|
|
Console::Print("Picture Width:%4.1f Height:%4.1f YOffset:%4.0f\n", PictureWidth, PictureHeight, FloatYOffset);
|
|
Console::Print("----------------------------------------------------------------\n");
|
|
*/
|
|
// ------------------------------
|
|
}
|
|
// -------------------------------------
|
|
|
|
// -----------------------------------------------------------------------
|
|
/* Adjustments to
|
|
FloatGLWidth
|
|
FloatGLHeight
|
|
XOffset
|
|
YOffset
|
|
are done. Calculate the new new windows width and height. */
|
|
// --------------------
|
|
int GLx = OpenGL_GetXoff(); // These two are zero
|
|
int GLy = OpenGL_GetYoff();
|
|
int GLWidth = ceil(FloatGLWidth);
|
|
int GLHeight = ceil(FloatGLHeight);
|
|
|
|
// Because there is no round() function we use round(float) = floor(float + 0.5) instead
|
|
int YOffset = floor(FloatYOffset + 0.5);
|
|
int XOffset = floor(FloatXOffset+ 0.5);
|
|
// -------------------------------------
|
|
|
|
|
|
// Update GLViewPort
|
|
glViewport(
|
|
GLx + XOffset,
|
|
GLy + YOffset,
|
|
GLWidth,
|
|
GLHeight
|
|
);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Show the finished picture
|
|
// --------------------
|
|
// Reset GL state
|
|
ResetGLState();
|
|
|
|
// Texture map s_RenderTargets[s_curtarget] onto the main buffer
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTarget);
|
|
TextureMngr::EnableTexRECT(0);
|
|
|
|
// Disable all other stages
|
|
for (int i = 1; i < 8; ++i)
|
|
TextureMngr::DisableStage(i);
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
// Place the texture
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0, 0); glVertex2f(-1,-1);
|
|
glTexCoord2f(0, (float)GetTargetHeight()); glVertex2f(-1,1);
|
|
glTexCoord2f((float)GetTargetWidth(), (float)GetTargetHeight()); glVertex2f(1,1);
|
|
glTexCoord2f((float)GetTargetWidth(), 0); glVertex2f(1,-1);
|
|
glEnd();
|
|
|
|
// Wireframe
|
|
if (g_Config.bWireFrame)
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
|
TextureMngr::DisableStage(0);
|
|
// -------------------------------------
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
/* Blacken out the borders in the 4:3 or the coming 16:9 aspect ratio modes. Somewhere in BPStructs
|
|
0x52 or elsewhere the area outside the actual picture, that we now show with the aspect ratio option
|
|
has been filled with either for example white, or have copies of old renderings on it. So we replace
|
|
that with blacknes. */
|
|
// --------------------
|
|
if(g_Config.bKeepAR)
|
|
{
|
|
// Set current drawing color to red
|
|
glColor3f(0.0, 0.0, 0.0); // Black
|
|
|
|
/* This doesn't work
|
|
glRecti, glRectf(
|
|
(float)Left,
|
|
(float)Right,
|
|
(float)Top,
|
|
(float)Bottom); */
|
|
|
|
/* The glVertex3f() coordinates are:
|
|
Top: 1 to -1 from top to bottom
|
|
Left: 1 to -1 from right to left
|
|
Height and width is therefore 2.0, zero is the center of the screen
|
|
*/
|
|
// The fraction of the screen that the image occupies
|
|
/* It's hard to make a border that always exactly begin where the screen ends. The ( -1) here will
|
|
sometimes hide one pixel to many, but if we use (- 0) we will on the other hand sometimes show
|
|
one line of pixels that we should not show. But a -0.5 adjustment seemed just right, it never hid
|
|
one pixel to much, and never one to little. So I settle with a -0.5 adjustment here. */
|
|
float HeightRatio = (((float)(rc.bottom)) - 0.5) / 480.0f;
|
|
|
|
// Bottom border
|
|
float FLeft = 1.0,
|
|
FWidth = -2.0,
|
|
FHeight = 2.0 * (1.0 - HeightRatio),
|
|
FTop = -1.0 + FHeight;
|
|
|
|
glBegin(GL_POLYGON);
|
|
glVertex3f (FLeft, FTop - FHeight, 0.0);
|
|
glVertex3f (FLeft + FWidth, FTop - FHeight, 0.0);
|
|
glVertex3f (FLeft + FWidth, FTop, 0.0);
|
|
glVertex3f (FLeft, FTop, 0.0);
|
|
glEnd();
|
|
//Console::Print("Border Left:%1.3f Top:%1.3f Width:%1.3f Height:%1.3f\n", FLeft, FTop, FWidth, FHeight);
|
|
}
|
|
// -------------------------------------
|
|
|
|
|
|
// Place messages on the picture, then copy it to the screen
|
|
SwapBuffers();
|
|
|
|
RestoreGLState();
|
|
#endif
|
|
GL_REPORT_ERRORD();
|
|
|
|
g_Config.iSaveTargetId = 0;
|
|
|
|
|
|
// for testing zbuffer targets
|
|
//Renderer::SetZBufferRender();
|
|
//SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, GetTargetWidth(), GetTargetHeight());
|
|
}
|
|
////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// We can now draw whatever we want on top of the picture. Then we copy the final picture to the output.
|
|
// ----------------------
|
|
void Renderer::SwapBuffers()
|
|
{
|
|
// -----------------------------------------------------------------------
|
|
/* Draw messages on the screen */
|
|
// --------------------
|
|
static int fpscount;
|
|
static int s_fps;
|
|
static unsigned long lasttime;
|
|
char debugtext_buffer[8192];
|
|
char *p = debugtext_buffer;
|
|
p[0] = 0;
|
|
|
|
++fpscount;
|
|
if (timeGetTime() - lasttime > 1000)
|
|
{
|
|
lasttime = timeGetTime();
|
|
s_fps = fpscount;
|
|
fpscount = 0;
|
|
}
|
|
|
|
if (g_Config.bShowFPS)
|
|
{
|
|
p+=sprintf(p, "FPS: %d\n", s_fps);
|
|
}
|
|
|
|
if (g_Config.bOverlayStats) {
|
|
p+=sprintf(p,"textures created: %i\n",stats.numTexturesCreated);
|
|
p+=sprintf(p,"textures alive: %i\n",stats.numTexturesAlive);
|
|
p+=sprintf(p,"pshaders created: %i\n",stats.numPixelShadersCreated);
|
|
p+=sprintf(p,"pshaders alive: %i\n",stats.numPixelShadersAlive);
|
|
p+=sprintf(p,"vshaders created: %i\n",stats.numVertexShadersCreated);
|
|
p+=sprintf(p,"vshaders alive: %i\n",stats.numVertexShadersAlive);
|
|
p+=sprintf(p,"dlists called: %i\n",stats.numDListsCalled);
|
|
p+=sprintf(p,"dlists called(f): %i\n",stats.thisFrame.numDListsCalled);
|
|
// not used.
|
|
//p+=sprintf(p,"dlists created: %i\n",stats.numDListsCreated);
|
|
//p+=sprintf(p,"dlists alive: %i\n",stats.numDListsAlive);
|
|
//p+=sprintf(p,"strip joins: %i\n",stats.numJoins);
|
|
p+=sprintf(p,"primitives: %i\n",stats.thisFrame.numPrims);
|
|
p+=sprintf(p,"primitive joins: %i\n",stats.thisFrame.numPrimitiveJoins);
|
|
p+=sprintf(p,"buffer splits: %i\n",stats.thisFrame.numBufferSplits);
|
|
p+=sprintf(p,"draw calls: %i\n",stats.thisFrame.numDrawCalls);
|
|
p+=sprintf(p,"primitives (DL): %i\n",stats.thisFrame.numDLPrims);
|
|
p+=sprintf(p,"XF loads: %i\n",stats.thisFrame.numXFLoads);
|
|
p+=sprintf(p,"XF loads (DL): %i\n",stats.thisFrame.numXFLoadsInDL);
|
|
p+=sprintf(p,"CP loads: %i\n",stats.thisFrame.numCPLoads);
|
|
p+=sprintf(p,"CP loads (DL): %i\n",stats.thisFrame.numCPLoadsInDL);
|
|
p+=sprintf(p,"BP loads: %i\n",stats.thisFrame.numBPLoads);
|
|
p+=sprintf(p,"BP loads (DL): %i\n",stats.thisFrame.numBPLoadsInDL);
|
|
p+=sprintf(p,"vertex loaders: %i\n",stats.numVertexLoaders);
|
|
|
|
std::string text1;
|
|
VertexLoaderManager::AppendListToString(&text1);
|
|
p+=sprintf(p,"%s",text1.c_str());
|
|
}
|
|
|
|
if (g_Config.bOverlayBlendStats)
|
|
{
|
|
p+=sprintf(p,"LogicOp Mode: %i\n", stats.logicOpMode);
|
|
p+=sprintf(p,"Source Factor: %i\n", stats.srcFactor);
|
|
p+=sprintf(p,"Destination Factor: %i\n", stats.dstFactor);
|
|
p+=sprintf(p,"Dithering: %s\n", stats.dither==1 ? "Enabled" : "Disabled");
|
|
p+=sprintf(p,"Color Update: %s\n", stats.colorUpdate==1 ? "Enabled" : "Disabled");
|
|
p+=sprintf(p,"Alpha Update: %s\n", stats.alphaUpdate==1 ? "Enabled" : "Disabled");
|
|
p+=sprintf(p,"Dst Alpha Enabled: %s\n", stats.dstAlphaEnable==1 ? "Enabled" : "Disabled");
|
|
p+=sprintf(p,"Dst Alpha: %08x\n", stats.dstAlpha);
|
|
|
|
}
|
|
|
|
if (g_Config.bOverlayProjStats)
|
|
{
|
|
p+=sprintf(p,"Projection #: X for Raw 6=0 (X for Raw 6!=0)\n\n");
|
|
p+=sprintf(p,"Projection 0: %f (%f) Raw 0: %f\n", stats.gproj_0, stats.g2proj_0, stats.proj_0);
|
|
p+=sprintf(p,"Projection 1: %f (%f)\n", stats.gproj_1, stats.g2proj_1);
|
|
p+=sprintf(p,"Projection 2: %f (%f) Raw 1: %f\n", stats.gproj_2, stats.g2proj_2, stats.proj_1);
|
|
p+=sprintf(p,"Projection 3: %f (%f)\n\n", stats.gproj_3, stats.g2proj_3);
|
|
p+=sprintf(p,"Projection 4: %f (%f)\n", stats.gproj_4, stats.g2proj_4);
|
|
p+=sprintf(p,"Projection 5: %f (%f) Raw 2: %f\n", stats.gproj_5, stats.g2proj_5, stats.proj_2);
|
|
p+=sprintf(p,"Projection 6: %f (%f) Raw 3: %f\n", stats.gproj_6, stats.g2proj_6, stats.proj_3);
|
|
p+=sprintf(p,"Projection 7: %f (%f)\n\n", stats.gproj_7, stats.g2proj_7);
|
|
p+=sprintf(p,"Projection 8: %f (%f)\n", stats.gproj_8, stats.g2proj_8);
|
|
p+=sprintf(p,"Projection 9: %f (%f)\n", stats.gproj_9, stats.g2proj_9);
|
|
p+=sprintf(p,"Projection 10: %f (%f) Raw 4: %f\n\n", stats.gproj_10, stats.g2proj_10, stats.proj_4);
|
|
p+=sprintf(p,"Projection 11: %f (%f) Raw 5: %f\n\n", stats.gproj_11, stats.g2proj_11, stats.proj_5);
|
|
p+=sprintf(p,"Projection 12: %f (%f)\n", stats.gproj_12, stats.g2proj_12);
|
|
p+=sprintf(p,"Projection 13: %f (%f)\n", stats.gproj_13, stats.g2proj_13);
|
|
p+=sprintf(p,"Projection 14: %f (%f)\n", stats.gproj_14, stats.g2proj_14);
|
|
p+=sprintf(p,"Projection 15: %f (%f)\n", stats.gproj_15, stats.g2proj_15);
|
|
}
|
|
|
|
// Render a shadow, and then the text.
|
|
Renderer::RenderText(debugtext_buffer, 21, 21, 0xDD000000);
|
|
Renderer::RenderText(debugtext_buffer, 20, 20, 0xFF00FFFF);
|
|
|
|
OSD::DrawMessages();
|
|
// -----------------------------
|
|
|
|
|
|
#if defined(DVPROFILE)
|
|
if (g_bWriteProfile) {
|
|
//g_bWriteProfile = 0;
|
|
static int framenum = 0;
|
|
const int UPDATE_FRAMES = 8;
|
|
if (++framenum >= UPDATE_FRAMES) {
|
|
DVProfWrite("prof.txt", UPDATE_FRAMES);
|
|
DVProfClear();
|
|
framenum = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Copy the rendered frame to the real window
|
|
OpenGL_SwapBuffers();
|
|
|
|
glClearColor(0,0,0,0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
GL_REPORT_ERRORD();
|
|
|
|
// Clean out old stuff from caches
|
|
PixelShaderCache::Cleanup();
|
|
TextureMngr::ProgressiveCleanup();
|
|
|
|
frameCount++;
|
|
|
|
// New frame
|
|
stats.ResetFrame();
|
|
|
|
// Render to the framebuffer.
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);
|
|
|
|
if (nZBufferRender > 0) {
|
|
if (--nZBufferRender == 0) {
|
|
// turn off
|
|
nZBufferRender = 0;
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
|
|
Renderer::SetRenderMode(RM_Normal); // turn off any zwrites
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::RenderText(const char* pstr, int left, int top, u32 color)
|
|
{
|
|
int nBackbufferWidth = (int)OpenGL_GetWidth();
|
|
int nBackbufferHeight = (int)OpenGL_GetHeight();
|
|
glColor4f(((color>>16) & 0xff)/255.0f, ((color>> 8) & 0xff)/255.0f,
|
|
((color>> 0) & 0xff)/255.0f, ((color>>24) & 0xFF)/255.0f);
|
|
s_pfont->printMultilineText(pstr,
|
|
left * 2.0f / (float)nBackbufferWidth - 1,
|
|
1 - top * 2.0f / (float)nBackbufferHeight,
|
|
0, nBackbufferWidth, nBackbufferHeight);
|
|
}
|
|
|
|
bool Renderer::SaveRenderTarget(const char* filename, int jpeg)
|
|
{
|
|
bool bflip = true;
|
|
int nBackbufferHeight = (int)OpenGL_GetHeight();
|
|
int nBackbufferWidth = (int)OpenGL_GetWidth();
|
|
|
|
std::vector<u32> data(nBackbufferWidth * nBackbufferHeight);
|
|
glReadPixels(0, 0, nBackbufferWidth, nBackbufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
|
|
if (glGetError() != GL_NO_ERROR)
|
|
return false;
|
|
|
|
if (bflip) {
|
|
// swap scanlines
|
|
std::vector<u32> scanline(nBackbufferWidth);
|
|
for (int i = 0; i < nBackbufferHeight/2; ++i) {
|
|
memcpy(&scanline[0], &data[i*nBackbufferWidth], nBackbufferWidth*4);
|
|
memcpy(&data[i*nBackbufferWidth], &data[(nBackbufferHeight-i-1)*nBackbufferWidth], nBackbufferWidth*4);
|
|
memcpy(&data[(nBackbufferHeight-i-1)*nBackbufferWidth], &scanline[0], nBackbufferWidth*4);
|
|
}
|
|
}
|
|
|
|
return SaveTGA(filename, nBackbufferWidth, nBackbufferHeight, &data[0]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Function: This function does not have the final picture. Use Renderer::Swap() to adjust the final picture.
|
|
// Call schedule: Called from VertexShaderManager
|
|
// ----------------------
|
|
void UpdateViewport()
|
|
{
|
|
// -----------------------------------------------------------------------
|
|
// Logging
|
|
// ------------------
|
|
// reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
|
|
// [0] = width/2
|
|
// [1] = height/2
|
|
// [2] = 16777215 * (farz - nearz)
|
|
// [3] = xorig + width/2 + 342
|
|
// [4] = yorig + height/2 + 342
|
|
// [5] = 16777215 * farz
|
|
|
|
/*INFO_LOG("view: topleft=(%f,%f), wh=(%f,%f), z=(%f,%f)\n",
|
|
rawViewport[3]-rawViewport[0]-342, rawViewport[4]+rawViewport[1]-342,
|
|
2 * rawViewport[0], 2 * rawViewport[1],
|
|
(rawViewport[5] - rawViewport[2]) / 16777215.0f, rawViewport[5] / 16777215.0f);*/
|
|
// --------------------------
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// GLViewPort variables
|
|
// ------------------
|
|
int GLWidth, GLHeight, GLx, GLy;
|
|
float FloatGLWidth = fabs(2 * xfregs.rawViewport[0]);
|
|
float FloatGLHeight = fabs(2 * xfregs.rawViewport[1]);
|
|
|
|
// rawViewport[0] = 320, rawViewport[1] = -240
|
|
int scissorXOff = bpmem.scissorOffset.x * 2 - 342;
|
|
int scissorYOff = bpmem.scissorOffset.y * 2 - 342;
|
|
// -------------------------------------
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Stretch picture while keeping the native resolution
|
|
// ------------------
|
|
if (g_Config.bStretchToFit)
|
|
{
|
|
GLx = (int)(xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff);
|
|
GLy = Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff));
|
|
// Round up to the nearest integer
|
|
GLWidth = (int)ceil(FloatGLWidth);
|
|
GLHeight = (int)ceil(FloatGLHeight);
|
|
}
|
|
// -----------------------------------------------------------------------
|
|
// Stretch picture with increased internal resolution
|
|
// ------------------
|
|
else
|
|
{
|
|
float MValueX = OpenGL_GetXmax();
|
|
float MValueY = OpenGL_GetYmax();
|
|
|
|
GLx = (int)ceil((xfregs.rawViewport[3] - xfregs.rawViewport[0] - 342 - scissorXOff) * MValueX);
|
|
GLy = (int)ceil(Renderer::GetTargetHeight() - ((int)(xfregs.rawViewport[4] - xfregs.rawViewport[1] - 342 - scissorYOff)) * MValueY);
|
|
GLWidth = (int)ceil(abs((int)(2 * xfregs.rawViewport[0])) * MValueX);
|
|
GLHeight = (int)ceil(abs((int)(2 * xfregs.rawViewport[1])) * MValueY);
|
|
}
|
|
// -------------------------------------
|
|
|
|
|
|
// Update the view port
|
|
glViewport(
|
|
GLx, GLy,
|
|
GLWidth, GLHeight
|
|
);
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// GLDepthRange
|
|
// ------------------
|
|
double GLNear = (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777215.0f;
|
|
double GLFar = xfregs.rawViewport[5] / 16777215.0f;
|
|
glDepthRange(GLNear, GLFar);
|
|
// -------------------------------------
|
|
|
|
// Logging
|
|
/*
|
|
RECT RcTop, RcParent, RcChild;
|
|
HWND Child = EmuWindow::GetWnd();
|
|
HWND Parent = GetParent(Child);
|
|
HWND Top = GetParent(Parent);
|
|
GetWindowRect(Top, &RcTop);
|
|
GetWindowRect(Parent, &RcParent);
|
|
GetWindowRect(Child, &RcChild);
|
|
|
|
//Console::ClearScreen();
|
|
Console::Print("----------------------------------------------------------------\n");
|
|
Console::Print("Top window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcTop.left, RcTop.top, RcTop.right - RcTop.left, RcTop.bottom - RcTop.top);
|
|
Console::Print("Parent window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcParent.left, RcParent.top, RcParent.right - RcParent.left, RcParent.bottom - RcParent.top);
|
|
Console::Print("Child window: X:%03i Y:%03i Width:%03i Height:%03i\n", RcChild.left, RcChild.top, RcChild.right - RcChild.left, RcChild.bottom - RcChild.top);
|
|
Console::Print("----------------------------------------------------------------\n");
|
|
Console::Print("Res. MValue: X:%f Y:%f XOffs:%f YOffs:%f\n", OpenGL_GetXmax(), OpenGL_GetYmax(), OpenGL_GetXoff(), OpenGL_GetYoff());
|
|
Console::Print("GLViewPort: X:%03i Y:%03i Width:%03i Height:%03i\n", GLx, GLy, GLWidth, GLHeight);
|
|
Console::Print("GLDepthRange: Near:%f Far:%f\n", GLNear, GLFar);
|
|
Console::Print("GLScissor: X:%03i Y:%03i Width:%03i Height:%03i\n", GLScissorX, GLScissorY, GLScissorW, GLScissorH);
|
|
Console::Print("----------------------------------------------------------------\n");
|
|
*/
|
|
}
|