/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code 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, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code 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 for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "sys/platform.h" #include "renderer/tr_local.h" frameData_t *frameData; backEndState_t backEnd; const unsigned int NUM_FRAME_DATA = 2; frameData_t *smpFrameData[NUM_FRAME_DATA]; volatile unsigned int smpFrame; /* ====================== RB_SetDefaultGLState This should initialize all GL state that any part of the entire program may touch, including the editor. ====================== */ void RB_SetDefaultGLState( void ) { int i; // Clear value for the Depth buffer qglClearDepthf(1.0f); // make sure our GL state vector is set correctly memset( &backEnd.glState, 0, sizeof( backEnd.glState ) ); backEnd.glState.forceGlState = true; // All color channels are used qglColorMask( 1, 1, 1, 1 ); qglEnable( GL_DEPTH_TEST ); qglEnable( GL_BLEND ); qglEnable( GL_SCISSOR_TEST ); qglEnable( GL_CULL_FACE ); qglDisable( GL_STENCIL_TEST ); qglDepthMask( GL_TRUE ); qglDepthFunc( GL_ALWAYS ); qglCullFace( GL_FRONT_AND_BACK ); if ( r_useScissor.GetBool() ) { qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); } backEnd.glState.currentTexture = -1; // Force texture unit to be reset for ( i = glConfig.maxTextureUnits - 1 ; i >= 0 ; i-- ) { GL_SelectTexture( i ); globalImages->BindNull(); } // Last active texture is Tex0 } /* ==================== GL_SelectTexture ==================== */ void GL_SelectTexture( int unit ) { if ( backEnd.glState.currentTexture != unit ) { qglActiveTexture(GL_TEXTURE0 + unit); backEnd.glState.currentTexture = unit; } } /* ==================== GL_Cull This handles the flipping needed when the view being rendered is a mirored view. ==================== */ void GL_Cull( int cullType ) { if ( backEnd.glState.faceCulling == cullType ) { return; } if ( cullType == CT_TWO_SIDED ) { qglDisable( GL_CULL_FACE ); } else { if ( backEnd.glState.faceCulling == CT_TWO_SIDED ) { qglEnable( GL_CULL_FACE ); } if ( cullType == CT_BACK_SIDED ) { if ( backEnd.viewDef->isMirror ) { qglCullFace( GL_FRONT ); } else { qglCullFace( GL_BACK ); } } else { if ( backEnd.viewDef->isMirror ) { qglCullFace( GL_BACK ); } else { qglCullFace( GL_FRONT ); } } } backEnd.glState.faceCulling = cullType; } /* ================= GL_ClearStateDelta Clears the state delta bits, so the next GL_State will set every item ================= */ void GL_ClearStateDelta( void ) { backEnd.glState.forceGlState = true; } /* ==================== GL_State This routine is responsible for setting the most commonly changed state ==================== */ void GL_State( int stateBits ) { int diff; if ( !r_useStateCaching.GetBool() || backEnd.glState.forceGlState ) { // make sure everything is set all the time, so we // can see if our delta checking is screwing up diff = -1; backEnd.glState.forceGlState = false; } else { diff = stateBits ^ backEnd.glState.glStateBits; if ( !diff ) { return; } } // // check depthFunc bits // if ( diff & ( GLS_DEPTHFUNC_EQUAL | GLS_DEPTHFUNC_LESS | GLS_DEPTHFUNC_ALWAYS ) ) { if ( stateBits & GLS_DEPTHFUNC_EQUAL ) { qglDepthFunc( GL_EQUAL ); } else if ( stateBits & GLS_DEPTHFUNC_ALWAYS ) { qglDepthFunc( GL_ALWAYS ); } else { qglDepthFunc( GL_LEQUAL ); } } // // check blend bits // if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) { GLenum srcFactor, dstFactor; switch ( stateBits & GLS_SRCBLEND_BITS ) { case GLS_SRCBLEND_ZERO: srcFactor = GL_ZERO; break; case GLS_SRCBLEND_ONE: srcFactor = GL_ONE; break; case GLS_SRCBLEND_DST_COLOR: srcFactor = GL_DST_COLOR; break; case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: srcFactor = GL_ONE_MINUS_DST_COLOR; break; case GLS_SRCBLEND_SRC_ALPHA: srcFactor = GL_SRC_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: srcFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_SRCBLEND_DST_ALPHA: srcFactor = GL_DST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: srcFactor = GL_ONE_MINUS_DST_ALPHA; break; case GLS_SRCBLEND_ALPHA_SATURATE: srcFactor = GL_SRC_ALPHA_SATURATE; break; default: srcFactor = GL_ONE; // to get warning to shut up common->Error( "GL_State: invalid src blend state bits\n" ); break; } switch ( stateBits & GLS_DSTBLEND_BITS ) { case GLS_DSTBLEND_ZERO: dstFactor = GL_ZERO; break; case GLS_DSTBLEND_ONE: dstFactor = GL_ONE; break; case GLS_DSTBLEND_SRC_COLOR: dstFactor = GL_SRC_COLOR; break; case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: dstFactor = GL_ONE_MINUS_SRC_COLOR; break; case GLS_DSTBLEND_SRC_ALPHA: dstFactor = GL_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: dstFactor = GL_ONE_MINUS_SRC_ALPHA; break; case GLS_DSTBLEND_DST_ALPHA: dstFactor = GL_DST_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: dstFactor = GL_ONE_MINUS_DST_ALPHA; break; default: dstFactor = GL_ONE; // to get warning to shut up common->Error( "GL_State: invalid dst blend state bits\n" ); break; } qglBlendFunc( srcFactor, dstFactor ); } // // check depthmask // if ( diff & GLS_DEPTHMASK ) { if ( stateBits & GLS_DEPTHMASK ) { qglDepthMask( GL_FALSE ); } else { qglDepthMask( GL_TRUE ); } } // // check colormask // if ( diff & (GLS_REDMASK|GLS_GREENMASK|GLS_BLUEMASK|GLS_ALPHAMASK) ) { GLboolean r, g, b, a; r = ( stateBits & GLS_REDMASK ) ? 0 : 1; g = ( stateBits & GLS_GREENMASK ) ? 0 : 1; b = ( stateBits & GLS_BLUEMASK ) ? 0 : 1; a = ( stateBits & GLS_ALPHAMASK ) ? 0 : 1; qglColorMask( r, g, b, a ); } backEnd.glState.glStateBits = stateBits; } /* ============================================================================ RENDER BACK END THREAD FUNCTIONS ============================================================================ */ /* ============= RB_SetBuffer ============= */ static void RB_SetBuffer( const void *data ) { const setBufferCommand_t *cmd; // see which draw buffer we want to render the frame to cmd = (const setBufferCommand_t *)data; backEnd.frameCount = cmd->frameCount; // Disabled for OES2 //qglDrawBuffer( cmd->buffer ); GLimp_SetupFrame(); // clear screen for debugging // automatically enable this with several other debug tools // that might leave unrendered portions of the screen if ( r_clear.GetFloat() || idStr::Length( r_clear.GetString() ) != 1 || r_lockSurfaces.GetBool() || r_singleArea.GetBool() ) { float c[3]; if ( sscanf( r_clear.GetString(), "%f %f %f", &c[0], &c[1], &c[2] ) == 3 ) { qglClearColor( c[0], c[1], c[2], 1 ); } else if ( r_clear.GetInteger() == 2 ) { qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); } else { qglClearColor( 0.4f, 0.0f, 0.25f, 1.0f ); } qglClear( GL_COLOR_BUFFER_BIT ); } } /* ============= RB_SwapBuffers ============= */ const void RB_SwapBuffers( const void *data ) { #ifdef WEBGL // GAB Note Dec 2018: Clear the Alpha channel, so that final render will not blend with the HTML5 background (canvas with premultiplied alpha) qglColorMask(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT); #endif // force a gl sync if requested if ( r_finish.GetBool() ) { qglFinish(); } // don't flip if drawing to front buffer GLimp_SwapBuffers(); } /* ============= RB_CopyRender Copy part of the current framebuffer to an image ============= */ const void RB_CopyRender( const void *data ) { const copyRenderCommand_t *cmd; cmd = (const copyRenderCommand_t *)data; if ( r_skipCopyTexture.GetBool() ) { return; } if (cmd->image) { cmd->image->CopyFramebuffer( cmd->x, cmd->y, cmd->imageWidth, cmd->imageHeight, false ); } } /* ==================== RB_ExecuteBackEndCommands This function will be called syncronously if running without smp extensions, or asyncronously by another thread. ==================== */ int backEndStartTime, backEndFinishTime; void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) { // r_debugRenderToTexture int c_draw3d = 0, c_draw2d = 0, c_setBuffers = 0, c_swapBuffers = 0, c_copyRenders = 0; if ( cmds->commandId == RC_NOP && !cmds->next ) { return; } { const emptyCommand_t *cmd = cmds; backEndStartTime = Sys_Milliseconds(); // needed for editor rendering RB_SetDefaultGLState(); for (; cmd; cmd = (const emptyCommand_t *) cmd->next) { switch (cmd->commandId) { case RC_NOP: break; case RC_DRAW_VIEW: RB_DrawView(cmd); if (((const drawSurfsCommand_t *) cmd)->viewDef->viewEntitys) { c_draw3d++; } else { c_draw2d++; } break; case RC_SET_BUFFER: RB_SetBuffer(cmd); c_setBuffers++; break; case RC_SWAP_BUFFERS: RB_SwapBuffers(cmd); c_swapBuffers++; break; case RC_COPY_RENDER: RB_CopyRender(cmd); c_copyRenders++; break; default: common->Error("RB_ExecuteBackEndCommands: bad commandId"); break; } } } // stop rendering on this thread backEndFinishTime = Sys_Milliseconds(); backEnd.pc.msec = backEndFinishTime - backEndStartTime; if ( r_debugRenderToTexture.GetInteger() == 1 ) { common->Printf( "3d: %i, 2d: %i, SetBuf: %i, SwpBuf: %i, CpyRenders: %i, CpyFrameBuf: %i\n", c_draw3d, c_draw2d, c_setBuffers, c_swapBuffers, c_copyRenders, backEnd.c_copyFrameBuffer ); backEnd.c_copyFrameBuffer = 0; } }