921 lines
31 KiB
C++
921 lines
31 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 <list>
|
|
|
|
#ifdef _WIN32
|
|
#include <mmsystem.h>
|
|
#endif
|
|
|
|
#include "GLInit.h"
|
|
#include "Profiler.h"
|
|
#include "Render.h"
|
|
#include "OpcodeDecoding.h"
|
|
#include "BPStructs.h"
|
|
#include "TextureMngr.h"
|
|
#include "rasterfont.h"
|
|
#include "VertexShader.h"
|
|
#include "PixelShaderManager.h"
|
|
#include "VertexLoader.h"
|
|
#include "XFB.h"
|
|
#include "Debugger/Debugger.h" // for the CDebugger class
|
|
#include "Logging/Logging.h" // for Logging()
|
|
|
|
#ifdef _WIN32
|
|
#include "OS/Win32.h"
|
|
#else
|
|
#endif
|
|
|
|
struct MESSAGE
|
|
{
|
|
MESSAGE() {}
|
|
MESSAGE(const char* p, u32 dw) { strcpy(str, p); dwTimeStamp = dw; }
|
|
char str[255];
|
|
u32 dwTimeStamp;
|
|
};
|
|
|
|
CGcontext g_cgcontext;
|
|
CGprofile g_cgvProf, g_cgfProf;
|
|
extern CDebugger* m_frame; // the debugging class
|
|
|
|
static int g_MaxTexWidth = 0, g_MaxTexHeight = 0;
|
|
static RasterFont* s_pfont = NULL;
|
|
static std::list<MESSAGE> s_listMsgs;
|
|
static bool s_bFullscreen = false;
|
|
static bool s_bOutputCgErrors = true;
|
|
static int nZBufferRender = 0; // if > 0, then using zbuffer render
|
|
static u32 s_uFramebuffer = 0;
|
|
static u32 s_RenderTargets[1] = {0}, s_DepthTarget = 0, s_ZBufferTarget = 0;
|
|
|
|
static bool s_bATIDrawBuffers = false, s_bHaveStencilBuffer = false;
|
|
static Renderer::RenderMode s_RenderMode = Renderer::RM_Normal;
|
|
static int s_nCurTarget = 0;
|
|
bool g_bBlendLogicOp = false;
|
|
|
|
|
|
void HandleCgError(CGcontext ctx, CGerror err, void* appdata);
|
|
|
|
|
|
bool Renderer::Create2()
|
|
{
|
|
bool bSuccess = true;
|
|
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;
|
|
|
|
__Log("Supported OpenGL Extensions:\n");
|
|
__Log(ptoken); // write to the log file
|
|
__Log("\n");
|
|
|
|
if( strstr(ptoken, "GL_EXT_blend_logic_op") != NULL )
|
|
g_bBlendLogicOp = true;
|
|
if( strstr(ptoken, "ATI_draw_buffers") != NULL && strstr(ptoken, "ARB_draw_buffers") == NULL)
|
|
//Checks if it ONLY has the ATI_draw_buffers extension, some have both
|
|
s_bATIDrawBuffers = true;
|
|
|
|
s_bFullscreen = g_Config.bFullscreen;
|
|
|
|
if (glewInit() != GLEW_OK) {
|
|
ERROR_LOG("glewInit() failed!\n");
|
|
return false;
|
|
}
|
|
|
|
if (!GLEW_EXT_framebuffer_object) {
|
|
ERROR_LOG("*********\nGPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets\nGPU: *********\n");
|
|
bSuccess = false;
|
|
}
|
|
|
|
if (!GLEW_EXT_secondary_color) {
|
|
ERROR_LOG("*********\nGPU: OGL ERROR: Need GL_EXT_secondary_color\nGPU: *********\n");
|
|
bSuccess = false;
|
|
}
|
|
|
|
int numvertexattribs=0;
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint *)&numvertexattribs);
|
|
|
|
if (numvertexattribs < 11) {
|
|
ERROR_LOG("*********\nGPU: OGL ERROR: Number of attributes %d not enough\nGPU: *********\n", 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)\n");
|
|
#else
|
|
#ifdef __linux__
|
|
if (glXSwapIntervalSGI)
|
|
glXSwapIntervalSGI(0);
|
|
else
|
|
ERROR_LOG("no support for SwapInterval (framerate clamped to monitor refresh rate)\n");
|
|
#else
|
|
|
|
//TODO
|
|
|
|
#endif
|
|
#endif
|
|
// check the max texture width and height
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&g_MaxTexWidth);
|
|
g_MaxTexHeight = g_MaxTexWidth;
|
|
|
|
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\n");
|
|
}
|
|
|
|
_assert_( glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT );
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, s_uFramebuffer );
|
|
|
|
// create the framebuffer targets
|
|
glGenTextures(ARRAYSIZE(s_RenderTargets), (GLuint *)s_RenderTargets);
|
|
for(u32 i = 0; i < ARRAYSIZE(s_RenderTargets); ++i) {
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTargets[i]);
|
|
// 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);
|
|
}
|
|
s_nCurTarget = 0;
|
|
|
|
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_RenderTargets[s_nCurTarget], 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);
|
|
|
|
//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();
|
|
|
|
SetAA(g_Config.iMultisampleMode);
|
|
GL_REPORT_ERROR();
|
|
|
|
// 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);//CG_PROFILE_ARBFP1;
|
|
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]);
|
|
__Log("max program env parameters: vert=%d, frag=%d\n", nenvvertparams, nenvfragparams);
|
|
__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;
|
|
}
|
|
|
|
//glEnable(GL_POLYGON_OFFSET_FILL);
|
|
//glEnable(GL_POLYGON_OFFSET_LINE);
|
|
//glPolygonOffset(0, 1);
|
|
|
|
if (!Initialize())
|
|
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 != 0) {
|
|
cgDestroyContext(g_cgcontext);
|
|
g_cgcontext = 0;
|
|
}
|
|
|
|
if (s_RenderTargets[0]) {
|
|
glDeleteTextures(ARRAYSIZE(s_RenderTargets), (GLuint *)s_RenderTargets);
|
|
memset(s_RenderTargets, 0, sizeof(s_RenderTargets));
|
|
}
|
|
if (s_DepthTarget) {
|
|
glDeleteRenderbuffersEXT(1, (GLuint *)&s_DepthTarget); s_DepthTarget = 0;
|
|
}
|
|
if (s_uFramebuffer != 0) {
|
|
glDeleteFramebuffersEXT( 1, (GLuint *)&s_uFramebuffer);
|
|
s_uFramebuffer = 0;
|
|
}
|
|
}
|
|
|
|
|
|
bool Renderer::Initialize()
|
|
{
|
|
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);
|
|
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // perspective correct interpolation of colors and tex coords
|
|
|
|
// setup the default vertex declaration
|
|
glDisable(GL_STENCIL_TEST);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(0,0,nBackbufferWidth,nBackbufferHeight);
|
|
glBlendColorEXT(0, 0, 0, 0.5f);
|
|
glClearDepth(1.0f);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
//Renderer::SetZBufferRender();
|
|
|
|
// legacy multitexturing: select texture channel only
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
|
|
|
|
s_RenderMode = Renderer::RM_Normal;
|
|
|
|
GLenum err = GL_NO_ERROR;
|
|
GL_REPORT_ERROR();
|
|
|
|
return err == GL_NO_ERROR;
|
|
}
|
|
|
|
void Renderer::AddMessage(const char* pstr, u32 ms)
|
|
{
|
|
s_listMsgs.push_back(MESSAGE(pstr, timeGetTime() + ms));
|
|
}
|
|
|
|
void Renderer::ProcessMessages()
|
|
{
|
|
GLboolean wasEnabled = glIsEnabled(GL_BLEND);
|
|
|
|
if(!wasEnabled) glEnable(GL_BLEND);
|
|
|
|
if (s_listMsgs.size() > 0) {
|
|
int left = 25, top = 15;
|
|
list<MESSAGE>::iterator it = s_listMsgs.begin();
|
|
|
|
while( it != s_listMsgs.end() )
|
|
{
|
|
int time_left = (int)(it->dwTimeStamp - timeGetTime());
|
|
int alpha = 255;
|
|
|
|
if(time_left<1024)
|
|
{
|
|
alpha=time_left>>2;
|
|
if(time_left<0) alpha=0;
|
|
}
|
|
|
|
alpha<<=24;
|
|
|
|
RenderText(it->str, left+1, top+1, 0x000000|alpha);
|
|
RenderText(it->str, left, top, 0xffff30|alpha);
|
|
top += 15;
|
|
|
|
if (time_left <= 0)
|
|
it = s_listMsgs.erase(it);
|
|
else ++it;
|
|
}
|
|
}
|
|
|
|
if(!wasEnabled) glDisable(GL_BLEND);
|
|
}
|
|
|
|
void Renderer::RenderText(const char* pstr, int left, int top, u32 color)
|
|
{
|
|
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);
|
|
}
|
|
|
|
void Renderer::SetAA(int aa)
|
|
{
|
|
|
|
}
|
|
|
|
void Renderer::ReinitView(int nNewWidth, int nNewHeight)
|
|
{
|
|
int oldscreen = s_bFullscreen;
|
|
|
|
OpenGL_Shutdown();
|
|
int oldwidth = nBackbufferWidth, oldheight = nBackbufferHeight;
|
|
if (!OpenGL_Create(g_VideoInitialize, nNewWidth, nNewHeight)) {//nNewWidth&~7, nNewHeight&~7) ) {
|
|
ERROR_LOG("Failed to recreate, reverting to old settings\n");
|
|
if (!OpenGL_Create(g_VideoInitialize, oldwidth, oldheight)) {
|
|
g_VideoInitialize.pSysMessage("Failed to revert, exiting...\n");
|
|
// TODO - don't takedown the entire emu
|
|
exit(0);
|
|
}
|
|
}
|
|
OpenGL_MakeCurrent();
|
|
|
|
if (oldscreen && !g_Config.bFullscreen) { // if transitioning from full screen
|
|
#ifdef _WIN32
|
|
RECT rc;
|
|
rc.left = 0; rc.top = 0;
|
|
rc.right = nNewWidth; rc.bottom = nNewHeight;
|
|
AdjustWindowRect(&rc, EmuWindow::g_winstyle, FALSE);
|
|
|
|
RECT rcdesktop;
|
|
GetWindowRect(GetDesktopWindow(), &rcdesktop);
|
|
|
|
SetWindowLong( EmuWindow::GetWnd(), GWL_STYLE, EmuWindow::g_winstyle );
|
|
SetWindowPos(EmuWindow::GetWnd(), HWND_TOP, ((rcdesktop.right-rcdesktop.left)-(rc.right-rc.left))/2,
|
|
((rcdesktop.bottom-rcdesktop.top)-(rc.bottom-rc.top))/2,
|
|
rc.right-rc.left, rc.bottom-rc.top, SWP_SHOWWINDOW);
|
|
UpdateWindow(EmuWindow::GetWnd());
|
|
#else // linux
|
|
#endif
|
|
}
|
|
|
|
nBackbufferWidth = nNewWidth > 16 ? nNewWidth : 16;
|
|
nBackbufferHeight = nNewHeight > 16 ? nNewHeight : 16;
|
|
}
|
|
int Renderer::GetTargetWidth()
|
|
{
|
|
if(g_Config.bStretchToFit && g_Config.renderToMainframe)
|
|
return 640;
|
|
else
|
|
return nBackbufferWidth; // return the actual window width
|
|
}
|
|
|
|
int Renderer::GetTargetHeight()
|
|
{
|
|
if(g_Config.bStretchToFit && g_Config.renderToMainframe)
|
|
return 480;
|
|
else
|
|
return nBackbufferHeight; // return the actual window height
|
|
}
|
|
|
|
bool Renderer::CanBlendLogicOp()
|
|
{
|
|
return g_bBlendLogicOp;
|
|
}
|
|
|
|
void Renderer::SetRenderTarget(u32 targ)
|
|
{
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, targ!=0?targ:s_RenderTargets[s_nCurTarget], 0 );
|
|
}
|
|
|
|
void Renderer::SetDepthTarget(u32 targ)
|
|
{
|
|
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, targ != 0 ? targ : s_DepthTarget );
|
|
}
|
|
|
|
void Renderer::SetFramebuffer(u32 fb)
|
|
{
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fb != 0 ? fb : s_uFramebuffer );
|
|
}
|
|
|
|
u32 Renderer::GetRenderTarget()
|
|
{
|
|
return s_RenderTargets[s_nCurTarget];
|
|
}
|
|
|
|
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.blendmode.blendenable) glEnable(GL_BLEND);
|
|
if(bpmem.zmode.updateenable) glDepthMask(GL_TRUE);
|
|
|
|
glEnable( GL_VERTEX_PROGRAM_ARB );
|
|
glEnable( GL_FRAGMENT_PROGRAM_ARB );
|
|
SetColorMask();
|
|
}
|
|
|
|
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();
|
|
|
|
if(g_Config.bStretchToFit && g_Config.renderToMainframe)
|
|
{
|
|
//TODO: Do Correctly in a bit
|
|
float FactorW = (float)640 / (float)nBackbufferWidth;
|
|
float FactorH = (float)480 / (float)nBackbufferHeight;
|
|
|
|
float Max = (FactorW < FactorH) ? FactorH : FactorW;
|
|
float Temp = 1 / 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);
|
|
|
|
__Log("%d, %d", FactorW, FactorH);
|
|
}
|
|
else
|
|
{
|
|
// setup the stencil to only accept pixels that have been written
|
|
glStencilFunc(GL_EQUAL, 1, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
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;
|
|
}
|
|
|
|
u32 Renderer::GetZBufferTarget()
|
|
{
|
|
return nZBufferRender > 0 ? s_ZBufferTarget : 0;
|
|
}
|
|
|
|
void Renderer::Swap(const TRectangle& rc)
|
|
{
|
|
OpenGL_Update(); // just updates the render window position and the backbuffer size
|
|
|
|
DVProfileFunc _pf("Renderer::Swap");
|
|
|
|
Renderer::SetRenderMode(Renderer::RM_Normal);
|
|
|
|
// render to the real buffer now
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch to the backbuffer
|
|
glViewport(nXoff, nYoff, nBackbufferWidth, nBackbufferHeight);
|
|
|
|
ResetGLState();
|
|
|
|
// texture map s_RenderTargets[s_curtarget] onto the main buffer
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s_RenderTargets[s_nCurTarget]);
|
|
TextureMngr::EnableTexRECT(0);
|
|
// disable all other stages
|
|
for(int i = 1; i < 8; ++i) TextureMngr::DisableStage(i);
|
|
GL_REPORT_ERRORD();
|
|
|
|
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();
|
|
|
|
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
|
TextureMngr::DisableStage(0);
|
|
|
|
static int fpscount;
|
|
static int s_fps;
|
|
static unsigned long lasttime;
|
|
++fpscount;
|
|
if( timeGetTime() - lasttime > 1000 )
|
|
{
|
|
lasttime = timeGetTime();
|
|
s_fps = fpscount;
|
|
fpscount = 0;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Write logging data to debugger
|
|
// -----------------
|
|
if(m_frame)
|
|
{
|
|
Logging(0);
|
|
}
|
|
|
|
|
|
if (g_Config.bOverlayStats) {
|
|
char st[2048];
|
|
char *p = st;
|
|
if(g_Config.bShowFPS)
|
|
p+=sprintf(p, "FPS: %d\n", s_fps); // So it shows up before the stats and doesn't make anyting ugly
|
|
p+=sprintf(p,"Num textures created: %i\n",stats.numTexturesCreated);
|
|
p+=sprintf(p,"Num textures alive: %i\n",stats.numTexturesAlive);
|
|
p+=sprintf(p,"Num pshaders created: %i\n",stats.numPixelShadersCreated);
|
|
p+=sprintf(p,"Num pshaders alive: %i\n",stats.numPixelShadersAlive);
|
|
p+=sprintf(p,"Num vshaders created: %i\n",stats.numVertexShadersCreated);
|
|
p+=sprintf(p,"Num vshaders alive: %i\n",stats.numVertexShadersAlive);
|
|
p+=sprintf(p,"Num dlists called: %i\n",stats.numDListsCalled);
|
|
p+=sprintf(p,"Num dlists called (frame): %i\n",stats.thisFrame.numDListsCalled);
|
|
// not used.
|
|
//p+=sprintf(p,"Num dlists created: %i\n",stats.numDListsCreated);
|
|
//p+=sprintf(p,"Num dlists alive: %i\n",stats.numDListsAlive);
|
|
//p+=sprintf(p,"Num strip joins: %i\n",stats.numJoins);
|
|
p+=sprintf(p,"Num primitives: %i\n",stats.thisFrame.numPrims);
|
|
p+=sprintf(p,"Num primitives (DL): %i\n",stats.thisFrame.numDLPrims);
|
|
p+=sprintf(p,"Num bad commands: %i%s\n",stats.thisFrame.numBadCommands,stats.thisFrame.numBadCommands?"!!!":"");
|
|
p+=sprintf(p,"Num XF loads: %i\n",stats.thisFrame.numXFLoads);
|
|
p+=sprintf(p,"Num XF loads (DL): %i\n",stats.thisFrame.numXFLoadsInDL);
|
|
p+=sprintf(p,"Num CP loads: %i\n",stats.thisFrame.numCPLoads);
|
|
p+=sprintf(p,"Num CP loads (DL): %i\n",stats.thisFrame.numCPLoadsInDL);
|
|
p+=sprintf(p,"Num BP loads: %i\n",stats.thisFrame.numBPLoads);
|
|
p+=sprintf(p,"Num BP loads (DL): %i\n",stats.thisFrame.numBPLoadsInDL);
|
|
|
|
Renderer::RenderText(st, 20, 20, 0xFF00FFFF);
|
|
}
|
|
else
|
|
{
|
|
if(g_Config.bShowFPS)
|
|
{
|
|
char strfps[25];
|
|
sprintf(strfps, "%d\n", s_fps);
|
|
Renderer::RenderText(strfps, 20, 20, 0xFF00FFFF);
|
|
}
|
|
}
|
|
|
|
Renderer::ProcessMessages();
|
|
|
|
#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 from 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
|
|
frameCount++;
|
|
PixelShaderMngr::Cleanup();
|
|
TextureMngr::Cleanup();
|
|
|
|
// New frame
|
|
stats.ResetFrame();
|
|
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, s_uFramebuffer );
|
|
|
|
// s_nCurTarget = !s_nCurTarget;
|
|
// SetRenderTarget(0);
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
RestoreGLState();
|
|
GL_REPORT_ERRORD();
|
|
|
|
g_Config.iSaveTargetId = 0;
|
|
|
|
// for testing zbuffer targets
|
|
//Renderer::SetZBufferRender();
|
|
//SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, s_ZBufferTarget, GetTargetWidth(), GetTargetHeight());
|
|
}
|
|
|
|
bool Renderer::SaveRenderTarget(const char* filename, int jpeg)
|
|
{
|
|
bool bflip = true;
|
|
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
|
|
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]);
|
|
}
|
|
|
|
void Renderer::SetCgErrorOutput(bool bOutput)
|
|
{
|
|
s_bOutputCgErrors = bOutput;
|
|
}
|
|
|
|
void HandleGLError()
|
|
{
|
|
const GLubyte* pstr = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
|
|
if (pstr != NULL && pstr[0] != 0 ) {
|
|
GLint loc=0;
|
|
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
|
|
ERROR_LOG("program error at %d: ", loc);
|
|
ERROR_LOG((char*)pstr);
|
|
ERROR_LOG("\n");
|
|
}
|
|
|
|
// check the error status of this framebuffer */
|
|
GLenum error = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
// if error != GL_FRAMEBUFFER_COMPLETE_EXT, there's an error of some sort
|
|
if (error != 0) {
|
|
int w, h;
|
|
GLint fmt;
|
|
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_INTERNAL_FORMAT_EXT, &fmt);
|
|
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_WIDTH_EXT, (GLint *)&w);
|
|
glGetRenderbufferParameterivEXT(GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_HEIGHT_EXT, (GLint *)&h);
|
|
|
|
switch(error)
|
|
{
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
|
|
ERROR_LOG("Error! missing a required image/buffer attachment!\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
|
|
ERROR_LOG("Error! has no images/buffers attached!\n");
|
|
break;
|
|
// case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
|
|
// ERROR_LOG("Error! has an image/buffer attached in multiple locations!\n");
|
|
// break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
|
ERROR_LOG("Error! has mismatched image/buffer dimensions!\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
|
|
ERROR_LOG("Error! colorbuffer attachments have different types!\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
|
|
ERROR_LOG("Error! trying to draw to non-attached color buffer!\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
|
|
ERROR_LOG("Error! trying to read from a non-attached color buffer!\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
ERROR_LOG("Error! format is not supported by current graphics card/driver!\n");
|
|
break;
|
|
default:
|
|
ERROR_LOG("*UNKNOWN ERROR* reported from glCheckFramebufferStatusEXT()!\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleCgError(CGcontext ctx, CGerror err, void* appdata)
|
|
{
|
|
if( s_bOutputCgErrors ) {
|
|
ERROR_LOG("Cg error: %s\n", cgGetErrorString(err));
|
|
const char* listing = cgGetLastListing(g_cgcontext);
|
|
if (listing != NULL) {
|
|
ERROR_LOG(" last listing: %s\n", listing);
|
|
}
|
|
// glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
|
|
// printf("pos: %d\n", loc);
|
|
}
|
|
}
|