3DEngineRender.cpp 154 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. // Original file Copyright Crytek GMBH or its affiliates, used under license.
  13. // Description : rendering
  14. #include "Cry3DEngine_precompiled.h"
  15. #include "3dEngine.h"
  16. #include "ObjMan.h"
  17. #include "VisAreas.h"
  18. #include "Ocean.h"
  19. #include <Terrain/Bus/TerrainProviderBus.h>
  20. #include <AzFramework/Terrain/TerrainDataRequestBus.h>
  21. #include "DecalManager.h"
  22. #include "SkyLightManager.h"
  23. #include "CullBuffer.h"
  24. #include "LightEntity.h"
  25. #include "FogVolumeRenderNode.h"
  26. #include "ObjectsTree.h"
  27. #include "CloudsManager.h"
  28. #include "MatMan.h"
  29. #include "VolumeObjectRenderNode.h"
  30. #include "CryPath.h"
  31. #include "ILocalMemoryUsage.h"
  32. #include "BitFiddling.h"
  33. #include "ObjMan.h"
  34. #include "GeomCacheManager.h"
  35. #include "ClipVolumeManager.h"
  36. #include "ITimeOfDay.h"
  37. #include "Environment/OceanEnvironmentBus.h"
  38. #include "INetwork.h"
  39. #include <ThermalInfo.h>
  40. #ifdef GetCharWidth
  41. #undef GetCharWidth
  42. #endif //GetCharWidth
  43. #ifdef WIN32
  44. #include <CryWindows.h>
  45. #endif
  46. #include <AzFramework/IO/FileOperations.h>
  47. #include <AzFramework/StringFunc/StringFunc.h>
  48. #include <AzFramework/API/AtomActiveInterface.h>
  49. #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
  50. #include <AzCore/Interface/Interface.h>
  51. #include "../RenderDll/Common/Memory/VRAMDrillerBus.h"
  52. ////////////////////////////////////////////////////////////////////////////////////////
  53. // RenderScene
  54. ////////////////////////////////////////////////////////////////////////////////////////
  55. #define FREE_MEMORY_YELLOW_LIMIT (30)
  56. #define FREE_MEMORY_RED_LIMIT (10)
  57. #define DISPLAY_INFO_SCALE (1.25f)
  58. #define DISPLAY_INFO_SCALE_SMALL (1.1f)
  59. #define STEP_SMALL_DIFF (2.f)
  60. #if defined(WIN32) || defined(WIN64) || defined(MAC)
  61. // for panorama screenshots
  62. class CStitchedImage
  63. : public Cry3DEngineBase
  64. {
  65. public:
  66. CStitchedImage(C3DEngine& rEngine,
  67. const uint32 dwWidth,
  68. const uint32 dwHeight,
  69. const uint32 dwVirtualWidth,
  70. const uint32 dwVirtualHeight,
  71. const uint32 dwSliceCount,
  72. const f32 fTransitionSize,
  73. const bool bMetaData = false)
  74. : m_rEngine(rEngine)
  75. , m_dwWidth(dwWidth)
  76. , m_dwHeight(dwHeight)
  77. , m_fInvWidth(1.f / static_cast<f32>(dwWidth))
  78. , m_fInvHeight(1.f / static_cast<f32>(dwHeight))
  79. , m_dwVirtualWidth(dwVirtualWidth)
  80. , m_dwVirtualHeight(dwVirtualHeight)
  81. , m_fInvVirtualWidth(1.f / static_cast<f32>(dwVirtualWidth))
  82. , m_fInvVirtualHeight(1.f / static_cast<f32>(dwVirtualHeight))
  83. , m_nFileId(0)
  84. , m_dwSliceCount(dwSliceCount)
  85. , m_fHorizFOV(2 * gf_PI / dwSliceCount)
  86. , m_bFlipY(false)
  87. , m_fTransitionSize(fTransitionSize)
  88. , m_bMetaData(bMetaData)
  89. {
  90. assert(dwWidth);
  91. assert(dwHeight);
  92. m_RGB.resize(m_dwWidth * 3 * m_dwHeight);
  93. // ratio between width and height defines angle 1 (angle from mid to cylinder edges)
  94. float fVert1Frac = (2 * gf_PI * m_dwHeight) / m_dwWidth;
  95. // slice count defines angle 2
  96. float fHorizFrac = tanf(GetHorizFOVWithBorder() * 0.5f);
  97. float fVert2Frac = 2.0f * fHorizFrac / rEngine.GetRenderer()->GetWidth() * rEngine.GetRenderer()->GetHeight();
  98. // float fVert2Frac = 2.0f * fHorizFrac / rEngine.GetRenderer()->GetWidth() * rEngine.GetRenderer()->GetHeight();
  99. // the bigger one defines the needed angle
  100. float fVertFrac = max(fVert1Frac, fVert2Frac);
  101. // planar image becomes a barrel after projection and we need to zoom in to only utilize the usable part (inner rect)
  102. // this is not always needed - for quality with low slice count we could be save some quality here
  103. fVertFrac /= cosf(GetHorizFOVWithBorder() * 0.5f);
  104. // compute FOV from Frac
  105. float fVertFOV = 2 * atanf(0.5f * fVertFrac);
  106. m_fPanoramaShotVertFOV = fabsf(fVertFOV);
  107. CryLog("RenderFov = %f degrees (%f = max(%f,%f)*fix)", RAD2DEG(m_fPanoramaShotVertFOV), fVertFrac, fVert1Frac, fVert2Frac);
  108. Clear();
  109. }
  110. void Clear()
  111. {
  112. memset(&m_RGB[0], 0, m_dwWidth * m_dwHeight * 3);
  113. }
  114. // szDirectory + "/" + file_id + "." + extension
  115. // logs errors in the case there are problems
  116. bool SaveImage(const char* szDirectory)
  117. {
  118. assert(szDirectory);
  119. const char* szExtension = m_rEngine.GetCVars()->e_ScreenShotFileFormat->GetString();
  120. if (azstricmp(szExtension, "dds") != 0 &&
  121. azstricmp(szExtension, "tga") != 0 &&
  122. azstricmp(szExtension, "jpg") != 0)
  123. {
  124. gEnv->pLog->LogError("Format e_ScreenShotFileFormat='%s' not supported", szExtension);
  125. return false;
  126. }
  127. const char* sRequestedName = m_rEngine.GetCVars()->e_ScreenShotFileName->GetString();
  128. char sFileName[AZ_MAX_PATH_LEN];
  129. if (azstricmp(sRequestedName, "") != 0)
  130. {
  131. AZStd::string folderPath;
  132. AZStd::string fileName;
  133. AzFramework::StringFunc::Path::Split(sRequestedName, nullptr, &folderPath, &fileName);
  134. gEnv->pFileIO->CreatePath((AZStd::string("@user@/ScreenShots/") + folderPath).c_str());
  135. azsnprintf(sFileName, sizeof(sFileName), "@user@/ScreenShots/%s.%s", sRequestedName, szExtension);
  136. }
  137. else
  138. {
  139. azsnprintf(sFileName, sizeof(sFileName), "@user@/ScreenShots/%s", szDirectory);
  140. gEnv->pFileIO->CreatePath(sFileName);
  141. // find free file id
  142. for (;; )
  143. {
  144. azsnprintf(sFileName, sizeof(sFileName), "@user@/ScreenShots/%s/%.5d.%s", szDirectory, m_nFileId, szExtension);
  145. AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(sFileName, "rb");
  146. if (fileHandle == AZ::IO::InvalidHandle)
  147. {
  148. break; // file doesn't exist
  149. }
  150. gEnv->pCryPak->FClose(fileHandle);
  151. m_nFileId++;
  152. }
  153. }
  154. bool bOk;
  155. if (azstricmp(szExtension, "dds") == 0)
  156. {
  157. bOk = gEnv->pRenderer->WriteDDS((byte*)&m_RGB[0], m_dwWidth, m_dwHeight, 3, sFileName, eTF_BC3, 1);
  158. }
  159. else
  160. if (azstricmp(szExtension, "tga") == 0)
  161. {
  162. bOk = gEnv->pRenderer->WriteTGA((byte*)&m_RGB[0], m_dwWidth, m_dwHeight, sFileName, 24, 24);
  163. }
  164. else
  165. {
  166. bOk = gEnv->pRenderer->WriteJPG((byte*)&m_RGB[0], m_dwWidth, m_dwHeight, sFileName, 24);
  167. }
  168. if (!bOk)
  169. {
  170. gEnv->pLog->LogError("Failed to write '%s' (not supported on this platform?)", sFileName);
  171. }
  172. else //write meta data
  173. {
  174. if (m_bMetaData)
  175. {
  176. const f32 fSizeX = GetCVars()->e_ScreenShotMapSizeX;
  177. const f32 fSizeY = GetCVars()->e_ScreenShotMapSizeY;
  178. const f32 fTLX = GetCVars()->e_ScreenShotMapCenterX - fSizeX;
  179. const f32 fTLY = GetCVars()->e_ScreenShotMapCenterY - fSizeY;
  180. const f32 fBRX = GetCVars()->e_ScreenShotMapCenterX + fSizeX;
  181. const f32 fBRY = GetCVars()->e_ScreenShotMapCenterY + fSizeY;
  182. snprintf(sFileName, sizeof(sFileName), "@user@/ScreenShots/%s/%.5d.%s", szDirectory, m_nFileId, "xml");
  183. AZ::IO::HandleType metaFileHandle = gEnv->pCryPak->FOpen(sFileName, "wt");
  184. if (metaFileHandle != AZ::IO::InvalidHandle)
  185. {
  186. char sFileData[1024];
  187. snprintf(sFileData, sizeof(sFileData), "<MiniMap Filename=\"%.5d.%s\" startX=\"%f\" startY=\"%f\" endX=\"%f\" endY=\"%f\"/>",
  188. m_nFileId, szExtension, fTLX, fTLY, fBRX, fBRY);
  189. string data(sFileData);
  190. gEnv->pCryPak->FWrite(data.c_str(), data.size(), metaFileHandle);
  191. gEnv->pCryPak->FClose(metaFileHandle);
  192. }
  193. }
  194. }
  195. // reset filename when done so user doesn't overwrite other screen shots (unless they want to)
  196. // this is done here as there is no callback for standard screenshots to allow the user to clear
  197. // this when done with the screen shot, so I decided to just always clear it when done
  198. m_rEngine.GetCVars()->e_ScreenShotFileName->Set("");
  199. return bOk;
  200. }
  201. // rasterize rectangle
  202. // Arguments:
  203. // x0 - <x1, including
  204. // y0 - <y1, including
  205. // x1 - >x0, excluding
  206. // y1 - >y0, excluding
  207. void RasterizeRect(const uint32* pRGBAImage,
  208. const uint32 dwWidth,
  209. const uint32 dwHeight,
  210. const uint32 dwSliceX,
  211. const uint32 dwSliceY,
  212. const f32 fTransitionSize,
  213. const bool bFadeBordersX,
  214. const bool bFadeBordersY)
  215. {
  216. {
  217. //calculate rect inside the whole image
  218. const int32 OrgX0 = static_cast<int32>(static_cast<f32>((dwSliceX * dwWidth) * m_dwWidth) * m_fInvVirtualWidth);
  219. const int32 OrgY0 = static_cast<int32>(static_cast<f32>((dwSliceY * dwHeight) * m_dwHeight) * m_fInvVirtualHeight);
  220. const int32 OrgX1 = min(static_cast<int32>(static_cast<f32>(((dwSliceX + 1) * dwWidth) * m_dwWidth) * m_fInvVirtualWidth), static_cast<int32>(m_dwWidth)) - (m_rEngine.GetCVars()->e_ScreenShotDebug == 1 ? 1 : 0);
  221. const int32 OrgY1 = min(static_cast<int32>(static_cast<f32>(((dwSliceY + 1) * dwHeight) * m_dwHeight) * m_fInvVirtualHeight), static_cast<int32>(m_dwHeight)) - (m_rEngine.GetCVars()->e_ScreenShotDebug == 1 ? 1 : 0);
  222. //expand bounds for borderblending
  223. const int32 CenterX = (OrgX0 + OrgX1) / 2;
  224. const int32 CenterY = (OrgY0 + OrgY1) / 2;
  225. const int32 X0 = static_cast<int32>(static_cast<f32>(OrgX0 - CenterX) * (1.f + fTransitionSize)) + CenterX;
  226. const int32 Y0 = static_cast<int32>(static_cast<f32>(OrgY0 - CenterY) * (1.f + fTransitionSize)) + CenterY;
  227. const int32 X1 = static_cast<int32>(static_cast<f32>(OrgX1 - CenterX) * (1.f + fTransitionSize)) + CenterX;
  228. const int32 Y1 = static_cast<int32>(static_cast<f32>(OrgY1 - CenterY) * (1.f + fTransitionSize)) + CenterY;
  229. const f32 InvBlendX = 1.f / max(static_cast<f32>(X1 - OrgX1), 0.01f);//0.5 is here because the border is two times wider then the border of the single segment in total
  230. const f32 InvBlendY = 1.f / max(static_cast<f32>(Y1 - OrgY1), 0.01f);
  231. const int32 DebugScale = (m_rEngine.GetCVars()->e_ScreenShotDebug == 2) ? 65536 : 0;
  232. for (int32 y = max(Y0, 0); y < Y1 && y < (int)m_dwHeight; y++)
  233. {
  234. const f32 WeightY = bFadeBordersY ? min(1.f, static_cast<f32>(min(y - Y0, Y1 - y)) * InvBlendY) : 1.f;
  235. for (int32 x = max(X0, 0); x < X1 && x < (int)m_dwWidth; x++)
  236. {
  237. uint8* pDst = &m_RGB[m_bFlipY ? 3 * (x + (m_dwHeight - y - 1) * m_dwWidth) : 3 * (x + y * m_dwWidth)];
  238. const f32 WeightX = bFadeBordersX ? min(1.f, static_cast<f32>(min(x - X0, X1 - x)) * InvBlendX) : 1.f;
  239. GetBilinearFilteredBlend(static_cast<int32>(static_cast<f32>(x - X0) / static_cast<f32>(X1 - X0) * static_cast<f32>(dwWidth) * 16.f),
  240. static_cast<int32>(static_cast<f32>(y - Y0) / static_cast<f32>(Y1 - Y0) * static_cast<f32>(dwHeight) * 16.f),
  241. pRGBAImage, dwWidth, dwHeight,
  242. max(static_cast<int32>(WeightX * WeightY * 65536.f), DebugScale), pDst);
  243. }
  244. }
  245. }
  246. }
  247. void RasterizeCylinder(const uint32* pRGBAImage,
  248. const uint32 dwWidth,
  249. const uint32 dwHeight,
  250. const uint32 dwSlice,
  251. const bool bFadeBorders)
  252. {
  253. float fSrcAngleMin = GetSliceAngle(dwSlice - 1);
  254. float fFractionVert = tanf(m_fPanoramaShotVertFOV * 0.5f);
  255. float fFractionHoriz = fFractionVert * gEnv->pRenderer->GetCamera().GetProjRatio();
  256. float fInvFractionHoriz = 1.0f / fFractionHoriz;
  257. // for soft transition
  258. float fFadeOutFov = GetHorizFOVWithBorder();
  259. float fFadeInFov = GetHorizFOV();
  260. int x0 = 0, y0 = 0, x1 = m_dwWidth, y1 = m_dwHeight;
  261. float fScaleX = 1.0f / m_dwWidth;
  262. float fScaleY = 0.5f * fInvFractionHoriz / (m_dwWidth / (2 * gf_PI)) / dwHeight * dwWidth; // this value is not correctly computed yet - but using many slices reduced the problem
  263. if (m_bFlipY)
  264. {
  265. fScaleY = -fScaleY;
  266. }
  267. // it's more efficient to process colums than lines
  268. for (int x = x0; x < x1; ++x)
  269. {
  270. uint8* pDst = &m_RGB[3 * (x + y0 * m_dwWidth)];
  271. float fSrcX = x * fScaleX - 0.5f; // -0.5 .. 0.5
  272. float fSrcAngleX = fSrcAngleMin + 2 * gf_PI * fSrcX;
  273. if (fSrcAngleX > gf_PI)
  274. {
  275. fSrcAngleX -= 2 * gf_PI;
  276. }
  277. if (fSrcAngleX < -gf_PI)
  278. {
  279. fSrcAngleX += 2 * gf_PI;
  280. }
  281. if (fabs(fSrcAngleX) > fFadeOutFov * 0.5f)
  282. {
  283. continue; // clip away curved parts of the barrel
  284. }
  285. float fScrPosX = (tanf(fSrcAngleX) * 0.5f * fInvFractionHoriz + 0.5f) * dwWidth;
  286. // float fInvCosSrcX = 1.0f / cos(fSrcAngleX);
  287. float fInvCosSrcX = 1.0f / cosf(fSrcAngleX);
  288. if (fScrPosX >= 0 && fScrPosX <= (float)dwWidth) // this is an optimization - but it could be done even more efficient
  289. {
  290. if (fInvCosSrcX > 0) // don't render the viewer opposing direction
  291. {
  292. int iSrcPosX16 = (int)(fScrPosX * 16.0f);
  293. float fYOffset = 16 * 0.5f * dwHeight - 16 * 0.5f * m_dwHeight * fScaleY * fInvCosSrcX * dwHeight;
  294. float fYMul = 16 * fScaleY * fInvCosSrcX * dwHeight;
  295. float fSrcY = y0 * fYMul + fYOffset;
  296. uint32 dwLerp64k = 256 * 256 - 1;
  297. if (!bFadeBorders)
  298. {
  299. // first pass - every second image without soft borders
  300. for (int y = y0; y < y1; ++y, fSrcY += fYMul, pDst += m_dwWidth * 3)
  301. {
  302. GetBilinearFiltered(iSrcPosX16, (int)fSrcY, pRGBAImage, dwWidth, dwHeight, pDst);
  303. }
  304. }
  305. else
  306. {
  307. // second pass - do all the inbetween with soft borders
  308. float fOffSlice = fabs(fSrcAngleX / fFadeInFov) - 0.5f;
  309. if (fOffSlice < 0)
  310. {
  311. fOffSlice = 0; // no transition in this area
  312. }
  313. float fBorder = (fFadeOutFov - fFadeInFov) * 0.5f;
  314. if (fBorder < 0.001f)
  315. {
  316. fBorder = 0.001f; // we do not have border
  317. }
  318. float fFade = 1.0f - fOffSlice * fFadeInFov / fBorder;
  319. if (fFade < 0.0f)
  320. {
  321. fFade = 0.0f; // don't use this slice here
  322. }
  323. dwLerp64k = (uint32)(fFade * (256.0f * 256.0f - 1.0f)); // 0..64k
  324. if (dwLerp64k) // optimization
  325. {
  326. for (int y = y0; y < y1; ++y, fSrcY += fYMul, pDst += m_dwWidth * 3)
  327. {
  328. GetBilinearFilteredBlend(iSrcPosX16, (int)fSrcY, pRGBAImage, dwWidth, dwHeight, dwLerp64k, pDst);
  329. }
  330. }
  331. }
  332. }
  333. }
  334. }
  335. }
  336. // fast, rgb only
  337. static inline ColorB lerp(const ColorB x, const ColorB y, const uint32 a, const uint32 dwBase)
  338. {
  339. const int32 b = dwBase - a;
  340. const int32 RC = dwBase / 2;//rounding correction
  341. return ColorB(((int)x.r * b + (int)y.r * a + RC) / dwBase,
  342. ((int)x.g * b + (int)y.g * a + RC) / dwBase,
  343. ((int)x.b * b + (int)y.b * a + RC) / dwBase);
  344. }
  345. static inline ColorB Mul(const ColorB x, const int32 a, const int32 dwBase)
  346. {
  347. return ColorB(((int)x.r * (int)a) / dwBase,
  348. ((int)x.g * (int)a) / dwBase,
  349. ((int)x.b * (int)a) / dwBase);
  350. }
  351. static inline ColorB MadSaturate(const ColorB x, const int32 a, const int32 dwBase, const ColorB y)
  352. {
  353. const int32 MAX_COLOR = 0xff;
  354. const ColorB PreMuled = Mul(x, a, dwBase);
  355. return ColorB(min((int)PreMuled.r + (int)y.r, MAX_COLOR),
  356. min((int)PreMuled.g + (int)y.g, MAX_COLOR),
  357. min((int)PreMuled.b + (int)y.b, MAX_COLOR));
  358. }
  359. // bilinear filtering in fixpoint,
  360. // 4bit fractional part -> multiplier 16
  361. // --lookup outside of the image is not defined
  362. // lookups outside the image are now clamped, needed due to some float inaccuracy while rasterizing a rect-screenshot
  363. // Arguments:
  364. // iX16 - fX mul 16
  365. // iY16 - fY mul 16
  366. // result - [0]=red, [1]=green, [2]=blue
  367. static inline bool GetBilinearFilteredRaw(const int iX16, const int iY16,
  368. const uint32* pRGBAImage,
  369. const uint32 dwWidth, const uint32 dwHeight,
  370. ColorB& result)
  371. {
  372. int iLocalX = min(max(iX16 / 16, 0), static_cast<int>(dwWidth - 1));
  373. int iLocalY = min(max(iY16 / 16, 0), static_cast<int>(dwHeight - 1));
  374. int iLerpX = iX16 & 0xf; // 0..15
  375. int iLerpY = iY16 & 0xf; // 0..15
  376. ColorB colS[4];
  377. const uint32* pRGBA = &pRGBAImage[iLocalX + iLocalY * dwWidth];
  378. colS[0] = pRGBA[0];
  379. colS[1] = pRGBA[1];
  380. colS[2] = pRGBA[iLocalY + 1uL < dwHeight ? dwWidth : 0];
  381. colS[3] = pRGBA[(iLocalX + 1uL < dwWidth ? 1 : 0) + (iLocalY + 1uL < dwHeight ? dwWidth : 0)];
  382. ColorB colTop, colBottom;
  383. colTop = lerp(colS[0], colS[1], iLerpX, 16);
  384. colBottom = lerp(colS[2], colS[3], iLerpX, 16);
  385. result = lerp(colTop, colBottom, iLerpY, 16);
  386. return true;
  387. }
  388. // blend with background
  389. static inline bool GetBilinearFiltered(const int iX16, const int iY16,
  390. const uint32* pRGBAImage,
  391. const uint32 dwWidth, const uint32 dwHeight,
  392. uint8 result[3])
  393. {
  394. ColorB colFiltered;
  395. if (GetBilinearFilteredRaw(iX16, iY16, pRGBAImage, dwWidth, dwHeight, colFiltered))
  396. {
  397. result[0] = colFiltered.r;
  398. result[1] = colFiltered.g;
  399. result[2] = colFiltered.b;
  400. return true;
  401. }
  402. return false;
  403. }
  404. static inline bool GetBilinearFilteredBlend(const int iX16, const int iY16,
  405. const uint32* pRGBAImage,
  406. const uint32 dwWidth, const uint32 dwHeight,
  407. const uint32 dwLerp64k,
  408. uint8 result[3])
  409. {
  410. ColorB colFiltered;
  411. if (GetBilinearFilteredRaw(iX16, iY16, pRGBAImage, dwWidth, dwHeight, colFiltered))
  412. {
  413. ColorB colRet = lerp(ColorB(result[0], result[1], result[2]), colFiltered, dwLerp64k, 256 * 256);
  414. result[0] = colRet.r;
  415. result[1] = colRet.g;
  416. result[2] = colRet.b;
  417. return true;
  418. }
  419. return false;
  420. }
  421. static inline bool GetBilinearFilteredAdd(const int iX16, const int iY16,
  422. const uint32* pRGBAImage,
  423. const uint32 dwWidth, const uint32 dwHeight,
  424. const uint32 dwLerp64k,
  425. uint8 result[3])
  426. {
  427. ColorB colFiltered;
  428. if (GetBilinearFilteredRaw(iX16, iY16, pRGBAImage, dwWidth, dwHeight, colFiltered))
  429. {
  430. ColorB colRet = MadSaturate(colFiltered, dwLerp64k, 256 * 256, ColorB(result[0], result[1], result[2]));
  431. result[0] = colRet.r;
  432. result[1] = colRet.g;
  433. result[2] = colRet.b;
  434. return true;
  435. }
  436. return false;
  437. }
  438. float GetSliceAngle(const uint32 dwSlice) const
  439. {
  440. uint32 dwAlternatingSlice = (dwSlice * 2) % m_dwSliceCount;
  441. float fAngleStep = m_fHorizFOV;
  442. float fRet = fAngleStep * dwAlternatingSlice;
  443. if (dwSlice * 2 >= m_dwSliceCount)
  444. {
  445. fRet += fAngleStep;
  446. }
  447. return fRet;
  448. }
  449. float GetHorizFOV() const
  450. {
  451. return m_fHorizFOV;
  452. }
  453. float GetHorizFOVWithBorder() const
  454. {
  455. return m_fHorizFOV * (1.0f + m_fTransitionSize);
  456. }
  457. void* GetBuffer(){ return &m_RGB[0]; }
  458. uint32 GetWidth() { return m_dwWidth; }
  459. uint32 GetHeight() { return m_dwHeight; }
  460. //private: // -------------------------------------------------------------------
  461. uint32 m_dwWidth; // >0
  462. uint32 m_dwHeight; // >0
  463. f32 m_fInvWidth; // >0
  464. f32 m_fInvHeight; // >0
  465. uint32 m_dwVirtualWidth; // >0
  466. uint32 m_dwVirtualHeight; // >0
  467. f32 m_fInvVirtualWidth; // >0
  468. f32 m_fInvVirtualHeight; // >0
  469. std::vector<uint8> m_RGB; // [channel + x*3 + m_dwWidth*3*y], channel=0..2, x<m_dwWidth, y<m_dwHeight, no alpha channel to occupy less memory
  470. uint32 m_nFileId; // counts up until it finds free file id
  471. bool m_bFlipY; // might be useful for some image formats
  472. bool m_bMetaData; // output additional metadata
  473. float m_fPanoramaShotVertFOV; // -1 means not set yet - in radians
  474. private:
  475. uint32 m_dwSliceCount; //
  476. C3DEngine& m_rEngine; //
  477. float m_fHorizFOV; // - in radians
  478. float m_fTransitionSize; // [0..1], 0=no transition, 1.0=full transition
  479. };
  480. #endif
  481. enum EScreenShotType
  482. {
  483. ESST_NONE = 0,
  484. ESST_HIGHRES = 1,
  485. ESST_PANORAMA,
  486. ESST_MAP_DELAYED,
  487. ESST_MAP,
  488. ESST_SWMAP,
  489. ESST_SWMAP_DELAYED,
  490. };
  491. void C3DEngine::ScreenshotDispatcher([[maybe_unused]] const int nRenderFlags, [[maybe_unused]] const SRenderingPassInfo& passInfo)
  492. {
  493. #if defined(WIN32) || defined(WIN64) || defined(MAC)
  494. CStitchedImage* pStitchedImage = 0;
  495. const uint32 dwPanWidth = max(1, GetCVars()->e_ScreenShotWidth);
  496. const uint32 dwPanHeight = max(1, GetCVars()->e_ScreenShotHeight);
  497. const f32 fTransitionSize = min(1.f, abs(GetCVars()->e_ScreenShotQuality) * 0.01f);
  498. const uint32 widthSlices = (dwPanWidth + GetRenderer()->GetWidth() - 1) / GetRenderer()->GetWidth();
  499. const uint32 heightSlices = (dwPanHeight + GetRenderer()->GetHeight() - 1) / GetRenderer()->GetHeight();
  500. uint32 MinSlices = max(widthSlices, heightSlices);
  501. MinSlices = max(MinSlices, (uint32)GetCVars()->e_ScreenShotMinSlices);
  502. const uint32 dwVirtualWidth = GetRenderer()->GetWidth() * MinSlices;
  503. const uint32 dwVirtualHeight = GetRenderer()->GetHeight() * MinSlices;
  504. GetRenderer()->StartScreenShot(GetCVars()->e_ScreenShot);
  505. switch (abs(GetCVars()->e_ScreenShot))
  506. {
  507. case ESST_HIGHRES:
  508. GetConsole()->ShowConsole(false);
  509. MinSlices = max(MinSlices, 1u);
  510. pStitchedImage = new CStitchedImage(*this, dwPanWidth, dwPanHeight, dwVirtualWidth, dwVirtualHeight, MinSlices, fTransitionSize);
  511. ScreenShotHighRes(pStitchedImage, nRenderFlags, passInfo, MinSlices, fTransitionSize);
  512. pStitchedImage->SaveImage("HiRes");
  513. pStitchedImage->Clear(); // good for debugging
  514. delete pStitchedImage;
  515. if (GetCVars()->e_ScreenShot > 0) // <0 is used for multiple frames (videos)
  516. {
  517. GetCVars()->e_ScreenShot = 0;
  518. }
  519. break;
  520. case ESST_PANORAMA:
  521. GetConsole()->ShowConsole(false);
  522. // Panorama screenshots will exhibit artifacts if insufficient slices are used to render them
  523. // 20 slices yields great quality.
  524. MinSlices = max(MinSlices, 20u);
  525. pStitchedImage = new CStitchedImage(*this, dwPanWidth, dwPanHeight, dwVirtualWidth, dwVirtualHeight, MinSlices, fTransitionSize);
  526. ScreenShotPanorama(pStitchedImage, nRenderFlags, passInfo, MinSlices, fTransitionSize);
  527. pStitchedImage->SaveImage("Panorama");
  528. pStitchedImage->Clear(); // good for debugging
  529. delete pStitchedImage;
  530. if (GetCVars()->e_ScreenShot > 0) // <0 is used for multiple frames (videos)
  531. {
  532. GetCVars()->e_ScreenShot = 0;
  533. }
  534. break;
  535. case ESST_MAP_DELAYED:
  536. {
  537. GetCVars()->e_ScreenShot = sgn(GetCVars()->e_ScreenShot) * ESST_MAP; // sgn() to keep sign bit , <0 is used for multiple frames (videos)
  538. }
  539. break;
  540. case ESST_SWMAP_DELAYED:
  541. {
  542. GetCVars()->e_ScreenShot = sgn(GetCVars()->e_ScreenShot) * ESST_SWMAP; // sgn() to keep sign bit , <0 is used for multiple frames (videos)
  543. }
  544. break;
  545. case ESST_SWMAP:
  546. case ESST_MAP:
  547. {
  548. static const unsigned int nMipMapSnapshotSize = 2048;
  549. GetRenderer()->ChangeViewport(0, 0, nMipMapSnapshotSize, nMipMapSnapshotSize);
  550. uint32 TmpHeight, TmpWidth, TmpVirtualHeight, TmpVirtualWidth;
  551. TmpHeight = TmpWidth = TmpVirtualHeight = TmpVirtualWidth = 1;
  552. while ((TmpHeight << 1) <= dwPanHeight)
  553. {
  554. TmpHeight <<= 1;
  555. }
  556. while ((TmpWidth << 1) <= dwPanWidth)
  557. {
  558. TmpWidth <<= 1;
  559. }
  560. const uint32 TmpMinSlices = max(max(1, GetCVars()->e_ScreenShotMinSlices),
  561. max(static_cast<int>((TmpWidth + nMipMapSnapshotSize - 1) / nMipMapSnapshotSize),
  562. static_cast<int>((TmpHeight + nMipMapSnapshotSize - 1) / nMipMapSnapshotSize)));
  563. while ((TmpVirtualHeight << 1) <= TmpMinSlices * nMipMapSnapshotSize)
  564. {
  565. TmpVirtualHeight <<= 1;
  566. }
  567. while ((TmpVirtualWidth << 1) <= TmpMinSlices * nMipMapSnapshotSize)
  568. {
  569. TmpVirtualWidth <<= 1;
  570. }
  571. GetConsole()->ShowConsole(false);
  572. pStitchedImage = new CStitchedImage(*this, TmpWidth, TmpHeight, TmpVirtualWidth, TmpVirtualHeight, TmpMinSlices, fTransitionSize, true);
  573. ScreenShotMap(pStitchedImage, nRenderFlags, passInfo, TmpMinSlices, fTransitionSize);
  574. if (abs(GetCVars()->e_ScreenShot) == ESST_MAP)
  575. {
  576. pStitchedImage->SaveImage("Map");
  577. }
  578. if (m_pScreenshotCallback)
  579. {
  580. const f32 fSizeX = GetCVars()->e_ScreenShotMapSizeX;
  581. const f32 fSizeY = GetCVars()->e_ScreenShotMapSizeY;
  582. const f32 fTLX = GetCVars()->e_ScreenShotMapCenterX - fSizeX;
  583. const f32 fTLY = GetCVars()->e_ScreenShotMapCenterY - fSizeY;
  584. const f32 fBRX = GetCVars()->e_ScreenShotMapCenterX + fSizeX;
  585. const f32 fBRY = GetCVars()->e_ScreenShotMapCenterY + fSizeY;
  586. m_pScreenshotCallback->SendParameters(pStitchedImage->GetBuffer(), pStitchedImage->GetWidth(), pStitchedImage->GetHeight(), fTLX, fTLY, fBRX, fBRY);
  587. }
  588. pStitchedImage->Clear(); // good for debugging
  589. delete pStitchedImage;
  590. }
  591. if (GetCVars()->e_ScreenShot > 0) // <0 is used for multiple frames (videos)
  592. {
  593. GetCVars()->e_ScreenShot = 0;
  594. }
  595. break;
  596. default:
  597. GetCVars()->e_ScreenShot = 0;
  598. }
  599. GetRenderer()->EndScreenShot(GetCVars()->e_ScreenShot);
  600. #endif //#if defined(WIN32) || defined(WIN64)
  601. }
  602. struct SDebugFrustrum
  603. {
  604. Vec3 m_vPos[8];
  605. const char* m_szName;
  606. CTimeValue m_TimeStamp;
  607. ColorB m_Color;
  608. float m_fQuadDist; // < 0 if not used
  609. };
  610. static StaticInstance<std::vector<SDebugFrustrum>> g_DebugFrustrums;
  611. void C3DEngine::DebugDraw_Draw()
  612. {
  613. #ifndef _RELEASE
  614. if (m_DebugDrawListMgr.IsEnabled())
  615. {
  616. m_DebugDrawListMgr.Update();
  617. }
  618. CTimeValue CurrentTime = gEnv->pTimer->GetFrameStartTime();
  619. IRenderAuxGeom* pAux = GetRenderer()->GetIRenderAuxGeom();
  620. SAuxGeomRenderFlags oldFlags = pAux->GetRenderFlags();
  621. SAuxGeomRenderFlags newFlags;
  622. newFlags.SetAlphaBlendMode(e_AlphaBlended);
  623. newFlags.SetCullMode(e_CullModeNone);
  624. newFlags.SetDepthWriteFlag(e_DepthWriteOff);
  625. pAux->SetRenderFlags(newFlags);
  626. std::vector<SDebugFrustrum>::iterator it;
  627. for (it = g_DebugFrustrums.begin(); it != g_DebugFrustrums.end(); )
  628. {
  629. SDebugFrustrum& ref = *it;
  630. float fRatio = (CurrentTime - ref.m_TimeStamp).GetSeconds() * 2.0f;
  631. if (fRatio > 1.0f)
  632. {
  633. it = g_DebugFrustrums.erase(it);
  634. continue;
  635. }
  636. vtx_idx pnInd[8] = { 0, 4, 1, 5, 2, 6, 3, 7 };
  637. float fRadius = ((ref.m_vPos[0] + ref.m_vPos[1] + ref.m_vPos[2] + ref.m_vPos[3]) - (ref.m_vPos[4] + ref.m_vPos[5] + ref.m_vPos[6] + ref.m_vPos[7])).GetLength() * 0.25f;
  638. float fDistance = min(fRadius, 33.0f); // in meters
  639. float fRenderRatio = fRatio * fDistance / fRadius;
  640. if (ref.m_fQuadDist > 0)
  641. {
  642. fRenderRatio = ref.m_fQuadDist / fRadius;
  643. }
  644. Vec3 vPos[4];
  645. for (uint32 i = 0; i < 4; ++i)
  646. {
  647. vPos[i] = ref.m_vPos[i] * fRenderRatio + ref.m_vPos[i + 4] * (1.0f - fRenderRatio);
  648. }
  649. Vec3 vMid = (vPos[0] + vPos[1] + vPos[2] + vPos[3]) * 0.25f;
  650. ColorB col = ref.m_Color;
  651. if (ref.m_fQuadDist <= 0)
  652. {
  653. for (uint32 i = 0; i < 4; ++i)
  654. {
  655. vPos[i] = vPos[i] * 0.95f + vMid * 0.05f;
  656. }
  657. // quad
  658. if (ref.m_fQuadDist != -999.f)
  659. {
  660. pAux->DrawTriangle(vPos[0], col, vPos[2], col, vPos[1], col);
  661. pAux->DrawTriangle(vPos[2], col, vPos[0], col, vPos[3], col);
  662. }
  663. // projection lines
  664. pAux->DrawLines(ref.m_vPos, 8, pnInd, 2, RGBA8(0xff, 0xff, 0x1f, 0xff));
  665. pAux->DrawLines(ref.m_vPos, 8, pnInd + 2, 2, RGBA8(0xff, 0xff, 0x1f, 0xff));
  666. pAux->DrawLines(ref.m_vPos, 8, pnInd + 4, 2, RGBA8(0xff, 0xff, 0x1f, 0xff));
  667. pAux->DrawLines(ref.m_vPos, 8, pnInd + 6, 2, RGBA8(0xff, 0xff, 0x1f, 0xff));
  668. }
  669. else
  670. {
  671. // rectangle
  672. pAux->DrawPolyline(vPos, 4, true, RGBA8(0xff, 0xff, 0x1f, 0xff));
  673. }
  674. ++it;
  675. }
  676. pAux->SetRenderFlags(oldFlags);
  677. if (GetCVars()->e_DebugDraw == 16)
  678. {
  679. DebugDraw_UpdateDebugNode();
  680. }
  681. else
  682. {
  683. GetRenderer()->SetDebugRenderNode(NULL);
  684. }
  685. #endif //_RELEASE
  686. }
  687. void C3DEngine::DebugDraw_UpdateDebugNode()
  688. {
  689. #ifndef _RELEASE
  690. #endif //_RELEASE
  691. }
  692. void C3DEngine::RenderWorld(const int nRenderFlags, const SRenderingPassInfo& passInfo, const char* szDebugName)
  693. {
  694. AZ_TRACE_METHOD();
  695. if (nRenderFlags & SHDF_ALLOW_AO)
  696. {
  697. SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::OnFrameStart, passInfo);
  698. }
  699. if (m_szLevelFolder[0] != 0)
  700. {
  701. m_nFramesSinceLevelStart++;
  702. }
  703. assert(szDebugName);
  704. if (!GetCVars()->e_Render)
  705. {
  706. return;
  707. }
  708. IF (!m_bEditor && (m_bInShutDown || m_bInUnload) && !GetRenderer()->IsPost3DRendererEnabled(), 0)
  709. {
  710. // Do not render during shutdown/unloading (should never reach here, unless something wrong with game/editor code)
  711. return;
  712. }
  713. #ifdef ENABLE_LW_PROFILERS
  714. int64 renderStart = CryGetTicks();
  715. #endif
  716. FUNCTION_PROFILER_3DENGINE;
  717. if (GetCVars()->e_ScreenShot)
  718. {
  719. ScreenshotDispatcher(nRenderFlags, passInfo);
  720. // screenshots can mess up the frame ids, be safe and recreate the rendering passinfo object after a screenshot
  721. const_cast<SRenderingPassInfo&>(passInfo) = SRenderingPassInfo::CreateGeneralPassRenderingInfo(passInfo.GetCamera());
  722. }
  723. if (GetCVars()->e_DefaultMaterial)
  724. {
  725. _smart_ptr<IMaterial> pMat = GetMaterialManager()->LoadMaterial("Materials/material_default");
  726. _smart_ptr<IMaterial> pTerrainMat = GetMaterialManager()->LoadMaterial("Materials/material_terrain_default");
  727. GetRenderer()->SetDefaultMaterials(pMat, pTerrainMat);
  728. }
  729. else
  730. {
  731. GetRenderer()->SetDefaultMaterials(NULL, NULL);
  732. }
  733. // skip rendering if camera is invalid
  734. if (IsCameraAnd3DEngineInvalid(passInfo, szDebugName))
  735. {
  736. return;
  737. }
  738. // this will also set the camera in passInfo for the General Pass (done here to support e_camerafreeze)
  739. UpdateRenderingCamera(szDebugName, passInfo);
  740. RenderInternal(nRenderFlags, passInfo, szDebugName);
  741. #if !defined(_RELEASE)
  742. PrintDebugInfo(passInfo);
  743. #endif
  744. }
  745. void C3DEngine::RenderInternal(const int nRenderFlags, const SRenderingPassInfo& passInfo, [[maybe_unused]] const char* szDebugName)
  746. {
  747. assert(m_pObjManager);
  748. if (AZ::Interface<AzFramework::AtomActiveInterface>::Get())
  749. {
  750. GetRenderer()->EF_EndEf3D(
  751. IsShadersSyncLoad() ? (nRenderFlags | SHDF_NOASYNC | SHDF_STREAM_SYNC) : nRenderFlags,
  752. GetObjManager()->GetUpdateStreamingPrioriryRoundId(),
  753. GetObjManager()->GetUpdateStreamingPrioriryRoundIdFast(),
  754. passInfo);
  755. }
  756. else
  757. {
  758. UpdatePreRender(passInfo);
  759. RenderScene(nRenderFlags, passInfo);
  760. UpdatePostRender(passInfo);
  761. }
  762. }
  763. void C3DEngine::PreWorldStreamUpdate(const CCamera& cam)
  764. {
  765. if (m_szLevelFolder[0] != 0)
  766. {
  767. m_nStreamingFramesSinceLevelStart++;
  768. }
  769. // force preload terrain data if camera was teleported more than 32 meters
  770. if (!IsAreaActivationInUse() || m_bLayersActivated)
  771. {
  772. float fDistance = m_vPrevMainFrameCamPos.GetDistance(cam.GetPosition());
  773. if (m_vPrevMainFrameCamPos != Vec3(-1000000.f, -1000000.f, -1000000.f))
  774. {
  775. m_vAverageCameraMoveDir = m_vAverageCameraMoveDir * .75f + (cam.GetPosition() - m_vPrevMainFrameCamPos) / max(0.01f, GetTimer()->GetFrameTime()) * .25f;
  776. if (m_vAverageCameraMoveDir.GetLength() > 10.f)
  777. {
  778. m_vAverageCameraMoveDir.SetLength(10.f);
  779. }
  780. float fNewSpeed = fDistance / max(0.001f, gEnv->pTimer->GetFrameTime());
  781. if (fNewSpeed > m_fAverageCameraSpeed)
  782. {
  783. m_fAverageCameraSpeed = fNewSpeed * .20f + m_fAverageCameraSpeed * .80f;
  784. }
  785. else
  786. {
  787. m_fAverageCameraSpeed = fNewSpeed * .02f + m_fAverageCameraSpeed * .98f;
  788. }
  789. m_fAverageCameraSpeed = CLAMP(m_fAverageCameraSpeed, 0, 10.f);
  790. }
  791. // Adjust streaming mip bias based on camera speed and depending on installed on HDD or not
  792. bool bStreamingFromHDD = gEnv->pSystem->GetStreamEngine()->IsStreamDataOnHDD();
  793. if (GetCVars()->e_StreamAutoMipFactorSpeedThreshold)
  794. {
  795. if (m_fAverageCameraSpeed > GetCVars()->e_StreamAutoMipFactorSpeedThreshold)
  796. {
  797. GetRenderer()->SetTexturesStreamingGlobalMipFactor(bStreamingFromHDD ? GetCVars()->e_StreamAutoMipFactorMax * .5f : GetCVars()->e_StreamAutoMipFactorMax);
  798. }
  799. else
  800. {
  801. GetRenderer()->SetTexturesStreamingGlobalMipFactor(bStreamingFromHDD ? GetCVars()->e_StreamAutoMipFactorMin * .5f : GetCVars()->e_StreamAutoMipFactorMin);
  802. }
  803. }
  804. else
  805. {
  806. if (bStreamingFromHDD)
  807. {
  808. GetRenderer()->SetTexturesStreamingGlobalMipFactor(0);
  809. }
  810. else
  811. {
  812. GetRenderer()->SetTexturesStreamingGlobalMipFactor(GetCVars()->e_StreamAutoMipFactorMaxDVD);
  813. }
  814. }
  815. if (GetCVars()->e_AutoPrecacheCameraJumpDist && fDistance > GetCVars()->e_AutoPrecacheCameraJumpDist)
  816. {
  817. m_bContentPrecacheRequested = true;
  818. // Invalidate existing precache info
  819. m_pObjManager->IncrementUpdateStreamingPrioriryRoundIdFast(8);
  820. m_pObjManager->IncrementUpdateStreamingPrioriryRoundId(8);
  821. }
  822. m_vPrevMainFrameCamPos = cam.GetPosition();
  823. }
  824. }
  825. void C3DEngine::WorldStreamUpdate()
  826. {
  827. #if defined(STREAMENGINE_ENABLE_STATS)
  828. static uint32 nCurrentRequestCount = 0;
  829. static uint64 nCurrentBytesRead = 0;
  830. if (m_nStreamingFramesSinceLevelStart == 1)
  831. {
  832. // store current streaming stats
  833. SStreamEngineStatistics& fullStats = gEnv->pSystem->GetStreamEngine()->GetStreamingStatistics();
  834. nCurrentBytesRead = fullStats.nTotalBytesRead;
  835. nCurrentRequestCount = fullStats.nTotalRequestCount;
  836. }
  837. #endif
  838. static float fTestStartTime = 0;
  839. if (m_nStreamingFramesSinceLevelStart == 1)
  840. {
  841. fTestStartTime = GetCurAsyncTimeSec();
  842. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME, 0, 0);
  843. }
  844. // Simple streaming performance test: Wait until all startup texture streaming jobs finish and print a message
  845. if (!m_bEditor)
  846. {
  847. if (!m_bPreCacheEndEventSent)
  848. {
  849. IStreamEngine* pSE = gEnv->pSystem->GetStreamEngine();
  850. SStreamEngineOpenStats openStats;
  851. pSE->GetStreamingOpenStatistics(openStats);
  852. bool bStarted =
  853. (openStats.nOpenRequestCountByType[eStreamTaskTypeTexture] > 0) ||
  854. (openStats.nOpenRequestCountByType[eStreamTaskTypeGeometry] > 0);
  855. float fTime = GetCurAsyncTimeSec() - fTestStartTime;
  856. switch (m_nStreamingFramesSinceLevelStart)
  857. {
  858. case 1:
  859. pSE->PauseStreaming(true, (1 << eStreamTaskTypeTexture) | (1 << eStreamTaskTypeGeometry));
  860. break;
  861. case 4:
  862. pSE->PauseStreaming(false, (1 << eStreamTaskTypeGeometry));
  863. break;
  864. case 8:
  865. pSE->PauseStreaming(false, (1 << eStreamTaskTypeTexture));
  866. break;
  867. }
  868. int nGlobalSystemState = gEnv->pSystem->GetSystemGlobalState();
  869. if ((nGlobalSystemState != ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_COMPLETE && (!bStarted || fTime >= 10.0f)) && m_nStreamingFramesSinceLevelStart > 16)
  870. {
  871. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_COMPLETE);
  872. if (!bStarted)
  873. {
  874. PrintMessage("Textures startup streaming finished in %.1f sec", fTime);
  875. }
  876. else
  877. {
  878. PrintMessage("Textures startup streaming timed out after %.1f sec", fTime);
  879. }
  880. m_fTimeStateStarted = fTime;
  881. }
  882. if (nGlobalSystemState == ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_COMPLETE && (fTime - m_fTimeStateStarted) > 0.4f)
  883. {
  884. pSE->PauseStreaming(false, (1 << eStreamTaskTypeTexture) | (1 << eStreamTaskTypeGeometry));
  885. m_bPreCacheEndEventSent = true;
  886. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_RUNNING);
  887. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_END, 0, 0);
  888. fTestStartTime = 0.f;
  889. #if defined(STREAMENGINE_ENABLE_STATS)
  890. SStreamEngineStatistics& fullStats = pSE->GetStreamingStatistics();
  891. uint64 nBytesRead = fullStats.nTotalBytesRead - nCurrentBytesRead;
  892. uint32 nRequestCount = fullStats.nTotalRequestCount - nCurrentRequestCount;
  893. uint32 nOverallFileReadKB = (uint32)(nBytesRead / 1024);
  894. uint32 nOverallFileReadNum = nRequestCount;
  895. uint32 nBlockSize = (uint32)(nBytesRead / max((uint32)1, nRequestCount));
  896. float fReadBandwidthMB = (float)fullStats.nTotalSessionReadBandwidth / (1024 * 1024);
  897. PrintMessage("Average block size: %d KB, Average throughput: %.1f MB/sec, Jobs processed: %d (%.1f MB), File IO Bandwidth: %.2fMB/s",
  898. (nBlockSize) / 1024, (float)(nOverallFileReadKB / max(fTime, 1.f)) / 1024.f,
  899. nOverallFileReadNum, (float)nOverallFileReadKB / 1024.f,
  900. fReadBandwidthMB);
  901. if (GetCVars()->e_StreamSaveStartupResultsIntoXML)
  902. {
  903. const char* testResultsFile = "@cache@/TestResults/Streaming_Level_Start_Throughput.xml";
  904. AZ::IO::HandleType resultsFile = gEnv->pCryPak->FOpen(testResultsFile, "wb");
  905. if (resultsFile != AZ::IO::InvalidHandle)
  906. {
  907. AZ::IO::Print(resultsFile,
  908. "<phase name=\"Streaming_Level_Start_Throughput\">\n"
  909. "<metrics name=\"Streaming\">\n"
  910. "<metric name=\"Duration_Sec\" value=\"%.1f\"/>\n"
  911. "<metric name=\"BlockSize_KB\" value=\"%d\"/>\n"
  912. "<metric name=\"Throughput_MB_Sec\" value=\"%.1f\"/>\n"
  913. "<metric name=\"Jobs_Num\" value=\"%d\"/>\n"
  914. "<metric name=\"Read_MB\" value=\"%.1f\"/>\n"
  915. "</metrics>\n"
  916. "</phase>\n",
  917. fTime,
  918. (nOverallFileReadKB / nOverallFileReadNum),
  919. (float)nOverallFileReadKB / max(fTime, 1.f) / 1024.f,
  920. nOverallFileReadNum,
  921. (float)nOverallFileReadKB / 1024.f);
  922. gEnv->pCryPak->FClose(resultsFile);
  923. }
  924. }
  925. #endif
  926. // gEnv->pCryPak->GetFileReadSequencer()->EndSection(); // STREAMING
  927. }
  928. else if (m_szLevelFolder[0])
  929. {
  930. ProposeContentPrecache();
  931. }
  932. }
  933. }
  934. else
  935. {
  936. if (!m_bPreCacheEndEventSent && m_nStreamingFramesSinceLevelStart == 4)
  937. {
  938. m_bPreCacheEndEventSent = true;
  939. gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_RUNNING);
  940. gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_END, 0, 0);
  941. }
  942. }
  943. }
  944. void C3DEngine::PrintDebugInfo(const SRenderingPassInfo& passInfo)
  945. {
  946. if (GetCVars()->e_DebugDraw)
  947. {
  948. f32 fColor[4] = {1, 1, 0, 1};
  949. float fYLine = 8.0f, fYStep = 20.0f;
  950. GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, fColor, false, "e_DebugDraw = %d", GetCVars()->e_DebugDraw);
  951. const char* szMode = "";
  952. switch (static_cast<int>(GetCVars()->e_DebugDraw))
  953. {
  954. case -1:
  955. szMode = "Showing bounding boxes";
  956. break;
  957. case 1:
  958. szMode = "bounding boxes, name of the used cgf, polycount, used LOD";
  959. break;
  960. case -2:
  961. case 2:
  962. szMode = "color coded polygon count(red,yellow,green,turqoise, blue)";
  963. break;
  964. case -3:
  965. szMode = "show color coded LODs count, flashing color indicates LOD.";
  966. break;
  967. case 3:
  968. szMode = "show color coded LODs count, flashing color indicates LOD.\nFormat: (Current LOD [Min LOD; Max LOD] (LOD Ratio / Distance to camera)";
  969. break;
  970. case -4:
  971. case 4:
  972. szMode = "object texture memory usage in KB";
  973. break;
  974. case -5:
  975. case 5:
  976. szMode = "number of render materials (color coded)";
  977. break;
  978. case 6:
  979. szMode = "ambient color (R,G,B,A)";
  980. break;
  981. case 7:
  982. szMode = "triangle count, number of render materials, texture memory in KB";
  983. break;
  984. case 8:
  985. szMode = "Free slot";
  986. break;
  987. case 9:
  988. szMode = "Free slot";
  989. break;
  990. case 10:
  991. szMode = "Deprecated option, use \"r_showlines 2\" instead";
  992. break;
  993. case 11:
  994. szMode = "Free slot";
  995. break;
  996. case 12:
  997. szMode = "Free slot";
  998. break;
  999. case 13:
  1000. szMode = "occlusion amount (used during AO computations)";
  1001. break;
  1002. // case 14: szMode="";break;
  1003. case 15:
  1004. szMode = "display helpers";
  1005. break;
  1006. case 16:
  1007. szMode = "Debug Gun";
  1008. break;
  1009. case 17:
  1010. szMode = "streaming: buffer sizes (black: geometry, blue: texture)";
  1011. if (gEnv->pLocalMemoryUsage)
  1012. {
  1013. gEnv->pLocalMemoryUsage->OnRender(GetRenderer(), &passInfo.GetCamera());
  1014. }
  1015. break;
  1016. case 18:
  1017. szMode = "Free slot";
  1018. break;
  1019. case 19:
  1020. szMode = "physics proxy triangle count";
  1021. break;
  1022. case 20:
  1023. szMode = "Character attachments texture memory usage";
  1024. break;
  1025. case 21:
  1026. szMode = "Display animated objects distance to camera";
  1027. break;
  1028. case -22:
  1029. case 22:
  1030. szMode = "object's current LOD vertex count";
  1031. break;
  1032. case 23:
  1033. szMode = "Display shadow casters in red";
  1034. break;
  1035. case 24:
  1036. szMode = "Objects without LODs.\n name - (triangle count)\n draw calls - zpass/general/transparent/shadows/misc";
  1037. break;
  1038. case 25:
  1039. szMode = "Objects without LODs (Red). Objects that need more LODs (Blue)\n name - (triangle count)\n draw calls - zpass/general/transparent/shadows/misc";
  1040. break;
  1041. default:
  1042. assert(0);
  1043. }
  1044. GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, fColor, false, " %s", szMode);
  1045. if (GetCVars()->e_DebugDraw == 17)
  1046. {
  1047. GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, fColor, false, " StatObj geometry used: %.2fMb / %dMb", CObjManager::s_nLastStreamingMemoryUsage / (1024.f * 1024.f), GetCVars()->e_StreamCgfPoolSize);
  1048. ICVar* cVar = GetConsole()->GetCVar("r_TexturesStreaming");
  1049. if (!cVar || !cVar->GetIVal())
  1050. {
  1051. GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, fColor, false, " You have to set r_TexturesStreaming = 1 to see texture information!");
  1052. }
  1053. }
  1054. }
  1055. float fTextPosX = 10, fTextPosY = 10, fTextStepY = 12;
  1056. // print list of streamed meshes
  1057. if (m_pObjManager && GetCVars()->e_StreamCgf && GetCVars()->e_StreamCgfDebug >= 3)
  1058. {
  1059. // overall status
  1060. {
  1061. static char szCGFStreaming[256] = "";
  1062. static SObjectsStreamingStatus objectsStreamingStatus = {0};
  1063. {
  1064. m_pObjManager->GetObjectsStreamingStatus(objectsStreamingStatus);
  1065. sprintf_s(szCGFStreaming, 256, "CgfStrm: Loaded:%d InProg:%d All:%d Act:%d MemUsed:%2.2f MemReq:%2.2f Pool:%d",
  1066. objectsStreamingStatus.nReady, objectsStreamingStatus.nInProgress, objectsStreamingStatus.nTotal, objectsStreamingStatus.nActive, float(objectsStreamingStatus.nAllocatedBytes) / 1024 / 1024, float(objectsStreamingStatus.nMemRequired) / 1024 / 1024, GetCVars()->e_StreamCgfPoolSize);
  1067. }
  1068. bool bOutOfMem((float(objectsStreamingStatus.nMemRequired) / 1024 / 1024) > GetCVars()->e_StreamCgfPoolSize);
  1069. bool bCloseToOutOfMem((float(objectsStreamingStatus.nMemRequired) / 1024 / 1024) > GetCVars()->e_StreamCgfPoolSize * 90 / 100);
  1070. ColorF color = Col_White;
  1071. if (bOutOfMem)
  1072. {
  1073. color = Col_Red;
  1074. }
  1075. else if (bCloseToOutOfMem)
  1076. {
  1077. color = Col_Orange;
  1078. }
  1079. DrawTextLeftAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, color, szCGFStreaming);
  1080. fTextPosY += fTextStepY;
  1081. }
  1082. DrawTextLeftAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_White, "------------------- List of meshes bigger than %d KB -------------------", GetCVars()->e_StreamCgfDebugMinObjSize);
  1083. for (int nObjId = 0; nObjId < m_pObjManager->GetArrStreamableObjects().Count(); nObjId++)
  1084. {
  1085. CStatObj* pStatObj = (CStatObj*)m_pObjManager->GetArrStreamableObjects()[nObjId].GetStreamAbleObject();
  1086. int nKB = pStatObj->GetStreamableContentMemoryUsage() >> 10;
  1087. int nSel = (pStatObj->m_nSelectedFrameId >= passInfo.GetMainFrameID() - 2);
  1088. string sName;
  1089. pStatObj->GetStreamableName(sName);
  1090. if ((nKB >= GetCVars()->e_StreamCgfDebugMinObjSize && strstr(sName.c_str(), GetCVars()->e_StreamCgfDebugFilter->GetString())) || nSel)
  1091. {
  1092. const char* pComment = 0;
  1093. if (!pStatObj->m_bCanUnload)
  1094. {
  1095. pComment = "NO_STRM";
  1096. }
  1097. else if (pStatObj->m_pLod0)
  1098. {
  1099. pComment = " LOD_X";
  1100. }
  1101. else if (!pStatObj->m_bLodsAreLoadedFromSeparateFile && pStatObj->m_nLoadedLodsNum > 1)
  1102. {
  1103. pComment = " SINGLE";
  1104. }
  1105. else if (pStatObj->m_nLoadedLodsNum > 1)
  1106. {
  1107. pComment = " LOD_0";
  1108. }
  1109. else
  1110. {
  1111. pComment = "NO_LODS";
  1112. }
  1113. int nDiff = SATURATEB(int(float(nKB - GetCVars()->e_StreamCgfDebugMinObjSize) / max(1, (int)GetCVars()->e_StreamCgfDebugMinObjSize) * 255));
  1114. ColorB col(nDiff, 255 - nDiff, 0, 255);
  1115. if (nSel && (1 & (int)(GetCurTimeSec() * 5.f)))
  1116. {
  1117. col = Col_Yellow;
  1118. }
  1119. ColorF fColor(col[0] / 255.f, col[1] / 255.f, col[2] / 255.f, col[3] / 255.f);
  1120. const char* pStatusText = "Unload";
  1121. if (pStatObj->m_eStreamingStatus == ecss_Ready)
  1122. {
  1123. pStatusText = "Ready ";
  1124. }
  1125. else if (pStatObj->m_eStreamingStatus == ecss_InProgress)
  1126. {
  1127. pStatusText = "InProg";
  1128. }
  1129. DrawTextLeftAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, fColor, "%1.2f mb, %s, %s, %s",
  1130. 1.f / 1024.f * nKB, pComment, pStatusText, sName.c_str());
  1131. if (fTextPosY > (float)gEnv->pRenderer->GetHeight())
  1132. {
  1133. break;
  1134. }
  1135. }
  1136. }
  1137. }
  1138. if (m_arrProcessStreamingLatencyTestResults.Count())
  1139. {
  1140. float fAverTime = 0;
  1141. for (int i = 0; i < m_arrProcessStreamingLatencyTestResults.Count(); i++)
  1142. {
  1143. fAverTime += m_arrProcessStreamingLatencyTestResults[i];
  1144. }
  1145. fAverTime /= m_arrProcessStreamingLatencyTestResults.Count();
  1146. int nAverTexNum = 0;
  1147. for (int i = 0; i < m_arrProcessStreamingLatencyTexNum.Count(); i++)
  1148. {
  1149. nAverTexNum += m_arrProcessStreamingLatencyTexNum[i];
  1150. }
  1151. nAverTexNum /= m_arrProcessStreamingLatencyTexNum.Count();
  1152. DrawTextLeftAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Yellow, "------ SQT Average Time = %.1f, TexNum = %d ------", fAverTime, nAverTexNum);
  1153. for (int i = 0; i < m_arrProcessStreamingLatencyTestResults.Count(); i++)
  1154. {
  1155. DrawTextLeftAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Yellow, "Run %d: Time = %.1f, TexNum = %d",
  1156. i, m_arrProcessStreamingLatencyTestResults[i], m_arrProcessStreamingLatencyTexNum[i]);
  1157. }
  1158. }
  1159. #if defined(USE_GEOM_CACHES)
  1160. #ifndef _RELEASE
  1161. if (GetCVars()->e_GeomCacheDebug)
  1162. {
  1163. m_pGeomCacheManager->DrawDebugInfo();
  1164. }
  1165. else
  1166. {
  1167. m_pGeomCacheManager->ResetDebugInfo();
  1168. }
  1169. #endif
  1170. #endif
  1171. }
  1172. void C3DEngine::UpdatePreRender(const SRenderingPassInfo& passInfo)
  1173. {
  1174. AZ_TRACE_METHOD();
  1175. FUNCTION_PROFILER(GetISystem(), PROFILE_3DENGINE);
  1176. assert(passInfo.IsGeneralPass());
  1177. // Compute global shadow cascade parameters.
  1178. {
  1179. m_fGsmRange = GetCVars()->e_GsmRange;
  1180. m_fGsmRangeStep = GetCVars()->e_GsmRangeStep;
  1181. //!!!also formulas for computing biases per gsm needs to be changed
  1182. m_fShadowsConstBias = GetCVars()->e_ShadowsConstBias;
  1183. m_fShadowsSlopeBias = GetCVars()->e_ShadowsSlopeBias;
  1184. if (m_eShadowMode == ESM_HIGHQUALITY)
  1185. {
  1186. m_fGsmRange = min(0.15f, GetCVars()->e_GsmRange);
  1187. m_fGsmRangeStep = min(2.8f, GetCVars()->e_GsmRangeStep);
  1188. m_fShadowsConstBias = min(GetCVars()->e_ShadowsConstBiasHQ, GetCVars()->e_ShadowsConstBias);
  1189. m_fShadowsSlopeBias = min(GetCVars()->e_ShadowsSlopeBiasHQ, GetCVars()->e_ShadowsSlopeBias);
  1190. }
  1191. const int nCascadeCount = Get3DEngine()->GetShadowsCascadeCount(NULL);
  1192. m_pObjManager->SetGSMMaxDistance(Get3DEngine()->m_fGsmRange * powf(Get3DEngine()->m_fGsmRangeStep, (float)nCascadeCount));
  1193. }
  1194. // (bethelz) This has to happen before particle updates.
  1195. m_PhysicsAreaUpdates.Update();
  1196. if (passInfo.RenderClouds())
  1197. {
  1198. if (m_pCloudsManager)
  1199. {
  1200. m_pCloudsManager->MoveClouds();
  1201. }
  1202. CVolumeObjectRenderNode::MoveVolumeObjects();
  1203. }
  1204. UpdateSun(passInfo);
  1205. // Set traceable fog volume areas
  1206. CFogVolumeRenderNode::SetTraceableArea(AABB(passInfo.GetCamera().GetPosition(), 1024.0f), passInfo);
  1207. }
  1208. void C3DEngine::UpdatePostRender(const SRenderingPassInfo& passInfo)
  1209. {
  1210. AZ_TRACE_METHOD();
  1211. FUNCTION_PROFILER(GetISystem(), PROFILE_3DENGINE);
  1212. assert (m_pObjManager);
  1213. m_pObjManager->CheckTextureReadyFlag();
  1214. if (GetCVars()->e_StreamCgf)
  1215. {
  1216. static Array2d<int> memUsage;
  1217. int nArrayDim = 256;
  1218. #ifndef CONSOLE_CONST_CVAR_MODE
  1219. if (GetCVars()->e_StreamCgfDebugHeatMap == 1)
  1220. {
  1221. memUsage.Allocate(nArrayDim);
  1222. CCamera camOld = passInfo.GetCamera();
  1223. PrintMessage("Computing mesh streaming heat map");
  1224. //The assumption is that this is called on Main Thread, otherwise the loop
  1225. //Should be wrapped inside a EnumerateHandlers lambda.
  1226. auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
  1227. const float defaultTerrainHeight = AzFramework::Terrain::TerrainDataRequests::GetDefaultTerrainHeight();
  1228. const AZ::Aabb terrainAabb = terrain ? terrain->GetTerrainAabb() : AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
  1229. const int nTerrainSizeX = static_cast<int>(terrainAabb.GetXExtent());
  1230. const int nTerrainSizeY = static_cast<int>(terrainAabb.GetYExtent());
  1231. const int nStepX = nTerrainSizeX / nArrayDim;
  1232. const int nStepY = nTerrainSizeY / nArrayDim;
  1233. for (int x = 0; x < nTerrainSizeX; x += nStepX)
  1234. {
  1235. for (int y = 0; y < nTerrainSizeY; y += nStepY)
  1236. {
  1237. CCamera camTmp = camOld;
  1238. float terrainHeight = terrain ? terrain->GetHeightFromFloats((float)x, (float)y) : defaultTerrainHeight;
  1239. camTmp.SetPosition(Vec3((float)x + (float)nStepX / 2.f, (float)y + (float)nStepY / 2.f, terrainHeight));
  1240. //SetCamera(camTmp);
  1241. m_pObjManager->ProcessObjectsStreaming(passInfo);
  1242. SObjectsStreamingStatus objectsStreamingStatus;
  1243. m_pObjManager->GetObjectsStreamingStatus(objectsStreamingStatus);
  1244. memUsage[x / nStepX][y / nStepY] = objectsStreamingStatus.nMemRequired;
  1245. }
  1246. if (!((x / nStepX) & 31))
  1247. {
  1248. PrintMessage(" working ...");
  1249. }
  1250. }
  1251. PrintMessage(" done");
  1252. GetCVars()->e_StreamCgfDebugHeatMap = 2;
  1253. //SetCamera(camOld);
  1254. }
  1255. else if (GetCVars()->e_StreamCgfDebugHeatMap == 2)
  1256. {
  1257. auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
  1258. const float defaultTerrainHeight = AzFramework::Terrain::TerrainDataRequests::GetDefaultTerrainHeight();
  1259. const AZ::Aabb terrainAabb = terrain ? terrain->GetTerrainAabb() : AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
  1260. const float terrainSizeX = terrainAabb.GetXExtent();
  1261. const float terrainSizeY = terrainAabb.GetYExtent();
  1262. const float fStepX = terrainSizeX / nArrayDim;
  1263. const float fStepY = terrainSizeY / nArrayDim;
  1264. for (int x = 0; x < memUsage.GetSize(); x++)
  1265. {
  1266. for (int y = 0; y < memUsage.GetSize(); y++)
  1267. {
  1268. float terrainHeight = terrain ? terrain->GetHeightFromFloats((float)x * fStepX, (float)y * fStepY) : defaultTerrainHeight;
  1269. Vec3 v0((float)x* fStepX, (float)y* fStepY, terrainHeight);
  1270. Vec3 v1((float)x* fStepX + fStepX, (float)y* fStepY + fStepY, v0.z + fStepX);
  1271. v0 += Vec3(.25f, .25f, .25f);
  1272. v1 -= Vec3(.25f, .25f, .25f);
  1273. AABB box(v0, v1);
  1274. if (!passInfo.GetCamera().IsAABBVisible_F(box))
  1275. {
  1276. continue;
  1277. }
  1278. int nMemUsageMB = memUsage[(int)(x)][(int)(y)] / 1024 / 1024;
  1279. int nOverLoad = nMemUsageMB - GetCVars()->e_StreamCgfPoolSize;
  1280. ColorB col = Col_Red;
  1281. if (nOverLoad < GetCVars()->e_StreamCgfPoolSize / 2)
  1282. {
  1283. col = Col_Yellow;
  1284. }
  1285. if (nOverLoad < 0)
  1286. {
  1287. col = Col_Green;
  1288. }
  1289. DrawBBox(box, col);
  1290. }
  1291. }
  1292. }
  1293. #endif //CONSOLE_CONST_CVAR_MODE
  1294. m_pObjManager->ProcessObjectsStreaming(passInfo);
  1295. }
  1296. else
  1297. {
  1298. m_pObjManager->GetStreamPreCacheCameras()[0].vPosition = passInfo.GetCamera().GetPosition();
  1299. if (Distance::Point_AABBSq(m_pObjManager->GetStreamPreCacheCameras()[0].vPosition, m_pObjManager->GetStreamPreCacheCameras()[0].bbox) > 0.0f)
  1300. {
  1301. m_pObjManager->GetStreamPreCacheCameras()[0].bbox = AABB(m_pObjManager->GetStreamPreCacheCameras()[0].vPosition, GetCVars()->e_StreamPredictionBoxRadius);
  1302. }
  1303. m_pObjManager->UpdateObjectsStreamingPriority(false, passInfo);
  1304. }
  1305. // (bethelz) Per-frame precache request handled by streaming systems.
  1306. m_bContentPrecacheRequested = false;
  1307. }
  1308. int __cdecl C3DEngine__Cmp_SRNInfo(const void* v1, const void* v2)
  1309. {
  1310. SRNInfo* p1 = (SRNInfo*)v1;
  1311. SRNInfo* p2 = (SRNInfo*)v2;
  1312. float fViewDist1 = p1->fMaxViewDist - p1->objSphere.radius;
  1313. float fViewDist2 = p2->fMaxViewDist - p2->objSphere.radius;
  1314. // if same - give closest sectors higher priority
  1315. if (fViewDist1 > fViewDist2)
  1316. {
  1317. return 1;
  1318. }
  1319. else if (fViewDist1 < fViewDist2)
  1320. {
  1321. return -1;
  1322. }
  1323. return 0;
  1324. }
  1325. void C3DEngine::SetSkyMaterialPath(const string& skyMatName)
  1326. {
  1327. m_skyMatName = skyMatName;
  1328. m_pSkyMat = nullptr;
  1329. }
  1330. void C3DEngine::SetSkyLowSpecMaterialPath(const string& skyLowSpecMatName)
  1331. {
  1332. m_skyLowSpecMatName = skyLowSpecMatName;
  1333. m_pSkyLowSpecMat = nullptr;
  1334. }
  1335. void C3DEngine::LoadSkyMaterial()
  1336. {
  1337. const int skyType = GetCVars()->e_SkyType;
  1338. if (skyType == 0)
  1339. {
  1340. if (!m_pSkyLowSpecMat)
  1341. {
  1342. m_pSkyLowSpecMat = m_skyLowSpecMatName.empty() ? nullptr : m_pMatMan->LoadMaterial(m_skyLowSpecMatName.c_str(), false, false, MTL_FLAG_IS_SKY);
  1343. AZ_Warning("3DEngine", m_pSkyLowSpecMat, "Missing low spec sky material: %s", m_skyLowSpecMatName.c_str());
  1344. }
  1345. }
  1346. else
  1347. {
  1348. if (!m_pSkyMat)
  1349. {
  1350. m_pSkyMat = m_skyMatName.empty() ? nullptr : m_pMatMan->LoadMaterial(m_skyMatName.c_str(), false, false, MTL_FLAG_IS_SKY);
  1351. AZ_Warning("3DEngine", m_pSkyMat, "Missing sky material: %s", m_skyMatName.c_str());
  1352. }
  1353. }
  1354. m_previousSkyType = skyType;
  1355. }
  1356. _smart_ptr<IMaterial> C3DEngine::GetSkyMaterial()
  1357. {
  1358. const int skyType = GetCVars()->e_SkyType;
  1359. // If e_SkyType has changed, then we may need to load a different sky material.
  1360. if (skyType != m_previousSkyType)
  1361. {
  1362. LoadSkyMaterial();
  1363. }
  1364. return (skyType == 0) ? m_pSkyLowSpecMat : m_pSkyMat;
  1365. }
  1366. void C3DEngine::SetSkyMaterial(_smart_ptr<IMaterial> pSkyMat)
  1367. {
  1368. m_pSkyMat = pSkyMat;
  1369. }
  1370. bool C3DEngine::IsHDRSkyMaterial(_smart_ptr<IMaterial> pMat) const
  1371. {
  1372. return pMat && !azstricmp(pMat->GetSafeSubMtl(0)->GetShaderItem().m_pShader->GetName(), "SkyHDR");
  1373. }
  1374. void C3DEngine::RenderScene(const int nRenderFlags, const SRenderingPassInfo& passInfo)
  1375. {
  1376. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  1377. AZ_TRACE_METHOD();
  1378. CRY_ASSERT(passInfo.IsGeneralPass());
  1379. CRY_ASSERT(m_pVisAreaManager);
  1380. CRY_ASSERT(m_pClipVolumeManager);
  1381. CRY_ASSERT(m_pDecalManager);
  1382. GetObjManager()->GetCullThread().SetActive(true);
  1383. if (GetCVars()->e_CoverageBuffer)
  1384. {
  1385. m_pCoverageBuffer->BeginFrame(passInfo);
  1386. }
  1387. if (m_pVisAreaManager != nullptr)
  1388. {
  1389. m_pVisAreaManager->DrawOcclusionAreasIntoCBuffer(m_pCoverageBuffer, passInfo);
  1390. m_pVisAreaManager->CheckVis(passInfo);
  1391. }
  1392. if (m_pClipVolumeManager)
  1393. {
  1394. m_pClipVolumeManager->PrepareVolumesForRendering(passInfo);
  1395. }
  1396. if (m_pObjManager)
  1397. {
  1398. m_pObjManager->RenderAllObjectDebugInfo();
  1399. }
  1400. SRendItemSorter rendItemSorter = SRendItemSorter::CreateRendItemSorter(passInfo);
  1401. // make sure all jobs from the previous frame have finished
  1402. threadID nPrevThreadID = 0;
  1403. gEnv->pRenderer->EF_Query(EFQ_RenderThreadList, nPrevThreadID);
  1404. gEnv->pRenderer->GetFinalizeRendItemJobExecutor(nPrevThreadID)->WaitForCompletion();
  1405. gEnv->pRenderer->GetFinalizeShadowRendItemJobExecutor(nPrevThreadID)->WaitForCompletion();
  1406. GetRenderer()->EF_ClearSkinningDataPool();
  1407. GetRenderer()->BeginSpawningGeneratingRendItemJobs(passInfo.ThreadID());
  1408. GetRenderer()->EF_StartEf(passInfo);
  1409. m_bIsInRenderScene = true;
  1410. COctreeNode::ReleaseEmptyNodes();
  1411. m_LightVolumesMgr.Clear(passInfo);
  1412. SubmitSun(passInfo);
  1413. if (GetCVars()->e_StatObjBufferRenderTasks && m_pObjManager != nullptr)
  1414. {
  1415. m_pObjManager->BeginOcclusionCulling(passInfo);
  1416. }
  1417. if (m_pVisAreaManager != nullptr)
  1418. {
  1419. m_pVisAreaManager->DrawVisibleSectors(passInfo, rendItemSorter);
  1420. }
  1421. m_nOceanRenderFlags &= ~OCR_OCEANVOLUME_VISIBLE;
  1422. if (IsOutdoorVisible() || GetRenderer()->IsPost3DRendererEnabled())
  1423. {
  1424. if (m_pVisAreaManager != nullptr && m_pVisAreaManager->m_lstOutdoorPortalCameras.Count() &&
  1425. (m_pVisAreaManager->m_pCurArea || m_pVisAreaManager->m_pCurPortal))
  1426. { // enable multi-camera culling
  1427. const_cast<CCamera&>(passInfo.GetCamera()).m_pMultiCamera = &m_pVisAreaManager->m_lstOutdoorPortalCameras;
  1428. }
  1429. if (IsOutdoorVisible())
  1430. {
  1431. RenderSkyBox(GetSkyMaterial(), passInfo);
  1432. }
  1433. rendItemSorter.IncreaseOctreeCounter();
  1434. {
  1435. FRAME_PROFILER_LEGACYONLY("COctreeNode::Render_____", GetSystem(), PROFILE_3DENGINE);
  1436. AZ_TRACE_METHOD_NAME("COctreeNode::Render");
  1437. if (m_pObjectsTree != nullptr)
  1438. {
  1439. m_pObjectsTree->Render_Object_Nodes(false, OCTREENODE_RENDER_FLAG_OBJECTS, passInfo, rendItemSorter);
  1440. }
  1441. }
  1442. rendItemSorter.IncreaseGroupCounter();
  1443. }
  1444. else if (m_pVisAreaManager && m_pVisAreaManager->IsSkyVisible())
  1445. {
  1446. RenderSkyBox(GetSkyMaterial(), passInfo);
  1447. }
  1448. // Outdoor is not visible, that means there is no SkyBox to render.
  1449. // So we want to clear the GBuffer RT/background in order to avoid artifacts.
  1450. GetRenderer()->SetClearBackground(!IsOutdoorVisible());
  1451. if (nRenderFlags & SHDF_ALLOW_AO)
  1452. {
  1453. SVOGILegacyRequestBus::Broadcast(&SVOGILegacyRequests::UpdateRenderData);
  1454. }
  1455. {
  1456. FRAME_PROFILER_LEGACYONLY("COctreeNode::Render_Object_Nodes_NEAR", GetSystem(), PROFILE_3DENGINE);
  1457. AZ_TRACE_METHOD_NAME("COctreeNode::Render_Object_Nodes_NEAR");
  1458. rendItemSorter.IncreaseOctreeCounter();
  1459. if (GetCVars()->e_PortalsBigEntitiesFix)
  1460. {
  1461. if (!IsOutdoorVisible() && GetVisAreaManager() != nullptr && GetVisAreaManager()->GetCurVisArea())
  1462. {
  1463. if (GetVisAreaManager()->GetCurVisArea()->IsConnectedToOutdoor())
  1464. {
  1465. CCamera cam = passInfo.GetCamera();
  1466. cam.SetFrustum(cam.GetViewSurfaceX(), cam.GetViewSurfaceZ(), cam.GetFov(), min(cam.GetNearPlane(), 1.f), 2.f, cam.GetPixelAspectRatio());
  1467. m_pObjectsTree->Render_Object_Nodes(false, OCTREENODE_RENDER_FLAG_OBJECTS | OCTREENODE_RENDER_FLAG_OBJECTS_ONLY_ENTITIES, SRenderingPassInfo::CreateTempRenderingInfo(cam, passInfo), rendItemSorter);
  1468. }
  1469. }
  1470. }
  1471. }
  1472. rendItemSorter.IncreaseGroupCounter();
  1473. // render special objects like laser beams intersecting entire level
  1474. for (int i = 0; i < m_lstAlwaysVisible.Count(); i++)
  1475. {
  1476. IRenderNode* pObj = m_lstAlwaysVisible[i];
  1477. const AABB& objBox = pObj->GetBBox();
  1478. // don't frustum cull the HUD. When e.g. zooming the FOV for this camera is very different to the
  1479. // fixed HUD FOV, and this can cull incorrectly.
  1480. const unsigned int dwRndFlags = pObj->GetRndFlags();
  1481. if (dwRndFlags & ERF_HUD || passInfo.GetCamera().IsAABBVisible_E(objBox))
  1482. {
  1483. FRAME_PROFILER_LEGACYONLY("C3DEngine::RenderScene_DrawAlwaysVisible", GetSystem(), PROFILE_3DENGINE);
  1484. AZ_TRACE_METHOD_NAME("COctreeNode::RenderScene_DrawAlwaysVisible");
  1485. Vec3 vCamPos = passInfo.GetCamera().GetPosition();
  1486. float fEntDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, objBox)) * passInfo.GetZoomFactor();
  1487. assert(fEntDistance >= 0 && _finite(fEntDistance));
  1488. if (fEntDistance < pObj->m_fWSMaxViewDist && GetObjManager() != nullptr)
  1489. {
  1490. GetObjManager()->RenderObject(pObj, objBox, fEntDistance, pObj->GetRenderNodeType(), passInfo, rendItemSorter);
  1491. }
  1492. }
  1493. }
  1494. rendItemSorter.IncreaseGroupCounter();
  1495. if (m_pOcean)
  1496. {
  1497. ProcessOcean(passInfo);
  1498. }
  1499. if (passInfo.RenderDecals() && m_pDecalManager != nullptr)
  1500. {
  1501. m_pDecalManager->Render(passInfo);
  1502. }
  1503. // tell the occlusion culler that no new work will be submitted
  1504. if (GetCVars()->e_StatObjBufferRenderTasks == 1 && GetObjManager() != nullptr)
  1505. {
  1506. GetObjManager()->PushIntoCullQueue(SCheckOcclusionJobData::CreateQuitJobData());
  1507. }
  1508. // fill shadow list here to allow more time between starting and waiting for the occlusion buffer
  1509. InitShadowFrustums(passInfo);
  1510. gEnv->pSystem->DoWorkDuringOcclusionChecks();
  1511. if (GetCVars()->e_StatObjBufferRenderTasks && m_pObjManager != nullptr)
  1512. {
  1513. m_pObjManager->RenderBufferedRenderMeshes(passInfo);
  1514. }
  1515. // don't start shadow jobs if we aren't generating shadows
  1516. if ((nRenderFlags & SHDF_NO_SHADOWGEN) == 0)
  1517. {
  1518. GetRenderer()->EF_InvokeShadowMapRenderJobs(IsShadersSyncLoad() ? (nRenderFlags | SHDF_NOASYNC | SHDF_STREAM_SYNC) : nRenderFlags);
  1519. }
  1520. m_LightVolumesMgr.Update(passInfo);
  1521. SetupDistanceFog();
  1522. SetupClearColor();
  1523. {
  1524. FRAME_PROFILER("Renderer::EF_EndEf3D", GetSystem(), PROFILE_RENDERER);
  1525. // TODO: separate SHDF_NOASYNC and SHDF_STREAM_SYNC flags
  1526. GetRenderer()->EF_EndEf3D(IsShadersSyncLoad() ? (nRenderFlags | SHDF_NOASYNC | SHDF_STREAM_SYNC) : nRenderFlags, GetObjManager()->GetUpdateStreamingPrioriryRoundId(), GetObjManager()->GetUpdateStreamingPrioriryRoundIdFast(), passInfo);
  1527. }
  1528. GetRenderer()->EnableFog(false);
  1529. bool bIsMultiThreadedRenderer = false;
  1530. gEnv->pRenderer->EF_Query(EFQ_RenderMultithreaded, bIsMultiThreadedRenderer);
  1531. if (bIsMultiThreadedRenderer)
  1532. {
  1533. gEnv->pRenderer->EndSpawningGeneratingRendItemJobs();
  1534. }
  1535. m_bIsInRenderScene = false;
  1536. #ifndef _RELEASE
  1537. IF (GetCVars()->e_LightVolumesDebug, 0)
  1538. {
  1539. m_LightVolumesMgr.DrawDebug(passInfo);
  1540. }
  1541. #endif
  1542. }
  1543. void C3DEngine::WaitForCullingJobsCompletion()
  1544. {
  1545. const bool waitForOcclusionJobCompletion = true;
  1546. m_pObjManager->EndOcclusionCulling(waitForOcclusionJobCompletion);
  1547. COctreeNode::WaitForContentJobCompletion();
  1548. }
  1549. void C3DEngine::RenderSceneReflection(const int nRenderFlags, const SRenderingPassInfo& passInfo)
  1550. {
  1551. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  1552. AZ_TRACE_METHOD();
  1553. CRY_ASSERT(passInfo.IsRecursivePass());
  1554. CRY_ASSERT(passInfo.GetRecursiveLevel() < MAX_RECURSION_LEVELS);
  1555. CRY_ASSERT(m_pVisAreaManager);
  1556. CRY_ASSERT(m_pClipVolumeManager);
  1557. CRY_ASSERT(m_pDecalManager);
  1558. if (!GetCVars()->e_Recursion)
  1559. {
  1560. return;
  1561. }
  1562. if (m_pVisAreaManager != nullptr)
  1563. {
  1564. m_pVisAreaManager->CheckVis(passInfo);
  1565. }
  1566. if (m_pClipVolumeManager != nullptr)
  1567. {
  1568. m_pClipVolumeManager->PrepareVolumesForRendering(passInfo);
  1569. }
  1570. ////////////////////////////////////////////////////////////////////////////////////////
  1571. // From here we add render elements of main scene
  1572. ////////////////////////////////////////////////////////////////////////////////////////
  1573. SRendItemSorter rendItemSorter = SRendItemSorter::CreateRendItemSorter(passInfo);
  1574. GetRenderer()->EF_StartEf(passInfo);
  1575. if (m_pVisAreaManager != nullptr)
  1576. {
  1577. m_pVisAreaManager->DrawVisibleSectors(passInfo, rendItemSorter);
  1578. }
  1579. if (IsOutdoorVisible() || GetRenderer()->IsPost3DRendererEnabled())
  1580. {
  1581. if (m_pVisAreaManager != nullptr && m_pVisAreaManager->m_lstOutdoorPortalCameras.Count() &&
  1582. (m_pVisAreaManager->m_pCurArea || m_pVisAreaManager->m_pCurPortal))
  1583. { // enable multi-camera culling
  1584. const_cast<CCamera&>(passInfo.GetCamera()).m_pMultiCamera = &m_pVisAreaManager->m_lstOutdoorPortalCameras;
  1585. }
  1586. if (IsOutdoorVisible())
  1587. {
  1588. RenderSkyBox(GetSkyMaterial(), passInfo);
  1589. }
  1590. {
  1591. rendItemSorter.IncreaseOctreeCounter();
  1592. FRAME_PROFILER("COctreeNode::Render_____", GetSystem(), PROFILE_3DENGINE);
  1593. if (m_pObjectsTree != nullptr)
  1594. {
  1595. m_pObjectsTree->Render_Object_Nodes(false, OCTREENODE_RENDER_FLAG_OBJECTS, passInfo, rendItemSorter);
  1596. }
  1597. }
  1598. rendItemSorter.IncreaseGroupCounter();
  1599. }
  1600. else if (m_pVisAreaManager != nullptr && m_pVisAreaManager->IsSkyVisible())
  1601. {
  1602. RenderSkyBox(GetSkyMaterial(), passInfo);
  1603. }
  1604. {
  1605. FRAME_PROFILER("COctreeNode::Render_Object_Nodes_NEAR", GetSystem(), PROFILE_3DENGINE);
  1606. rendItemSorter.IncreaseOctreeCounter();
  1607. if (GetCVars()->e_PortalsBigEntitiesFix)
  1608. {
  1609. if (!IsOutdoorVisible() && GetVisAreaManager() != nullptr && GetVisAreaManager()->GetCurVisArea())
  1610. {
  1611. if (GetVisAreaManager()->GetCurVisArea()->IsConnectedToOutdoor())
  1612. {
  1613. CCamera cam = passInfo.GetCamera();
  1614. cam.SetFrustum(cam.GetViewSurfaceX(), cam.GetViewSurfaceZ(), cam.GetFov(), min(cam.GetNearPlane(), 1.f), 2.f, cam.GetPixelAspectRatio());
  1615. if (m_pObjectsTree != nullptr)
  1616. {
  1617. m_pObjectsTree->Render_Object_Nodes(false, OCTREENODE_RENDER_FLAG_OBJECTS | OCTREENODE_RENDER_FLAG_OBJECTS_ONLY_ENTITIES, SRenderingPassInfo::CreateTempRenderingInfo(cam, passInfo), rendItemSorter);
  1618. }
  1619. }
  1620. }
  1621. }
  1622. }
  1623. rendItemSorter.IncreaseGroupCounter();
  1624. // render special objects like laser beams intersecting entire level
  1625. for (int i = 0; i < m_lstAlwaysVisible.Count(); i++)
  1626. {
  1627. IRenderNode* pObj = m_lstAlwaysVisible[i];
  1628. const AABB& objBox = pObj->GetBBox();
  1629. // don't frustum cull the HUD. When e.g. zooming the FOV for this camera is very different to the
  1630. // fixed HUD FOV, and this can cull incorrectly.
  1631. const unsigned int dwRndFlags = pObj->GetRndFlags();
  1632. if (dwRndFlags & ERF_HUD || passInfo.GetCamera().IsAABBVisible_E(objBox))
  1633. {
  1634. FRAME_PROFILER("C3DEngine::RenderScene_DrawAlwaysVisible", GetSystem(), PROFILE_3DENGINE);
  1635. Vec3 vCamPos = passInfo.GetCamera().GetPosition();
  1636. float fEntDistance = sqrt_tpl(Distance::Point_AABBSq(vCamPos, objBox)) * passInfo.GetZoomFactor();
  1637. assert(fEntDistance >= 0 && _finite(fEntDistance));
  1638. if (fEntDistance < pObj->m_fWSMaxViewDist)
  1639. {
  1640. GetObjManager()->RenderObject(pObj, objBox, fEntDistance, pObj->GetRenderNodeType(), passInfo, rendItemSorter);
  1641. }
  1642. }
  1643. }
  1644. rendItemSorter.IncreaseGroupCounter();
  1645. if (m_pOcean)
  1646. {
  1647. ProcessOcean(passInfo);
  1648. }
  1649. //Update light volumes again. Processing particles may have resulted in an increase in the number of light volumes.
  1650. m_LightVolumesMgr.Update(passInfo);
  1651. if (passInfo.RenderDecals() && m_pDecalManager != nullptr)
  1652. {
  1653. m_pDecalManager->Render(passInfo);
  1654. }
  1655. {
  1656. FRAME_PROFILER("Renderer::EF_EndEf3D", GetSystem(), PROFILE_RENDERER);
  1657. GetRenderer()->EF_EndEf3D(IsShadersSyncLoad() ? (nRenderFlags | SHDF_NOASYNC | SHDF_STREAM_SYNC) : nRenderFlags, GetObjManager()->GetUpdateStreamingPrioriryRoundId(), GetObjManager()->GetUpdateStreamingPrioriryRoundIdFast(), passInfo);
  1658. }
  1659. }
  1660. void C3DEngine::ProcessOcean(const SRenderingPassInfo& passInfo)
  1661. {
  1662. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  1663. AZ_TRACE_METHOD();
  1664. AZ_Assert(m_pOcean != nullptr, "Ocean pointer must be validated before calling ProcessOcean");
  1665. if (GetOceanRenderFlags() & OCR_NO_DRAW || !GetVisAreaManager() || GetCVars()->e_DefaultMaterial)
  1666. {
  1667. return;
  1668. }
  1669. bool bOceanIsForcedByVisAreaFlags = GetVisAreaManager()->IsOceanVisible();
  1670. if (!IsOutdoorVisible() && !bOceanIsForcedByVisAreaFlags)
  1671. {
  1672. return;
  1673. }
  1674. bool bOceanVisible = false;
  1675. if (OceanToggle::IsActive())
  1676. {
  1677. bOceanVisible = OceanRequest::OceanIsEnabled();
  1678. }
  1679. else
  1680. {
  1681. bOceanVisible = true;
  1682. }
  1683. if (bOceanVisible && passInfo.RenderWaterOcean() && m_bOcean)
  1684. {
  1685. Vec3 vCamPos = passInfo.GetCamera().GetPosition();
  1686. float fWaterPlaneSize = passInfo.GetCamera().GetFarPlane();
  1687. const float fOceanLevel = OceanToggle::IsActive() ? OceanRequest::GetOceanLevel(): m_pOcean->GetWaterLevel();
  1688. AABB boxOcean(Vec3(vCamPos.x - fWaterPlaneSize, vCamPos.y - fWaterPlaneSize, std::numeric_limits<float>::lowest()),
  1689. Vec3(vCamPos.x + fWaterPlaneSize, vCamPos.y + fWaterPlaneSize, fOceanLevel + 0.5f));
  1690. if ((!bOceanIsForcedByVisAreaFlags && passInfo.GetCamera().IsAABBVisible_EM(boxOcean)) ||
  1691. (bOceanIsForcedByVisAreaFlags && passInfo.GetCamera().IsAABBVisible_E (boxOcean)))
  1692. {
  1693. bool bOceanIsVisibleFromIndoor = true;
  1694. if (class PodArray<CCamera>* pMultiCamera = passInfo.GetCamera().m_pMultiCamera)
  1695. {
  1696. for (int i = 0; i < pMultiCamera->Count(); i++)
  1697. {
  1698. CVisArea* pExitPortal = (CVisArea*)(pMultiCamera->Get(i))->m_pPortal;
  1699. float fMinZ = pExitPortal->GetAABBox()->min.z;
  1700. float fMaxZ = pExitPortal->GetAABBox()->max.z;
  1701. if (!bOceanIsForcedByVisAreaFlags)
  1702. {
  1703. if (fMinZ > fOceanLevel && vCamPos.z < fMinZ)
  1704. {
  1705. bOceanIsVisibleFromIndoor = false;
  1706. }
  1707. if (fMaxZ < fOceanLevel && vCamPos.z > fMaxZ)
  1708. {
  1709. bOceanIsVisibleFromIndoor = false;
  1710. }
  1711. }
  1712. }
  1713. }
  1714. if (bOceanIsVisibleFromIndoor)
  1715. {
  1716. m_pOcean->Update(passInfo);
  1717. if ((GetOceanRenderFlags() & OCR_OCEANVOLUME_VISIBLE))
  1718. {
  1719. if (passInfo.RenderWaterOcean())
  1720. {
  1721. m_pOcean->Render(passInfo);
  1722. m_pOcean->SetLastFov(passInfo.GetCamera().GetFov());
  1723. }
  1724. }
  1725. }
  1726. }
  1727. }
  1728. if (GetCVars()->e_WaterRipplesDebug > 0)
  1729. {
  1730. GetRenderer()->EF_DrawWaterSimHits();
  1731. }
  1732. }
  1733. void C3DEngine::RenderSkyBox(_smart_ptr<IMaterial> pMat, const SRenderingPassInfo& passInfo)
  1734. {
  1735. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  1736. AZ_TRACE_METHOD();
  1737. if (!Get3DEngine()->GetCoverageBuffer()->IsOutdooVisible())
  1738. {
  1739. return;
  1740. }
  1741. const float fForceDrawLastSortOffset = 100000.0f;
  1742. // hdr sky dome
  1743. // TODO: temporary workaround to force the right sky dome for the selected shader
  1744. if (m_pREHDRSky && IsHDRSkyMaterial(pMat))
  1745. {
  1746. if (GetCVars()->e_SkyBox)
  1747. {
  1748. #ifndef CONSOLE_CONST_CVAR_MODE
  1749. if (GetCVars()->e_SkyQuality < 1)
  1750. {
  1751. GetCVars()->e_SkyQuality = 1;
  1752. }
  1753. else if (GetCVars()->e_SkyQuality > 2)
  1754. {
  1755. GetCVars()->e_SkyQuality = 2;
  1756. }
  1757. #endif
  1758. m_pSkyLightManager->SetQuality(GetCVars()->e_SkyQuality);
  1759. // set sky light incremental update rate and perform update
  1760. if (GetCVars()->e_SkyUpdateRate <= 0.0f)
  1761. {
  1762. GetCVars()->e_SkyUpdateRate = 0.01f;
  1763. }
  1764. m_pSkyLightManager->IncrementalUpdate(GetCVars()->e_SkyUpdateRate, passInfo);
  1765. // prepare render object
  1766. CRenderObject* pObj = GetRenderer()->EF_GetObject_Temp(passInfo.ThreadID());
  1767. if (!pObj)
  1768. {
  1769. return;
  1770. }
  1771. pObj->m_II.m_Matrix.SetTranslationMat(passInfo.GetCamera().GetPosition());
  1772. pObj->m_pRenderNode = 0;//m_pREHDRSky;
  1773. pObj->m_fSort = fForceDrawLastSortOffset; // force sky to draw last
  1774. /* if( 0 == m_nRenderStackLevel )
  1775. {
  1776. // set scissor rect
  1777. pObj->m_nScissorX1 = GetCamera().m_ScissorInfo.x1;
  1778. pObj->m_nScissorY1 = GetCamera().m_ScissorInfo.y1;
  1779. pObj->m_nScissorX2 = GetCamera().m_ScissorInfo.x2;
  1780. pObj->m_nScissorY2 = GetCamera().m_ScissorInfo.y2;
  1781. }*/
  1782. m_pREHDRSky->m_pRenderParams = m_pSkyLightManager->GetRenderParams();
  1783. m_pREHDRSky->m_moonTexId = m_nNightMoonTexId;
  1784. // add sky dome to render list
  1785. SRendItemSorter rendItemSorter = SRendItemSorter::CreateRendItemSorter(passInfo);
  1786. GetRenderer()->EF_AddEf(m_pREHDRSky, pMat->GetSafeSubMtl(0)->GetShaderItem(), pObj, passInfo, EFSLIST_GENERAL, 1, rendItemSorter);
  1787. }
  1788. }
  1789. // skybox
  1790. else
  1791. {
  1792. if (pMat && m_pRESky && GetCVars()->e_SkyBox)
  1793. {
  1794. CRenderObject* pObj = GetRenderer()->EF_GetObject_Temp(passInfo.ThreadID());
  1795. if (!pObj)
  1796. {
  1797. return;
  1798. }
  1799. pObj->m_II.m_Matrix.SetTranslationMat(passInfo.GetCamera().GetPosition());
  1800. pObj->m_II.m_Matrix = pObj->m_II.m_Matrix * Matrix33::CreateRotationZ(DEG2RAD(m_fSkyBoxAngle));
  1801. pObj->m_fSort = fForceDrawLastSortOffset; // force sky to draw last
  1802. if (OceanToggle::IsActive())
  1803. {
  1804. m_pRESky->m_fTerrainWaterLevel = OceanRequest::GetOceanLevelOrDefault(-100000.0f);
  1805. }
  1806. else
  1807. {
  1808. const float waterLevel = m_pOcean ? m_pOcean->GetWaterLevel() : 0.0f;
  1809. m_pRESky->m_fTerrainWaterLevel = max(0.0f, waterLevel);
  1810. }
  1811. m_pRESky->m_fSkyBoxStretching = m_fSkyBoxStretching;
  1812. SRendItemSorter rendItemSorter = SRendItemSorter::CreateRendItemSorter(passInfo);
  1813. GetRenderer()->EF_AddEf(m_pRESky, pMat->GetSafeSubMtl(0)->GetShaderItem(), pObj, passInfo, EFSLIST_GENERAL, 1, rendItemSorter);
  1814. }
  1815. }
  1816. }
  1817. void C3DEngine::DrawTextRightAligned(const float x, const float y, const char* format, ...)
  1818. {
  1819. va_list args;
  1820. va_start(args, format);
  1821. SDrawTextInfo ti;
  1822. ti.flags = eDrawText_FixedSize | eDrawText_Right | eDrawText_2D | eDrawText_Monospace;
  1823. ti.xscale = ti.yscale = DISPLAY_INFO_SCALE;
  1824. GetRenderer()->DrawTextQueued(Vec3(x, y, 1.0f), ti, format, args);
  1825. va_end(args);
  1826. }
  1827. void C3DEngine::DrawTextAligned(int flags, const float x, const float y, const float scale, const ColorF& color, const char* format, ...)
  1828. {
  1829. va_list args;
  1830. va_start(args, format);
  1831. SDrawTextInfo ti;
  1832. ti.flags = flags;
  1833. ti.color[0] = color[0];
  1834. ti.color[1] = color[1];
  1835. ti.color[2] = color[2];
  1836. ti.color[3] = color[3];
  1837. ti.xscale = ti.yscale = scale;
  1838. GetRenderer()->DrawTextQueued(Vec3(x, y, 1.0f), ti, format, args);
  1839. va_end(args);
  1840. }
  1841. void C3DEngine::DrawTextLeftAligned(const float x, const float y, const float scale, const ColorF& color, const char* format, ...)
  1842. {
  1843. va_list args;
  1844. va_start(args, format);
  1845. SDrawTextInfo ti;
  1846. ti.flags = eDrawText_FixedSize | eDrawText_2D | eDrawText_Monospace;
  1847. ti.color[0] = color[0];
  1848. ti.color[1] = color[1];
  1849. ti.color[2] = color[2];
  1850. ti.color[3] = color[3];
  1851. ti.xscale = ti.yscale = scale;
  1852. GetRenderer()->DrawTextQueued(Vec3(x, y, 1.0f), ti, format, args);
  1853. va_end(args);
  1854. }
  1855. void C3DEngine::DrawTextRightAligned(const float x, const float y, const float scale, const ColorF& color, const char* format, ...)
  1856. {
  1857. va_list args;
  1858. va_start(args, format);
  1859. SDrawTextInfo ti;
  1860. ti.flags = eDrawText_FixedSize | eDrawText_Right | eDrawText_2D | eDrawText_Monospace;
  1861. ti.color[0] = color[0];
  1862. ti.color[1] = color[1];
  1863. ti.color[2] = color[2];
  1864. ti.color[3] = color[3];
  1865. ti.xscale = ti.yscale = scale;
  1866. GetRenderer()->DrawTextQueued(Vec3(x, y, 1.0f), ti, format, args);
  1867. va_end(args);
  1868. }
  1869. int __cdecl C3DEngine__Cmp_FPS(const void* v1, const void* v2)
  1870. {
  1871. float f1 = *(float*)v1;
  1872. float f2 = *(float*)v2;
  1873. if (f1 > f2)
  1874. {
  1875. return 1;
  1876. }
  1877. else if (f1 < f2)
  1878. {
  1879. return -1;
  1880. }
  1881. return 0;
  1882. }
  1883. inline void Blend(float& Stat, float StatCur, float fBlendCur)
  1884. {
  1885. Stat = Stat * (1.f - fBlendCur) + StatCur * fBlendCur;
  1886. }
  1887. inline void Blend(float& Stat, int& StatCur, float fBlendCur)
  1888. {
  1889. Blend(Stat, float(StatCur), fBlendCur);
  1890. StatCur = int_round(Stat);
  1891. }
  1892. #ifdef ENABLE_LW_PROFILERS
  1893. static void AppendString(char*& szEnd, const char* szToAppend)
  1894. {
  1895. assert(szToAppend);
  1896. while (*szToAppend)
  1897. {
  1898. *szEnd++ = *szToAppend++;
  1899. }
  1900. *szEnd++ = ' ';
  1901. *szEnd = 0;
  1902. }
  1903. #endif
  1904. void C3DEngine::DisplayInfo([[maybe_unused]] float& fTextPosX, [[maybe_unused]] float& fTextPosY, [[maybe_unused]] float& fTextStepY, [[maybe_unused]] const bool bEnhanced)
  1905. {
  1906. #ifdef ENABLE_LW_PROFILERS
  1907. // FUNCTION_PROFILER_3DENGINE; causes 0 fps in stats
  1908. static ICVar* pDisplayInfo = GetConsole()->GetCVar("r_DisplayInfo");
  1909. assert(pDisplayInfo);
  1910. if (pDisplayInfo && pDisplayInfo->GetIVal() == 0)
  1911. {
  1912. return;
  1913. }
  1914. if (gEnv->IsDedicated())
  1915. {
  1916. return;
  1917. }
  1918. #if defined(INFO_FRAME_COUNTER)
  1919. static int frameCounter = 0;
  1920. #endif
  1921. GetRenderer()->SetState(GS_NODEPTHTEST);
  1922. fTextPosY = -10;
  1923. fTextStepY = 13;
  1924. fTextPosX = (float)GetRenderer()->GetOverlayWidth() - 5.0f;
  1925. const char* description = GetRenderer()->GetRenderDescription();
  1926. if (description && description[0] != 0)
  1927. {
  1928. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.5f, ColorF(1.0f, 1.0f, 0.5f, 1.0f),
  1929. "%s", description);
  1930. }
  1931. // If stat averaging is on, compute blend amount for current stats.
  1932. float fFPS = GetTimer()->GetFrameRate();
  1933. // Limit the FPS history for a single level to ~1 hour.
  1934. // This vector is cleared on each level load, but during a soak test this continues to grow every frame
  1935. const AZStd::size_t maxFPSEntries = 60 * 60 * 60; // 60ms * 60s * 60min
  1936. if (arrFPSforSaveLevelStats.size() < maxFPSEntries)
  1937. {
  1938. arrFPSforSaveLevelStats.push_back(SATURATEB((int)fFPS));
  1939. }
  1940. float fBlendTime = GetTimer()->GetCurrTime();
  1941. int iBlendMode = 0;
  1942. float fBlendCur = GetTimer()->GetProfileFrameBlending(&fBlendTime, &iBlendMode);
  1943. if (pDisplayInfo && pDisplayInfo->GetIVal() == 3)
  1944. {
  1945. static float fCurrentFPS, fCurrentFrameTime;
  1946. Blend(fCurrentFPS, fFPS, fBlendCur);
  1947. Blend(fCurrentFrameTime, GetTimer()->GetRealFrameTime() * 1000.0f, fBlendCur);
  1948. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.5f, ColorF(1.0f, 1.0f, 0.5f, 1.0f),
  1949. "FPS %.1f - %.1fms", fCurrentFPS, fCurrentFrameTime);
  1950. return;
  1951. }
  1952. // make level name
  1953. char szLevelName[128];
  1954. *szLevelName = 0;
  1955. {
  1956. int ii;
  1957. for (ii = strlen(m_szLevelFolder) - 2; ii > 0; ii--)
  1958. {
  1959. if (m_szLevelFolder[ii] == '\\' || m_szLevelFolder[ii] == '/')
  1960. {
  1961. break;
  1962. }
  1963. }
  1964. if (ii >= 0)
  1965. {
  1966. cry_strcpy(szLevelName, &m_szLevelFolder[ii + 1]);
  1967. for (int i = strlen(szLevelName) - 1; i > 0; i--)
  1968. {
  1969. if (szLevelName[i] == '\\' || szLevelName[i] == '/')
  1970. {
  1971. szLevelName[i] = 0;
  1972. }
  1973. }
  1974. }
  1975. }
  1976. Matrix33 m = Matrix33(GetRenderingCamera().GetMatrix());
  1977. //m.OrthonormalizeFast(); // why is that needed? is it?
  1978. Ang3 aAng = RAD2DEG(Ang3::GetAnglesXYZ(m));
  1979. Vec3 vPos = GetRenderingCamera().GetPosition();
  1980. // Time of day info
  1981. int hours = 0;
  1982. int minutes = 0;
  1983. ITimeOfDay* timeOfDay = GetTimeOfDay();
  1984. if (timeOfDay)
  1985. {
  1986. float time = timeOfDay->GetTime();
  1987. hours = (int)time;
  1988. minutes = (int)((time - hours) * 60);
  1989. }
  1990. // display out of memory message if an allocation failed
  1991. IF (gEnv->bIsOutOfMemory, 0)
  1992. {
  1993. ColorF fColor(1.0f, 0.0f, 0.0f, 1.0f);
  1994. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 4.0f, fColor, "**** Out of Memory ****");
  1995. fTextPosY += 40.0f;
  1996. }
  1997. // display out of memory message if an allocation failed
  1998. IF (gEnv->bIsOutOfVideoMemory, 0)
  1999. {
  2000. ColorF fColor(1.0f, 0.0f, 0.0f, 1.0f);
  2001. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 4.0f, fColor, "**** Out of Video Memory ****");
  2002. fTextPosY += 40.0f;
  2003. }
  2004. float fogCullDist = 0.0f;
  2005. Vec2 vViewportScale = Vec2(0.0f, 0.0f);
  2006. m_pRenderer->EF_Query(EFQ_GetFogCullDistance, fogCullDist);
  2007. m_pRenderer->EF_Query(EFQ_GetViewportDownscaleFactor, vViewportScale);
  2008. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "CamPos=%.2f %.2f %.2f Angl=%3d %2d %3d ZN=%.2f ZF=%d",
  2009. vPos.x, vPos.y, vPos.z, (int)aAng.x, (int)aAng.y, (int)aAng.z,
  2010. GetRenderingCamera().GetNearPlane(), (int)GetRenderingCamera().GetFarPlane());
  2011. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "Cam FC=%.2f VS=%.2f,%.2f Zoom=%.2f Speed=%1.2f TimeOfDay=%02d:%02d",
  2012. fogCullDist, vViewportScale.x, vViewportScale.y,
  2013. GetZoomFactor(), GetAverageCameraSpeed(), hours, minutes);
  2014. // get version
  2015. const SFileVersion& ver = GetSystem()->GetFileVersion();
  2016. //char sVersion[128];
  2017. //ver.ToString(sVersion);
  2018. // Get memory usage.
  2019. static IMemoryManager::SProcessMemInfo processMemInfo;
  2020. {
  2021. static int nGetMemInfoCount = 0;
  2022. if ((nGetMemInfoCount & 0x1F) == 0 && GetISystem()->GetIMemoryManager())
  2023. {
  2024. // Only get mem stats every 32 frames.
  2025. GetISystem()->GetIMemoryManager()->GetProcessMemInfo(processMemInfo);
  2026. }
  2027. nGetMemInfoCount++;
  2028. }
  2029. bool bMultiGPU;
  2030. m_pRenderer->EF_Query(EFQ_MultiGPUEnabled, bMultiGPU);
  2031. const char* pRenderType(0);
  2032. if (AZ::Interface<AzFramework::AtomActiveInterface>::Get())
  2033. {
  2034. pRenderType = "DX11";
  2035. }
  2036. else
  2037. {
  2038. switch (gEnv->pRenderer->GetRenderType())
  2039. {
  2040. case eRT_OpenGL:
  2041. pRenderType = "GL";
  2042. break;
  2043. case eRT_DX11:
  2044. pRenderType = "DX11";
  2045. break;
  2046. case eRT_DX12:
  2047. pRenderType = "DX12";
  2048. break;
  2049. case eRT_Xenia:
  2050. pRenderType = "Xenia";
  2051. break;
  2052. case eRT_Jasper:
  2053. pRenderType = "Jasper";
  2054. break;
  2055. case eRT_Provo:
  2056. pRenderType = "Provo";
  2057. break;
  2058. case eRT_Metal:
  2059. pRenderType = "Metal";
  2060. break;
  2061. case eRT_Null:
  2062. pRenderType = "Null";
  2063. break;
  2064. case eRT_Undefined:
  2065. default:
  2066. assert(0);
  2067. pRenderType = "Undefined";
  2068. break;
  2069. }
  2070. }
  2071. assert(gEnv->pSystem);
  2072. bool bTextureStreamingEnabled = false;
  2073. m_pRenderer->EF_Query(EFQ_TextureStreamingEnabled, bTextureStreamingEnabled);
  2074. const bool bCGFStreaming = GetCVars()->e_StreamCgf && m_pObjManager;
  2075. const bool bTexStreaming = gEnv->pSystem->GetStreamEngine() && bTextureStreamingEnabled;
  2076. char szFlags[128], * szFlagsEnd = szFlags;
  2077. #ifndef _RELEASE
  2078. ESystemConfigSpec spec = GetISystem()->GetConfigSpec();
  2079. switch (spec)
  2080. {
  2081. case CONFIG_AUTO_SPEC:
  2082. AppendString(szFlagsEnd, "Auto");
  2083. break;
  2084. case CONFIG_LOW_SPEC:
  2085. AppendString(szFlagsEnd, "LowSpec");
  2086. break;
  2087. case CONFIG_MEDIUM_SPEC:
  2088. AppendString(szFlagsEnd, "MedSpec");
  2089. break;
  2090. case CONFIG_HIGH_SPEC:
  2091. AppendString(szFlagsEnd, "HighSpec");
  2092. break;
  2093. case CONFIG_VERYHIGH_SPEC:
  2094. AppendString(szFlagsEnd, "VeryHighSpec");
  2095. break;
  2096. default:
  2097. assert(0);
  2098. }
  2099. #endif
  2100. #ifndef CONSOLE_CONST_CVAR_MODE
  2101. static ICVar* pMultiThreaded = GetConsole()->GetCVar("r_MultiThreaded");
  2102. if (pMultiThreaded && pMultiThreaded->GetIVal() > 0)
  2103. #endif
  2104. AppendString(szFlagsEnd, "MT");
  2105. char* sAAMode = NULL;
  2106. m_pRenderer->EF_Query(EFQ_AAMode, sAAMode);
  2107. AppendString(szFlagsEnd, sAAMode);
  2108. if (IsAreaActivationInUse())
  2109. {
  2110. AppendString(szFlagsEnd, "LA");
  2111. }
  2112. if (bMultiGPU)
  2113. {
  2114. AppendString(szFlagsEnd, "MGPU");
  2115. }
  2116. if (gEnv->pSystem->IsDevMode())
  2117. {
  2118. AppendString(szFlagsEnd, gEnv->IsEditor() ? "DevMode (Editor)" : "DevMode");
  2119. }
  2120. if (bCGFStreaming || bTexStreaming)
  2121. {
  2122. if (bCGFStreaming && !bTexStreaming)
  2123. {
  2124. AppendString(szFlagsEnd, "StG");
  2125. }
  2126. if (bTexStreaming && !bCGFStreaming)
  2127. {
  2128. AppendString(szFlagsEnd, "StT");
  2129. }
  2130. if (bTexStreaming && bCGFStreaming)
  2131. {
  2132. AppendString(szFlagsEnd, "StGT");
  2133. }
  2134. }
  2135. // remove last space
  2136. if (szFlags != szFlagsEnd)
  2137. {
  2138. *(szFlagsEnd - 1) = 0;
  2139. }
  2140. #ifdef _RELEASE
  2141. const char* mode = "Release";
  2142. #else
  2143. const char* mode = "Profile";
  2144. #endif
  2145. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "%s %s %dbit %s %s [%d.%d]",
  2146. pRenderType, mode, (int)sizeof(char*) * 8, szFlags, szLevelName, ver.v[1], ver.v[0]);
  2147. // Polys in scene
  2148. int nPolygons, nShadowPolygons;
  2149. GetRenderer()->GetPolyCount(nPolygons, nShadowPolygons);
  2150. int nDrawCalls, nShadowGenDrawCalls;
  2151. GetRenderer()->GetCurrentNumberOfDrawCalls(nDrawCalls, nShadowGenDrawCalls);
  2152. int nGeomInstances = GetRenderer()->GetNumGeomInstances();
  2153. int nGeomInstanceDrawCalls = GetRenderer()->GetNumGeomInstanceDrawCalls();
  2154. if (fBlendCur != 1.f)
  2155. {
  2156. // Smooth over time.
  2157. static float fPolygons, fShadowVolPolys, fDrawCalls, fShadowGenDrawCalls, fGeomInstances, fGeomInstanceDrawCalls;
  2158. Blend(fPolygons, nPolygons, fBlendCur);
  2159. Blend(fShadowVolPolys, nShadowPolygons, fBlendCur);
  2160. Blend(fDrawCalls, nDrawCalls, fBlendCur);
  2161. Blend(fShadowGenDrawCalls, nShadowGenDrawCalls, fBlendCur);
  2162. Blend(fGeomInstances, nGeomInstances, fBlendCur);
  2163. Blend(fGeomInstanceDrawCalls, nGeomInstanceDrawCalls, fBlendCur);
  2164. }
  2165. //
  2166. static float m_lastAverageDPTime = -FLT_MAX;
  2167. float curTime = gEnv->pTimer->GetAsyncCurTime();
  2168. static int lastDrawCalls = 0;
  2169. static int lastShadowGenDrawCalls = 0;
  2170. static int avgPolys = 0;
  2171. static int avgShadowPolys = 0;
  2172. static int sumPolys = 0;
  2173. static int sumShadowPolys = 0;
  2174. static int nPolysFrames = 0;
  2175. if (curTime < m_lastAverageDPTime)
  2176. {
  2177. m_lastAverageDPTime = curTime;
  2178. }
  2179. if (curTime - m_lastAverageDPTime > 1.0f)
  2180. {
  2181. lastDrawCalls = nDrawCalls;
  2182. lastShadowGenDrawCalls = nShadowGenDrawCalls;
  2183. m_lastAverageDPTime = curTime;
  2184. avgPolys = nPolysFrames ? sumPolys / nPolysFrames : 0;
  2185. avgShadowPolys = nPolysFrames ? sumShadowPolys / nPolysFrames : 0;
  2186. sumPolys = nPolygons;
  2187. sumShadowPolys = nShadowPolygons;
  2188. nPolysFrames = 1;
  2189. }
  2190. else
  2191. {
  2192. nPolysFrames++;
  2193. sumPolys += nPolygons;
  2194. sumShadowPolys += nShadowPolygons;
  2195. }
  2196. //
  2197. int nMaxDrawCalls = GetCVars()->e_MaxDrawCalls <= 0 ? 2000 : GetCVars()->e_MaxDrawCalls;
  2198. bool bInRed = (nDrawCalls + nShadowGenDrawCalls) > nMaxDrawCalls;
  2199. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, bInRed ? Col_Red : Col_White, "DP: %04d (%04d) ShadowGen:%04d (%04d) - Total: %04d Instanced: %04d",
  2200. nDrawCalls, lastDrawCalls, nShadowGenDrawCalls, lastShadowGenDrawCalls, nDrawCalls + nShadowGenDrawCalls, nDrawCalls + nShadowGenDrawCalls - nGeomInstances + nGeomInstanceDrawCalls);
  2201. #if defined(MOBILE)
  2202. bInRed = nPolygons > 500000;
  2203. #else
  2204. bInRed = nPolygons > 1500000;
  2205. #endif
  2206. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, bInRed ? Col_Red : Col_White, "Polys: %03d,%03d (%03d,%03d) Shadow:%03d,%03d (%03d,%03d)",
  2207. nPolygons / 1000, nPolygons % 1000, avgPolys / 1000, avgPolys % 1000,
  2208. nShadowPolygons / 1000, nShadowPolygons % 1000, avgShadowPolys / 1000, avgShadowPolys % 1000);
  2209. {
  2210. SShaderCacheStatistics stats;
  2211. m_pRenderer->EF_Query(EFQ_GetShaderCacheInfo, stats);
  2212. {
  2213. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_White, "ShaderCache: %d GCM | %d Async Reqs | Compile: %s",
  2214. (int)stats.m_nGlobalShaderCacheMisses, (int)stats.m_nNumShaderAsyncCompiles, stats.m_bShaderCompileActive ? "On" : "Off");
  2215. }
  2216. }
  2217. // print stats about CGF's streaming
  2218. if (bCGFStreaming)
  2219. {
  2220. static char szCGFStreaming[256] = "";
  2221. static SObjectsStreamingStatus objectsStreamingStatus = {0};
  2222. if (!(GetRenderer()->GetFrameID(false) & 15) || !szCGFStreaming[0] || GetCVars()->e_StreamCgfDebug)
  2223. {
  2224. m_pObjManager->GetObjectsStreamingStatus(objectsStreamingStatus);
  2225. sprintf_s(szCGFStreaming, 256, "CgfStrm: Loaded:%d InProg:%d All:%d Act:%d PcP:%d MemUsed:%2.2f MemReq:%2.2f Pool:%d",
  2226. objectsStreamingStatus.nReady, objectsStreamingStatus.nInProgress, objectsStreamingStatus.nTotal, objectsStreamingStatus.nActive,
  2227. (int)m_pObjManager->GetStreamPreCachePointDefs().size(),
  2228. float(objectsStreamingStatus.nAllocatedBytes) / 1024 / 1024, float(objectsStreamingStatus.nMemRequired) / 1024 / 1024, GetCVars()->e_StreamCgfPoolSize);
  2229. }
  2230. bool bOutOfMem((float(objectsStreamingStatus.nMemRequired) / 1024 / 1024) > GetCVars()->e_StreamCgfPoolSize);
  2231. bool bCloseToOutOfMem((float(objectsStreamingStatus.nMemRequired) / 1024 / 1024) > GetCVars()->e_StreamCgfPoolSize * 90 / 100);
  2232. ColorF color = Col_White;
  2233. if (bOutOfMem)
  2234. {
  2235. color = Col_Red;
  2236. }
  2237. else if (bCloseToOutOfMem)
  2238. {
  2239. color = Col_Orange;
  2240. }
  2241. // if(bTooManyRequests)
  2242. // color = Col_Magenta;
  2243. if ((pDisplayInfo->GetIVal() == 2 || GetCVars()->e_StreamCgfDebug) || bOutOfMem || bCloseToOutOfMem)
  2244. {
  2245. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, color, szCGFStreaming);
  2246. }
  2247. }
  2248. // print stats about textures' streaming
  2249. if (bTexStreaming)
  2250. {
  2251. static char szTexStreaming[256] = "";
  2252. static bool bCloseToOutOfMem = false;
  2253. static bool bOutOfMem = false;
  2254. static bool bTooManyRequests = false;
  2255. static bool bOverloadedPool = false;
  2256. static uint32 nTexCount = 0;
  2257. static uint32 nTexSize = 0;
  2258. float fTexBandwidthRequired = 0.f;
  2259. m_pRenderer->GetBandwidthStats(&fTexBandwidthRequired);
  2260. if (!(GetRenderer()->GetFrameID(false) % 30) || !szTexStreaming[0])
  2261. {
  2262. STextureStreamingStats stats(!(GetRenderer()->GetFrameID(false) % 120));
  2263. m_pRenderer->EF_Query(EFQ_GetTexStreamingInfo, stats);
  2264. if (!(GetRenderer()->GetFrameID(false) % 120))
  2265. {
  2266. bOverloadedPool = stats.bPoolOverflowTotally;
  2267. nTexCount = stats.nRequiredStreamedTexturesCount;
  2268. nTexSize = stats.nRequiredStreamedTexturesSize;
  2269. }
  2270. int nPlatformSize = nTexSize;
  2271. const int iPercentage = int((float)stats.nCurrentPoolSize / stats.nMaxPoolSize * 100.f);
  2272. const int iStaticPercentage = int((float)stats.nStaticTexturesSize / stats.nMaxPoolSize * 100.f);
  2273. sprintf_s(szTexStreaming, "TexStrm: TexRend: %u NumTex: %u Req:%.1fMB Mem(strm/stat/tot):%.1f/%.1f/%.1fMB(%d%%/%d%%) PoolSize:%" PRISIZE_T "MB PoolFrag:%.1f%%",
  2274. stats.nNumTexturesPerFrame, nTexCount, (float)nPlatformSize / 1024 / 1024,
  2275. (float)stats.nStreamedTexturesSize / 1024 / 1024, (float)stats.nStaticTexturesSize / 1024 / 1024, (float)stats.nCurrentPoolSize / 1024 / 1024,
  2276. iPercentage, iStaticPercentage, stats.nMaxPoolSize / 1024 / 1024,
  2277. stats.fPoolFragmentation * 100.0f
  2278. );
  2279. bOverloadedPool |= stats.bPoolOverflowTotally;
  2280. bCloseToOutOfMem = iPercentage >= 90;
  2281. bOutOfMem = stats.bPoolOverflow;
  2282. }
  2283. if (pDisplayInfo->GetIVal() == 2 || bCloseToOutOfMem || bTooManyRequests || bOverloadedPool)
  2284. {
  2285. ColorF color = Col_White;
  2286. if (bOutOfMem)
  2287. {
  2288. color = Col_Red;
  2289. }
  2290. else if (bCloseToOutOfMem)
  2291. {
  2292. color = Col_Orange;
  2293. }
  2294. if (bTooManyRequests)
  2295. {
  2296. color = Col_Magenta;
  2297. }
  2298. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, color, "%s", szTexStreaming);
  2299. }
  2300. if (pDisplayInfo->GetIVal() > 0 && bOverloadedPool)
  2301. {
  2302. DrawTextLeftAligned(0, 10, 2.3f, Col_Red, "Texture pool totally overloaded!");
  2303. }
  2304. }
  2305. {
  2306. static char szMeshPoolUse[256] = "";
  2307. static unsigned nFlushFrameId = 0U;
  2308. static unsigned nFallbackFrameId = 0U;
  2309. static SMeshPoolStatistics lastStats;
  2310. static SMeshPoolStatistics stats;
  2311. const unsigned nMainFrameId = GetRenderer()->GetFrameID(false);
  2312. m_pRenderer->EF_Query(EFQ_GetMeshPoolInfo, stats);
  2313. const int iPercentage = int((float)stats.nPoolInUse / (stats.nPoolSize ? stats.nPoolSize : 1U) * 100.f);
  2314. const int iVolatilePercentage = int((float)stats.nInstancePoolInUse / (stats.nInstancePoolSize ? stats.nInstancePoolSize : 1U) * 100.f);
  2315. nFallbackFrameId = lastStats.nFallbacks < stats.nFallbacks ? nMainFrameId : nFallbackFrameId;
  2316. nFlushFrameId = lastStats.nFlushes < stats.nFlushes ? nMainFrameId : nFlushFrameId;
  2317. const bool bOverflow = nMainFrameId - nFlushFrameId < 50;
  2318. const bool bFallback = nMainFrameId - nFallbackFrameId < 50;
  2319. sprintf_s(szMeshPoolUse,
  2320. "Mesh Pool: MemUsed:%.2fKB(%d%%%%) Peak %.fKB PoolSize:%" PRISIZE_T "KB Flushes %" PRISIZE_T " Fallbacks %.3fKB %s",
  2321. (float)stats.nPoolInUse / 1024,
  2322. iPercentage,
  2323. (float)stats.nPoolInUsePeak / 1024,
  2324. stats.nPoolSize / 1024,
  2325. stats.nFlushes,
  2326. (float)stats.nFallbacks / 1024.0f,
  2327. (bFallback ? "FULL!" : bOverflow ? "OVERFLOW" : ""));
  2328. if (stats.nPoolSize && (pDisplayInfo->GetIVal() == 2 || bOverflow || bFallback))
  2329. {
  2330. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE,
  2331. bFallback ? Col_Red : bOverflow ? Col_Orange : Col_White,
  2332. szMeshPoolUse);
  2333. }
  2334. if (stats.nPoolSize && pDisplayInfo->GetIVal() == 2)
  2335. {
  2336. char szVolatilePoolsUse[256];
  2337. sprintf_s(szVolatilePoolsUse,
  2338. "Mesh Instance Pool: MemUsed:%.2fKB(%d%%%%) Peak %.fKB PoolSize:%" PRISIZE_T "KB Fallbacks %.3fKB",
  2339. (float)stats.nInstancePoolInUse / 1024,
  2340. iVolatilePercentage,
  2341. (float)stats.nInstancePoolInUsePeak / 1024,
  2342. stats.nInstancePoolSize / 1024,
  2343. (float)stats.nInstanceFallbacks / 1024.0f);
  2344. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE,
  2345. Col_White, szVolatilePoolsUse);
  2346. }
  2347. memcpy(&lastStats, &stats, sizeof(lastStats));
  2348. }
  2349. // streaming info
  2350. {
  2351. IStreamEngine* pSE = gEnv->pSystem->GetStreamEngine();
  2352. if (pSE)
  2353. {
  2354. SStreamEngineStatistics& stats = pSE->GetStreamingStatistics();
  2355. SStreamEngineOpenStats openStats;
  2356. pSE->GetStreamingOpenStatistics(openStats);
  2357. static char szStreaming[128] = "";
  2358. if (!(GetRenderer()->GetFrameID(false) & 7))
  2359. {
  2360. if (pDisplayInfo->GetIVal() == 2)
  2361. {
  2362. sprintf_s(szStreaming, "Streaming IO: ACT: %3dmsec, Jobs:%2d Total:%5d",
  2363. (uint32)stats.fAverageCompletionTime, openStats.nOpenRequestCount, stats.nTotalStreamingRequestCount);
  2364. }
  2365. else
  2366. {
  2367. sprintf_s(szStreaming, "Streaming IO: ACT: %3dmsec, Jobs:%2d",
  2368. (uint32)stats.fAverageCompletionTime, openStats.nOpenRequestCount);
  2369. }
  2370. }
  2371. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, szStreaming);
  2372. if (stats.bTempMemOutOfBudget)
  2373. {
  2374. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.3f, Col_Red, "Temporary Streaming Memory Pool Out of Budget!");
  2375. }
  2376. }
  2377. if (pDisplayInfo && pDisplayInfo->GetIVal() == 2) // more streaming info
  2378. {
  2379. SStreamEngineStatistics& stats = gEnv->pSystem->GetStreamEngine()->GetStreamingStatistics();
  2380. { // HDD stats
  2381. static char szStreaming[512] = "";
  2382. sprintf_s(szStreaming, "HDD: BW:%1.2f|%1.2fMb/s (Eff:%2.1f|%2.1fMb/s) - Seek:%1.2fGB - Active:%2.1f%%%%",
  2383. (float)stats.hddInfo.nCurrentReadBandwidth / (1024 * 1024), (float)stats.hddInfo.nSessionReadBandwidth / (1024 * 1024),
  2384. (float)stats.hddInfo.nActualReadBandwidth / (1024 * 1024), (float)stats.hddInfo.nAverageActualReadBandwidth / (1024 * 1024),
  2385. (float)stats.hddInfo.nAverageSeekOffset / (1024 * 1024), stats.hddInfo.fAverageActiveTime);
  2386. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, szStreaming);
  2387. }
  2388. }
  2389. }
  2390. //////////////////////////////////////////////////////////////////////////
  2391. // Display Info about dynamic lights.
  2392. //////////////////////////////////////////////////////////////////////////
  2393. {
  2394. {
  2395. #ifndef _RELEASE
  2396. // Checkpoint loading information
  2397. if (!gEnv->bMultiplayer)
  2398. {
  2399. ISystem::ICheckpointData data;
  2400. gEnv->pSystem->GetCheckpointData(data);
  2401. if (data.m_loadOrigin != ISystem::eLLO_Unknown)
  2402. {
  2403. static const char* loadStates[] =
  2404. {
  2405. "",
  2406. "New Level",
  2407. "Level to Level",
  2408. "Resumed Game",
  2409. "Map Command",
  2410. };
  2411. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.3f, Col_White, "%s, Checkpoint loads: %i", loadStates[(int)data.m_loadOrigin], (int)data.m_totalLoads);
  2412. }
  2413. }
  2414. #endif
  2415. int nPeakMemMB = (int)(processMemInfo.PeakPagefileUsage >> 20);
  2416. int nVirtMemMB = (int)(processMemInfo.PagefileUsage >> 20);
  2417. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "Mem=%d Peak=%d DLights=(%d)", nVirtMemMB, nPeakMemMB, m_nRealLightsNum + m_nDeferredLightsNum);
  2418. uint32 nShadowFrustums = 0;
  2419. uint32 nShadowAllocs = 0;
  2420. uint32 nShadowMaskChannels = 0;
  2421. m_pRenderer->EF_Query(EFQ_GetShadowPoolFrustumsNum, nShadowFrustums);
  2422. m_pRenderer->EF_Query(EFQ_GetShadowPoolAllocThisFrameNum, nShadowAllocs);
  2423. m_pRenderer->EF_Query(EFQ_GetShadowMaskChannelsNum, nShadowMaskChannels);
  2424. bool bThrash = (nShadowAllocs & 0x80000000) ? true : false;
  2425. nShadowAllocs &= ~0x80000000;
  2426. uint32 nAvailableShadowMaskChannels = nShadowMaskChannels >> 16;
  2427. uint32 nUsedShadowMaskChannels = nShadowMaskChannels & 0xFFFF;
  2428. bool bTooManyLights = nUsedShadowMaskChannels > nAvailableShadowMaskChannels ? true : false;
  2429. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, (nShadowFrustums || nShadowAllocs) ? Col_Yellow : Col_White, "%d Shadow Mask Channels, %3d Shadow Frustums, %3d Frustum Renders This Frame",
  2430. nUsedShadowMaskChannels, nShadowFrustums, nShadowAllocs);
  2431. if (bThrash)
  2432. {
  2433. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Red, "SHADOW POOL THRASHING!!!");
  2434. }
  2435. if (bTooManyLights)
  2436. {
  2437. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Red, "TOO MANY SHADOW CASTING LIGHTS (%d/%d)!!!", nUsedShadowMaskChannels, nAvailableShadowMaskChannels);
  2438. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Red, "Consider increasing 'r_ShadowCastingLightsMaxCount'");
  2439. }
  2440. #ifndef _RELEASE
  2441. uint32 numTiledShadingSkippedLights;
  2442. m_pRenderer->EF_Query(EFQ_GetTiledShadingSkippedLightsNum, numTiledShadingSkippedLights);
  2443. if (numTiledShadingSkippedLights > 0)
  2444. {
  2445. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, Col_Red, "TILED SHADING: SKIPPED %d LIGHTS", numTiledShadingSkippedLights);
  2446. }
  2447. if (GetCVars()->e_levelStartupFrameNum)
  2448. {
  2449. static float startupAvgFPS = 0.f;
  2450. static float levelStartupTime = 0;
  2451. static int levelStartupFrameEnd = GetCVars()->e_levelStartupFrameNum + GetCVars()->e_levelStartupFrameDelay;
  2452. int curFrameID = GetRenderer()->GetFrameID(false);
  2453. if (curFrameID >= GetCVars()->e_levelStartupFrameDelay)
  2454. {
  2455. if (curFrameID == GetCVars()->e_levelStartupFrameDelay)
  2456. {
  2457. levelStartupTime = gEnv->pTimer->GetAsyncCurTime();
  2458. }
  2459. if (curFrameID == levelStartupFrameEnd)
  2460. {
  2461. startupAvgFPS = (float)GetCVars()->e_levelStartupFrameNum / (gEnv->pTimer->GetAsyncCurTime() - levelStartupTime);
  2462. }
  2463. if (curFrameID >= levelStartupFrameEnd)
  2464. {
  2465. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 2.f, Col_Red, "Startup AVG FPS: %.2f", startupAvgFPS);
  2466. fTextPosY += fTextStepY;
  2467. }
  2468. }
  2469. }
  2470. #endif //_RELEASE
  2471. }
  2472. m_nDeferredLightsNum = 0;
  2473. }
  2474. assert(pDisplayInfo);
  2475. if (bEnhanced)
  2476. {
  2477. #define CONVX(x) (((x) / (float)gUpdateTimesNum))
  2478. #define CONVY(y) (1.f - ((y) / 720.f))
  2479. #define TICKS_TO_MS(t) (1000.f * gEnv->pTimer->TicksToSeconds(t))
  2480. # define MAX_PHYS_TIME 32.f
  2481. # define MAX_PLE_TIME 4.f
  2482. uint32 gUpdateTimeIdx = 0, gUpdateTimesNum = 0;
  2483. const sUpdateTimes* gUpdateTimes = gEnv->pSystem->GetUpdateTimeStats(gUpdateTimeIdx, gUpdateTimesNum);
  2484. if (pDisplayInfo->GetIVal() >= 5)
  2485. {
  2486. const SAuxGeomRenderFlags flags = gEnv->pRenderer->GetIRenderAuxGeom()->GetRenderFlags();
  2487. SAuxGeomRenderFlags newFlags(flags);
  2488. newFlags.SetAlphaBlendMode(e_AlphaNone);
  2489. newFlags.SetMode2D3DFlag(e_Mode2D);
  2490. newFlags.SetCullMode(e_CullModeNone);
  2491. newFlags.SetDepthWriteFlag(e_DepthWriteOff);
  2492. newFlags.SetDepthTestFlag(e_DepthTestOff);
  2493. newFlags.SetFillMode(e_FillModeSolid);
  2494. gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(newFlags);
  2495. const ColorF colorPhysFull = Col_Blue;
  2496. const ColorF colorSysFull = Col_Green;
  2497. const ColorF colorRenFull = Col_Red;
  2498. const ColorF colorPhysHalf = colorPhysFull * 0.15f;
  2499. const ColorF colorSysHalf = colorSysFull * 0.15f;
  2500. const ColorF colorRenHalf = colorRenFull * 0.15f;
  2501. float phys = (TICKS_TO_MS(gUpdateTimes[0].PhysStepTime) / 66.f) * 720.f;
  2502. float sys = (TICKS_TO_MS(gUpdateTimes[0].SysUpdateTime) / 66.f) * 720.f;
  2503. float ren = (TICKS_TO_MS(gUpdateTimes[0].RenderTime) / 66.f) * 720.f;
  2504. float _lerp = ((float)(max((int)gUpdateTimeIdx - (int)0, 0) / (float)gUpdateTimesNum));
  2505. ColorB colorPhysLast;
  2506. colorPhysLast.lerpFloat(colorPhysFull, colorPhysHalf, _lerp);
  2507. ColorB colorSysLast;
  2508. colorSysLast.lerpFloat(colorSysFull, colorSysHalf, _lerp);
  2509. ColorB colorRenLast;
  2510. colorRenLast.lerpFloat(colorRenFull, colorRenHalf, _lerp);
  2511. Vec3 lastPhys(CONVX(0), CONVY(phys), 1.f);
  2512. Vec3 lastSys(CONVX(0), CONVY(sys), 1.f);
  2513. Vec3 lastRen(CONVX(0), CONVY(ren), 1.f);
  2514. for (uint32 i = 0; i < gUpdateTimesNum; ++i)
  2515. {
  2516. const float x = (float)i;
  2517. _lerp = ((float)(max((int)gUpdateTimeIdx - (int)i, 0) / (float)gUpdateTimesNum));
  2518. const sUpdateTimes& sample = gUpdateTimes[i];
  2519. phys = (TICKS_TO_MS(sample.PhysStepTime) / 66.f) * 720.f;
  2520. sys = (TICKS_TO_MS(sample.SysUpdateTime) / 66.f) * 720.f;
  2521. ren = (TICKS_TO_MS(sample.RenderTime) / 66.f) * 720.f;
  2522. Vec3 curPhys(CONVX(x), CONVY(phys), 1.f);
  2523. Vec3 curSys(CONVX(x), CONVY(sys), 1.f);
  2524. Vec3 curRen(CONVX(x), CONVY(ren), 1.f);
  2525. ColorB colorPhys;
  2526. colorPhys.lerpFloat(colorPhysFull, colorPhysHalf, _lerp);
  2527. ColorB colorSys;
  2528. colorSys.lerpFloat(colorSysFull, colorSysHalf, _lerp);
  2529. ColorB colorRen;
  2530. colorRen.lerpFloat(colorRenFull, colorRenHalf, _lerp);
  2531. gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(lastPhys, colorPhysLast, curPhys, colorPhys);
  2532. gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(lastSys, colorSysLast, curSys, colorSys);
  2533. gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(lastRen, colorRenLast, curRen, colorRen);
  2534. lastPhys = curPhys;
  2535. colorPhysLast = colorPhys;
  2536. lastSys = curSys;
  2537. colorSysLast = colorSys;
  2538. lastRen = curRen;
  2539. colorRenLast = colorRen;
  2540. }
  2541. gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(flags);
  2542. }
  2543. const float curPhysTime = TICKS_TO_MS(gUpdateTimes[gUpdateTimeIdx].PhysStepTime);
  2544. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE_SMALL, curPhysTime > MAX_PHYS_TIME ? Col_Red : Col_White, "%3.1f ms Phys", curPhysTime);
  2545. const float curPhysWaitTime = TICKS_TO_MS(gUpdateTimes[gUpdateTimeIdx].physWaitTime);
  2546. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE_SMALL, curPhysTime > MAX_PHYS_TIME ? Col_Red : Col_White, "%3.1f ms WaitPhys", curPhysWaitTime);
  2547. float partTicks = 0;
  2548. //3dengine stats from RenderWorld
  2549. {
  2550. #if defined(MOBILE)
  2551. const float maxVal = 12.f;
  2552. #else
  2553. const float maxVal = 50.f;
  2554. #endif
  2555. float fTimeMS = TICKS_TO_MS(m_nRenderWorldUSecs) - partTicks;
  2556. DrawTextRightAligned(fTextPosX, fTextPosY += (fTextStepY - STEP_SMALL_DIFF), DISPLAY_INFO_SCALE_SMALL, fTimeMS > maxVal ? Col_Red : Col_White, "%.2f ms RendWorld", fTimeMS);
  2557. }
  2558. {
  2559. SStreamEngineStatistics stat = gEnv->pSystem->GetStreamEngine()->GetStreamingStatistics();
  2560. float fTimeMS = 1000.0f * gEnv->pTimer->TicksToSeconds(stat.nMainStreamingThreadWait);
  2561. DrawTextRightAligned(fTextPosX, fTextPosY += (fTextStepY - STEP_SMALL_DIFF),
  2562. DISPLAY_INFO_SCALE_SMALL, Col_White, "%3.1f ms StreamFin", fTimeMS);
  2563. }
  2564. {
  2565. SNetworkPerformance stat;
  2566. gEnv->pNetwork->GetPerformanceStatistics(&stat);
  2567. float fTimeMS = 1000.0f * gEnv->pTimer->TicksToSeconds(stat.m_nNetworkSync);
  2568. DrawTextRightAligned(fTextPosX, fTextPosY += (fTextStepY - STEP_SMALL_DIFF),
  2569. DISPLAY_INFO_SCALE_SMALL, Col_White, "%3.1f ms NetworkSync", fTimeMS);
  2570. }
  2571. }
  2572. #undef MAX_PHYS_TIME
  2573. #undef TICKS_TO_MS
  2574. #undef CONVY
  2575. #undef CONVX
  2576. //////////////////////////////////////////////////////////////////////////
  2577. // Display Thermal information of the device (if supported)
  2578. //////////////////////////////////////////////////////////////////////////
  2579. if (ThermalInfoRequestsBus::GetTotalNumOfEventHandlers())
  2580. {
  2581. const int thermalSensorCount = static_cast<int>(ThermalSensorType::Count);
  2582. const char* sensorStrings[thermalSensorCount] = { "CPU", "GPU", "Battery" };
  2583. for (int i = 0; i < thermalSensorCount; ++i)
  2584. {
  2585. float temperature = 0.f;
  2586. ThermalSensorType sensor = static_cast<ThermalSensorType>(i);
  2587. EBUS_EVENT_RESULT(temperature, ThermalInfoRequestsBus, GetSensorTemp, sensor);
  2588. AZStd::string tempText;
  2589. ColorF tempColor;
  2590. if (temperature > 0.f)
  2591. {
  2592. float overheatingTemp = 0.f;
  2593. EBUS_EVENT_RESULT(overheatingTemp, ThermalInfoRequestsBus, GetSensorOverheatingTemp, sensor);
  2594. tempText = AZStd::string::format(" %.1f C", temperature);
  2595. tempColor = temperature >= overheatingTemp ? Col_Red : Col_White;
  2596. }
  2597. else
  2598. {
  2599. tempText = "N/A";
  2600. tempColor = Col_White;
  2601. }
  2602. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE, tempColor, "%s Temp %s", sensorStrings[i], tempText.c_str());
  2603. }
  2604. }
  2605. //////////////////////////////////////////////////////////////////////////
  2606. // Display Current fps
  2607. //////////////////////////////////////////////////////////////////////////
  2608. if (iBlendMode)
  2609. {
  2610. // Track FPS frequency, report min/max.
  2611. Blend(m_fAverageFPS, fFPS, fBlendCur);
  2612. Blend(m_fMinFPSDecay, fFPS, fBlendCur);
  2613. if (fFPS <= m_fMinFPSDecay)
  2614. {
  2615. m_fMinFPS = m_fMinFPSDecay = fFPS;
  2616. }
  2617. Blend(m_fMaxFPSDecay, fFPS, fBlendCur);
  2618. if (fFPS >= m_fMaxFPSDecay)
  2619. {
  2620. m_fMaxFPS = m_fMaxFPSDecay = fFPS;
  2621. }
  2622. const char* sMode = "";
  2623. switch (iBlendMode)
  2624. {
  2625. case 1:
  2626. sMode = "frame avg";
  2627. break;
  2628. case 2:
  2629. sMode = "time avg";
  2630. break;
  2631. case 3:
  2632. sMode = "peak hold";
  2633. break;
  2634. }
  2635. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.5f, ColorF(1.0f, 1.0f, 0.5f, 1.0f),
  2636. "FPS %.1f [%.0f..%.0f], %s over %.1f s",
  2637. m_fAverageFPS, m_fMinFPS, m_fMaxFPS, sMode, fBlendTime);
  2638. }
  2639. else
  2640. {
  2641. const int nHistorySize = 16;
  2642. static float arrfFrameRateHistory[nHistorySize] = {0};
  2643. static int nFrameId = 0;
  2644. nFrameId++;
  2645. int nSlotId = nFrameId % nHistorySize;
  2646. assert(nSlotId >= 0 && nSlotId < nHistorySize);
  2647. arrfFrameRateHistory[nSlotId] = min(9999.f, GetTimer()->GetFrameRate());
  2648. float fMinFPS = 9999.0f;
  2649. float fMaxFPS = 0;
  2650. for (int i = 0; i < nHistorySize; i++)
  2651. {
  2652. if (arrfFrameRateHistory[i] < fMinFPS)
  2653. {
  2654. fMinFPS = arrfFrameRateHistory[i];
  2655. }
  2656. if (arrfFrameRateHistory[i] > fMaxFPS)
  2657. {
  2658. fMaxFPS = arrfFrameRateHistory[i];
  2659. }
  2660. }
  2661. float fFrameRate = 0;
  2662. float fValidFrames = 0;
  2663. for (int i = 0; i < nHistorySize; i++)
  2664. {
  2665. int s = (nFrameId - i) % nHistorySize;
  2666. fFrameRate += arrfFrameRateHistory[s];
  2667. fValidFrames++;
  2668. }
  2669. fFrameRate /= fValidFrames;
  2670. m_fAverageFPS = fFrameRate;
  2671. m_fMinFPS = m_fMinFPSDecay = fMinFPS;
  2672. m_fMaxFPS = m_fMaxFPSDecay = fMaxFPS;
  2673. //only difference to r_DisplayInfo 1, need ms for GPU time
  2674. float fMax = (int(GetCurTimeSec() * 2) & 1) ? 999.f : 888.f;
  2675. if (bEnhanced)
  2676. {
  2677. /* DrawTextRightAligned( fTextPosX, fTextPosY+=fTextStepY, "%6.2f ~%6.2f ms (%6.2f..%6.2f) CPU",
  2678. GetTimer()->GetFrameTime()*1000.0f, 1000.0f/max(0.0001f,fFrameRate),
  2679. 1000.0f/max(0.0001f,fMinFPS),
  2680. 1000.0f/max(0.0001f,fMaxFPS));
  2681. */
  2682. const RPProfilerStats* pFrameRPPStats = GetRenderer()->GetRPPStats(eRPPSTATS_OverallFrame);
  2683. float gpuTime = pFrameRPPStats ? pFrameRPPStats->gpuTime : 0.0f;
  2684. static float sGPUTime = 0.f;
  2685. if (gpuTime < 1000.f && gpuTime > 0.01f)
  2686. {
  2687. sGPUTime = gpuTime; //catch sporadic jumps
  2688. }
  2689. if (sGPUTime > 0.01f)
  2690. {
  2691. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, DISPLAY_INFO_SCALE_SMALL, (gpuTime >= 40.f) ? Col_Red : Col_White, "%3.1f ms GPU", sGPUTime);
  2692. }
  2693. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.4f, ColorF(1.0f, 1.0f, 0.2f, 1.0f), "FPS %5.1f (%3d..%3d)(%3.1f ms)",
  2694. min(fMax, fFrameRate), (int)min(fMax, fMinFPS), (int)min(fMax, fMaxFPS), GetTimer()->GetFrameTime() * 1000.0f);
  2695. }
  2696. else
  2697. {
  2698. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, 1.4f, ColorF(1.0f, 1.0f, 0.2f, 1.0f), "FPS %5.1f (%3d..%3d)",
  2699. min(fMax, fFrameRate), (int)min(fMax, fMinFPS), (int)min(fMax, fMaxFPS));
  2700. }
  2701. }
  2702. #ifndef _RELEASE
  2703. if (GetCVars()->e_GsmStats)
  2704. {
  2705. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "--------------- GSM Stats ---------------");
  2706. if (m_pSun && m_pSun->m_pShadowMapInfo)
  2707. {
  2708. CLightEntity::ShadowMapInfo* pSMI = m_pSun->m_pShadowMapInfo;
  2709. int arrGSMCastersCount[MAX_GSM_LODS_NUM];
  2710. memset(arrGSMCastersCount, 0, sizeof(arrGSMCastersCount));
  2711. char szText[256] = "Objects count per shadow map: ";
  2712. for (int nLod = 0; nLod < Get3DEngine()->GetShadowsCascadeCount(NULL) && nLod < MAX_GSM_LODS_NUM; nLod++)
  2713. {
  2714. ShadowMapFrustum*& pLsource = pSMI->pGSM[nLod];
  2715. if (nLod)
  2716. {
  2717. azstrcat(szText, AZ_ARRAY_SIZE(szText), ", ");
  2718. }
  2719. char* pstr = szText + strlen(szText);
  2720. sprintf_s(pstr, sizeof(szText) - (pstr - szText), "%d", pLsource->m_castersList.Count());
  2721. }
  2722. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, szText);
  2723. }
  2724. for (int nSunInUse = 0; nSunInUse < 2; nSunInUse++)
  2725. {
  2726. if (nSunInUse)
  2727. {
  2728. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "WithSun ListId FrNum UserNum");
  2729. }
  2730. else
  2731. {
  2732. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "NoSun ListId FrNum UserNum");
  2733. }
  2734. // TODO: For Nick, check if needed anymore
  2735. //for(ShadowFrustumListsCache::iterator it = m_FrustumsCache[nSunInUse].begin(); it != m_FrustumsCache[nSunInUse].end(); ++it)
  2736. //{
  2737. // int nListId = (int)it->first;
  2738. // PodArray<ShadowMapFrustum*> * pList = it->second;
  2739. // DrawTextRightAligned( fTextPosX, fTextPosY+=fTextStepY,
  2740. // "%8d %8d %8d",
  2741. // nListId,
  2742. // pList->Count(), m_FrustumsCacheUsers[nSunInUse][nListId]);
  2743. //}
  2744. }
  2745. }
  2746. // objects counter
  2747. if (GetCVars()->e_ObjStats)
  2748. {
  2749. #define DRAW_OBJ_STATS(_var) DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "%s: %d", (#_var), GetInstCount(_var))
  2750. DRAW_OBJ_STATS(eERType_NotRenderNode);
  2751. DRAW_OBJ_STATS(eERType_Light);
  2752. DRAW_OBJ_STATS(eERType_Cloud);
  2753. DRAW_OBJ_STATS(eERType_FogVolume);
  2754. DRAW_OBJ_STATS(eERType_Decal);
  2755. DRAW_OBJ_STATS(eERType_WaterVolume);
  2756. DRAW_OBJ_STATS(eERType_DistanceCloud);
  2757. DRAW_OBJ_STATS(eERType_VolumeObject);
  2758. DRAW_OBJ_STATS(eERType_Rope);
  2759. DRAW_OBJ_STATS(eERType_PrismObject);
  2760. DRAW_OBJ_STATS(eERType_RenderComponent);
  2761. DRAW_OBJ_STATS(eERType_StaticMeshRenderComponent);
  2762. DRAW_OBJ_STATS(eERType_DynamicMeshRenderComponent);
  2763. DRAW_OBJ_STATS(eERType_SkinnedMeshRenderComponent);
  2764. DRAW_OBJ_STATS(eERType_GameEffect);
  2765. DRAW_OBJ_STATS(eERType_BreakableGlass);
  2766. if (IsObjectTreeReady())
  2767. {
  2768. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "--- By list type: ---");
  2769. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, " Main: %d", m_pObjectsTree->GetObjectsCount(eMain));
  2770. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "Caster: %d", m_pObjectsTree->GetObjectsCount(eCasters));
  2771. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "LigAll: %d", m_lstStaticLights.Count());
  2772. }
  2773. int nFree = m_LTPRootFree.Count();
  2774. int nUsed = m_LTPRootUsed.Count();
  2775. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "RNTmpData(Used+Free): %d + %d = %d (%d KB)",
  2776. nUsed, nFree, nUsed + nFree, (nUsed + nFree) * (int)sizeof(CRNTmpData) / 1024);
  2777. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "COctreeNode::m_arrEmptyNodes.Count() = %d", COctreeNode::m_arrEmptyNodes.Count());
  2778. }
  2779. CCullBuffer* pCB = GetCoverageBuffer();
  2780. if (pCB && GetCVars()->e_CoverageBuffer && GetCVars()->e_CoverageBufferDebug && pCB->TrisWritten())
  2781. {
  2782. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY,
  2783. "CB: Write:%3d/%2d Test:%4d/%4d/%3d ZFarM:%.2f ZNearM:%.2f Res:%d OI:%s",
  2784. pCB->TrisWritten(), pCB->ObjectsWritten(),
  2785. pCB->TrisTested(), pCB->ObjectsTested(), pCB->ObjectsTestedAndRejected(),
  2786. pCB->GetZFarInMeters(), pCB->GetZNearInMeters(), pCB->SelRes(),
  2787. pCB->IsOutdooVisible() ? "Out" : "In");
  2788. }
  2789. #if defined(INFO_FRAME_COUNTER)
  2790. ++frameCounter;
  2791. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "Frame #%d", frameCounter);
  2792. #endif
  2793. ITimeOfDay* pTimeOfDay = Get3DEngine()->GetTimeOfDay();
  2794. if (GetCVars()->e_TimeOfDayDebug && pTimeOfDay)
  2795. {
  2796. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "---------------------------------------");
  2797. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "------------ Time of Day -------------");
  2798. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, " ");
  2799. int nVarCount = pTimeOfDay->GetVariableCount();
  2800. for (int v = 0; v < nVarCount; ++v)
  2801. {
  2802. ITimeOfDay::SVariableInfo pVar;
  2803. pTimeOfDay->GetVariableInfo(v, pVar);
  2804. if (pVar.type == ITimeOfDay::TYPE_FLOAT)
  2805. {
  2806. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, " %s: %.9f", pVar.displayName, pVar.fValue[0]);
  2807. }
  2808. else
  2809. {
  2810. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, " %s: %.3f %.3f %.3f", pVar.displayName, pVar.fValue[0], pVar.fValue[1], pVar.fValue[2]);
  2811. }
  2812. }
  2813. DrawTextRightAligned(fTextPosX, fTextPosY += fTextStepY, "---------------------------------------");
  2814. }
  2815. #endif
  2816. // We only show memory usage in dev mode.
  2817. if (gEnv->pSystem->IsDevMode())
  2818. {
  2819. if (GetCVars()->e_DisplayMemoryUsageIcon)
  2820. {
  2821. uint64 nAverageMemoryUsage(0);
  2822. uint64 nHighMemoryUsage(0);
  2823. uint64 nCurrentMemoryUsage(0);
  2824. const uint64 nMegabyte(1024 * 1024);
  2825. // Copied from D3DDriver.cpp, function CD3D9Renderer::RT_EndFrame().
  2826. int nIconSize = 16;
  2827. nCurrentMemoryUsage = processMemInfo.TotalPhysicalMemory - processMemInfo.FreePhysicalMemory;
  2828. #if defined(_WIN64) || defined(WIN64) || defined(MAC) || defined(LINUX64)
  2829. nAverageMemoryUsage = 3000;
  2830. nHighMemoryUsage = 6000;
  2831. // This is the same value as measured in the editor.
  2832. nCurrentMemoryUsage = processMemInfo.PagefileUsage / nMegabyte;
  2833. #elif defined(_WIN32) || defined(LINUX32)
  2834. nAverageMemoryUsage = 800;
  2835. nHighMemoryUsage = 1200;
  2836. // This is the same value as measured in the editor.
  2837. nCurrentMemoryUsage = processMemInfo.PagefileUsage / nMegabyte;
  2838. #endif //_WIN32
  2839. ITexture* pRenderTexture(m_ptexIconAverageMemoryUsage);
  2840. if (nCurrentMemoryUsage > nHighMemoryUsage)
  2841. {
  2842. pRenderTexture = m_ptexIconHighMemoryUsage;
  2843. }
  2844. else if (nCurrentMemoryUsage < nAverageMemoryUsage)
  2845. {
  2846. pRenderTexture = m_ptexIconLowMemoryUsage;
  2847. }
  2848. if (pRenderTexture && gEnv->pRenderer)
  2849. {
  2850. float vpWidth = (float)gEnv->pRenderer->GetOverlayWidth(), vpHeight = (float)gEnv->pRenderer->GetOverlayHeight();
  2851. float iconWidth = (float)nIconSize / vpWidth * 800.0f;
  2852. float iconHeight = (float)nIconSize / vpHeight * 600.0f;
  2853. gEnv->pRenderer->Push2dImage((fTextPosX / vpWidth) * 800.0f - iconWidth, ((fTextPosY += nIconSize + 3) / vpHeight) * 600.0f,
  2854. iconWidth, iconHeight, pRenderTexture->GetTextureID(), 0, 1.0f, 1.0f, 0);
  2855. }
  2856. }
  2857. }
  2858. #endif
  2859. }
  2860. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  2861. static const float DISPLAY_MEMORY_ROW_MARGIN = 16.0f;
  2862. static const float DISPLAY_MEMORY_ROW_HEIGHT = 32.0f;
  2863. static const float DISPLAY_MEMORY_ROW_NUMBER_WIDTH = 128.0f;
  2864. static const float DISPLAY_MEMORY_ROW_FONT_SCALE = 1.5f;
  2865. static const float DISPLAY_MEMORY_COL_LABEL_FONT_SCALE = 1.0f;
  2866. static inline void AdjustDisplayMemoryParameters(float& yPos, float& columnInset, float columnWidth, float screenHeight)
  2867. {
  2868. int column = (int)(yPos + DISPLAY_MEMORY_ROW_HEIGHT) / (int)screenHeight;
  2869. columnInset += columnWidth * column;
  2870. yPos -= screenHeight * column;
  2871. }
  2872. static void DisplayMemoryRow(C3DEngine& engine, float columnWidth, float screenHeight, float yPos, float valueA, float valueB, const char* valueBFormat, const ColorF& color, const char* categoryName, const char* subcategoryName = nullptr)
  2873. {
  2874. float columnInset = columnWidth - DISPLAY_MEMORY_ROW_MARGIN;
  2875. AdjustDisplayMemoryParameters(yPos, columnInset, columnWidth, screenHeight);
  2876. if (valueA != -1.0f)
  2877. {
  2878. engine.DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, color, "%.1fMB", valueA);
  2879. }
  2880. if (valueB != -1.0f)
  2881. {
  2882. engine.DrawTextRightAligned(columnInset, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, color, valueBFormat, valueB);
  2883. }
  2884. if (subcategoryName)
  2885. {
  2886. static const float MAIN_TEXT_SCALE = 1.5f;
  2887. static const float SUB_TEXT_SCALE = 1.0f;
  2888. static const float SUB_LINE_OFFSET_Y = 16.0f;
  2889. engine.DrawTextLeftAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 4, yPos, MAIN_TEXT_SCALE, color, "%s", categoryName);
  2890. engine.DrawTextLeftAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 4, yPos + SUB_LINE_OFFSET_Y, SUB_TEXT_SCALE, color, "%s", subcategoryName);
  2891. }
  2892. else
  2893. {
  2894. engine.DrawTextLeftAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 4, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, color, "%s", categoryName);
  2895. }
  2896. }
  2897. void C3DEngine::DisplayMemoryStatistics()
  2898. {
  2899. const ColorF headerColor = ColorF(0.4f, 0.9f, 0.3f, 1.0f);
  2900. const ColorF statisticColor = ColorF(0.4f, 0.9f, 0.9f, 1.0f);
  2901. const ColorF subtotalColor = ColorF(0.4f, 0.3f, 0.9f, 1.0f);
  2902. const ColorF totalColor = ColorF(0.9f, 0.9f, 0.9f, 1.0f);
  2903. const ColorF labelColor = ColorF(0.4f, 0.3f, 0.3f, 1.0f);
  2904. const float screenHeight = (float)m_pRenderer->GetHeight();
  2905. if (GetCVars()->e_MemoryProfiling == 1)
  2906. {
  2907. const float columnWidth = (float)(m_pRenderer->GetWidth() / 2);
  2908. float columnInset = columnWidth - DISPLAY_MEMORY_ROW_MARGIN;
  2909. float memoryYPos = DISPLAY_MEMORY_ROW_HEIGHT;
  2910. float memoryYPosStepSize = DISPLAY_MEMORY_ROW_HEIGHT;
  2911. // Add column labels and header
  2912. this->DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH, memoryYPos, DISPLAY_MEMORY_COL_LABEL_FONT_SCALE, labelColor, "Allocated");
  2913. this->DrawTextRightAligned(columnInset, memoryYPos, DISPLAY_MEMORY_COL_LABEL_FONT_SCALE, labelColor, "No. Allocations");
  2914. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, -1.0f, -1.0f, "%.1fMB", headerColor, "VRAM Usage");
  2915. memoryYPos += (memoryYPosStepSize * 0.5f);
  2916. float totalTrackedGPUAlloc = 0.0f;
  2917. // Print the memory usage of each major VRAM category and each subcategory
  2918. for (int category = 0; category < Render::Debug::VRAM_CATEGORY_NUMBER_CATEGORIES; ++category)
  2919. {
  2920. float categorySubTotal = 0.0f;
  2921. AZStd::string categoryName;
  2922. for (int subcategory = 0; subcategory < Render::Debug::VRAM_SUBCATEGORY_NUMBER_SUBCATEGORIES; ++subcategory)
  2923. {
  2924. AZStd::string subcategoryName;
  2925. size_t numberBytesAllocated = 0;
  2926. size_t numberAllocations = 0;
  2927. EBUS_EVENT(Render::Debug::VRAMDrillerBus, GetCurrentVRAMStats, static_cast<Render::Debug::VRAMAllocationCategory>(category),
  2928. static_cast<Render::Debug::VRAMAllocationSubcategory>(subcategory), categoryName, subcategoryName, numberBytesAllocated, numberAllocations);
  2929. if (numberAllocations != 0)
  2930. {
  2931. float numMBallocated = numberBytesAllocated / (1024.0f * 1024.0f);
  2932. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, numMBallocated, (float)numberAllocations, "%.0f", statisticColor, categoryName.c_str(), subcategoryName.c_str());
  2933. memoryYPos += memoryYPosStepSize;
  2934. totalTrackedGPUAlloc += numMBallocated;
  2935. categorySubTotal += numMBallocated;
  2936. }
  2937. }
  2938. if (categorySubTotal > 0.0f)
  2939. {
  2940. float yPos = memoryYPos;
  2941. AdjustDisplayMemoryParameters(yPos, columnInset, columnWidth, screenHeight);
  2942. DrawTextLeftAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 4, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, subtotalColor, "%s Subtotal", categoryName.c_str());
  2943. DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, subtotalColor, "%.1fMB", categorySubTotal);
  2944. memoryYPos += (memoryYPosStepSize * 0.5f);
  2945. }
  2946. }
  2947. float allocatedVideoMemoryMB = -1.0f, reservedVideoMemoryMB = -1.0f;
  2948. #if defined(AZ_PLATFORM_PROVO)
  2949. size_t allocatedVideoMemoryBytes = 0, reservedVideoMemoryBytes = 0;
  2950. VirtualAllocator::QueryVideoMemory(allocatedVideoMemoryBytes, reservedVideoMemoryBytes);
  2951. allocatedVideoMemoryMB = static_cast<float>(allocatedVideoMemoryBytes) / (1024.0f * 1024.0f);
  2952. reservedVideoMemoryMB = static_cast<float>(reservedVideoMemoryBytes) / (1024.0f * 1024.0f);
  2953. #else
  2954. // Non PROVO platforms just sum up the tracked allocations
  2955. allocatedVideoMemoryMB = totalTrackedGPUAlloc;
  2956. #endif
  2957. DrawTextLeftAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 4, memoryYPos, DISPLAY_MEMORY_ROW_FONT_SCALE, totalColor, "Total");
  2958. if (reservedVideoMemoryMB != -1.0f)
  2959. {
  2960. DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 1, memoryYPos, DISPLAY_MEMORY_ROW_FONT_SCALE, totalColor, "%.1fMB/%.1fMB", allocatedVideoMemoryMB, reservedVideoMemoryMB);
  2961. memoryYPos += (memoryYPosStepSize * 0.5f);
  2962. }
  2963. else
  2964. {
  2965. DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH * 1, memoryYPos, DISPLAY_MEMORY_ROW_FONT_SCALE, totalColor, "%.1fMB", allocatedVideoMemoryMB);
  2966. memoryYPos += (memoryYPosStepSize * 0.5f);
  2967. }
  2968. // Spacer
  2969. memoryYPos += (memoryYPosStepSize * 0.5f);
  2970. // Add column labels and header
  2971. this->DrawTextRightAligned(columnInset - DISPLAY_MEMORY_ROW_NUMBER_WIDTH, memoryYPos, DISPLAY_MEMORY_COL_LABEL_FONT_SCALE, labelColor, "Allocated");
  2972. this->DrawTextRightAligned(columnInset, memoryYPos, DISPLAY_MEMORY_COL_LABEL_FONT_SCALE, labelColor, "Capacity");
  2973. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, -1.0f, -1.0f, "%.1fMB", headerColor, "CPU Memory Usage");
  2974. memoryYPos += (memoryYPosStepSize * 0.5f);
  2975. float totalTrackedCPUAlloc = 0.0f;
  2976. float totalCapacityCPUAlloc = 0.0f;
  2977. AZ::AllocatorManager& allocatorManager = AZ::AllocatorManager::Instance();
  2978. const size_t allocatorCount = allocatorManager.GetNumAllocators();
  2979. AZStd::map<AZ::IAllocatorAllocate*, AZ::IAllocator*> existingAllocators;
  2980. AZStd::map<AZ::IAllocatorAllocate*, AZ::IAllocator*> sourcesToAllocators;
  2981. // Build a mapping of original allocator sources to their allocators
  2982. for (int i = 0; i < allocatorCount; ++i)
  2983. {
  2984. AZ::IAllocator* allocator = allocatorManager.GetAllocator(i);
  2985. sourcesToAllocators.emplace(allocator->GetOriginalAllocationSource(), allocator);
  2986. }
  2987. // Group up any allocators under this size
  2988. static float smallAllocatorCapacityMaxMB = 10.0f;
  2989. float smallAllocatorsTotalCapacityMB = 0.0f;
  2990. float smallAllocatorsTotalAllocatedMB = 0.0f;
  2991. for (int i = 0; i < allocatorCount; ++i)
  2992. {
  2993. AZ::IAllocator* allocator = allocatorManager.GetAllocator(i);
  2994. AZ::IAllocatorAllocate* source = allocator->GetAllocationSource();
  2995. AZ::IAllocatorAllocate* originalSource = allocator->GetOriginalAllocationSource();
  2996. AZ::IAllocatorAllocate* schema = allocator->GetSchema();
  2997. AZ::IAllocator* alias = (source != originalSource) ? sourcesToAllocators[source] : nullptr;
  2998. if (schema && !alias)
  2999. {
  3000. // Check to see if this allocator's source maps to another allocator
  3001. // Need to check both the schema and the allocator itself, as either one might be used as the alias depending on how it's implemented
  3002. AZStd::array<AZ::IAllocatorAllocate*, 2> checkAllocators = { { schema, allocator->GetAllocationSource() } };
  3003. for (AZ::IAllocatorAllocate* check : checkAllocators)
  3004. {
  3005. auto existing = existingAllocators.emplace(check, allocator);
  3006. if (!existing.second)
  3007. {
  3008. alias = existing.first->second;
  3009. // Do not break out of the loop as we need to add to the map for all entries
  3010. }
  3011. }
  3012. }
  3013. if (!alias)
  3014. {
  3015. static const AZ::IAllocator* OS_ALLOCATOR = &AZ::AllocatorInstance<AZ::OSAllocator>::GetAllocator();
  3016. float allocatedMB = (float)source->NumAllocatedBytes() / (1024.0f * 1024.0f);
  3017. float capacityMB = (float)source->Capacity() / (1024.0f * 1024.0f);
  3018. totalTrackedCPUAlloc += allocatedMB;
  3019. totalCapacityCPUAlloc += capacityMB;
  3020. // Skip over smaller allocators so the display is readable.
  3021. if (capacityMB < smallAllocatorCapacityMaxMB)
  3022. {
  3023. smallAllocatorsTotalCapacityMB += capacityMB;
  3024. smallAllocatorsTotalAllocatedMB += allocatedMB;
  3025. continue;
  3026. }
  3027. if (allocator == OS_ALLOCATOR)
  3028. {
  3029. // Need to special case the OS allocator because its capacity is a made-up number. Better to just use the allocated amount, it will hopefully be small anyway.
  3030. capacityMB = allocatedMB;
  3031. }
  3032. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, allocatedMB, capacityMB, "%.1fMB", statisticColor, allocator->GetName(), allocator->GetDescription());
  3033. memoryYPos += memoryYPosStepSize;
  3034. }
  3035. }
  3036. if (smallAllocatorCapacityMaxMB > 0.0f)
  3037. {
  3038. AZStd::string subText = AZStd::string::format("Allocators smaller than %.0f MB", smallAllocatorCapacityMaxMB);
  3039. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, smallAllocatorsTotalAllocatedMB, smallAllocatorsTotalCapacityMB, "%.1fMB", statisticColor, "All Small Allocators", subText.c_str());
  3040. memoryYPos += memoryYPosStepSize;
  3041. }
  3042. DisplayMemoryRow(*this, columnWidth, screenHeight, memoryYPos, totalTrackedCPUAlloc, totalCapacityCPUAlloc, "%.1fMB", totalColor, "Total");
  3043. memoryYPos += (memoryYPosStepSize * 0.5f);
  3044. }
  3045. else if (GetCVars()->e_MemoryProfiling == 2)
  3046. {
  3047. const float columnWidth = (float)(m_pRenderer->GetWidth() / 2);
  3048. float memoryYPos = DISPLAY_MEMORY_ROW_HEIGHT;
  3049. float memoryYPosStepSize = DISPLAY_MEMORY_ROW_HEIGHT;
  3050. AZ::AllocatorManager& allocatorManager = AZ::AllocatorManager::Instance();
  3051. const size_t allocatorCount = allocatorManager.GetNumAllocators();
  3052. AZStd::map<AZ::IAllocatorAllocate*, AZ::IAllocator*> existingAllocators;
  3053. AZStd::map<AZ::IAllocatorAllocate*, AZ::IAllocator*> sourcesToAllocators;
  3054. // Build a mapping of original allocator sources to their allocators
  3055. for (int i = 0; i < allocatorCount; ++i)
  3056. {
  3057. AZ::IAllocator* allocator = allocatorManager.GetAllocator(i);
  3058. sourcesToAllocators.emplace(allocator->GetOriginalAllocationSource(), allocator);
  3059. }
  3060. for (int i = 0; i < allocatorCount; ++i)
  3061. {
  3062. AZ::IAllocator* allocator = allocatorManager.GetAllocator(i);
  3063. AZ::IAllocatorAllocate* source = allocator->GetAllocationSource();
  3064. AZ::IAllocatorAllocate* originalSource = allocator->GetOriginalAllocationSource();
  3065. AZ::IAllocatorAllocate* schema = allocator->GetSchema();
  3066. AZ::IAllocator* alias = (source != originalSource) ? sourcesToAllocators[source] : nullptr;
  3067. if (schema && !alias)
  3068. {
  3069. // Check to see if this allocator's source maps to another allocator
  3070. // Need to check both the schema and the allocator itself, as either one might be used as the alias depending on how it's implemented
  3071. AZStd::array<AZ::IAllocatorAllocate*, 2> checkAllocators = { { schema, allocator->GetAllocationSource() } };
  3072. for (AZ::IAllocatorAllocate* check : checkAllocators)
  3073. {
  3074. auto existing = existingAllocators.emplace(check, allocator);
  3075. if (!existing.second)
  3076. {
  3077. alias = existing.first->second;
  3078. // Do not break out of the loop as we need to add to the map for all entries
  3079. }
  3080. }
  3081. }
  3082. if (alias)
  3083. {
  3084. float columnInset = columnWidth - DISPLAY_MEMORY_ROW_MARGIN;
  3085. float yPos = memoryYPos;
  3086. AdjustDisplayMemoryParameters(yPos, columnInset, columnWidth, screenHeight);
  3087. DrawTextRightAligned(columnInset, yPos, DISPLAY_MEMORY_ROW_FONT_SCALE, statisticColor, "%s => %s", allocator->GetName(), alias->GetName());
  3088. memoryYPos += (memoryYPosStepSize * 0.5f);
  3089. }
  3090. }
  3091. }
  3092. }
  3093. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  3094. void C3DEngine::SetupDistanceFog()
  3095. {
  3096. FUNCTION_PROFILER_3DENGINE;
  3097. #if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
  3098. // render to texture does not support volumetric fog
  3099. if (GetRenderer()->IsRenderToTextureActive() && (GetCVars()->e_VolumetricFog != 0))
  3100. {
  3101. GetRenderer()->EnableFog(false);
  3102. return;
  3103. }
  3104. #endif // AZ_RENDER_TO_TEXTURE_GEM_ENABLED
  3105. GetRenderer()->SetFogColor(ColorF(m_vFogColor.x, m_vFogColor.y, m_vFogColor.z, 1.0f));
  3106. GetRenderer()->EnableFog(GetCVars()->e_Fog > 0);
  3107. }
  3108. void C3DEngine::ScreenShotHighRes([[maybe_unused]] CStitchedImage* pStitchedImage, [[maybe_unused]] const int nRenderFlags, [[maybe_unused]] const SRenderingPassInfo& passInfo, [[maybe_unused]] uint32 SliceCount, [[maybe_unused]] f32 fTransitionSize)
  3109. {
  3110. #if defined(WIN32) || defined(WIN64) || defined(MAC)
  3111. //If the requested format is TGA we want the framebuffer in BGR format; otherwise we want RGB
  3112. const char* szExtension = GetCVars()->e_ScreenShotFileFormat->GetString();
  3113. bool BGRA = (azstricmp(szExtension, "tga") == 0) ? true : false;
  3114. // finish frame started by system
  3115. GetRenderer()->EndFrame();
  3116. // The occlusion system does not like being restarted mid-frame like this. Disable it for
  3117. // the screenshot system.
  3118. AZ::s32 statObjBufferRenderTasks = GetCVars()->e_StatObjBufferRenderTasks;
  3119. GetCVars()->e_StatObjBufferRenderTasks = 0;
  3120. GetConsole()->SetScrollMax(0);
  3121. const uint32 ScreenWidth = GetRenderer()->GetWidth();
  3122. const uint32 ScreenHeight = GetRenderer()->GetHeight();
  3123. uint32* pImage = new uint32[ScreenWidth * ScreenHeight];
  3124. for (uint32 yy = 0; yy < SliceCount; yy++)
  3125. {
  3126. for (uint32 xx = 0; xx < SliceCount; xx++)
  3127. {
  3128. const int BlendX = (xx * 2) / SliceCount;
  3129. const int BlendY = (yy * 2) / SliceCount;
  3130. const int x = (((xx * 2) % SliceCount) & ~1) + BlendX;
  3131. const int y = (((yy * 2) % SliceCount) & ~1) + BlendY;
  3132. const int reverseX = SliceCount - 1 - x;
  3133. const int reverseY = SliceCount - 1 - y;
  3134. const float halfTransitionSize = fTransitionSize * 0.5f;
  3135. const float sliceCountF = static_cast<float>(SliceCount);
  3136. // start new frame and define needed tile
  3137. const f32 ScreenScale = 1.0f / ((1.0f / sliceCountF) * (1.0f + fTransitionSize));
  3138. GetRenderer()->BeginFrame();
  3139. // This has to happen after BeginFrame(), because BeginFrame increments the frame counter, and SRenderingPassInfo
  3140. // pulls from that counter in the constructor. Individual render nodes track the frame they were last rendered with
  3141. // and will bail if the same frame is rendered twice.
  3142. SRenderingPassInfo screenShotPassInfo = SRenderingPassInfo::CreateGeneralPassRenderingInfo(passInfo.GetCamera());
  3143. PrintMessage("Rendering tile %d of %d ... ", xx + yy * SliceCount + 1, SliceCount * SliceCount);
  3144. const float normalizedX = ((static_cast<f32>(reverseX) - halfTransitionSize) / sliceCountF);
  3145. const float normalizedY = ((static_cast<f32>(reverseY) - halfTransitionSize) / sliceCountF);
  3146. GetRenderer()->SetRenderTile(
  3147. ScreenScale * normalizedX,
  3148. ScreenScale * normalizedY,
  3149. ScreenScale, ScreenScale);
  3150. UpdateRenderingCamera("ScreenShotHighRes", screenShotPassInfo);
  3151. RenderInternal(nRenderFlags, screenShotPassInfo, "ScreenShotHighRes");
  3152. // Make sure we've composited to the final back buffer.
  3153. GetRenderer()->SwitchToNativeResolutionBackbuffer();
  3154. GetRenderer()->EndFrame();
  3155. PrintMessagePlus("reading frame buffer ... ");
  3156. GetRenderer()->ReadFrameBufferFast(pImage, ScreenWidth, ScreenHeight, BGRA);
  3157. pStitchedImage->RasterizeRect(pImage, ScreenWidth, ScreenHeight, x, y, fTransitionSize,
  3158. fTransitionSize > 0.0001f && BlendX,
  3159. fTransitionSize > 0.0001f && BlendY);
  3160. PrintMessagePlus("ok");
  3161. }
  3162. }
  3163. delete[] pImage;
  3164. GetCVars()->e_StatObjBufferRenderTasks = statObjBufferRenderTasks;
  3165. // re-start frame so system can safely finish it
  3166. GetRenderer()->BeginFrame();
  3167. // restore initial state
  3168. GetRenderer()->SetViewport(0, 0, GetRenderer()->GetWidth(), GetRenderer()->GetHeight());
  3169. GetConsole()->SetScrollMax(300);
  3170. GetRenderer()->SetRenderTile();
  3171. PrintMessagePlus(" ok");
  3172. #endif // #if defined(WIN32) || defined(WIN64)
  3173. }
  3174. bool C3DEngine::ScreenShotMap([[maybe_unused]] CStitchedImage* pStitchedImage,
  3175. [[maybe_unused]] const int nRenderFlags,
  3176. [[maybe_unused]] const SRenderingPassInfo& passInfo,
  3177. [[maybe_unused]] const uint32 SliceCount,
  3178. [[maybe_unused]] const f32 fTransitionSize)
  3179. {
  3180. #if defined(WIN32) || defined(WIN64) || defined(MAC)
  3181. const f32 fTLX = GetCVars()->e_ScreenShotMapCenterX - GetCVars()->e_ScreenShotMapSizeX + fTransitionSize * GetRenderer()->GetWidth();
  3182. const f32 fTLY = GetCVars()->e_ScreenShotMapCenterY - GetCVars()->e_ScreenShotMapSizeY + fTransitionSize * GetRenderer()->GetHeight();
  3183. const f32 fBRX = GetCVars()->e_ScreenShotMapCenterX + GetCVars()->e_ScreenShotMapSizeX + fTransitionSize * GetRenderer()->GetWidth();
  3184. const f32 fBRY = GetCVars()->e_ScreenShotMapCenterY + GetCVars()->e_ScreenShotMapSizeY + fTransitionSize * GetRenderer()->GetHeight();
  3185. const f32 Height = GetCVars()->e_ScreenShotMapCamHeight;
  3186. const int Orient = GetCVars()->e_ScreenShotMapOrientation;
  3187. const char* SettingsFileName = GetLevelFilePath("ScreenshotMap.Settings");
  3188. AZ::IO::HandleType metaFileHandle = gEnv->pCryPak->FOpen(SettingsFileName, "wt");
  3189. if (metaFileHandle != AZ::IO::InvalidHandle)
  3190. {
  3191. char Data[1024 * 8];
  3192. snprintf(Data, sizeof(Data), "<Map CenterX=\"%f\" CenterY=\"%f\" SizeX=\"%f\" SizeY=\"%f\" Height=\"%f\" Quality=\"%d\" Orientation=\"%d\" />",
  3193. GetCVars()->e_ScreenShotMapCenterX,
  3194. GetCVars()->e_ScreenShotMapCenterY,
  3195. GetCVars()->e_ScreenShotMapSizeX,
  3196. GetCVars()->e_ScreenShotMapSizeY,
  3197. GetCVars()->e_ScreenShotMapCamHeight,
  3198. GetCVars()->e_ScreenShotQuality,
  3199. GetCVars()->e_ScreenShotMapOrientation);
  3200. string data(Data);
  3201. gEnv->pCryPak->FWrite(data.c_str(), data.size(), metaFileHandle);
  3202. gEnv->pCryPak->FClose(metaFileHandle);
  3203. }
  3204. // This bit is necessary because we don't have a way to render the world using an orthographic projection. This is doing
  3205. // a hacky orthographic projection by shifting the camera up to a sufficient height to fake it. To preserve depth range
  3206. // we define a maximum range then then fit the near / far planes to extend [-HeightRangeMax, HeightRangeMax] along Z (which is the up axis).
  3207. const float HeightRangeMax = 4096;
  3208. const float HeightRangeMaxDiv2 = HeightRangeMax / 2.0f;
  3209. const float NearClip = max(Height - HeightRangeMaxDiv2, 1.0f);
  3210. const float FarClip = max(Height + HeightRangeMaxDiv2, HeightRangeMax);
  3211. CCamera cam = passInfo.GetCamera();
  3212. Matrix34 tmX, tmY;
  3213. float xrot = -gf_PI * 0.5f;
  3214. float yrot = Orient == 0 ? -gf_PI * 0.5f : -0.0f;
  3215. tmX.SetRotationX(xrot);
  3216. tmY.SetRotationY(yrot);
  3217. Matrix34 tm = tmX * tmY;
  3218. tm.SetTranslation(Vec3((fTLX + fBRX) * 0.5f, (fTLY + fBRY) * 0.5f, Height));
  3219. cam.SetMatrix(tm);
  3220. const f32 AngleX = atanf(((fBRX - fTLX) * 0.5f) / Height);
  3221. const f32 AngleY = atanf(((fBRY - fTLY) * 0.5f) / Height);
  3222. ICVar* r_drawnearfov = GetConsole()->GetCVar("r_DrawNearFoV");
  3223. assert(r_drawnearfov);
  3224. const f32 drawnearfov_backup = r_drawnearfov->GetFVal();
  3225. const f32 ViewingSize = (float)min(cam.GetViewSurfaceX(), cam.GetViewSurfaceZ());
  3226. if (max(AngleX, AngleY) <= 0)
  3227. {
  3228. return false;
  3229. }
  3230. cam.SetFrustum((int)ViewingSize, (int)ViewingSize, max(0.001f, max(AngleX, AngleY) * 2.f), NearClip, FarClip);
  3231. r_drawnearfov->Set(-1);
  3232. ScreenShotHighRes(pStitchedImage, nRenderFlags, SRenderingPassInfo::CreateTempRenderingInfo(cam, passInfo), SliceCount, fTransitionSize);
  3233. r_drawnearfov->Set(drawnearfov_backup);
  3234. return true;
  3235. #else // #if defined(WIN32) || defined(WIN64)
  3236. return false;
  3237. #endif // #if defined(WIN32) || defined(WIN64)
  3238. }
  3239. bool C3DEngine::ScreenShotPanorama([[maybe_unused]] CStitchedImage* pStitchedImage, [[maybe_unused]] const int nRenderFlags, [[maybe_unused]] const SRenderingPassInfo& passInfo, [[maybe_unused]] uint32 SliceCount, [[maybe_unused]] f32 fTransitionSize)
  3240. {
  3241. #if defined(WIN32) || defined(WIN64) || defined(MAC)
  3242. //If the requested format is TGA we want the framebuffer in BGR format; otherwise we want RGB
  3243. const char* szExtension = GetCVars()->e_ScreenShotFileFormat->GetString();
  3244. bool BGRA = (azstricmp(szExtension, "tga") == 0) ? true : false;
  3245. // finish frame started by system
  3246. GetRenderer()->EndFrame();
  3247. float r_drawnearfov_backup = -1;
  3248. ICVar* r_drawnearfov = GetConsole()->GetCVar("r_DrawNearFoV");
  3249. assert(r_drawnearfov);
  3250. r_drawnearfov_backup = r_drawnearfov->GetFVal();
  3251. r_drawnearfov->Set(-1); // means the fov override should be switched off
  3252. // The occlusion system does not like being restarted mid-frame like this. Disable it for
  3253. // the screenshot system.
  3254. AZ::s32 statObjBufferRenderTasks = GetCVars()->e_StatObjBufferRenderTasks;
  3255. GetCVars()->e_StatObjBufferRenderTasks = 0;
  3256. GetTimer()->EnableTimer(false);
  3257. uint32* pImage = new uint32[GetRenderer()->GetWidth() * GetRenderer()->GetHeight()];
  3258. for (int iSlice = SliceCount - 1; iSlice >= 0; --iSlice)
  3259. {
  3260. if (iSlice == 0) // the last one should do eye adaption
  3261. {
  3262. GetTimer()->EnableTimer(true);
  3263. }
  3264. GetRenderer()->BeginFrame();
  3265. Matrix33 rot;
  3266. rot.SetIdentity();
  3267. float fAngle = pStitchedImage->GetSliceAngle(iSlice);
  3268. rot.SetRotationZ(fAngle);
  3269. CCamera cam = passInfo.GetCamera();
  3270. Matrix34 tm = cam.GetMatrix();
  3271. tm = tm * rot;
  3272. tm.SetTranslation(passInfo.GetCamera().GetPosition());
  3273. cam.SetMatrix(tm);
  3274. cam.SetFrustum(cam.GetViewSurfaceX(), cam.GetViewSurfaceZ(), pStitchedImage->m_fPanoramaShotVertFOV, cam.GetNearPlane(), cam.GetFarPlane(), cam.GetPixelAspectRatio());
  3275. SRenderingPassInfo screenShotPassInfo = SRenderingPassInfo::CreateGeneralPassRenderingInfo(cam);
  3276. UpdateRenderingCamera("ScreenShotPanorama", screenShotPassInfo);
  3277. // render scene
  3278. RenderInternal(nRenderFlags, screenShotPassInfo, "ScreenShotPanorama");
  3279. // Make sure we've composited to the final back buffer.
  3280. GetRenderer()->SwitchToNativeResolutionBackbuffer();
  3281. GetRenderer()->ReadFrameBufferFast(pImage, GetRenderer()->GetWidth(), GetRenderer()->GetHeight(), BGRA);
  3282. GetRenderer()->EndFrame(); // show last frame (from direction)
  3283. const bool bFadeBorders = (iSlice + 1) * 2 <= (int)SliceCount;
  3284. PrintMessage("PanoramaScreenShot %d/%d FadeBorders:%c (id: %d/%d)", iSlice + 1, SliceCount, bFadeBorders ? 't' : 'f', GetRenderer()->GetFrameID(false), GetRenderer()->GetFrameID(true));
  3285. pStitchedImage->RasterizeCylinder(pImage, GetRenderer()->GetWidth(), GetRenderer()->GetHeight(), iSlice + 1, bFadeBorders);
  3286. if (GetCVars()->e_ScreenShotQuality < 0) // to debug FadeBorders
  3287. {
  3288. if (iSlice * 2 == SliceCount)
  3289. {
  3290. pStitchedImage->Clear();
  3291. PrintMessage("PanoramaScreenShot clear");
  3292. }
  3293. }
  3294. }
  3295. delete [] pImage;
  3296. r_drawnearfov->Set(r_drawnearfov_backup);
  3297. GetCVars()->e_StatObjBufferRenderTasks = statObjBufferRenderTasks;
  3298. // re-start frame so system can safely finish it
  3299. GetRenderer()->BeginFrame();
  3300. return true;
  3301. #else // #if defined(WIN32) || defined(WIN64)
  3302. return false;
  3303. #endif // #if defined(WIN32) || defined(WIN64)
  3304. }
  3305. void C3DEngine::SetupClearColor()
  3306. {
  3307. FUNCTION_PROFILER_3DENGINE;
  3308. bool bCameraInOutdoors = m_pVisAreaManager && !m_pVisAreaManager->m_pCurArea && !(m_pVisAreaManager->m_pCurPortal && m_pVisAreaManager->m_pCurPortal->m_lstConnections.Count() > 1);
  3309. GetRenderer()->SetClearColor(bCameraInOutdoors ? m_vFogColor : Vec3(0, 0, 0));
  3310. }
  3311. void C3DEngine::FillDebugFPSInfo(SDebugFPSInfo& info)
  3312. {
  3313. size_t c = 0;
  3314. float average = 0.0f, min = 0.0f, max = 0.0f;
  3315. const float clampFPS = 200.0f;
  3316. for (size_t i = 0, end = arrFPSforSaveLevelStats.size(); i < end; ++i)
  3317. {
  3318. if (arrFPSforSaveLevelStats[i] > 1.0f && arrFPSforSaveLevelStats[i] < clampFPS)
  3319. {
  3320. ++c;
  3321. average += arrFPSforSaveLevelStats[i];
  3322. }
  3323. }
  3324. if (c)
  3325. {
  3326. average /= (float)c;
  3327. }
  3328. int minc = 0, maxc = 0;
  3329. for (size_t i = 0, end = arrFPSforSaveLevelStats.size(); i < end; ++i)
  3330. {
  3331. if (arrFPSforSaveLevelStats[i] > average && arrFPSforSaveLevelStats[i] < clampFPS)
  3332. {
  3333. ++maxc;
  3334. max += arrFPSforSaveLevelStats[i];
  3335. }
  3336. if (arrFPSforSaveLevelStats[i] < average && arrFPSforSaveLevelStats[i] < clampFPS)
  3337. {
  3338. ++minc;
  3339. min += arrFPSforSaveLevelStats[i];
  3340. }
  3341. }
  3342. if (minc == 0)
  3343. {
  3344. minc = 1;
  3345. }
  3346. if (maxc == 0)
  3347. {
  3348. maxc = 1;
  3349. }
  3350. info.fAverageFPS = average;
  3351. info.fMinFPS = min / (float)minc;
  3352. info.fMaxFPS = max / (float)maxc;
  3353. }