123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition 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 BFG Edition 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.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "tr_local.h"
- /*
- ==========================================================================================
- FRAME MEMORY ALLOCATION
- ==========================================================================================
- */
- static const unsigned int NUM_FRAME_DATA = 2;
- static const unsigned int FRAME_ALLOC_ALIGNMENT = 128;
- static const unsigned int MAX_FRAME_MEMORY = 64 * 1024 * 1024; // larger so that we can noclip on PC for dev purposes
- idFrameData smpFrameData[NUM_FRAME_DATA];
- idFrameData * frameData;
- unsigned int smpFrame;
- //#define TRACK_FRAME_ALLOCS
- #if defined( TRACK_FRAME_ALLOCS )
- idSysInterlockedInteger frameAllocTypeCount[FRAME_ALLOC_MAX];
- int frameHighWaterTypeCount[FRAME_ALLOC_MAX];
- #endif
- /*
- ====================
- R_ToggleSmpFrame
- ====================
- */
- void R_ToggleSmpFrame() {
- // update the highwater mark
- if ( frameData->frameMemoryAllocated.GetValue() > frameData->highWaterAllocated ) {
- frameData->highWaterAllocated = frameData->frameMemoryAllocated.GetValue();
- #if defined( TRACK_FRAME_ALLOCS )
- frameData->highWaterUsed = frameData->frameMemoryUsed.GetValue();
- for ( int i = 0; i < FRAME_ALLOC_MAX; i++ ) {
- frameHighWaterTypeCount[i] = frameAllocTypeCount[i].GetValue();
- }
- #endif
- }
- // switch to the next frame
- smpFrame++;
- frameData = &smpFrameData[smpFrame % NUM_FRAME_DATA];
- // reset the memory allocation
- const unsigned int bytesNeededForAlignment = FRAME_ALLOC_ALIGNMENT - ( (unsigned int)frameData->frameMemory & ( FRAME_ALLOC_ALIGNMENT - 1 ) );
- frameData->frameMemoryAllocated.SetValue( bytesNeededForAlignment );
- frameData->frameMemoryUsed.SetValue( 0 );
- #if defined( TRACK_FRAME_ALLOCS )
- for ( int i = 0; i < FRAME_ALLOC_MAX; i++ ) {
- frameAllocTypeCount[i].SetValue( 0 );
- }
- #endif
- // clear the command chain and make a RC_NOP command the only thing on the list
- frameData->cmdHead = frameData->cmdTail = (emptyCommand_t *)R_FrameAlloc( sizeof( *frameData->cmdHead ), FRAME_ALLOC_DRAW_COMMAND );
- frameData->cmdHead->commandId = RC_NOP;
- frameData->cmdHead->next = NULL;
- }
- /*
- =====================
- R_ShutdownFrameData
- =====================
- */
- void R_ShutdownFrameData() {
- frameData = NULL;
- for ( int i = 0; i < NUM_FRAME_DATA; i++ ) {
- Mem_Free16( smpFrameData[i].frameMemory );
- smpFrameData[i].frameMemory = NULL;
- }
- }
- /*
- =====================
- R_InitFrameData
- =====================
- */
- void R_InitFrameData() {
- R_ShutdownFrameData();
- for ( int i = 0; i < NUM_FRAME_DATA; i++ ) {
- smpFrameData[i].frameMemory = (byte *) Mem_Alloc16( MAX_FRAME_MEMORY, TAG_RENDER );
- }
- // must be set before calling R_ToggleSmpFrame()
- frameData = &smpFrameData[ 0 ];
- R_ToggleSmpFrame();
- }
- /*
- ================
- R_FrameAlloc
- This data will be automatically freed when the
- current frame's back end completes.
- This should only be called by the front end. The
- back end shouldn't need to allocate memory.
- All temporary data, like dynamic tesselations
- and local spaces are allocated here.
- All memory is cache-line-cleared for the best performance.
- ================
- */
- void *R_FrameAlloc( int bytes, frameAllocType_t type ) {
- #if defined( TRACK_FRAME_ALLOCS )
- frameData->frameMemoryUsed.Add( bytes );
- frameAllocTypeCount[type].Add( bytes );
- #endif
- bytes = ( bytes + FRAME_ALLOC_ALIGNMENT - 1 ) & ~ ( FRAME_ALLOC_ALIGNMENT - 1 );
- // thread safe add
- int end = frameData->frameMemoryAllocated.Add( bytes );
- if ( end > MAX_FRAME_MEMORY ) {
- idLib::Error( "R_FrameAlloc ran out of memory. bytes = %d, end = %d, highWaterAllocated = %d\n", bytes, end, frameData->highWaterAllocated );
- }
- byte * ptr = frameData->frameMemory + end - bytes;
- // cache line clear the memory
- for ( int offset = 0; offset < bytes; offset += CACHE_LINE_SIZE ) {
- ZeroCacheLine( ptr, offset );
- }
- return ptr;
- }
- /*
- ==================
- R_ClearedFrameAlloc
- ==================
- */
- void *R_ClearedFrameAlloc( int bytes, frameAllocType_t type ) {
- // NOTE: every allocation is cache line cleared
- return R_FrameAlloc( bytes, type );
- }
- /*
- ==========================================================================================
- FONT-END STATIC MEMORY ALLOCATION
- ==========================================================================================
- */
- /*
- =================
- R_StaticAlloc
- =================
- */
- void *R_StaticAlloc( int bytes, const memTag_t tag ) {
- tr.pc.c_alloc++;
- void * buf = Mem_Alloc( bytes, tag );
- // don't exit on failure on zero length allocations since the old code didn't
- if ( buf == NULL && bytes != 0 ) {
- common->FatalError( "R_StaticAlloc failed on %i bytes", bytes );
- }
- return buf;
- }
- /*
- =================
- R_ClearedStaticAlloc
- =================
- */
- void *R_ClearedStaticAlloc( int bytes ) {
- void * buf = R_StaticAlloc( bytes );
- memset( buf, 0, bytes );
- return buf;
- }
- /*
- =================
- R_StaticFree
- =================
- */
- void R_StaticFree( void *data ) {
- tr.pc.c_free++;
- Mem_Free( data );
- }
- /*
- ==========================================================================================
- FONT-END RENDERING
- ==========================================================================================
- */
- /*
- =================
- R_SortDrawSurfs
- =================
- */
- static void R_SortDrawSurfs( drawSurf_t ** drawSurfs, const int numDrawSurfs ) {
- #if 1
- uint64 * indices = (uint64 *) _alloca16( numDrawSurfs * sizeof( indices[0] ) );
- // sort the draw surfs based on:
- // 1. sort value (largest first)
- // 2. depth (smallest first)
- // 3. index (largest first)
- assert( numDrawSurfs <= 0xFFFF );
- for ( int i = 0; i < numDrawSurfs; i++ ) {
- float sort = SS_POST_PROCESS - drawSurfs[i]->sort;
- assert( sort >= 0.0f );
- uint64 dist = 0;
- if ( drawSurfs[i]->frontEndGeo != NULL ) {
- float min = 0.0f;
- float max = 1.0f;
- idRenderMatrix::DepthBoundsForBounds( min, max, drawSurfs[i]->space->mvp, drawSurfs[i]->frontEndGeo->bounds );
- dist = idMath::Ftoui16( min * 0xFFFF );
- }
-
- indices[i] = ( ( numDrawSurfs - i ) & 0xFFFF ) | ( dist << 16 ) | ( (uint64) ( *(uint32 *)&sort ) << 32 );
- }
- const int64 MAX_LEVELS = 128;
- int64 lo[MAX_LEVELS];
- int64 hi[MAX_LEVELS];
- // Keep the top of the stack in registers to avoid load-hit-stores.
- register int64 st_lo = 0;
- register int64 st_hi = numDrawSurfs - 1;
- register int64 level = 0;
- for ( ; ; ) {
- register int64 i = st_lo;
- register int64 j = st_hi;
- if ( j - i >= 4 && level < MAX_LEVELS - 1 ) {
- register uint64 pivot = indices[( i + j ) / 2];
- do {
- while ( indices[i] > pivot ) i++;
- while ( indices[j] < pivot ) j--;
- if ( i > j ) break;
- uint64 h = indices[i]; indices[i] = indices[j]; indices[j] = h;
- } while ( ++i <= --j );
- // No need for these iterations because we are always sorting unique values.
- //while ( indices[j] == pivot && st_lo < j ) j--;
- //while ( indices[i] == pivot && i < st_hi ) i++;
- assert( level < MAX_LEVELS - 1 );
- lo[level] = i;
- hi[level] = st_hi;
- st_hi = j;
- level++;
- } else {
- for( ; i < j; j-- ) {
- register int64 m = i;
- for ( int64 k = i + 1; k <= j; k++ ) {
- if ( indices[k] < indices[m] ) {
- m = k;
- }
- }
- uint64 h = indices[m]; indices[m] = indices[j]; indices[j] = h;
- }
- if ( --level < 0 ) {
- break;
- }
- st_lo = lo[level];
- st_hi = hi[level];
- }
- }
- drawSurf_t ** newDrawSurfs = (drawSurf_t **) indices;
- for ( int i = 0; i < numDrawSurfs; i++ ) {
- newDrawSurfs[i] = drawSurfs[numDrawSurfs - ( indices[i] & 0xFFFF )];
- }
- memcpy( drawSurfs, newDrawSurfs, numDrawSurfs * sizeof( drawSurfs[0] ) );
- #else
- struct local_t {
- static int R_QsortSurfaces( const void *a, const void *b ) {
- const drawSurf_t * ea = *(drawSurf_t **)a;
- const drawSurf_t * eb = *(drawSurf_t **)b;
- if ( ea->sort < eb->sort ) {
- return -1;
- }
- if ( ea->sort > eb->sort ) {
- return 1;
- }
- return 0;
- }
- };
- // Add a sort offset so surfaces with equal sort orders still deterministically
- // draw in the order they were added, at least within a given model.
- float sorfOffset = 0.0f;
- for ( int i = 0; i < numDrawSurfs; i++ ) {
- drawSurf[i]->sort += sorfOffset;
- sorfOffset += 0.000001f;
- }
- // sort the drawsurfs
- qsort( drawSurfs, numDrawSurfs, sizeof( drawSurfs[0] ), local_t::R_QsortSurfaces );
- #endif
- }
- /*
- ================
- R_RenderView
- A view may be either the actual camera view,
- a mirror / remote location, or a 3D view on a gui surface.
- Parms will typically be allocated with R_FrameAlloc
- ================
- */
- void R_RenderView( viewDef_t *parms ) {
- // save view in case we are a subview
- viewDef_t * oldView = tr.viewDef;
- tr.viewDef = parms;
- // setup the matrix for world space to eye space
- R_SetupViewMatrix( tr.viewDef );
- // we need to set the projection matrix before doing
- // portal-to-screen scissor calculations
- R_SetupProjectionMatrix( tr.viewDef );
- // setup render matrices for faster culling
- idRenderMatrix::Transpose( *(idRenderMatrix *)tr.viewDef->projectionMatrix, tr.viewDef->projectionRenderMatrix );
- idRenderMatrix viewRenderMatrix;
- idRenderMatrix::Transpose( *(idRenderMatrix *)tr.viewDef->worldSpace.modelViewMatrix, viewRenderMatrix );
- idRenderMatrix::Multiply( tr.viewDef->projectionRenderMatrix, viewRenderMatrix, tr.viewDef->worldSpace.mvp );
- // the planes of the view frustum are needed for portal visibility culling
- idRenderMatrix::GetFrustumPlanes( tr.viewDef->frustum, tr.viewDef->worldSpace.mvp, false, true );
- // the DOOM 3 frustum planes point outside the frustum
- for ( int i = 0; i < 6; i++ ) {
- tr.viewDef->frustum[i] = - tr.viewDef->frustum[i];
- }
- // remove the Z-near to avoid portals from being near clipped
- tr.viewDef->frustum[4][3] -= r_znear.GetFloat();
- // identify all the visible portal areas, and create view lights and view entities
- // for all the the entityDefs and lightDefs that are in the visible portal areas
- static_cast<idRenderWorldLocal *>(parms->renderWorld)->FindViewLightsAndEntities();
- // wait for any shadow volume jobs from the previous frame to finish
- tr.frontEndJobList->Wait();
- // make sure that interactions exist for all light / entity combinations that are visible
- // add any pre-generated light shadows, and calculate the light shader values
- R_AddLights();
- // adds ambient surfaces and create any necessary interaction surfaces to add to the light lists
- R_AddModels();
- // build up the GUIs on world surfaces
- R_AddInGameGuis( tr.viewDef->drawSurfs, tr.viewDef->numDrawSurfs );
- // any viewLight that didn't have visible surfaces can have it's shadows removed
- R_OptimizeViewLightsList();
- // sort all the ambient surfaces for translucency ordering
- R_SortDrawSurfs( tr.viewDef->drawSurfs, tr.viewDef->numDrawSurfs );
- // generate any subviews (mirrors, cameras, etc) before adding this view
- if ( R_GenerateSubViews( tr.viewDef->drawSurfs, tr.viewDef->numDrawSurfs ) ) {
- // if we are debugging subviews, allow the skipping of the main view draw
- if ( r_subviewOnly.GetBool() ) {
- return;
- }
- }
- // write everything needed to the demo file
- if ( common->WriteDemo() ) {
- static_cast<idRenderWorldLocal *>(parms->renderWorld)->WriteVisibleDefs( tr.viewDef );
- }
- // add the rendering commands for this viewDef
- R_AddDrawViewCmd( parms, false );
- // restore view in case we are a subview
- tr.viewDef = oldView;
- }
- /*
- ================
- R_RenderPostProcess
- Because R_RenderView may be called by subviews we have to make sure the post process
- pass happens after the active view and its subviews is done rendering.
- ================
- */
- void R_RenderPostProcess( viewDef_t *parms ) {
- viewDef_t * oldView = tr.viewDef;
- R_AddDrawPostProcess( parms );
- tr.viewDef = oldView;
- }
|