/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena 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 2 of the License, or (at your option) any later version. Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "tr_local.h" void R_IssueRenderCommands() { renderCommandList_t* cmdList = &backEndData->commands; // add an end-of-list command *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST; // clear it out, in case this is a sync and not a buffer flip cmdList->used = 0; RB_ExecuteRenderCommands( cmdList->cmds ); } void* R_FindRenderCommand( renderCommand_t type ) { renderCommandList_t* cmdList = &backEndData->commands; void* data = cmdList->cmds; void* end = cmdList->cmds + cmdList->used; while ( 1 ) { data = PADP(data, sizeof(void *)); if( *(int *)data == type ) return data; if ( data >= end ) return NULL; switch ( *(const int *)data ) { case RC_SET_COLOR: data = (char*)data + sizeof(setColorCommand_t); break; case RC_STRETCH_PIC: data = (char*)data + sizeof(stretchPicCommand_t); break; case RC_TRIANGLE: data = (char*)data + sizeof(triangleCommand_t); break; case RC_DRAW_SURFS: data = (char*)data + sizeof(drawSurfsCommand_t); break; case RC_BEGIN_FRAME: data = (char*)data + sizeof(beginFrameCommand_t); break; case RC_SWAP_BUFFERS: data = (char*)data + sizeof(swapBuffersCommand_t); break; case RC_SCREENSHOT: data = (char*)data + sizeof(screenshotCommand_t); break; case RC_VIDEOFRAME: data = (char*)data + sizeof(videoFrameCommand_t); break; case RC_END_OF_LIST: default: return NULL; } } } static void R_RemoveRenderCommand( void* cmd, int cmdSize ) { renderCommandList_t* cmdList = &backEndData->commands; const int endOffset = ((char*)cmd + cmdSize) - (char*)cmdList->cmds; const int endBytes = cmdList->used - endOffset; assert( cmd >= cmdList->cmds && ((char*)cmd + cmdSize) <= ((char*)cmdList->cmds + cmdList->used) ); memmove( cmd, (char*)cmd + cmdSize, endBytes ); cmdList->used -= cmdSize; } /* ============ R_GetCommandBuffer make sure there is enough command space, waiting on the render thread if needed. ============ */ void *R_GetCommandBuffer( int bytes ) { renderCommandList_t *cmdList; cmdList = &backEndData->commands; bytes = PAD(bytes, sizeof(void *)); // always leave room for the end of list command if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { if ( bytes > MAX_RENDER_COMMANDS - 4 ) { ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); } // if we run out of room, just start dropping commands return NULL; } cmdList->used += bytes; return cmdList->cmds + cmdList->used - bytes; } // technically, all commands should probably check tr.registered // but realistically, only begin+end frame really need to #define R_CMD(T, ID) T* cmd = (T*)R_GetCommandBuffer( sizeof(T) ); if (!cmd) return; cmd->commandId = ID; void R_AddDrawSurfCmd( drawSurf_t* drawSurfs, int numDrawSurfs, int numTranspSurfs ) { R_CMD( drawSurfsCommand_t, RC_DRAW_SURFS ); cmd->drawSurfs = drawSurfs; cmd->numDrawSurfs = numDrawSurfs; cmd->numTranspSurfs = numTranspSurfs; cmd->refdef = tr.refdef; cmd->viewParms = tr.viewParms; } // passing NULL will set the color to white void RE_SetColor( const float* rgba ) { R_CMD( setColorCommand_t, RC_SET_COLOR ); if ( !rgba ) rgba = colorWhite; cmd->color[0] = rgba[0]; cmd->color[1] = rgba[1]; cmd->color[2] = rgba[2]; cmd->color[3] = rgba[3]; } void RE_StretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) { R_CMD( stretchPicCommand_t, RC_STRETCH_PIC ); cmd->shader = R_GetShaderByHandle( hShader ); cmd->x = x; cmd->y = y; cmd->w = w; cmd->h = h; cmd->s1 = s1; cmd->t1 = t1; cmd->s2 = s2; cmd->t2 = t2; } void RE_DrawTriangle( float x0, float y0, float x1, float y1, float x2, float y2, float s0, float t0, float s1, float t1, float s2, float t2, qhandle_t hShader ) { R_CMD( triangleCommand_t, RC_TRIANGLE ); cmd->shader = R_GetShaderByHandle( hShader ); cmd->x0 = x0; cmd->y0 = y0; cmd->x1 = x1; cmd->y1 = y1; cmd->x2 = x2; cmd->y2 = y2; cmd->s0 = s0; cmd->t0 = t0; cmd->s1 = s1; cmd->t1 = t1; cmd->s2 = s2; cmd->t2 = t2; } // if running in stereo, RE_BeginFrame will be called twice for each RE_EndFrame void RE_BeginFrame( stereoFrame_t stereoFrame ) { if (!tr.registered) return; tr.frameCount++; tr.frameSceneNum = 0; // delayed screenshot if ( r_delayedScreenshotPending ) { r_delayedScreenshotFrame++; if ( r_delayedScreenshotFrame >= 2 ) { R_CMD( screenshotCommand_t, RC_SCREENSHOT ); *cmd = r_delayedScreenshot; r_delayedScreenshotPending = qfalse; r_delayedScreenshotFrame = 0; } } // // draw buffer stuff // R_CMD( beginFrameCommand_t, RC_BEGIN_FRAME ); } void RE_EndFrame( int* pcFE, int* pc2D, int* pc3D, qbool render ) { if (!tr.registered) return; qbool delayScreenshot = qfalse; if ( !render && r_delayedScreenshotPending ) render = qtrue; if ( !render ) { screenshotCommand_t* ssCmd = (screenshotCommand_t*)R_FindRenderCommand( RC_SCREENSHOT ); if ( ssCmd ) render = qtrue; if ( ssCmd && !ssCmd->delayed ) { // save and remove the command so we can push it back after the frame's done r_delayedScreenshot = *ssCmd; r_delayedScreenshot.delayed = qtrue; R_RemoveRenderCommand( ssCmd, sizeof(screenshotCommand_t) ); delayScreenshot = qtrue; } } if ( render ) { R_CMD( swapBuffersCommand_t, RC_SWAP_BUFFERS ); if ( delayScreenshot ) { R_CMD( screenshotCommand_t, RC_SCREENSHOT ); *cmd = r_delayedScreenshot; } } else { R_ClearFrame(); } R_IssueRenderCommands(); R_ClearFrame(); if (pcFE) Com_Memcpy( pcFE, &tr.pc, sizeof( tr.pc ) ); if (pc2D) Com_Memcpy( pc2D, &backEnd.pc2D, sizeof( backEnd.pc2D ) ); if (pc3D) Com_Memcpy( pc3D, &backEnd.pc3D, sizeof( backEnd.pc3D ) ); Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); Com_Memset( &backEnd.pc2D, 0, sizeof( backEnd.pc2D ) ); Com_Memset( &backEnd.pc3D, 0, sizeof( backEnd.pc3D ) ); } void RE_TakeVideoFrame( int width, int height, byte *captureBuffer, byte *encodeBuffer, qbool motionJpeg ) { R_CMD( videoFrameCommand_t, RC_VIDEOFRAME ); cmd->width = width; cmd->height = height; cmd->captureBuffer = captureBuffer; cmd->encodeBuffer = encodeBuffer; cmd->motionJpeg = motionJpeg; }