You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1366 lines
43 KiB
1366 lines
43 KiB
>//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: The thread which performs lighting preview
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "stdafx.h"
|
|
#include "lpreview_thread.h"
|
|
|
|
#define HAMMER_RAYTRACE
|
|
#include "raytrace.h"
|
|
#include "hammer.h"
|
|
#include "mainfrm.h"
|
|
#include "lprvwindow.h"
|
|
#include "threadtools.h"
|
|
#include "vstdlib/jobthread.h"
|
|
#include "mathlib/halton.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
|
|
#define LPREVIEW_MULTITHREAD 1
|
|
|
|
CInterlockedInt n_gbufs_queued;
|
|
CInterlockedInt n_result_bms_queued;
|
|
|
|
#define NUMBER_OF_LINES_TO_CALCULATE_PER_STEP 8
|
|
|
|
// the current lighting preview output, if we have one
|
|
Bitmap_t * g_pLPreviewOutputBitmap;
|
|
IThreadPool *s_pThreadPool;
|
|
static class CLightingPreviewThread *s_pThis;
|
|
static int s_nNumThreads;
|
|
|
|
enum IncrementalLightState
|
|
{
|
|
INCR_STATE_NO_RESULTS = 0, // we threw away the results for this light
|
|
INCR_STATE_PARTIAL_RESULTS = 1, // have done some but not all
|
|
INCR_STATE_NEW = 2, // we know nothing about this light
|
|
INCR_STATE_HAVE_FULL_RESULTS = 3, // we are done
|
|
};
|
|
|
|
|
|
class CLightingPreviewThread;
|
|
|
|
#define MAX_IMAGE_HEIGHT 1024
|
|
|
|
// attributes for result soa containers
|
|
|
|
#define RSLT_ATTR_DIFFUSE_RGB 0
|
|
|
|
#define RSLT_BUFFER_RSLT_RGB 3
|
|
|
|
#define GBUFFER_ATTR_POSITION 0
|
|
#define GBUFFER_ATTR_ALBEDO 1
|
|
#define GBUFFER_ATTR_NORMAL 2
|
|
|
|
class CIncrementalLightInfo
|
|
{
|
|
public:
|
|
CIncrementalLightInfo * m_pNext;
|
|
CLightingPreviewLightDescription * m_pLight;
|
|
// incremental lighting tracking information
|
|
int m_nObjectID;
|
|
int m_nNumLinesCalculated;
|
|
IncrementalLightState m_eIncrState;
|
|
CSOAContainer m_CalculatedContribution;
|
|
float m_fTotalContribution; // current magnitude of light effect
|
|
float m_flLastContribution; // the last amount added
|
|
|
|
int m_nBitmapGenerationCounter; // set on receive of new data from master
|
|
float m_fDistanceToEye;
|
|
int m_nMostRecentNonZeroContributionTimeStamp;
|
|
uint8 m_nCalculationLevel[MAX_IMAGE_HEIGHT]; // 0 = not calculated, 1 = calculated at 1:1, etc.
|
|
int m_nMaxCalculatedLine;
|
|
int m_nFirstCalculatedLine;
|
|
bool m_bCreatedIndirectLights;
|
|
|
|
bool m_bDisabled;
|
|
|
|
CIncrementalLightInfo( void )
|
|
{
|
|
m_bDisabled = false;
|
|
m_bCreatedIndirectLights = false;
|
|
m_nObjectID = - 1;
|
|
m_pNext = NULL;
|
|
m_eIncrState = INCR_STATE_NEW;
|
|
m_fTotalContribution = 0.;
|
|
m_flLastContribution = 0.;
|
|
m_nNumLinesCalculated = 0;
|
|
m_nMostRecentNonZeroContributionTimeStamp = 0;
|
|
m_nMaxCalculatedLine = -1;
|
|
m_nFirstCalculatedLine = INT_MAX;
|
|
memset( m_nCalculationLevel, 0, sizeof( m_nCalculationLevel ) );
|
|
}
|
|
|
|
float PredictedContribution( void ) const;
|
|
|
|
|
|
void SetContributionSize( int nWidth, int nHeight )
|
|
{
|
|
if ( ( m_CalculatedContribution.NumCols() != nWidth ) || ( m_CalculatedContribution.NumRows() != nHeight ) )
|
|
{
|
|
m_CalculatedContribution.Purge();
|
|
if ( nWidth && nHeight )
|
|
{
|
|
m_CalculatedContribution.SetAttributeType( RSLT_ATTR_DIFFUSE_RGB, ATTRDATATYPE_4V );
|
|
m_CalculatedContribution.AllocateData( nWidth, nHeight );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DiscardResults( void )
|
|
{
|
|
m_bDisabled = false;
|
|
m_CalculatedContribution.Purge();
|
|
memset( m_nCalculationLevel, 0, sizeof( m_nCalculationLevel ) );
|
|
m_nMaxCalculatedLine = -1;
|
|
m_nFirstCalculatedLine = INT_MAX;
|
|
if ( m_eIncrState != INCR_STATE_NEW )
|
|
m_eIncrState = INCR_STATE_NO_RESULTS;
|
|
m_nNumLinesCalculated = 0;
|
|
}
|
|
|
|
void ClearIncremental( void )
|
|
{
|
|
m_eIncrState = INCR_STATE_NEW;
|
|
// free calculated lighting matrix
|
|
DiscardResults();
|
|
}
|
|
|
|
bool HasWorkToDo( void ) const
|
|
{
|
|
if ( m_bDisabled )
|
|
return false;
|
|
return ( m_eIncrState != INCR_STATE_HAVE_FULL_RESULTS );
|
|
}
|
|
|
|
|
|
bool IsLowerPriorityThan( CLightingPreviewThread * pLPV,
|
|
CIncrementalLightInfo const & other ) const;
|
|
|
|
bool IsHighPriority( CLightingPreviewThread * pLPV ) const;
|
|
};
|
|
|
|
#define N_INCREMENTAL_STEPS 32
|
|
|
|
|
|
class CLightingPreviewThread
|
|
{
|
|
public:
|
|
CUtlIntrusiveList< CLightingPreviewLightDescription > m_LightList;
|
|
|
|
CSOAContainer m_GBuffer;
|
|
CSOAContainer m_GBufferLowRes;
|
|
|
|
|
|
RayTracingEnvironment * m_pRtEnv;
|
|
CIncrementalLightInfo * m_pIncrementalLightInfoList;
|
|
|
|
bool m_bAccStructureBuilt;
|
|
Vector m_LastEyePosition;
|
|
|
|
bool m_bResultChangedSinceLastSend;
|
|
float m_fLastSendTime;
|
|
|
|
int m_nBitmapGenerationCounter;
|
|
int m_nContributionCounter;
|
|
|
|
// bounidng box of the rendered scene+ the eye
|
|
Vector m_MinViewCoords;
|
|
Vector m_MaxViewCoords;
|
|
|
|
// sets that we are doing the first update since a discard and should do more lights per pass
|
|
bool m_bFirstWork;
|
|
|
|
CLightingPreviewThread( void )
|
|
{
|
|
m_nBitmapGenerationCounter = - 1;
|
|
m_pRtEnv = NULL;
|
|
m_bAccStructureBuilt = false;
|
|
m_pIncrementalLightInfoList = NULL;
|
|
m_fLastSendTime = - 1.0e6;
|
|
m_bResultChangedSinceLastSend = false;
|
|
m_nContributionCounter = 1000000;
|
|
m_bFirstWork = true;
|
|
}
|
|
|
|
|
|
~CLightingPreviewThread( void )
|
|
{
|
|
m_LightList.Purge();
|
|
while ( m_pIncrementalLightInfoList )
|
|
{
|
|
CIncrementalLightInfo * n = m_pIncrementalLightInfoList->m_pNext;
|
|
delete m_pIncrementalLightInfoList;
|
|
m_pIncrementalLightInfoList = n;
|
|
}
|
|
}
|
|
|
|
// check if the master has new work for us to do, meaning we should abort rendering
|
|
bool ShouldAbort( void )
|
|
{
|
|
return g_HammerToLPreviewMsgQueue.MessageWaiting();
|
|
}
|
|
|
|
// main loop
|
|
void Run( void );
|
|
|
|
// handle new g-buffers from master
|
|
void HandleGBuffersMessage( MessageToLPreview & msg_in );
|
|
|
|
// accept triangle list from master
|
|
void HandleGeomMessage( MessageToLPreview & msg_in );
|
|
|
|
// send one of our output images back
|
|
void SendResultRendering( CSOAContainer &rsltBuffer );
|
|
|
|
// calculate m_MinViewCoords, m_MaxViewCoords - the bounding box of the rendered pixels+the eye
|
|
void CalculateSceneBounds( void );
|
|
|
|
// inner lighting loop. meant to be multithreaded on dual-core (or more)
|
|
void CalculateForLightTask( int nLineStart, int nLineEnd,
|
|
CLightingPreviewLightDescription *l,
|
|
float * fContributionOut,
|
|
CIncrementalLightInfo *pInfremental );
|
|
|
|
void CalculateForLight( CLightingPreviewLightDescription *l );
|
|
|
|
// send our current output back
|
|
void SendResult( void );
|
|
|
|
void UpdateIncrementalForNewLightList( void );
|
|
|
|
void DiscardResults( void )
|
|
{
|
|
// Warning(" invalidate\n" );
|
|
// invalidate all per light result data
|
|
for( CIncrementalLightInfo * i = m_pIncrementalLightInfoList; i; i = i->m_pNext )
|
|
{
|
|
i->DiscardResults();
|
|
}
|
|
|
|
// bump time stamp
|
|
m_nContributionCounter++;
|
|
// update distances to lights
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
if ( l->m_Type == MATERIAL_LIGHT_DIRECTIONAL )
|
|
l_info->m_fDistanceToEye = 0; // high priority
|
|
else
|
|
l_info->m_fDistanceToEye = m_LastEyePosition.DistTo( l->m_Position );
|
|
}
|
|
m_bResultChangedSinceLastSend = true;
|
|
m_fLastSendTime = Plat_FloatTime() - 12; // force send
|
|
m_bFirstWork = true;
|
|
}
|
|
|
|
// handle a message. returns true if the thread shuold exit
|
|
bool HandleAMessage( void );
|
|
|
|
// returns whether or not there is useful work to do
|
|
bool AnyUsefulWorkToDo( void );
|
|
|
|
// do some work, like a rendering for one light
|
|
void DoWork( void );
|
|
|
|
Vector EstimatedUnshotAmbient( void )
|
|
{
|
|
// return Vector( 1,1,1 );
|
|
float sum_weights = 0.0001;
|
|
Vector sum_colors( sum_weights, sum_weights, sum_weights );
|
|
// calculate an ambient color based on light calculcated so far
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
if ( l_info &&
|
|
( l_info->m_eIncrState == INCR_STATE_HAVE_FULL_RESULTS ) ||
|
|
( l_info->m_eIncrState == INCR_STATE_PARTIAL_RESULTS ) )
|
|
{
|
|
float flPredictedContribution = l_info->PredictedContribution();
|
|
sum_weights += flPredictedContribution;
|
|
sum_colors.x += flPredictedContribution * l->m_Color.x;
|
|
sum_colors.y += flPredictedContribution * l->m_Color.y;
|
|
sum_colors.z += flPredictedContribution * l->m_Color.z;
|
|
}
|
|
}
|
|
sum_colors.NormalizeInPlace();
|
|
sum_colors *= 0.05;
|
|
return sum_colors;
|
|
}
|
|
void AccumulateOuput( int nLineMask, CSOAContainer *pResult, CSOAContainer *pLowresResule );
|
|
|
|
void AddLowresResultToHires( CSOAContainer &lowres, CSOAContainer &hires );
|
|
};
|
|
|
|
|
|
bool CIncrementalLightInfo::IsHighPriority( CLightingPreviewThread * pLPV ) const
|
|
{
|
|
// is this lighjt prioirty-boosted in some way?
|
|
if ( m_eIncrState == INCR_STATE_NEW )
|
|
{
|
|
// uncalculated lights within the view range are highest priority
|
|
if ( m_pLight->m_Position.WithinAABox( pLPV->m_MinViewCoords,
|
|
pLPV->m_MaxViewCoords ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
bool CIncrementalLightInfo::IsLowerPriorityThan( CLightingPreviewThread * pLPV,
|
|
CIncrementalLightInfo const & other ) const
|
|
{
|
|
// a NEW light within the view volume is highest priority
|
|
bool highpriority = IsHighPriority( pLPV );
|
|
bool other_highpriority = other.IsHighPriority( pLPV );
|
|
|
|
if ( highpriority && ( ! other_highpriority ) )
|
|
return false;
|
|
if ( other_highpriority && ( ! highpriority ) )
|
|
return true;
|
|
|
|
int state_combo = m_eIncrState + 16 * other.m_eIncrState;
|
|
switch ( state_combo )
|
|
{
|
|
case INCR_STATE_NEW + 16 * INCR_STATE_NEW:
|
|
{
|
|
// if both are new, closest to eye is best
|
|
return ( m_fDistanceToEye > other.m_fDistanceToEye );
|
|
}
|
|
|
|
case INCR_STATE_NEW + 16 * INCR_STATE_NO_RESULTS:
|
|
{
|
|
// new loses to something we know is probably going to contribute light
|
|
return ( other.m_fTotalContribution > 0 );
|
|
}
|
|
|
|
case INCR_STATE_NEW + 16 * INCR_STATE_PARTIAL_RESULTS:
|
|
{
|
|
return false;
|
|
}
|
|
|
|
case INCR_STATE_PARTIAL_RESULTS + 16 * INCR_STATE_NEW:
|
|
{
|
|
return true;
|
|
}
|
|
|
|
case INCR_STATE_NO_RESULTS + 16 * INCR_STATE_NEW:
|
|
{
|
|
// partial or discarded with no brightness loses to new
|
|
return ( m_fTotalContribution == 0 );
|
|
}
|
|
|
|
|
|
case INCR_STATE_PARTIAL_RESULTS + 16 * INCR_STATE_PARTIAL_RESULTS:
|
|
{
|
|
// if incrmental vs incremental, and no light from either, do most recently lit one
|
|
if ( ( m_fTotalContribution == 0.0 ) && ( other.m_fTotalContribution == 0.0 ) &&
|
|
( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp ) )
|
|
return true;
|
|
|
|
int nMaxLines = max( m_nNumLinesCalculated, other.m_nNumLinesCalculated );
|
|
int nMinLines = min( m_nNumLinesCalculated, other.m_nNumLinesCalculated );
|
|
|
|
// if other is black, and ratios aren't extremely far off, keep this one
|
|
if ( nMaxLines <= 16 * nMinLines )
|
|
{
|
|
if ( ( other.m_fTotalContribution == 0.0 ) && ( m_fTotalContribution > 0 ) )
|
|
return false;
|
|
if ( ( m_fTotalContribution == 0.0 ) && ( other.m_fTotalContribution > 0 ) )
|
|
return true;
|
|
}
|
|
|
|
// if incremental states are close, do brightest
|
|
if ( nMaxLines <= 2 * nMinLines )
|
|
return ( PredictedContribution() < other.PredictedContribution() );
|
|
|
|
// else do least refined
|
|
return ( m_nNumLinesCalculated > other.m_nNumLinesCalculated );
|
|
}
|
|
case INCR_STATE_PARTIAL_RESULTS + 16 * INCR_STATE_NO_RESULTS:
|
|
{
|
|
if ( other.m_fTotalContribution )
|
|
return true;
|
|
if ( ( m_fTotalContribution == 0.0 ) && ( other.m_fTotalContribution == 0.0 ) )
|
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp );
|
|
return ( PredictedContribution() < other.PredictedContribution() );
|
|
}
|
|
case INCR_STATE_NO_RESULTS + 16 * INCR_STATE_PARTIAL_RESULTS:
|
|
{
|
|
if ( m_fTotalContribution )
|
|
return false;
|
|
if ( ( m_fTotalContribution == 0.0 ) && ( other.m_fTotalContribution == 0.0 ) )
|
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp );
|
|
return ( PredictedContribution() < other.PredictedContribution() );
|
|
}
|
|
case INCR_STATE_NO_RESULTS * 16 + INCR_STATE_NO_RESULTS:
|
|
{
|
|
// if incrmental vs discarded, brightest or most recently bright wins
|
|
if ( ( m_fTotalContribution == 0.0 ) && ( other.m_fTotalContribution == 0.0 ) )
|
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp );
|
|
return ( PredictedContribution() < other.PredictedContribution() );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
float cg[3]={ 1, 0, 0};
|
|
float cr[3]={ 0, 1, 0 };
|
|
float cb[3]={ 0, 0, 1 };
|
|
|
|
void CLightingPreviewThread::HandleGeomMessage( MessageToLPreview & msg_in )
|
|
{
|
|
if ( m_pRtEnv )
|
|
{
|
|
delete m_pRtEnv;
|
|
m_pRtEnv = NULL;
|
|
}
|
|
CUtlVector < Vector > & tris = * ( msg_in.m_pShadowTriangleList );
|
|
if ( tris.Count() )
|
|
{
|
|
// FILE *fp = fopen( "c:\\gl.out", "w" );
|
|
m_pRtEnv = new RayTracingEnvironment;
|
|
for( int i = 0; i < tris.Count(); i += 3 )
|
|
{
|
|
// fprintf(fp,"3\n");
|
|
// for(int j=0;j<3;j++)
|
|
// fprintf( fp,"%f %f %f %f %f %f\n", tris[j+i].x,tris[j+i].y,tris[j+i].z, cr[j],cg[j],cb[j] );
|
|
m_pRtEnv->AddTriangle( i, tris[i], tris[1 + i], tris[2 + i], Vector( .5, .5, .5 ) );
|
|
}
|
|
// fclose( fp );
|
|
}
|
|
delete msg_in.m_pShadowTriangleList;
|
|
m_bAccStructureBuilt = false;
|
|
DiscardResults();
|
|
|
|
}
|
|
|
|
|
|
float CIncrementalLightInfo::PredictedContribution( void ) const
|
|
{
|
|
if ( ( m_fTotalContribution == 0 ) || ( ! m_nNumLinesCalculated ) )
|
|
return 0.;
|
|
else
|
|
return m_fTotalContribution * ( m_CalculatedContribution.NumRows() * ( 1.0 / m_nNumLinesCalculated ) );
|
|
}
|
|
|
|
void CLightingPreviewThread::CalculateSceneBounds( void )
|
|
{
|
|
FourVectors minbound, maxbound;
|
|
minbound.DuplicateVector( m_LastEyePosition );
|
|
maxbound.DuplicateVector( m_LastEyePosition );
|
|
for( int y = 0; y < m_GBuffer.NumRows(); y++ )
|
|
{
|
|
FourVectors const *cptr = m_GBuffer.RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, y );
|
|
for( int x = 0; x < m_GBuffer.NumQuadsPerRow(); x++ )
|
|
{
|
|
minbound.x = MinSIMD( cptr->x, minbound.x );
|
|
minbound.y = MinSIMD( cptr->y, minbound.y );
|
|
minbound.z = MinSIMD( cptr->z, minbound.z );
|
|
|
|
maxbound.x = MaxSIMD( cptr->x, maxbound.x );
|
|
maxbound.y = MaxSIMD( cptr->y, maxbound.y );
|
|
maxbound.z = MaxSIMD( cptr->z, maxbound.z );
|
|
cptr++;
|
|
}
|
|
}
|
|
m_MinViewCoords = minbound.Vec( 0 );
|
|
m_MaxViewCoords = maxbound.Vec( 0 );
|
|
for( int v = 1; v < 4; v++ )
|
|
{
|
|
m_MinViewCoords = m_MinViewCoords.Min( minbound.Vec( v ) );
|
|
m_MaxViewCoords = m_MaxViewCoords.Max( maxbound.Vec( v ) );
|
|
}
|
|
}
|
|
|
|
|
|
void CLightingPreviewThread::UpdateIncrementalForNewLightList( void )
|
|
{
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
// see if we know about this light
|
|
for( CIncrementalLightInfo * i = m_pIncrementalLightInfoList; i; i = i->m_pNext )
|
|
{
|
|
if ( i->m_nObjectID == l->m_nObjectID )
|
|
{
|
|
// found it!
|
|
l->m_pIncrementalInfo = i;
|
|
i->m_pLight = l;
|
|
break;
|
|
}
|
|
}
|
|
if ( ! l->m_pIncrementalInfo )
|
|
{
|
|
l->m_pIncrementalInfo = new CIncrementalLightInfo;
|
|
l->m_pIncrementalInfo->m_nObjectID = l->m_nObjectID;
|
|
l->m_pIncrementalInfo->m_pLight = l;
|
|
|
|
// add to list
|
|
l->m_pIncrementalInfo->m_pNext = m_pIncrementalLightInfoList;
|
|
m_pIncrementalLightInfoList = l->m_pIncrementalInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CLightingPreviewThread::Run( void )
|
|
{
|
|
bool should_quit = false;
|
|
while( ! should_quit )
|
|
{
|
|
while (
|
|
( ! should_quit ) &&
|
|
( ( ! AnyUsefulWorkToDo() ) || ( g_HammerToLPreviewMsgQueue.MessageWaiting() ) ) )
|
|
should_quit |= HandleAMessage();
|
|
if ( ( ! should_quit ) && ( AnyUsefulWorkToDo() ) )
|
|
DoWork();
|
|
if ( m_bResultChangedSinceLastSend )
|
|
{
|
|
float newtime = Plat_FloatTime();
|
|
if ( ( newtime - m_fLastSendTime > 2.0 ) || ( ! AnyUsefulWorkToDo() ) )
|
|
{
|
|
SendResult();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bool CLightingPreviewThread::HandleAMessage( void )
|
|
{
|
|
MessageToLPreview msg_in;
|
|
g_HammerToLPreviewMsgQueue.WaitMessage( & msg_in );
|
|
switch( msg_in.m_MsgType )
|
|
{
|
|
case LPREVIEW_MSG_EXIT:
|
|
return true; // return from thread
|
|
|
|
case LPREVIEW_MSG_LIGHT_DATA:
|
|
{
|
|
m_LightList.Purge();
|
|
m_LightList = msg_in.m_LightList;
|
|
m_LastEyePosition = msg_in.m_EyePosition;
|
|
UpdateIncrementalForNewLightList();
|
|
DiscardResults();
|
|
}
|
|
break;
|
|
|
|
case LPREVIEW_MSG_GEOM_DATA:
|
|
HandleGeomMessage( msg_in );
|
|
DiscardResults();
|
|
break;
|
|
|
|
case LPREVIEW_MSG_G_BUFFERS:
|
|
HandleGBuffersMessage( msg_in );
|
|
DiscardResults();
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CLightingPreviewThread::AnyUsefulWorkToDo( void )
|
|
{
|
|
if ( m_GBuffer.NumRows() )
|
|
{
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
CIncrementalLightInfo *l_info = l->m_pIncrementalInfo;
|
|
if ( l_info->HasWorkToDo() )
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void s_CalculateForLight( CLightingPreviewLightDescription * &pLight )
|
|
{
|
|
s_pThis->CalculateForLight( pLight );
|
|
}
|
|
|
|
void CLightingPreviewThread::DoWork( void )
|
|
{
|
|
if ( m_pRtEnv && ( ! m_bAccStructureBuilt ) )
|
|
{
|
|
m_bAccStructureBuilt = true;
|
|
m_pRtEnv->SetupAccelerationStructure();
|
|
}
|
|
CLightingPreviewLightDescription *pLightsToRun[8];
|
|
int nNumLightJobs = 0;
|
|
int nJobsToDo = s_nNumThreads + 1;
|
|
if ( m_bFirstWork )
|
|
{
|
|
nJobsToDo *= 2;
|
|
m_bFirstWork = false;
|
|
}
|
|
#if LPREVIEW_MULTITHREAD == 0
|
|
nJobsToDo = 1;
|
|
#endif
|
|
|
|
for( int i = 0; i < nJobsToDo; i++ )
|
|
{
|
|
CLightingPreviewLightDescription *best_l = NULL;
|
|
CIncrementalLightInfo * best_l_info = NULL;
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
// check if light could influence scene
|
|
if ( l_info->m_bDisabled )
|
|
continue; // this light can't effect the visible scene
|
|
if ( l->m_Type != MATERIAL_LIGHT_DIRECTIONAL )
|
|
{
|
|
float lrad = l->DistanceAtWhichBrightnessIsLessThan( 1.0 / 500.0 );
|
|
if ( ! l->m_Position.WithinAABox( m_MinViewCoords - ReplicateToVector( lrad ),
|
|
m_MaxViewCoords + ReplicateToVector( lrad ) ) )
|
|
{
|
|
l_info->m_bDisabled = true;
|
|
}
|
|
}
|
|
if ( l_info->m_bDisabled )
|
|
continue; // this light can't effect the visible scene
|
|
|
|
// check that we don't have it
|
|
bool bHaveit = false;
|
|
for( int j = 0; j < nNumLightJobs; j++ )
|
|
if ( pLightsToRun[j] == l )
|
|
bHaveit = true;
|
|
if ( (! bHaveit) && ( l_info->HasWorkToDo() ) )
|
|
{
|
|
if ( ( ! best_l ) ||
|
|
( best_l->m_pIncrementalInfo->IsLowerPriorityThan( this, *l_info ) ) )
|
|
{
|
|
best_l_info = l_info;
|
|
best_l = l;
|
|
}
|
|
}
|
|
}
|
|
if ( best_l )
|
|
{
|
|
pLightsToRun[nNumLightJobs++] = best_l;
|
|
}
|
|
}
|
|
// now, process in parallel
|
|
if ( nNumLightJobs )
|
|
{
|
|
#if LPREVIEW_MULTITHREAD
|
|
ParallelProcess( s_pThreadPool, pLightsToRun, nNumLightJobs, s_CalculateForLight );
|
|
#else
|
|
for( int i = 0; i < nNumLightJobs; i++ )
|
|
{
|
|
Warning( "process light %p lnum=%d contribution = %f predicted = %f\n", pLightsToRun[i], pLightsToRun[i]->m_pIncrementalInfo->m_nNumLinesCalculated, pLightsToRun[i]->m_pIncrementalInfo->m_fTotalContribution, pLightsToRun[i]->m_pIncrementalInfo->PredictedContribution() );
|
|
s_CalculateForLight( pLightsToRun[i] );
|
|
}
|
|
#endif
|
|
// now, some lights may have created lights for indirect light. We must move these to the global list.
|
|
// we could not do this while creating them, because of thread-safety.
|
|
for( int i = 0; i < nNumLightJobs; i++ )
|
|
{
|
|
if ( pLightsToRun[i]->m_pIncrementalInfo->m_flLastContribution )
|
|
{
|
|
m_bResultChangedSinceLastSend = true;
|
|
}
|
|
for( int j = 0; j < pLightsToRun[i]->m_TempChildren.Count(); j++ )
|
|
{
|
|
CLightingPreviewLightDescription *pNew = pLightsToRun[i]->m_TempChildren[j];
|
|
pNew->m_pIncrementalInfo = new CIncrementalLightInfo;
|
|
pNew->m_pIncrementalInfo->m_nObjectID = pNew->m_nObjectID;
|
|
pNew->m_pIncrementalInfo->m_pLight = pNew;
|
|
pNew->m_pIncrementalInfo->m_pNext = m_pIncrementalLightInfoList;
|
|
m_pIncrementalLightInfoList = pNew->m_pIncrementalInfo;
|
|
m_LightList.AddToTail( pNew );
|
|
}
|
|
pLightsToRun[i]->m_TempChildren.Purge();
|
|
pLightsToRun[i]->m_bDidIndirect = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CLightingPreviewThread::HandleGBuffersMessage( MessageToLPreview & msg_in )
|
|
{
|
|
m_GBuffer.Purge();
|
|
m_GBuffer.SetAttributeType( GBUFFER_ATTR_POSITION, ATTRDATATYPE_4V );
|
|
m_GBuffer.SetAttributeType( GBUFFER_ATTR_ALBEDO, ATTRDATATYPE_4V );
|
|
m_GBuffer.SetAttributeType( GBUFFER_ATTR_NORMAL, ATTRDATATYPE_4V );
|
|
m_GBuffer.AllocateData( msg_in.m_pDefferedRenderingBMs[0]->NumCols(),
|
|
msg_in.m_pDefferedRenderingBMs[0]->NumRows() );
|
|
|
|
m_GBuffer.PackScalarAttributesToVectorAttribute( msg_in.m_pDefferedRenderingBMs[0],
|
|
GBUFFER_ATTR_ALBEDO,
|
|
FBM_ATTR_RED, FBM_ATTR_GREEN, FBM_ATTR_BLUE );
|
|
m_GBuffer.PackScalarAttributesToVectorAttribute( msg_in.m_pDefferedRenderingBMs[1],
|
|
GBUFFER_ATTR_NORMAL,
|
|
FBM_ATTR_RED, FBM_ATTR_GREEN, FBM_ATTR_BLUE );
|
|
m_GBuffer.PackScalarAttributesToVectorAttribute( msg_in.m_pDefferedRenderingBMs[2],
|
|
GBUFFER_ATTR_POSITION,
|
|
FBM_ATTR_RED, FBM_ATTR_GREEN, FBM_ATTR_BLUE );
|
|
|
|
m_GBufferLowRes.Purge();
|
|
m_GBufferLowRes.SetAttributeType( GBUFFER_ATTR_POSITION, ATTRDATATYPE_4V );
|
|
m_GBufferLowRes.SetAttributeType( GBUFFER_ATTR_ALBEDO, ATTRDATATYPE_4V );
|
|
m_GBufferLowRes.SetAttributeType( GBUFFER_ATTR_NORMAL, ATTRDATATYPE_4V );
|
|
m_GBufferLowRes.AllocateData( msg_in.m_pDefferedRenderingBMs[0]->NumCols() / 4,
|
|
msg_in.m_pDefferedRenderingBMs[0]->NumRows() / 4 );
|
|
|
|
// now, downsample
|
|
m_GBufferLowRes.ResampleAttribute( m_GBuffer, GBUFFER_ATTR_POSITION );
|
|
m_GBufferLowRes.ResampleAttribute( m_GBuffer, GBUFFER_ATTR_ALBEDO );
|
|
m_GBufferLowRes.ResampleAttribute( m_GBuffer, GBUFFER_ATTR_NORMAL );
|
|
|
|
|
|
m_LastEyePosition = msg_in.m_EyePosition;
|
|
for( int i = 0;i < ARRAYSIZE( msg_in.m_pDefferedRenderingBMs ); i++ )
|
|
delete msg_in.m_pDefferedRenderingBMs[i];
|
|
n_gbufs_queued--;
|
|
m_nBitmapGenerationCounter = msg_in.m_nBitmapGenerationCounter;
|
|
CalculateSceneBounds();
|
|
|
|
}
|
|
|
|
|
|
void CLightingPreviewThread::AccumulateOuput( int nLineMask, CSOAContainer *rslt, CSOAContainer *rslt1 )
|
|
{
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
CSOAContainer *pRslt = rslt;
|
|
CSOAContainer *pGB = &m_GBuffer;
|
|
if ( l->m_bLowRes )
|
|
{
|
|
pGB = &m_GBufferLowRes;
|
|
pRslt = rslt1;
|
|
}
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
if ( ( l_info->m_fTotalContribution > 0.0 ) &&
|
|
( l_info->m_eIncrState >= INCR_STATE_PARTIAL_RESULTS ) )
|
|
{
|
|
// need to add partials, replicated to handle undone lines
|
|
CSOAContainer &src = l_info->m_CalculatedContribution;
|
|
int nY0 = l_info->m_nFirstCalculatedLine;
|
|
int nY1 = nY0;
|
|
// scan forward to find the next calculated line, if any
|
|
while ( nY1 < l_info->m_nMaxCalculatedLine )
|
|
{
|
|
nY1++;
|
|
if ( l_info->m_nCalculationLevel[nY1] )
|
|
break;
|
|
}
|
|
fltx4 fl4NormalFactorScale = ReplicateX4( 4.0f );
|
|
fltx4 fl4NormalBias = ReplicateX4( 0.0f ); //1.01 ); // prevent 0.
|
|
for( int y = 0; y < pGB->NumRows(); y++ )
|
|
{
|
|
if ( nLineMask & ( 1 << ( y & 31 ) ) )
|
|
{
|
|
fltx4 fl4Weights[2];
|
|
if ( ( y < nY0 ) || ( nY0 == nY1 ) )
|
|
{
|
|
fl4Weights[0] = Four_Ones;
|
|
fl4Weights[1] = Four_Zeros;
|
|
}
|
|
else
|
|
{
|
|
fl4Weights[1] = ReplicateX4( ( y - nY0 ) * ( 1.0 / ( nY1 - nY0 ) ) );
|
|
fl4Weights[0] = SubSIMD( Four_Ones, fl4Weights[1] );
|
|
}
|
|
FourVectors *pRslts[2];
|
|
pRslts[0] = src.RowPtr<FourVectors>( RSLT_ATTR_DIFFUSE_RGB, nY0 );
|
|
pRslts[1] = src.RowPtr<FourVectors>( RSLT_ATTR_DIFFUSE_RGB, nY1 );
|
|
FourVectors *dest = pRslt->RowPtr<FourVectors>( RSLT_BUFFER_RSLT_RGB, y);
|
|
FourVectors const * pNormal = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, y );
|
|
FourVectors const * pRsltNormals[2];
|
|
pRsltNormals[0] = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, nY0 );
|
|
pRsltNormals[1] = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, nY1 );
|
|
|
|
FourVectors const * pCoord = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, y );
|
|
FourVectors const * pCoords[2];
|
|
pCoords[0] = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, nY0 );
|
|
pCoords[1] = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, nY1 );
|
|
|
|
fltx4 fl4DistanceScale = ReplicateX4( 1.0 / 36.0 );
|
|
for( int x = 0; x < pGB->NumQuadsPerRow(); x++ )
|
|
{
|
|
|
|
FourVectors l1 = *( pRslts[1]++ );
|
|
fltx4 fl4Dot = ( *pRsltNormals[1]++ ) * ( *pNormal );
|
|
fl4Dot = MaxSIMD( Four_Epsilons,
|
|
MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, fl4Dot ) ) );
|
|
|
|
FourVectors fl4Delta = *( pCoords[1]++ );
|
|
fl4Delta -= *pCoord;
|
|
fltx4 fl4Distance = fl4Delta.length();
|
|
fl4Distance = ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( fl4Distance, fl4DistanceScale ) ) );
|
|
|
|
fltx4 fl4SumWeights = MulSIMD( fl4Distance, MulSIMD( fl4Weights[1], fl4Dot ) );
|
|
l1 *= fl4SumWeights;
|
|
|
|
fl4Dot = ( *pRsltNormals[0]++ ) * ( *pNormal++ );
|
|
fl4Dot = MaxSIMD( Four_Epsilons,
|
|
MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, fl4Dot ) ) );
|
|
fl4Delta = *( pCoords[0]++ );
|
|
fl4Delta -= *pCoord;
|
|
fl4Distance = fl4Delta.length();
|
|
pCoord++;
|
|
|
|
fl4Distance = ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( fl4Distance, fl4DistanceScale ) ) );
|
|
FourVectors l2 = *( pRslts[0]++ );
|
|
fltx4 w0 = MulSIMD( fl4Distance, MulSIMD( fl4Dot, fl4Weights[0] ) );
|
|
l2 *= w0;
|
|
l1 += l2;
|
|
|
|
fl4SumWeights = AddSIMD( fl4SumWeights, w0 );
|
|
l1 *= ReciprocalSIMD( fl4SumWeights );
|
|
|
|
* ( dest++ ) += l1;
|
|
}
|
|
}
|
|
// now, update line indices
|
|
if ( y >= nY1 )
|
|
{
|
|
nY0 = nY1;
|
|
while ( nY1 < l_info->m_nMaxCalculatedLine )
|
|
{
|
|
nY1++;
|
|
if ( l_info->m_nCalculationLevel[nY1] )
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static CSOAContainer *s_pResultBuffer;
|
|
static CSOAContainer *s_pResultBuffer1;
|
|
|
|
void s_AccumulateOutput( int &nLineMask )
|
|
{
|
|
s_pThis->AccumulateOuput( nLineMask, s_pResultBuffer, s_pResultBuffer1 );
|
|
}
|
|
|
|
#define GRAB4PIXELS( base ) \
|
|
base##AAAA.x = SplatXSIMD( base##ShiftRegister0.x ); \
|
|
base##AAAA.y = SplatXSIMD( base##ShiftRegister0.y ); \
|
|
base##AAAA.z = SplatXSIMD( base##ShiftRegister0.z ); \
|
|
base##BBBB.x = SplatYSIMD( base##ShiftRegister0.x ); \
|
|
base##BBBB.y = SplatYSIMD( base##ShiftRegister0.y ); \
|
|
base##BBBB.z = SplatYSIMD( base##ShiftRegister0.z ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister0.x, base##ShiftRegister0a.x ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister0.y, base##ShiftRegister0a.y ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister0.z, base##ShiftRegister0a.z ); \
|
|
base##EEEE.x = SplatXSIMD( base##ShiftRegister1.x ); \
|
|
base##EEEE.y = SplatXSIMD( base##ShiftRegister1.y ); \
|
|
base##EEEE.z = SplatXSIMD( base##ShiftRegister1.z ); \
|
|
base##FFFF.x = SplatYSIMD( base##ShiftRegister1.x ); \
|
|
base##FFFF.y = SplatYSIMD( base##ShiftRegister1.y ); \
|
|
base##FFFF.z = SplatYSIMD( base##ShiftRegister1.z ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister1.x, base##ShiftRegister1a.x ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister1.y, base##ShiftRegister1a.y ); \
|
|
RotateLeftDoubleSIMD( base##ShiftRegister1.z, base##ShiftRegister1a.z );
|
|
|
|
void CLightingPreviewThread::AddLowresResultToHires( CSOAContainer &lores, CSOAContainer &hires )
|
|
{
|
|
// we will bilaterally upsample lowres and add it to hires. This is coded for the specific case of a 4x4 downsamping
|
|
fltx4 fl4NormalFactorScale = ReplicateX4( 4.0f );
|
|
fltx4 fl4NormalBias = ReplicateX4( 0.0f ); //1.01 ); // prevent 0.
|
|
fltx4 fl4DistanceScale = ReplicateX4( 1.0 / 36.0 );
|
|
Assert( lores.NumRows() == m_GBufferLowRes.NumRows() );
|
|
Assert( lores.NumCols() == m_GBufferLowRes.NumCols() );
|
|
for( int y = 0; y < hires.NumRows(); y++ )
|
|
{
|
|
int ysrc0 = min( ( y >> 2 ), lores.NumRows() -1 );
|
|
int ysrc1 = min( ysrc0 + 1, lores.NumRows() - 1 );
|
|
int nIterations = hires.NumQuadsPerRow();
|
|
int numFetches = lores.NumQuadsPerRow();
|
|
|
|
FourVectors *pSrc0 = lores.RowPtr<FourVectors>( RSLT_BUFFER_RSLT_RGB, ysrc0 );
|
|
FourVectors *pSrc1 = lores.RowPtr<FourVectors>( RSLT_BUFFER_RSLT_RGB, ysrc1 );
|
|
FourVectors rsltShiftRegister0 = *( pSrc0++ );
|
|
FourVectors rsltShiftRegister0a;
|
|
FourVectors rsltShiftRegister1 = *( pSrc1++ );
|
|
FourVectors rsltShiftRegister1a;
|
|
|
|
FourVectors *pSrcNormal0 = m_GBufferLowRes.RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, ysrc0 );
|
|
FourVectors *pSrcNormal1 = m_GBufferLowRes.RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, ysrc1 );
|
|
FourVectors normShiftRegister0 = *( pSrcNormal0++ );
|
|
FourVectors normShiftRegister0a;
|
|
FourVectors normShiftRegister1 = *( pSrcNormal1++ );
|
|
FourVectors normShiftRegister1a;
|
|
|
|
FourVectors *pSrcPos0 = m_GBufferLowRes.RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, ysrc0 );
|
|
FourVectors *pSrcPos1 = m_GBufferLowRes.RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, ysrc1 );
|
|
FourVectors posShiftRegister0 = *( pSrcPos0++ );
|
|
FourVectors posShiftRegister0a;
|
|
FourVectors posShiftRegister1 = *( pSrcPos1++ );
|
|
FourVectors posShiftRegister1a;
|
|
|
|
FourVectors *pDest = hires.RowPtr<FourVectors>( RSLT_BUFFER_RSLT_RGB, y );
|
|
FourVectors *pDestNormal = m_GBuffer.RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, y );
|
|
FourVectors *pDestPos = m_GBuffer.RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, y );
|
|
|
|
numFetches--;
|
|
|
|
for( int x = 0; x < nIterations; x++ )
|
|
{
|
|
if ( ( ( x & 3 ) == 0 ) && numFetches ) // need to fetch new data every 4 outputs
|
|
{
|
|
numFetches--;
|
|
rsltShiftRegister0a = *( pSrc0++ );
|
|
rsltShiftRegister1a = *( pSrc1++ );
|
|
|
|
normShiftRegister0a = *( pSrcNormal0++ );
|
|
normShiftRegister1a = *( pSrcNormal1++ );
|
|
|
|
posShiftRegister0a = *( pSrcPos0++ );
|
|
posShiftRegister1a = *( pSrcPos1++ );
|
|
}
|
|
|
|
FourVectors rsltAAAA, rsltBBBB, rsltEEEE, rsltFFFF;
|
|
GRAB4PIXELS( rslt );
|
|
|
|
FourVectors normAAAA, normBBBB, normEEEE, normFFFF;
|
|
GRAB4PIXELS( norm );
|
|
|
|
FourVectors posAAAA, posBBBB, posEEEE, posFFFF;
|
|
GRAB4PIXELS( pos );
|
|
|
|
// we should now be ready to filter. We will take the 4 pixels we have, and produce 4 output pixels
|
|
FourVectors dNorm = *( pDestNormal++ );
|
|
fltx4 fl4ADot = MaxSIMD( Four_Epsilons, MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, dNorm * normAAAA ) ) );
|
|
fltx4 fl4BDot = MaxSIMD( Four_Epsilons, MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, dNorm * normBBBB ) ) );
|
|
fltx4 fl4EDot = MaxSIMD( Four_Epsilons, MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, dNorm * normEEEE ) ) );
|
|
fltx4 fl4FDot = MaxSIMD( Four_Epsilons, MulSIMD( fl4NormalFactorScale, AddSIMD( fl4NormalBias, dNorm * normFFFF ) ) );
|
|
|
|
FourVectors fl4Pos = *( pDestPos++ );
|
|
FourVectors v4Delta = posAAAA;
|
|
v4Delta -= fl4Pos;
|
|
fltx4 fl4WA =
|
|
MulSIMD( fl4ADot, ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( v4Delta.length(), fl4DistanceScale ) ) ) );
|
|
|
|
v4Delta = posBBBB;
|
|
v4Delta -= fl4Pos;
|
|
fltx4 fl4WB =
|
|
MulSIMD( fl4BDot, ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( v4Delta.length(), fl4DistanceScale ) ) ) );
|
|
|
|
v4Delta = posEEEE;
|
|
v4Delta -= fl4Pos;
|
|
fltx4 fl4WE =
|
|
MulSIMD( fl4EDot, ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( v4Delta.length(), fl4DistanceScale ) ) ) );
|
|
|
|
v4Delta = posFFFF;
|
|
v4Delta -= fl4Pos;
|
|
fltx4 fl4WF =
|
|
MulSIMD( fl4FDot, ReciprocalEstSIMD( AddSIMD( Four_Ones, MulSIMD( v4Delta.length(), fl4DistanceScale ) ) ) );
|
|
|
|
fltx4 fl4OOSumWeights = ReciprocalSIMD( AddSIMD( AddSIMD( fl4WA, fl4WB ), AddSIMD( fl4WE, fl4WF ) ) );
|
|
|
|
// now, calculate the output color
|
|
FourVectors out = rsltAAAA;
|
|
out *= fl4WA;
|
|
|
|
FourVectors out1 = rsltBBBB;
|
|
out1 *= fl4WB;
|
|
out += out1;
|
|
|
|
out1 = rsltEEEE;
|
|
out1 *= fl4WE;
|
|
out += out1;
|
|
|
|
out1 = rsltFFFF;
|
|
out1 *= fl4WF;
|
|
out += out1;
|
|
|
|
out *= fl4OOSumWeights;
|
|
|
|
*( pDest++ ) += out;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLightingPreviewThread::SendResult( void )
|
|
{
|
|
if ( m_GBuffer.NumRows() && m_GBuffer.NumCols() )
|
|
{
|
|
// Warning("send\n");
|
|
CSOAContainer rsltBuffer;
|
|
rsltBuffer.SetAttributeType( RSLT_BUFFER_RSLT_RGB, ATTRDATATYPE_4V );
|
|
rsltBuffer.AllocateData( m_GBuffer.NumCols(), m_GBuffer.NumRows() );
|
|
rsltBuffer.FillAttr( RSLT_BUFFER_RSLT_RGB, EstimatedUnshotAmbient() );
|
|
s_pResultBuffer = &rsltBuffer;
|
|
|
|
bool bDidLoRes = false;
|
|
for( CLightingPreviewLightDescription *l = m_LightList.Head(); l; l = l->m_pNext )
|
|
{
|
|
if ( l->m_bLowRes )
|
|
bDidLoRes = true;
|
|
}
|
|
|
|
CSOAContainer rsltBuffer1;
|
|
rsltBuffer1.SetAttributeType( RSLT_BUFFER_RSLT_RGB, ATTRDATATYPE_4V );
|
|
rsltBuffer1.AllocateData( m_GBufferLowRes.NumCols(), m_GBufferLowRes.NumRows() );
|
|
if ( bDidLoRes )
|
|
{
|
|
rsltBuffer1.FillAttr( RSLT_BUFFER_RSLT_RGB, Vector( 0, 0, 0 ) );
|
|
}
|
|
s_pResultBuffer1 = &rsltBuffer1;
|
|
int nProcessMasks[32];
|
|
for( int i = 0; i < 32; i++ )
|
|
nProcessMasks[i] = ( 1 << i );
|
|
ParallelProcess( s_pThreadPool, nProcessMasks, 32, s_AccumulateOutput );
|
|
|
|
if ( bDidLoRes )
|
|
AddLowresResultToHires( rsltBuffer1, rsltBuffer );
|
|
|
|
// now, multiply by albedo
|
|
rsltBuffer.MulAttr( m_GBuffer, GBUFFER_ATTR_ALBEDO, RSLT_BUFFER_RSLT_RGB );
|
|
|
|
SendResultRendering( rsltBuffer );
|
|
m_fLastSendTime = Plat_FloatTime();
|
|
m_bResultChangedSinceLastSend = false;
|
|
}
|
|
}
|
|
|
|
void CLightingPreviewThread::CalculateForLightTask( int nLineStart, int nLineEnd,
|
|
CLightingPreviewLightDescription *l,
|
|
float *fContributionOut,
|
|
CIncrementalLightInfo *pLInfo )
|
|
|
|
{
|
|
FourVectors zero_vector;
|
|
zero_vector.x = Four_Zeros;
|
|
zero_vector.y = Four_Zeros;
|
|
zero_vector.z = Four_Zeros;
|
|
|
|
FourVectors total_light = zero_vector;
|
|
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
CSOAContainer &rslt = l_info->m_CalculatedContribution;
|
|
// figure out what lines to do
|
|
fltx4 ThresholdBrightness = ReplicateX4( 0.1 / 1024.0 );
|
|
FourVectors LastLinesTotalLight = zero_vector;
|
|
|
|
|
|
// calculate jitter stuff
|
|
fltx4 fl4RandRange = ReplicateX4( l->m_flJitterAmount );
|
|
bool bJitter = l->m_flJitterAmount > 0.0;
|
|
int nCtx = GetSIMDRandContext();
|
|
CSOAContainer *pGB = ( l->m_bLowRes ) ? &m_GBufferLowRes : &m_GBuffer;
|
|
|
|
for( int idx = nLineStart; idx <= nLineEnd; idx++ )
|
|
{
|
|
int y = InsideOut( rslt.NumRows(), idx );
|
|
FourVectors ThisLinesTotalLight = zero_vector;
|
|
FourVectors *pDataOut = rslt.RowPtr<FourVectors>( RSLT_ATTR_DIFFUSE_RGB, y );
|
|
FourVectors *pAlbedo = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_ALBEDO, y );
|
|
FourVectors *pPos = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_POSITION, y );
|
|
FourVectors *pNormal = pGB->RowPtr<FourVectors>( GBUFFER_ATTR_NORMAL, y );
|
|
for( int x = 0; x < rslt.NumQuadsPerRow(); x++ )
|
|
{
|
|
// shadow check
|
|
FourVectors pos = *( pPos++ );
|
|
FourVectors normal = *( pNormal++ );
|
|
|
|
FourVectors l_add = zero_vector;
|
|
l->ComputeLightAtPoints( pos, normal, l_add, false );
|
|
fltx4 v_or = OrSIMD( l_add.x, OrSIMD( l_add.y, l_add.z ) );
|
|
if ( ! IsAllZeros( v_or ) )
|
|
{
|
|
FourVectors lpos;
|
|
lpos.DuplicateVector( l->m_Position );
|
|
// jitter light position
|
|
if ( bJitter )
|
|
{
|
|
lpos.x =
|
|
AddSIMD( lpos.x, MulSIMD( fl4RandRange, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nCtx ) ), Four_Ones ) ) );
|
|
lpos.y =
|
|
AddSIMD( lpos.y, MulSIMD( fl4RandRange, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nCtx ) ), Four_Ones ) ) );
|
|
lpos.z =
|
|
AddSIMD( lpos.z, MulSIMD( fl4RandRange, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nCtx ) ), Four_Ones ) ) );
|
|
}
|
|
|
|
FourRays myray;
|
|
myray.direction = lpos;
|
|
myray.direction -= pos;
|
|
fltx4 len = myray.direction.length();
|
|
myray.direction *= ReciprocalSIMD( len );
|
|
|
|
// slide towards light to avoid self-intersection
|
|
myray.origin = myray.direction;
|
|
myray.origin *= 0.02;
|
|
myray.origin += pos;
|
|
|
|
RayTracingResult r_rslt;
|
|
m_pRtEnv->Trace4Rays( myray, Four_Zeros, ReplicateX4( 1.0e9 ), & r_rslt );
|
|
|
|
for( int c = 0; c < 4; c++ ) // !!speed!! use sse logic ops here
|
|
{
|
|
if ( ( r_rslt.HitIds[c] != - 1 ) &&
|
|
( r_rslt.HitDistance.m128_f32[c] < len.m128_f32[c] ) )
|
|
{
|
|
l_add.x.m128_f32[c]= 0.0;
|
|
l_add.y.m128_f32[c]= 0.0;
|
|
l_add.z.m128_f32[c]= 0.0;
|
|
}
|
|
}
|
|
*( pDataOut ) = l_add;
|
|
l_add *= *(pAlbedo );
|
|
// now, supress brightness < threshold so as to not falsely think
|
|
// far away lights are interesting
|
|
l_add.x = AndSIMD( l_add.x, CmpGtSIMD( l_add.x, ThresholdBrightness ) );
|
|
l_add.y = AndSIMD( l_add.y, CmpGtSIMD( l_add.y, ThresholdBrightness ) );
|
|
l_add.z = AndSIMD( l_add.z, CmpGtSIMD( l_add.z, ThresholdBrightness ) );
|
|
ThisLinesTotalLight += l_add;
|
|
}
|
|
else
|
|
*( pDataOut ) = l_add;
|
|
pDataOut++;
|
|
pAlbedo++;
|
|
}
|
|
pLInfo->m_nCalculationLevel[y] = 1;
|
|
pLInfo->m_nMaxCalculatedLine = max( y, pLInfo->m_nMaxCalculatedLine );
|
|
pLInfo->m_nFirstCalculatedLine = min( y, pLInfo->m_nFirstCalculatedLine );
|
|
total_light += ThisLinesTotalLight;
|
|
}
|
|
ReleaseSIMDRandContext( nCtx );
|
|
fltx4 lmag = total_light.length();
|
|
* ( fContributionOut ) = lmag.m128_f32[0]+ lmag.m128_f32[1]+ lmag.m128_f32[2]+ lmag.m128_f32[3];
|
|
}
|
|
|
|
|
|
#define N_FAKE_LIGHTS_FOR_INDIRECT 50
|
|
|
|
void CLightingPreviewThread::CalculateForLight( CLightingPreviewLightDescription *l )
|
|
{
|
|
if ( ! l->m_bDidIndirect )
|
|
{
|
|
// create a bunch of pseudo lights for this light
|
|
float lrad = l->DistanceAtWhichBrightnessIsLessThan( 1.0 / 500.0 );
|
|
RayTracingSingleResult rslts[N_FAKE_LIGHTS_FOR_INDIRECT];
|
|
Vector rayDirs[N_FAKE_LIGHTS_FOR_INDIRECT];
|
|
DirectionalSampler_t sampler;
|
|
RayStream myStream;
|
|
Vector rayStart = l->m_Position;
|
|
for( int i = 0; i < N_FAKE_LIGHTS_FOR_INDIRECT; i++ )
|
|
{
|
|
rayDirs[i] = sampler.NextValue();
|
|
m_pRtEnv->AddToRayStream( myStream, rayStart, rayStart + lrad * rayDirs[i], rslts + i );
|
|
}
|
|
m_pRtEnv->FinishRayStream( myStream);
|
|
// now, we have a bunch of raytracing results
|
|
for( int i = 0; i < N_FAKE_LIGHTS_FOR_INDIRECT; i++ )
|
|
{
|
|
if ( rslts[i].HitID != -1 ) // hit something
|
|
{
|
|
Vector vecHitPos = rayStart + rslts[i].HitDistance * rayDirs[i];
|
|
FourVectors v4Pnt;
|
|
v4Pnt.DuplicateVector( vecHitPos );
|
|
FourVectors v4Normal;
|
|
v4Normal.DuplicateVector( rslts[i].surface_normal );
|
|
FourVectors v4Color;
|
|
l->ComputeLightAtPoints( v4Pnt, v4Normal, v4Color );
|
|
Vector vecColorToShoot = v4Color.Vec( 0 ) * 0.25 / N_FAKE_LIGHTS_FOR_INDIRECT;
|
|
|
|
if ( vecColorToShoot.Length() > 1/255.0 )
|
|
{
|
|
CLightingPreviewLightDescription *pNew = new CLightingPreviewLightDescription;
|
|
pNew->Init( 0xf0000000 );
|
|
pNew->m_Position = vecHitPos + rslts[i].surface_normal * 2;
|
|
pNew->m_Type = MATERIAL_LIGHT_SPOT;
|
|
pNew->m_Color = vecColorToShoot;
|
|
pNew->m_Direction = rslts[i].surface_normal;
|
|
pNew->m_Theta = 0;
|
|
pNew->m_Phi = M_PI;
|
|
pNew->RecalculateDerivedValues();
|
|
pNew->m_Falloff = 5.0;
|
|
pNew->m_Range = 0.;
|
|
pNew->m_Attenuation0 = 0;
|
|
pNew->m_Attenuation1 = 0;
|
|
pNew->m_Attenuation2 = 1;
|
|
pNew->m_bDidIndirect = true;
|
|
l->m_TempChildren.AddToTail( pNew );
|
|
}
|
|
}
|
|
}
|
|
l->m_bDidIndirect = true;
|
|
}
|
|
|
|
CIncrementalLightInfo * l_info = l->m_pIncrementalInfo;
|
|
CSOAContainer *pGB = &m_GBuffer;
|
|
if ( l->m_bLowRes )
|
|
pGB = &m_GBufferLowRes;
|
|
|
|
l_info->SetContributionSize( pGB->NumCols(), pGB->NumRows() );
|
|
|
|
// figure out which lines need to be calculated
|
|
int nStartIteration = l_info->m_nNumLinesCalculated;
|
|
int nEndIteration =
|
|
min( l_info->m_nNumLinesCalculated + NUMBER_OF_LINES_TO_CALCULATE_PER_STEP, pGB->NumRows() - 1 );
|
|
|
|
|
|
float total_light;
|
|
CalculateForLightTask( nStartIteration, nEndIteration, l, &total_light, l_info );
|
|
l_info->m_flLastContribution = total_light;
|
|
l_info->m_fTotalContribution += total_light;
|
|
|
|
// throw away light array if no contribution ?????
|
|
if ( l_info->m_fTotalContribution == 0.0 )
|
|
l_info->m_CalculatedContribution.Purge();
|
|
else
|
|
{
|
|
l_info->m_nMostRecentNonZeroContributionTimeStamp = m_nContributionCounter;
|
|
}
|
|
l_info->m_nNumLinesCalculated = nEndIteration + 1;
|
|
if ( nEndIteration == pGB->NumRows() - 1 )
|
|
l_info->m_eIncrState = INCR_STATE_HAVE_FULL_RESULTS;
|
|
else
|
|
l_info->m_eIncrState = INCR_STATE_PARTIAL_RESULTS;
|
|
}
|
|
|
|
void CLightingPreviewThread::SendResultRendering( CSOAContainer &rsltBuffer )
|
|
{
|
|
Bitmap_t *ret_bm = new Bitmap_t;
|
|
#if 0
|
|
ret_bm->Init( m_GBuffer.NumCols(), m_GBuffer.NumRows(), IMAGE_FORMAT_RGBA8888 );
|
|
// lets copy into the output bitmap
|
|
for( int y = 0; y < ret_bm->m_nHeight; y++ )
|
|
{
|
|
float const *pRGBData = m_GBuffer.RowPtr<float>( GBUFFER_ATTR_NORMAL, y );
|
|
for( int x = 0; x < ret_bm->m_nWidth; x++ )
|
|
{
|
|
Vector color;
|
|
color.Init( 0.5 + 0.5 * pRGBData[0] , 0.5 + 0.5 * pRGBData[4], 0.5 + 0.5 * pRGBData[8] );
|
|
* ( ret_bm->GetPixel( x, y ) + 0 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.z, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 1 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.y, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 2 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.x, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 3 ) = 0;
|
|
pRGBData++;
|
|
if ( ( x & 3 ) == 3 )
|
|
pRGBData += 8;
|
|
}
|
|
}
|
|
#else
|
|
ret_bm->Init( rsltBuffer.NumCols(), rsltBuffer.NumRows(), IMAGE_FORMAT_RGBA8888 );
|
|
// lets copy into the output bitmap
|
|
for( int y = 0; y < ret_bm->Height(); y++ )
|
|
{
|
|
float const *pRGBData = rsltBuffer.RowPtr<float>( RSLT_BUFFER_RSLT_RGB, y );
|
|
for( int x = 0; x < ret_bm->Width(); x++ )
|
|
{
|
|
Vector color;
|
|
color.Init( pRGBData[0], pRGBData[4], pRGBData[8] );
|
|
* ( ret_bm->GetPixel( x, y ) + 0 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.z, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 1 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.y, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 2 ) = ( uint8 ) min( 255, ( 255.0 * pow( color.x, ( float ) ( 1 / 2.2 ) ) ) );
|
|
* ( ret_bm->GetPixel( x, y ) + 3 ) = 0;
|
|
pRGBData++;
|
|
if ( ( x & 3 ) == 3 )
|
|
pRGBData += 8;
|
|
}
|
|
}
|
|
#endif
|
|
MessageFromLPreview ret_msg( LPREVIEW_MSG_DISPLAY_RESULT );
|
|
// n_result_bms_queued++;
|
|
ret_msg.m_pBitmapToDisplay = ret_bm;
|
|
ret_msg.m_nBitmapGenerationCounter = m_nBitmapGenerationCounter;
|
|
g_LPreviewToHammerMsgQueue.QueueMessage( ret_msg );
|
|
}
|
|
|
|
|
|
|
|
// master side of lighting preview
|
|
|
|
unsigned LightingPreviewThreadFN( void * thread_start_arg )
|
|
{
|
|
CLightingPreviewThread LPreviewObject;
|
|
s_pThis = &LPreviewObject;
|
|
ThreadSetPriority( -2 ); // low
|
|
s_pThreadPool = CreateNewThreadPool();
|
|
|
|
CPUInformation pCPUInfo = GetCPUInformation();
|
|
ThreadPoolStartParams_t startParams;
|
|
startParams.nThreads = pCPUInfo.m_nPhysicalProcessors - 1;
|
|
s_nNumThreads = startParams.nThreads;
|
|
startParams.nStackSize = 1024*1024;
|
|
startParams.fDistribute = TRS_TRUE;
|
|
startParams.iThreadPriority = -2;
|
|
s_pThreadPool->Start( startParams );
|
|
|
|
LPreviewObject.Run();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void HandleLightingPreview( void )
|
|
{
|
|
if ( GetMainWnd()->m_pLightingPreviewOutputWindow && !GetMainWnd()->m_bLightingPreviewOutputWindowShowing )
|
|
{
|
|
delete GetMainWnd()->m_pLightingPreviewOutputWindow;
|
|
GetMainWnd()->m_pLightingPreviewOutputWindow = NULL;
|
|
}
|
|
|
|
// called during main loop
|
|
while ( g_LPreviewToHammerMsgQueue.MessageWaiting() )
|
|
{
|
|
MessageFromLPreview msg;
|
|
g_LPreviewToHammerMsgQueue.WaitMessage( & msg );
|
|
switch( msg.m_MsgType )
|
|
{
|
|
case LPREVIEW_MSG_DISPLAY_RESULT:
|
|
{
|
|
n_result_bms_queued--;
|
|
if ( g_pLPreviewOutputBitmap )
|
|
delete g_pLPreviewOutputBitmap;
|
|
g_pLPreviewOutputBitmap = NULL;
|
|
// if ( msg.m_nBitmapGenerationCounter == g_nBitmapGenerationCounter )
|
|
{
|
|
g_pLPreviewOutputBitmap = msg.m_pBitmapToDisplay;
|
|
if ( g_pLPreviewOutputBitmap && ( g_pLPreviewOutputBitmap->Width() > 10 ) )
|
|
{
|
|
SignalUpdate( EVTYPE_BITMAP_RECEIVED_FROM_LPREVIEW );
|
|
CLightingPreviewResultsWindow *w=GetMainWnd()->m_pLightingPreviewOutputWindow;
|
|
if ( !GetMainWnd()->m_bLightingPreviewOutputWindowShowing )
|
|
{
|
|
w = new CLightingPreviewResultsWindow;
|
|
GetMainWnd()->m_pLightingPreviewOutputWindow = w;
|
|
w->Create( GetMainWnd() );
|
|
GetMainWnd()->m_bLightingPreviewOutputWindowShowing = true;
|
|
}
|
|
if ( ! w->IsWindowVisible() )
|
|
w->ShowWindow( SW_SHOW );
|
|
RECT existing_rect;
|
|
w->GetClientRect( & existing_rect );
|
|
if (
|
|
( existing_rect.right != g_pLPreviewOutputBitmap->Width() - 1 ) ||
|
|
( existing_rect.bottom != g_pLPreviewOutputBitmap->Height() - 1 ) )
|
|
{
|
|
CRect myRect;
|
|
myRect.top = 0;
|
|
myRect.left = 0;
|
|
myRect.right = g_pLPreviewOutputBitmap->Width() - 1;
|
|
myRect.bottom = g_pLPreviewOutputBitmap->Height() - 1;
|
|
w->CalcWindowRect( & myRect );
|
|
w->SetWindowPos(
|
|
NULL, 0, 0,
|
|
myRect.Width(), myRect.Height(),
|
|
SWP_NOMOVE | SWP_NOZORDER );
|
|
}
|
|
|
|
w->Invalidate( false );
|
|
w->UpdateWindow();
|
|
}
|
|
}
|
|
// else
|
|
// delete msg.m_pBitmapToDisplay; // its old
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|