ProfileAnalyzerWindow.cs 243 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642
  1. #if !UNITY_2019_1_OR_NEWER
  2. #define UNITY_PRE_2019_1
  3. #endif
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Linq;
  9. #if !UNITY_2020_1_OR_NEWER
  10. using System.Reflection;
  11. #endif
  12. using System.Text.RegularExpressions;
  13. using System.Threading;
  14. using UnityEditor.IMGUI.Controls;
  15. using UnityEditorInternal;
  16. using UnityEngine;
  17. using UnityEngine.Profiling;
  18. using Debug = UnityEngine.Debug;
  19. #if UNITY_2017_1_OR_NEWER
  20. using ProfilerCommon = UnityEditorInternal.ProfilerDriver;
  21. #else
  22. using ProfilerCommon = UnityEngine.Profiling.Profiler;
  23. #endif
  24. #if UNITY_2018_3_OR_NEWER
  25. using ProfilerMarkerAbstracted = Unity.Profiling.ProfilerMarker;
  26. #else
  27. using ProfilerMarkerAbstracted = UnityEditor.Performance.ProfileAnalyzer.FakeScopedMarker;
  28. #endif
  29. namespace UnityEditor.Performance.ProfileAnalyzer
  30. {
  31. enum ThreadRange
  32. {
  33. Median,
  34. UpperQuartile,
  35. Max
  36. };
  37. enum ActiveTab
  38. {
  39. Summary,
  40. Compare,
  41. };
  42. enum ActiveView
  43. {
  44. Single,
  45. Left,
  46. Right
  47. }
  48. enum ThreadActivity
  49. {
  50. None,
  51. Analyze,
  52. AnalyzeDone,
  53. Compare,
  54. CompareDone,
  55. Load,
  56. LoadDone
  57. };
  58. enum TopTenDisplay
  59. {
  60. Normalized,
  61. LongestTime,
  62. };
  63. enum NameFilterOperation
  64. {
  65. All, // AND
  66. Any, // OR
  67. };
  68. /// <summary>
  69. /// Main profile Analyzer UI window
  70. /// </summary>
  71. public class ProfileAnalyzerWindow : EditorWindow
  72. {
  73. internal static class Styles
  74. {
  75. public static readonly GUIContent emptyString = new GUIContent("", "");
  76. public static readonly GUIContent dash = new GUIContent("-", "");
  77. public static readonly GUIContent thread = new GUIContent("Thread", "");
  78. public static readonly GUIContent noThread = new GUIContent("", "Thread not present on this data set");
  79. public static readonly GUIContent max = new GUIContent("Max", "The peak value in the data set");
  80. public static readonly GUIContent upperQuartile = new GUIContent("Upper Quartile", "The middle value between the median and the highest value of the data set. I.e. at 75% of the ordered data.");
  81. public static readonly GUIContent mean = new GUIContent("Mean", "The average value in the data set");
  82. public static readonly GUIContent median = new GUIContent("Median", "The central value in the data set");
  83. public static readonly GUIContent lowerQuartile = new GUIContent("Lower Quartile", "The middle number between the smallest number and the median of the data set. I.e. at 25% of the ordered data.");
  84. public static readonly GUIContent min = new GUIContent("Min", "The minimum value in the data set");
  85. public static readonly GUIContent individualMin = new GUIContent("Individual Min", "The minimum value in the data set for an individual marker instance (not the total in the frame)");
  86. public static readonly GUIContent individualMax = new GUIContent("Individual Max", "The maximum value in the data set for an individual marker instance (not the total in the frame)");
  87. public static readonly GUIContent export = new GUIContent("Export", "Export profiler data as CSV files");
  88. public static readonly GUIContent pullOpen = new GUIContent("Pull Data", "Pull data from Unity profiler.\nFirst you must open Unity profiler to pull data from it");
  89. public static readonly GUIContent pullRange = new GUIContent("Pull Data", "Pull data from Unity profiler.\nFirst you must use the Unity profiler to capture data from application");
  90. public static readonly GUIContent pullRecording = new GUIContent("Pull Data", "Pull data from Unity profiler.\nStop Unity profiler recording to enable pulling data");
  91. public static readonly GUIContent pull = new GUIContent("Pull Data", "Pull data from Unity profiler");
  92. public static readonly GUIContent nameFilter = new GUIContent("Name Filter : ", "Only show markers containing the strings");
  93. public static readonly GUIContent nameExclude = new GUIContent("Exclude Names : ", "Excludes markers containing the strings");
  94. public static readonly GUIContent threadFilter = new GUIContent("Thread : ", "Select threads to focus on");
  95. public static readonly GUIContent threadFilterSelect = new GUIContent("Select", "Select threads to focus on");
  96. public static readonly GUIContent unitFilter = new GUIContent("Units : ", "Units to show in UI");
  97. public static readonly GUIContent timingFilter = new GUIContent("Analysis Type : ", TimingOptions.Tooltip);
  98. public static readonly GUIContent markerColumns = new GUIContent("Marker Columns : ");
  99. public static readonly GUIContent graphPairing = new GUIContent("Pair Graph Selection", "Selections on one graph will affect the other");
  100. public static readonly GUIContent frameSummary = new GUIContent("Frame Summary", "");
  101. public static readonly GUIContent frameCount = new GUIContent("Frame Count", "Frame Count");
  102. public static readonly GUIContent frameStart = new GUIContent("Start", "Frame Start");
  103. public static readonly GUIContent frameEnd = new GUIContent("End", "Frame End");
  104. public static readonly GUIContent threadSummary = new GUIContent("Thread Summary", "");
  105. public static readonly GUIContent threadGraphScale = new GUIContent("Graph Scale : ", "");
  106. public static readonly GUIContent[] threadRanges =
  107. {
  108. new GUIContent("Median", "Median frame time"),
  109. new GUIContent("Upper quartile", "Upper quartile of frame time"),
  110. new GUIContent("Max", "Max frame time")
  111. };
  112. public static readonly GUIContent markerSummary = new GUIContent("Marker Summary", "");
  113. public static readonly GUIContent filters = new GUIContent("Filters", "");
  114. public static readonly GUIContent profileTable = new GUIContent("Marker Details for currently selected range", "");
  115. public static readonly GUIContent comparisonTable = new GUIContent("Marker Comparison for currently selected range", "");
  116. public static readonly GUIContent depthTitle = new GUIContent("Depth Slice : ", "Marker callstack depth to analyze");
  117. public static readonly GUIContent leftDepthTitle = new GUIContent("Left : ", "Marker callstack depth to analyze");
  118. public static readonly GUIContent rightDepthTitle = new GUIContent("Right : ", "Marker callstack depth to analyze");
  119. public static readonly string autoDepthTitleText = "Auto Depth (Diff: {0:+##;-##;None})";
  120. public static readonly GUIContent autoDepthTitle = new GUIContent("Auto Depth", "Match up the depth levels based on the most common difference between markers present in both data sets. If the selected depth is at a depth not present in the other data set, after applying this difference, it will use the deepest level.");
  121. public static readonly GUIContent parentMarker = new GUIContent("Parent Marker : ", "Marker to start analysis from. Parent of the hierarchy to analyze.");
  122. public static readonly GUIContent selectParentMarker = new GUIContent("None", "Select using right click context menu on marker names in marker table");
  123. public static readonly GUIContent topMarkerRatio = new GUIContent("Ratio : ", "Normalize\tNormalized to time of the individual set\nLongest\tRatio based on longest time of the two");
  124. public static readonly GUIContent firstFrame = new GUIContent("First frame", "");
  125. public static readonly GUIContent[] topTenDisplayOptions =
  126. {
  127. new GUIContent("Normalized", "Ratio normalized to time of the individual data set"),
  128. new GUIContent("Longest", "Ratio based on longest time of the two data sets")
  129. };
  130. public static readonly GUIContent[] nameFilterOperation =
  131. {
  132. new GUIContent("All", "Marker name contains all strings"),
  133. new GUIContent("Any", "Marker name contains any of the strings")
  134. };
  135. public static readonly GUIContent menuItemSelectFramesInAll = new GUIContent("Select Frames that contain this marker (within whole data set)", "");
  136. public static readonly GUIContent menuItemSelectFramesInCurrent = new GUIContent("Select Frames that contain this marker (within current selection)", "");
  137. public static readonly GUIContent menuItemSelectFramesAll = new GUIContent("Clear Selection", "");
  138. public static readonly GUIContent frameCosts = new GUIContent(" by frame costs", "Contains accumulated marker cost within the frame");
  139. public static readonly GUIContent dataMissing = new GUIContent("Pull or load a data set for analysis", "Pull data from Unity Profiler or load a previously saved analysis data set");
  140. public static readonly GUIContent comparisonDataMissing = new GUIContent("Pull or load a data set for comparison", "Pull data from Unity Profiler or load previously saved analysis data sets");
  141. public static readonly string topMarkersTooltip = "Top markers for the median frame.\nThe length of this frame is the median of those in the data set.\nIt is likely to be the most representative frame.";
  142. public static readonly string medianFrameTooltip = "The length of this frame is the median of those in the data set.\nIt is likely to be the most representative frame.";
  143. public static readonly string helpText =
  144. @"This tool can analyze Unity Profiler data, to find representative frames and perform comparisons of data sets.
  145. To gather data to analyze:
  146. * Open the Unity Profiler. Either via the Unity menu under 'Windows', 'Analysis' or via the 'Open Profile Window' in the tool bar.
  147. * Capture some profiling data in the Unity Profiler by selecting a target application and click the 'Record' button.
  148. * Stop the capture by clicking again on the 'Record' button.
  149. To analyze the data:
  150. * Pull the Unity Profiler data into this tool by clicking the 'Pull Data' button in the single or compare views.
  151. * The analysis will be automatically triggered (in the compare view two data sets are required before analysis is performed).
  152. * Select a marker to see more detailed information about its time utilization over the frame time range.
  153. * Save off a data file from here to keep for future use. (Recommend saving the profile .data file in the same folder).
  154. To compare two data sets:
  155. * Click the compare tab. The data in the single tab will be used by default. You can also load previously saved analysis data.
  156. * Drag select a region in the frame time graph (above) to choose 1 or more frames for each of the two data sets.
  157. * The comparison will be automatically triggered as the selection is made.";
  158. }
  159. const float k_ProgressBarHeight = 2f;
  160. ProgressBarDisplay m_ProgressBar;
  161. ProfileAnalyzer m_ProfileAnalyzer;
  162. ProfilerWindowInterface m_ProfilerWindowInterface;
  163. string m_LastProfilerSelectedMarker;
  164. string m_LastMarkerSuccesfullySyncedWithProfilerWindow = null;
  165. [NonSerialized] bool m_SelectionEventFromProfilerWindowInProgress = false;
  166. int m_TopNumber;
  167. string[] m_TopStrings;
  168. int[] m_TopValues;
  169. [SerializeField] DepthSliceUI m_DepthSliceUI;
  170. [SerializeField]
  171. TimingOptions.TimingOption m_TimingOption = TimingOptions.TimingOption.Time;
  172. [SerializeField]
  173. string m_ParentMarker = null;
  174. List<string> m_ThreadUINames = new List<string>();
  175. List<string> m_ThreadNames = new List<string>();
  176. Dictionary<string, string> m_ThreadNameToUIName;
  177. [SerializeField]
  178. ThreadSelection m_ThreadSelection = new ThreadSelection();
  179. ThreadSelection m_ThreadSelectionNew;
  180. string m_ThreadSelectionSummary;
  181. [SerializeField]
  182. DisplayUnits m_DisplayUnits = new DisplayUnits(Units.Milliseconds);
  183. string[] m_UnitNames;
  184. [SerializeField]
  185. string m_NameFilter = "";
  186. [SerializeField]
  187. string m_NameExclude = "";
  188. [SerializeField]
  189. MarkerColumnFilter m_SingleModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount);
  190. [SerializeField]
  191. MarkerColumnFilter m_CompareModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount);
  192. [SerializeField]
  193. TopTenDisplay m_TopTenDisplay = TopTenDisplay.Normalized;
  194. [SerializeField]
  195. NameFilterOperation m_NameFilterOperation = NameFilterOperation.All;
  196. [SerializeField]
  197. NameFilterOperation m_NameExcludeOperation = NameFilterOperation.Any;
  198. int m_ProfilerFirstFrameIndex = 0;
  199. int m_ProfilerLastFrameIndex = 0;
  200. const int k_ProfileDataDefaultDisplayOffset = 1;
  201. ActiveTab m_NextActiveTab = ActiveTab.Summary;
  202. ActiveTab m_ActiveTab = ActiveTab.Summary;
  203. bool m_OtherTabDirty = false;
  204. bool m_OtherTableDirty = false;
  205. [SerializeField]
  206. bool m_ShowFilters = true;
  207. [SerializeField]
  208. bool m_ShowTopNMarkers = true;
  209. [SerializeField]
  210. bool m_ShowFrameSummary = true;
  211. [SerializeField]
  212. bool m_ShowThreadSummary = false;
  213. [SerializeField]
  214. bool m_ShowMarkerSummary = true;
  215. [SerializeField]
  216. bool m_ShowMarkerTable = true;
  217. #if UNITY_PRE_2019_1
  218. bool m_NeedPre2019Enable;
  219. #endif
  220. internal static class UIColor
  221. {
  222. static internal Color Color256(int r, int g, int b, int a)
  223. {
  224. return new Color((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)a / 255.0f);
  225. }
  226. public static readonly Color white = new UnityEngine.Color(1.0f, 1.0f, 1.0f);
  227. public static readonly Color barBackground = new Color(0.5f, 0.5f, 0.5f);
  228. public static readonly Color barBackgroundSelected = new Color(0.6f, 0.6f, 0.6f);
  229. public static readonly Color boxAndWhiskerBoxColor = Color256(112, 112, 112, 255);
  230. public static readonly Color boxAndWhiskerLineColorLeft = Color256(206, 219, 238, 255);
  231. public static readonly Color boxAndWhiskerBoxColorLeft = Color256(59, 104, 144, 255);
  232. public static readonly Color boxAndWhiskerLineColorRight = Color256(247, 212, 201, 255);
  233. public static readonly Color boxAndWhiskerBoxColorRight = Color256(161, 83, 30, 255);
  234. public static readonly Color bar = new Color(0.95f, 0.95f, 0.95f);
  235. public static readonly Color barSelected = new Color(0.5f, 1.0f, 0.5f);
  236. public static readonly Color standardLine = new Color(1.0f, 1.0f, 1.0f);
  237. public static readonly Color gridLines = new Color(0.4f, 0.4f, 0.4f);
  238. public static readonly Color left = Color256(111, 163, 216, 255);
  239. public static readonly Color leftSelected = Color256(06, 219, 238, 255);
  240. public static readonly Color right = Color256(238, 134, 84, 255);
  241. public static readonly Color rightSelected = Color256(247, 212, 201, 255);
  242. public static readonly Color both = Color256(175, 150, 150, 255);
  243. public static readonly Color textTopMarkers = Color256(0, 0, 0, 255);
  244. public static readonly Color marker = new Color(0.0f, 0.5f, 0.5f);
  245. public static readonly Color markerSelected = new Color(0.0f, 0.6f, 0.6f);
  246. public static readonly Color thread = new Color(0.5f, 0.0f, 0.5f);
  247. public static readonly Color threadSelected = new Color(0.6f, 0.0f, 0.6f);
  248. }
  249. [SerializeField]
  250. ProfileDataView m_ProfileSingleView;
  251. [SerializeField]
  252. ProfileDataView m_ProfileLeftView;
  253. [SerializeField]
  254. ProfileDataView m_ProfileRightView;
  255. [SerializeField] ThreadMarkerInfo m_SelectedMarker = new ThreadMarkerInfo();
  256. [Serializable]
  257. struct ThreadMarkerInfo
  258. {
  259. [SerializeField]
  260. public int id;
  261. [SerializeField]
  262. public string threadName;
  263. [SerializeField]
  264. public string threadGroupName;
  265. [SerializeField]
  266. public string name;
  267. }
  268. FrameTimeGraphGlobalSettings m_FrameTimeGraphGlobalSettings;
  269. FrameTimeGraph m_FrameTimeGraph;
  270. FrameTimeGraph m_LeftFrameTimeGraph;
  271. FrameTimeGraph m_RightFrameTimeGraph;
  272. bool m_FrameTimeGraphsPaired = true;
  273. TopMarkers m_TopMarkers;
  274. TopMarkers m_TopMarkersLeft;
  275. TopMarkers m_TopMarkersRight;
  276. List<MarkerPairing> m_PairingsNew;
  277. int m_TotalCombinedMarkerCountNew;
  278. [SerializeField]
  279. List<MarkerPairing> m_Pairings = new List<MarkerPairing>();
  280. int m_TotalCombinedMarkerCount = 0;
  281. [SerializeField]
  282. int m_SelectedPairing = 0;
  283. [SerializeField]
  284. TreeViewState m_ProfileTreeViewState;
  285. [SerializeField]
  286. MultiColumnHeaderState m_ProfileMulticolumnHeaderState;
  287. ProfileTable m_ProfileTable;
  288. [SerializeField]
  289. TreeViewState m_ComparisonTreeViewState;
  290. [SerializeField]
  291. MultiColumnHeaderState m_ComparisonMulticolumnHeaderState;
  292. ComparisonTable m_ComparisonTable;
  293. internal static class LayoutSize
  294. {
  295. public static readonly int WidthColumn0 = 100;
  296. public static readonly int WidthColumn1 = 52; // +2 to prevent some
  297. public static readonly int WidthColumn2 = 52;
  298. public static readonly int WidthColumn3 = 52;
  299. public static readonly int WidthRHS = 290; // Column widths + label padding between (276) + scrollbar width
  300. public static readonly int FilterOptionsLeftLabelWidth = 100;
  301. public static readonly int FilterOptionsEnumWidth = 50;
  302. public static readonly int FilterOptionsLockedEnumWidth = 120;
  303. public static readonly int FilterOptionsRightLabelWidth = 110;
  304. public static readonly int FilterOptionsRightEnumWidth = 150;
  305. public static readonly int HistogramWidth = 153;
  306. public static readonly int MinWindowWidth = 800 + WidthRHS;
  307. public static readonly int MinWindowHeight = 480;
  308. public static readonly int WindowWidth = MinWindowWidth;
  309. public static readonly int WindowHeight = 840;
  310. public static readonly int ScrollBarPadding = 6; // this is legacy and we might be able to kill it but it will slightly change the layout of the window.
  311. }
  312. Columns m_Columns = new Columns(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  313. [SerializeField]
  314. ThreadRange m_ThreadRange = ThreadRange.UpperQuartile;
  315. internal Draw2D m_2D;
  316. bool m_Async = true;
  317. Thread m_BackgroundThread;
  318. ThreadActivity m_ThreadActivity;
  319. ProfileData m_ProfilerData;
  320. string m_Path;
  321. int m_ThreadPhase;
  322. int m_ThreadPhases;
  323. int m_ThreadProgress;
  324. bool m_RequestRepaint;
  325. bool m_RequestAnalysis;
  326. bool m_RequestCompare;
  327. bool m_FullAnalysisRequired;
  328. bool m_FullCompareRequired;
  329. [SerializeField]
  330. int m_TopNBars = 10;
  331. bool m_EnableAnalysisProfiling = false;
  332. int m_AnalyzeInUpdatePhase = 0;
  333. string m_LastAnalysisTime = "";
  334. string m_LastCompareTime = "";
  335. float m_LastAnalysisTimeMilliseconds;
  336. float m_LastCompareTimeMilliseconds;
  337. bool m_NewDataLoaded = false;
  338. bool m_NewComparisonDataLoaded = false;
  339. Vector2 m_HelpScroll = new Vector2(0, 0);
  340. Vector2 m_ThreadScroll = new Vector2(0, 0);
  341. Vector2 m_MarkerSummaryScroll = new Vector2(0, 0);
  342. Rect m_ThreadsAreaRect = new Rect();
  343. Rect m_ComparisonThreadsAreaRect = new Rect();
  344. Vector2 m_LastScreenSize = new Vector2(0, 0);
  345. bool m_ScreenSizeChanged;
  346. double m_ScreenSizeChangedTimeStarted;
  347. double m_ScreenSizeChangedTimeFinished;
  348. ActiveTab m_ScreenSizeChangedTab;
  349. GUIStyle m_StyleMiddleRight;
  350. GUIStyle m_StyleUpperLeft;
  351. bool m_StylesSetup = false;
  352. static Regex quotedStringWithoutQuotes = new Regex("\"([^\"]*)\"");
  353. static Regex quotedString = new Regex("(\"[^\"]*\")");
  354. static Regex stringWithoutWhiteSpace = new Regex("([^ \t]+)");
  355. /*
  356. static Regex lastSpace = new Regex("(.+)[ ]([^ ]*)");
  357. */
  358. #if UNITY_2018_1_OR_NEWER
  359. [MenuItem("Window/Analysis/Profile Analyzer")]
  360. #else
  361. [MenuItem("Window/Profile Analyzer")]
  362. #endif
  363. static void Init()
  364. {
  365. var window = GetWindow<ProfileAnalyzerWindow>("Profile Analyzer");
  366. window.minSize = new Vector2(LayoutSize.MinWindowWidth, LayoutSize.MinWindowHeight);
  367. window.position.size.Set(LayoutSize.WindowWidth, LayoutSize.WindowHeight);
  368. window.Show();
  369. window.m_LastScreenSize = window.position.size;
  370. }
  371. /// <summary>
  372. /// Open profile analyzer window
  373. /// </summary>
  374. public static void OpenProfileAnalyzer()
  375. {
  376. Init();
  377. }
  378. void Awake()
  379. {
  380. m_ScreenSizeChanged = false;
  381. m_ScreenSizeChangedTimeStarted = 0.0;
  382. m_ScreenSizeChangedTimeFinished = 0.0;
  383. m_ScreenSizeChangedTab = ActiveTab.Summary;
  384. m_ProfileSingleView = new ProfileDataView();
  385. m_ProfileLeftView = new ProfileDataView();
  386. m_ProfileRightView = new ProfileDataView();
  387. m_RequestRepaint = false;
  388. m_RequestAnalysis = false;
  389. m_RequestCompare = false;
  390. m_FrameTimeGraphGlobalSettings = new FrameTimeGraphGlobalSettings();
  391. }
  392. static int s_TmpCount = 0;
  393. static string s_TmpDir = "";
  394. ActiveView m_ActiveLoadingView;
  395. static string s_ApplicationDataPath;
  396. internal static string TmpDir
  397. {
  398. get
  399. {
  400. if (string.IsNullOrEmpty(s_ApplicationDataPath))
  401. s_ApplicationDataPath = Application.dataPath;
  402. if (string.IsNullOrEmpty(s_TmpDir))
  403. s_TmpDir = string.Format("{0}{1}ProfileAnalyzer{1}", Directory.GetParent(s_ApplicationDataPath).FullName, Path.DirectorySeparatorChar);
  404. return s_TmpDir;
  405. }
  406. }
  407. internal static string TmpPath
  408. {
  409. get
  410. {
  411. if (!Directory.Exists(TmpDir))
  412. Directory.CreateDirectory(TmpDir);
  413. while (File.Exists(string.Format("{0}tmp{1}.pdata", TmpDir, s_TmpCount)))
  414. {
  415. s_TmpCount++;
  416. }
  417. return string.Format("{0}tmp{1}.pdata", TmpDir, s_TmpCount);
  418. }
  419. }
  420. void OnEnable()
  421. {
  422. //do this here to safeguard against Application.dataPAth being accessed off the main thread
  423. s_ApplicationDataPath = Application.dataPath;
  424. // Update styles so we get the theme changes
  425. m_StylesSetup = false;
  426. ProfileAnalyzerAnalytics.EnableAnalytics();
  427. m_ProgressBar = new ProgressBarDisplay();
  428. if (m_DepthSliceUI == null)
  429. m_DepthSliceUI = new DepthSliceUI(b => UpdateActiveTab(b));
  430. else
  431. m_DepthSliceUI.OnEnable(b => UpdateActiveTab(b));
  432. m_ProfilerWindowInterface = new ProfilerWindowInterface(m_ProgressBar);
  433. if (!m_ProfilerWindowInterface.IsReady())
  434. {
  435. m_ProfilerWindowInterface.GetProfilerWindowHandle();
  436. }
  437. if (!string.IsNullOrEmpty(m_SelectedMarker.name))
  438. {
  439. var oldSelectedMarkerName = m_SelectedMarker;
  440. m_SelectedMarker.name = null;
  441. // wait a frame for the ProfilerWindow to get Enabled before re-setting the selection
  442. EditorApplication.delayCall += () => SelectMarkerByName(oldSelectedMarkerName.name, oldSelectedMarkerName.threadGroupName, oldSelectedMarkerName.threadName);
  443. }
  444. m_ProfilerWindowInterface.selectedMarkerChanged -= OnProfilerWindowCpuModuleSelectionChanged;
  445. m_ProfilerWindowInterface.selectedMarkerChanged += OnProfilerWindowCpuModuleSelectionChanged;
  446. m_ProfilerWindowInterface.selectedFrameChanged -= OnProfilerWindowSelectedFrameChanged;
  447. m_ProfilerWindowInterface.selectedFrameChanged += OnProfilerWindowSelectedFrameChanged;
  448. m_ProfileAnalyzer = new ProfileAnalyzer();
  449. if (m_ThreadSelection == null || m_ThreadSelection.empty)
  450. {
  451. ThreadIdentifier mainThreadSelection = new ThreadIdentifier("Main Thread", 1);
  452. m_ThreadSelection.Set(mainThreadSelection.threadNameWithIndex);
  453. }
  454. if (m_ThreadSelectionNew != null && m_ThreadSelectionNew.empty)
  455. m_ThreadSelectionNew = null;
  456. m_2D = new Draw2D("Unlit/ProfileAnalyzerShader");
  457. FrameTimeGraph.SetGlobalSettings(m_FrameTimeGraphGlobalSettings);
  458. m_FrameTimeGraph = new FrameTimeGraph(0, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.bar, UIColor.barSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines);
  459. m_FrameTimeGraph.SetRangeCallback(SetRange);
  460. m_FrameTimeGraph.SetActiveCallback(GraphActive);
  461. m_LeftFrameTimeGraph = new FrameTimeGraph(1, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.left, UIColor.leftSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines);
  462. m_LeftFrameTimeGraph.SetRangeCallback(SetLeftRange);
  463. m_LeftFrameTimeGraph.SetActiveCallback(GraphActive);
  464. m_RightFrameTimeGraph = new FrameTimeGraph(2, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.right, UIColor.rightSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines);
  465. m_RightFrameTimeGraph.SetRangeCallback(SetRightRange);
  466. m_RightFrameTimeGraph.SetActiveCallback(GraphActive);
  467. m_LeftFrameTimeGraph.PairWith(m_FrameTimeGraphsPaired ? m_RightFrameTimeGraph : null);
  468. m_TopMarkers = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers);
  469. m_TopMarkersLeft = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers);
  470. m_TopMarkersRight = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers);
  471. m_ThreadActivity = ThreadActivity.None;
  472. m_ThreadProgress = 0;
  473. m_ThreadPhase = 0;
  474. List<int> values = new List<int>();
  475. List<String> strings = new List<string>();
  476. for (int i = 1; i <= 10; i++)
  477. {
  478. values.Add(i);
  479. strings.Add(i.ToString());
  480. }
  481. m_TopValues = values.ToArray();
  482. m_TopStrings = strings.ToArray();
  483. m_TopNumber = 3;
  484. List<string> unitNames = new List<string>(DisplayUnits.UnitNames);
  485. unitNames.RemoveAt(unitNames.Count - 1);
  486. m_UnitNames = unitNames.ToArray();
  487. // Regrenerate analysis if just re initialised with the existing profile data reloaded from serialisation (e.g. on enter play mode)
  488. // As we don't serialise the analysis itself.
  489. // UpdateActiveTab(true);
  490. UpdateThreadNames();
  491. if (m_ProfileSingleView.analysis != null)
  492. {
  493. CreateProfileTable();
  494. m_RequestRepaint = true;
  495. }
  496. if (m_ProfileLeftView.analysis != null && m_ProfileRightView.analysis != null)
  497. {
  498. CreateComparisonTable();
  499. m_RequestRepaint = true;
  500. }
  501. // Mouse movement calls OnGui
  502. wantsMouseMove = true;
  503. }
  504. void OnDisable()
  505. {
  506. if (ProfileAnalyzerExportWindow.IsOpen())
  507. ProfileAnalyzerExportWindow.CloseAll();
  508. m_ProfilerWindowInterface.selectedMarkerChanged -= OnProfilerWindowCpuModuleSelectionChanged;
  509. m_ProfilerWindowInterface.selectedFrameChanged -= OnProfilerWindowSelectedFrameChanged;
  510. m_ProfilerWindowInterface.OnDisable();
  511. m_ProfilerWindowInterface = null;
  512. }
  513. void OnDestroy()
  514. {
  515. if (m_BackgroundThread != null)
  516. m_BackgroundThread.Abort();
  517. Pre2019ReEnableRecording();
  518. if (m_ProfileSingleView != null && m_ProfileSingleView.data != null)
  519. m_ProfileSingleView.data.DeleteTmpFiles();
  520. if (m_ProfileLeftView != null && m_ProfileLeftView.data != null)
  521. m_ProfileLeftView.data.DeleteTmpFiles();
  522. if (m_ProfileRightView != null && m_ProfileRightView.data != null)
  523. m_ProfileRightView.data.DeleteTmpFiles();
  524. if (Directory.Exists(TmpDir) && Directory.GetFiles(TmpDir).Length == 0)
  525. Directory.Delete(TmpDir, true);
  526. }
  527. bool DisplayCount()
  528. {
  529. switch (m_SingleModeFilter.mode)
  530. {
  531. case MarkerColumnFilter.Mode.CountTotals:
  532. case MarkerColumnFilter.Mode.CountPerFrame:
  533. return true;
  534. default:
  535. return false;
  536. }
  537. }
  538. void OnGUI()
  539. {
  540. if (Event.current.type != EventType.MouseMove)
  541. {
  542. m_2D.OnGUI();
  543. Draw();
  544. }
  545. ProcessInput();
  546. }
  547. bool TmpInUse(ProfileDataView dv, string path)
  548. {
  549. if (dv != m_ProfileSingleView && m_ProfileSingleView.data != null && m_ProfileSingleView.data.FilePath == path)
  550. return true;
  551. if (dv != m_ProfileLeftView && m_ProfileLeftView.data != null && m_ProfileLeftView.data.FilePath == path)
  552. return true;
  553. if (dv != m_ProfileRightView && m_ProfileRightView.data != null && m_ProfileRightView.data.FilePath == path)
  554. return true;
  555. return false;
  556. }
  557. void SetView(ProfileDataView dst, ProfileData data, string path, FrameTimeGraph graph)
  558. {
  559. if (!data.IsSame(dst.data))
  560. {
  561. if (dst == m_ProfileSingleView)
  562. m_NewDataLoaded = true;
  563. else
  564. m_NewComparisonDataLoaded = true;
  565. }
  566. if (dst.data != null && (m_NewDataLoaded || m_NewComparisonDataLoaded) && !TmpInUse(dst, dst.data.FilePath))
  567. dst.data.DeleteTmpFiles();
  568. dst.data = data;
  569. dst.path = path;
  570. dst.SelectFullRange();
  571. graph.Reset();
  572. graph.SetData(GetFrameTimeData(dst.data));
  573. // One of the views changed so make sure the export window knows if its open
  574. ProfileAnalyzerExportWindow exportWindow = ProfileAnalyzerExportWindow.FindOpenWindow();
  575. if (exportWindow != null)
  576. {
  577. exportWindow.SetData(m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView);
  578. }
  579. }
  580. void SetView(ProfileDataView dst, ProfileDataView src, FrameTimeGraph graph)
  581. {
  582. SetView(dst, src.data, src.path, graph);
  583. }
  584. void UpdateThreadNames()
  585. {
  586. // Update threads list
  587. switch (m_ActiveTab)
  588. {
  589. case ActiveTab.Summary:
  590. GetThreadNames(m_ProfileSingleView.data, out m_ThreadUINames, out m_ThreadNames, out m_ThreadNameToUIName);
  591. break;
  592. case ActiveTab.Compare:
  593. GetThreadNames(m_ProfileLeftView.data, m_ProfileRightView.data, out m_ThreadUINames, out m_ThreadNames, out m_ThreadNameToUIName);
  594. break;
  595. }
  596. UpdateThreadGroupSelection(m_ThreadNames, m_ThreadSelection);
  597. m_ThreadSelectionSummary = CalculateSelectedThreadsSummary();
  598. }
  599. void ProcessTabSwitch()
  600. {
  601. if (m_NextActiveTab != m_ActiveTab)
  602. {
  603. m_ActiveTab = m_NextActiveTab;
  604. // Copy data if none present for this tab
  605. switch (m_ActiveTab)
  606. {
  607. case ActiveTab.Summary:
  608. if (!m_ProfileSingleView.IsDataValid())
  609. {
  610. if (m_ProfileLeftView.IsDataValid())
  611. {
  612. SetView(m_ProfileSingleView, m_ProfileLeftView, m_FrameTimeGraph);
  613. m_RequestAnalysis = true;
  614. m_FullAnalysisRequired = true;
  615. }
  616. else if (m_ProfileRightView.IsDataValid())
  617. {
  618. SetView(m_ProfileSingleView, m_ProfileRightView, m_FrameTimeGraph);
  619. m_RequestAnalysis = true;
  620. m_FullAnalysisRequired = true;
  621. }
  622. }
  623. break;
  624. case ActiveTab.Compare:
  625. if ((!m_ProfileLeftView.IsDataValid() || !m_ProfileRightView.IsDataValid()) && m_ProfileSingleView.IsDataValid())
  626. {
  627. if (!m_ProfileLeftView.IsDataValid())
  628. {
  629. SetView(m_ProfileLeftView, m_ProfileSingleView, m_LeftFrameTimeGraph);
  630. }
  631. if (!m_ProfileRightView.IsDataValid())
  632. {
  633. SetView(m_ProfileRightView, m_ProfileSingleView, m_RightFrameTimeGraph);
  634. }
  635. // Remove pairing of both left/right point at the same data
  636. if (m_ProfileLeftView.path == m_ProfileRightView.path)
  637. {
  638. SetFrameTimeGraphPairing(false);
  639. }
  640. m_RequestCompare = true;
  641. m_FullCompareRequired = true;
  642. }
  643. break;
  644. }
  645. UpdateThreadNames();
  646. if (!m_OtherTableDirty)
  647. SelectMarker(m_SelectedMarker.name);
  648. if (m_OtherTabDirty)
  649. {
  650. UpdateActiveTab(true, false); // Make sure any depth/thread updates are applied when switching tabs, but don't dirty the other tab
  651. m_OtherTabDirty = false;
  652. }
  653. if (m_OtherTableDirty)
  654. {
  655. UpdateMarkerTable(false); // Make sure any marker selection updates are applied when switching tabs, but don't dirty the other tab
  656. m_OtherTableDirty = false;
  657. }
  658. if (!m_RequestAnalysis && !m_RequestCompare)
  659. m_DepthSliceUI.UpdateDepthFilters(m_ActiveTab == ActiveTab.Summary, m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView);
  660. }
  661. }
  662. bool IsDocked()
  663. {
  664. #if UNITY_2020_1_OR_NEWER
  665. return docked;
  666. #else
  667. BindingFlags fullBinding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
  668. MethodInfo isDockedMethod = typeof(EditorWindow).GetProperty("docked", fullBinding).GetGetMethod(true);
  669. bool isDocked = (bool)isDockedMethod.Invoke(this, null);
  670. return isDocked;
  671. #endif // UNITY_2020_1_OR_NEWER
  672. }
  673. void CheckScreenSizeChanges()
  674. {
  675. // We get a 5 pixel change in y height during initialization.
  676. // We could wait before considering size changes but using a delta is also useful
  677. float sizeDeltaForChange = 10;
  678. Vector2 sizeDiff = position.size - m_LastScreenSize;
  679. if (Math.Abs(sizeDiff.x) > sizeDeltaForChange || Math.Abs(sizeDiff.y) > sizeDeltaForChange)
  680. {
  681. if (m_LastScreenSize.x != 0) // At initialization time the screen size has not yet been recorded. Don't consider this a screen size change
  682. {
  683. m_LastScreenSize = position.size;
  684. if (!m_ScreenSizeChanged)
  685. {
  686. // Record when we started the change
  687. m_ScreenSizeChanged = true;
  688. m_ScreenSizeChangedTimeStarted = EditorApplication.timeSinceStartup;
  689. }
  690. // Record the last time of a change
  691. m_ScreenSizeChangedTimeFinished = EditorApplication.timeSinceStartup;
  692. // Record which tab we were on when it was changed
  693. m_ScreenSizeChangedTab = m_ActiveTab;
  694. }
  695. }
  696. if (m_ScreenSizeChanged)
  697. {
  698. double secondsSinceChanged = (EditorApplication.timeSinceStartup - m_ScreenSizeChangedTimeFinished);
  699. double secondsToDelay = 3f;
  700. if (secondsSinceChanged > secondsToDelay)
  701. {
  702. // Send analytic
  703. var uiResizeView = m_ScreenSizeChangedTab == ActiveTab.Summary ? ProfileAnalyzerAnalytics.UIResizeView.Single : ProfileAnalyzerAnalytics.UIResizeView.Comparison;
  704. float durationInSeconds = (float)(m_ScreenSizeChangedTimeFinished - m_ScreenSizeChangedTimeStarted);
  705. ProfileAnalyzerAnalytics.SendUIResizeEvent(uiResizeView, durationInSeconds, position.size.x, position.size.y, IsDocked());
  706. m_ScreenSizeChanged = false;
  707. }
  708. }
  709. }
  710. internal void RequestRepaint()
  711. {
  712. m_RequestRepaint = true;
  713. }
  714. void ProcessInput()
  715. {
  716. FrameTimeGraph.State inputStatus = FrameTimeGraph.State.None;
  717. if (m_ActiveTab == ActiveTab.Summary)
  718. {
  719. inputStatus = m_FrameTimeGraph.ProcessInput();
  720. }
  721. else if (m_ActiveTab == ActiveTab.Compare)
  722. {
  723. if (m_ProfileLeftView.IsDataValid() && inputStatus == FrameTimeGraph.State.None)
  724. inputStatus = m_LeftFrameTimeGraph.ProcessInput();
  725. if (m_ProfileRightView.IsDataValid() && inputStatus == FrameTimeGraph.State.None)
  726. inputStatus = m_RightFrameTimeGraph.ProcessInput();
  727. }
  728. switch (inputStatus)
  729. {
  730. case FrameTimeGraph.State.Dragging:
  731. m_RequestRepaint = true;
  732. break;
  733. case FrameTimeGraph.State.DragComplete:
  734. m_RequestCompare = true;
  735. break;
  736. }
  737. if (Event.current.isKey && Event.current.type == EventType.KeyDown)
  738. {
  739. switch (Event.current.keyCode)
  740. {
  741. case KeyCode.Alpha1:
  742. if (m_ActiveTab == ActiveTab.Summary)
  743. {
  744. m_FrameTimeGraph.MakeGraphActive(true);
  745. GUI.FocusControl("FrameTimeGraph");
  746. }
  747. else if (m_ActiveTab == ActiveTab.Compare)
  748. {
  749. m_LeftFrameTimeGraph.MakeGraphActive(true);
  750. GUI.FocusControl("LeftFrameTimeGraph");
  751. }
  752. m_RequestRepaint = true;
  753. break;
  754. case KeyCode.Alpha2:
  755. if (m_ActiveTab == ActiveTab.Compare)
  756. {
  757. m_RightFrameTimeGraph.MakeGraphActive(true);
  758. GUI.FocusControl("RightFrameTimeGraph");
  759. }
  760. m_RequestRepaint = true;
  761. break;
  762. }
  763. }
  764. }
  765. //Check if the ProfileDataView is in sync with the loaded frame data inside the profiler window
  766. //We are required to do this check in order to either enable or disable the ability to
  767. //jump into the matching frame data(loaded in the profiler window) for a specific profile analyzer capture
  768. void VerifyFrameDataInSyncWithProfilerWindow(ProfileDataView dataView)
  769. {
  770. var firstFrameIdx = m_ProfilerFirstFrameIndex - 1;
  771. var incompleteFrameCount = 0;
  772. if (dataView != null && dataView.data != null)
  773. incompleteFrameCount = (dataView.data.FirstFrameIncomplete ? 1 : 0) + (dataView.data.LastFrameIncomplete ? 1 : 0);
  774. var loadedFrameCount = m_ProfilerLastFrameIndex - m_ProfilerFirstFrameIndex + (firstFrameIdx != -1 ? 1 : 0)
  775. - incompleteFrameCount;
  776. if (loadedFrameCount == 0
  777. || !dataView.IsDataValid() //check if the data is valid and potentially reload the file, .data shouldn't be accessed before this point
  778. || dataView.data.GetFrameCount() != loadedFrameCount
  779. || m_ProfilerWindowInterface.GetThreadCountForFrame(firstFrameIdx) != dataView.data.GetFrame(0).threads.Count)
  780. {
  781. dataView.inSyncWithProfilerData = false;
  782. }
  783. else
  784. {
  785. var pDataFrame = dataView.data.GetFrame(0); //get the first frame we don't care about the offset as we only need to compare frames
  786. var loadedFrame = m_ProfilerWindowInterface.GetProfileFrameForThread(firstFrameIdx, 0);
  787. //compare frame start time and duration
  788. //todo improve this
  789. if (pDataFrame.msStartTime != loadedFrame.msStartTime
  790. || pDataFrame.msFrame != loadedFrame.msFrame)
  791. {
  792. dataView.inSyncWithProfilerData = false;
  793. }
  794. else
  795. {
  796. dataView.inSyncWithProfilerData = true;
  797. }
  798. }
  799. }
  800. //Returns true if we were able to sync with the window, but not necessarily if the data is in sync
  801. bool SyncWithProfilerWindow()
  802. {
  803. if (m_ProfilerWindowInterface.IsReady())
  804. {
  805. // Check if a new profile has been recorded (or loaded) by checking the frame index range.
  806. int first;
  807. int last;
  808. m_ProfilerWindowInterface.GetFrameRangeFromProfiler(out first, out last);
  809. if (first != m_ProfilerFirstFrameIndex || last != m_ProfilerLastFrameIndex)
  810. {
  811. // Store the updated range and alter the pull range
  812. m_ProfilerFirstFrameIndex = first;
  813. m_ProfilerLastFrameIndex = last;
  814. }
  815. VerifyFrameDataInSyncWithProfilerWindow(m_ProfileSingleView);
  816. VerifyFrameDataInSyncWithProfilerWindow(m_ProfileLeftView);
  817. VerifyFrameDataInSyncWithProfilerWindow(m_ProfileRightView);
  818. return true;
  819. }
  820. m_ProfilerWindowInterface.GetProfilerWindowHandle();
  821. return false;
  822. }
  823. void OnProfilerWindowCpuModuleSelectionChanged(string selectedMarker, string threadGroupName, string threadName)
  824. {
  825. // selectedMarker can be "" if in play mode and no active timeline shown (on versions pre 2021.1
  826. if (!string.IsNullOrEmpty(selectedMarker) && selectedMarker != m_LastProfilerSelectedMarker)
  827. {
  828. m_LastProfilerSelectedMarker = selectedMarker;
  829. m_SelectionEventFromProfilerWindowInProgress = true;
  830. SelectMarker(selectedMarker, threadGroupName, threadName);
  831. m_SelectionEventFromProfilerWindowInProgress = false;
  832. Repaint();
  833. }
  834. }
  835. void OnProfilerWindowSelectedFrameChanged(int newlySelectedFrame)
  836. {
  837. // selectedMarker can be "" if in play mode and no active timeline shown (on versions pre 2021.1
  838. if (!string.IsNullOrEmpty(m_LastProfilerSelectedMarker))
  839. {
  840. m_SelectionEventFromProfilerWindowInProgress = true;
  841. UpdateSelectedMarkerName(m_LastProfilerSelectedMarker);
  842. m_SelectionEventFromProfilerWindowInProgress = false;
  843. Repaint();
  844. }
  845. }
  846. void Update()
  847. {
  848. CheckScreenSizeChanges();
  849. // Check if profiler is open
  850. if (SyncWithProfilerWindow())
  851. {
  852. // Check if the selected marker in the profiler has changed
  853. m_ProfilerWindowInterface.PollProfilerWindowMarkerName();
  854. m_ProfilerWindowInterface.PollSelectedFrameChanges();
  855. }
  856. // Deferred to here so drawing isn't messed up by changing tab half way through a function rendering the old tab
  857. ProcessTabSwitch();
  858. // Force repaint for the progress bar
  859. if (IsAnalysisRunning())
  860. {
  861. int loadingProgress;
  862. int analysisProgress;
  863. if (IsLoading())
  864. {
  865. loadingProgress = (int)(ProfileData.GetLoadingProgress() * 100);
  866. analysisProgress = 0;
  867. }
  868. else
  869. {
  870. loadingProgress = 100;
  871. analysisProgress = m_ProfileAnalyzer.GetProgress();
  872. if (m_ThreadPhases > 1)
  873. {
  874. // Use thread phases to evaluate the progress as analysis process might contain multiple ProfileAnalyzer passes.
  875. analysisProgress = (100 * m_ThreadPhase) / m_ThreadPhases;
  876. }
  877. }
  878. int progress = (loadingProgress + analysisProgress) / 2;
  879. if (m_ThreadProgress != progress)
  880. {
  881. m_ThreadProgress = progress;
  882. m_RequestRepaint = true;
  883. }
  884. }
  885. if (m_ThreadSelectionNew != null)
  886. {
  887. m_ThreadSelection = new ThreadSelection(m_ThreadSelectionNew);
  888. m_ThreadSelectionNew = null;
  889. m_ThreadSelectionSummary = CalculateSelectedThreadsSummary();
  890. }
  891. switch (m_ThreadActivity)
  892. {
  893. case ThreadActivity.AnalyzeDone:
  894. // Create table when analysis complete
  895. Pre2019ReEnableRecording();
  896. UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired);
  897. m_FullAnalysisRequired = false;
  898. UpdateThreadNames();
  899. if (m_ProfileSingleView.analysis != null)
  900. {
  901. CreateProfileTable();
  902. m_RequestRepaint = true;
  903. }
  904. m_ThreadActivity = ThreadActivity.None;
  905. if (m_NewDataLoaded)
  906. {
  907. if (m_ProfileSingleView.IsDataValid())
  908. {
  909. // Don't bother sending an analytic if the data set is empty (should never occur anyway but consistent with comparison flow)
  910. ProfileAnalyzerAnalytics.SendUIUsageModeEvent(ProfileAnalyzerAnalytics.UIUsageMode.Single, m_LastAnalysisTimeMilliseconds / 1000f);
  911. }
  912. m_NewDataLoaded = false;
  913. }
  914. SelectMarker(m_SelectedMarker.name);
  915. break;
  916. case ThreadActivity.CompareDone:
  917. Pre2019ReEnableRecording();
  918. UpdateAnalysisFromAsyncProcessing(m_ProfileLeftView, m_FullCompareRequired);
  919. UpdateAnalysisFromAsyncProcessing(m_ProfileRightView, m_FullCompareRequired);
  920. m_FullCompareRequired = false;
  921. m_Pairings = m_PairingsNew;
  922. m_TotalCombinedMarkerCount = m_TotalCombinedMarkerCountNew;
  923. UpdateThreadNames();
  924. if (m_ProfileLeftView.analysis != null && m_ProfileRightView.analysis != null)
  925. {
  926. CreateComparisonTable();
  927. m_RequestRepaint = true;
  928. }
  929. m_ThreadActivity = ThreadActivity.None;
  930. if (m_NewComparisonDataLoaded)
  931. {
  932. if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid())
  933. {
  934. // Don't bother sending an analytic when one (or more) of the data sets is blank (as no comparison is really made)
  935. ProfileAnalyzerAnalytics.SendUIUsageModeEvent(ProfileAnalyzerAnalytics.UIUsageMode.Comparison, m_LastCompareTimeMilliseconds / 1000f);
  936. }
  937. m_NewComparisonDataLoaded = false;
  938. }
  939. SelectMarker(m_SelectedMarker.name);
  940. break;
  941. case ThreadActivity.LoadDone:
  942. Pre2019ReEnableRecording();
  943. SetView(GetActiveView, m_ProfilerData, m_Path, GetActiveFrameTimeGraph);
  944. switch (m_ActiveTab)
  945. {
  946. case ActiveTab.Compare:
  947. // Remove pairing if both left/right point at the same data
  948. if (m_ProfileLeftView.path == m_ProfileRightView.path)
  949. {
  950. SetFrameTimeGraphPairing(false);
  951. }
  952. m_FullCompareRequired = true;
  953. m_RequestCompare = true;
  954. break;
  955. case ActiveTab.Summary:
  956. m_RequestAnalysis = true;
  957. m_FullAnalysisRequired = true;
  958. break;
  959. default:
  960. throw new ArgumentOutOfRangeException();
  961. }
  962. m_ThreadActivity = ThreadActivity.None;
  963. break;
  964. }
  965. if (m_RequestAnalysis)
  966. {
  967. if (!IsAnalysisRunning())
  968. {
  969. Analyze();
  970. m_RequestAnalysis = false;
  971. }
  972. }
  973. if (m_RequestCompare)
  974. {
  975. if (!IsAnalysisRunning())
  976. {
  977. Compare();
  978. m_RequestCompare = false;
  979. }
  980. }
  981. if (m_RequestRepaint)
  982. {
  983. Repaint();
  984. m_RequestRepaint = false;
  985. }
  986. if (m_AnalyzeInUpdatePhase > 0)
  987. {
  988. switch (m_AnalyzeInUpdatePhase)
  989. {
  990. case 1:
  991. UnityEngine.Profiling.Profiler.enabled = true;
  992. m_AnalyzeInUpdatePhase++;
  993. return;
  994. case 2:
  995. AnalyzeSync();
  996. UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired);
  997. m_FullAnalysisRequired = false;
  998. m_AnalyzeInUpdatePhase++;
  999. return;
  1000. case 3:
  1001. m_AnalyzeInUpdatePhase++;
  1002. return;
  1003. case 4:
  1004. UnityEngine.Profiling.Profiler.enabled = false;
  1005. m_AnalyzeInUpdatePhase++;
  1006. return;
  1007. default:
  1008. m_AnalyzeInUpdatePhase = 0;
  1009. break;
  1010. }
  1011. }
  1012. }
  1013. void Pre2019ReEnableRecording()
  1014. {
  1015. #if UNITY_PRE_2019_1
  1016. if (m_NeedPre2019Enable)
  1017. {
  1018. m_NeedPre2019Enable = false;
  1019. ProfilerCommon.enabled = true;
  1020. }
  1021. #endif
  1022. }
  1023. void Pre2019DisableRecording()
  1024. {
  1025. #if UNITY_PRE_2019_1
  1026. if (ProfilerCommon.enabled && ProfilerDriver.deepProfiling && (EditorApplication.isPlaying || ProfilerDriver.profileEditor))
  1027. {
  1028. ProfilerCommon.enabled = false;
  1029. m_NeedPre2019Enable = true;
  1030. }
  1031. #endif
  1032. }
  1033. ProfileDataView GetActiveView
  1034. {
  1035. get
  1036. {
  1037. switch (m_ActiveLoadingView)
  1038. {
  1039. case ActiveView.Single:
  1040. return m_ProfileSingleView;
  1041. case ActiveView.Left:
  1042. return m_ProfileLeftView;
  1043. case ActiveView.Right:
  1044. return m_ProfileRightView;
  1045. default:
  1046. throw new ArgumentOutOfRangeException();
  1047. }
  1048. }
  1049. }
  1050. FrameTimeGraph GetActiveFrameTimeGraph
  1051. {
  1052. get
  1053. {
  1054. switch (m_ActiveLoadingView)
  1055. {
  1056. case ActiveView.Single:
  1057. return m_FrameTimeGraph;
  1058. case ActiveView.Left:
  1059. return m_LeftFrameTimeGraph;
  1060. case ActiveView.Right:
  1061. return m_RightFrameTimeGraph;
  1062. default:
  1063. throw new ArgumentOutOfRangeException();
  1064. }
  1065. }
  1066. }
  1067. void UpdateAnalysisFromAsyncProcessing(ProfileDataView view, bool full)
  1068. {
  1069. view.analysis = view.analysisNew;
  1070. if (full)
  1071. {
  1072. if (view.selectedIndices != null && view.IsDataValid() && view.selectedIndices.Count == view.data.GetFrameCount())
  1073. view.analysisFull = view.analysis;
  1074. else
  1075. view.analysisFull = view.analysisFullNew;
  1076. }
  1077. }
  1078. List<FrameTimeGraph.Data> GetFrameTimeData(ProfileData profileData)
  1079. {
  1080. List<FrameTimeGraph.Data> data = new List<FrameTimeGraph.Data>();
  1081. int frames = profileData.GetFrameCount();
  1082. for (int frameOffset = 0; frameOffset < frames; frameOffset++)
  1083. {
  1084. float ms = profileData.GetFrame(frameOffset).msFrame;
  1085. FrameTimeGraph.Data dataPoint = new FrameTimeGraph.Data(ms, frameOffset);
  1086. data.Add(dataPoint);
  1087. }
  1088. return data;
  1089. }
  1090. void Load()
  1091. {
  1092. m_Path = EditorUtility.OpenFilePanel("Load profile analyzer data file", "", "pdata");
  1093. if (m_Path.Length != 0)
  1094. {
  1095. m_ActiveLoadingView = ActiveView.Single;
  1096. Pre2019DisableRecording();
  1097. BeginAsyncAction(ThreadActivity.Load);
  1098. }
  1099. GUIUtility.ExitGUI();
  1100. }
  1101. void UpdateMatchingProfileData(ProfileData data, ref string path, ProfileAnalysis analysis, string newPath)
  1102. {
  1103. // Update left/right data if we are effectively overwriting it.
  1104. if (m_ProfileLeftView.path == newPath)
  1105. {
  1106. SetView(m_ProfileLeftView, data, newPath, m_LeftFrameTimeGraph);
  1107. m_RequestCompare = true;
  1108. m_FullCompareRequired = true;
  1109. }
  1110. if (m_ProfileRightView.path == newPath)
  1111. {
  1112. SetView(m_ProfileRightView, data, newPath, m_RightFrameTimeGraph);
  1113. m_RequestCompare = true;
  1114. m_FullCompareRequired = true;
  1115. }
  1116. // Update single view if needed
  1117. if (m_ProfileSingleView.path == newPath)
  1118. {
  1119. SetView(m_ProfileSingleView, data, newPath, m_FrameTimeGraph);
  1120. m_ProfileSingleView.analysis = analysis;
  1121. }
  1122. path = newPath;
  1123. }
  1124. void Save(ProfileDataView dataView, bool updateDataViewWithSelectedPath = false)
  1125. {
  1126. string newPath = EditorUtility.SaveFilePanel("Save profile analyzer data file", "", "capture.pdata", "pdata");
  1127. if (newPath.Length != 0)
  1128. {
  1129. if (updateDataViewWithSelectedPath)
  1130. {
  1131. dataView.path = newPath;
  1132. }
  1133. if (ProfileData.Save(newPath, dataView.data))
  1134. {
  1135. UpdateMatchingProfileData(dataView.data, ref dataView.path, dataView.analysis, newPath);
  1136. }
  1137. }
  1138. GUIUtility.ExitGUI();
  1139. }
  1140. int GetTotalCombinedMarkerCount(ProfileData left, ProfileData right)
  1141. {
  1142. if (left == null)
  1143. return 0;
  1144. if (right == null)
  1145. return 0;
  1146. List<string> leftMarkers = left.GetMarkerNames();
  1147. if (leftMarkers == null)
  1148. return 0;
  1149. List<string> rightMarkers = right.GetMarkerNames();
  1150. if (rightMarkers == null)
  1151. return 0;
  1152. HashSet<string> markerPairs = new HashSet<string>();
  1153. for (int index = 0; index < leftMarkers.Count; index++)
  1154. {
  1155. string markerName = leftMarkers[index];
  1156. markerPairs.Add(markerName);
  1157. }
  1158. for (int index = 0; index < rightMarkers.Count; index++)
  1159. {
  1160. string markerName = rightMarkers[index];
  1161. if (!markerPairs.Contains(markerName))
  1162. {
  1163. markerPairs.Add(markerName);
  1164. }
  1165. }
  1166. return markerPairs.Count;
  1167. }
  1168. List<MarkerPairing> GeneratePairings(ProfileAnalysis leftAnalysis, ProfileAnalysis rightAnalysis)
  1169. {
  1170. if (leftAnalysis == null)
  1171. return null;
  1172. if (rightAnalysis == null)
  1173. return null;
  1174. List<MarkerData> leftMarkers = leftAnalysis.GetMarkers();
  1175. if (leftMarkers == null)
  1176. return null;
  1177. List<MarkerData> rightMarkers = rightAnalysis.GetMarkers();
  1178. if (rightMarkers == null)
  1179. return null;
  1180. Dictionary<string, MarkerPairing> markerPairs = new Dictionary<string, MarkerPairing>();
  1181. for (int index = 0; index < leftMarkers.Count; index++)
  1182. {
  1183. MarkerData marker = leftMarkers[index];
  1184. MarkerPairing pair = new MarkerPairing
  1185. {
  1186. name = marker.name,
  1187. leftIndex = index,
  1188. rightIndex = -1
  1189. };
  1190. markerPairs[marker.name] = pair;
  1191. }
  1192. for (int index = 0; index < rightMarkers.Count; index++)
  1193. {
  1194. MarkerData marker = rightMarkers[index];
  1195. if (markerPairs.ContainsKey(marker.name))
  1196. {
  1197. MarkerPairing pair = markerPairs[marker.name];
  1198. pair.rightIndex = index;
  1199. markerPairs[marker.name] = pair;
  1200. }
  1201. else
  1202. {
  1203. MarkerPairing pair = new MarkerPairing
  1204. {
  1205. name = marker.name,
  1206. leftIndex = -1,
  1207. rightIndex = index
  1208. };
  1209. markerPairs[marker.name] = pair;
  1210. }
  1211. }
  1212. List<MarkerPairing> pairings = new List<MarkerPairing>();
  1213. foreach (MarkerPairing pair in markerPairs.Values)
  1214. pairings.Add(pair);
  1215. return pairings;
  1216. }
  1217. void SetThreadPhaseCount(ThreadActivity activity)
  1218. {
  1219. // Will be refined by the analysis functions
  1220. if (activity == ThreadActivity.Compare)
  1221. {
  1222. m_ThreadPhases = 8;
  1223. }
  1224. else
  1225. {
  1226. m_ThreadPhases = 2;
  1227. }
  1228. }
  1229. void BeginAsyncAction(ThreadActivity activity)
  1230. {
  1231. if (IsAnalysisRunning())
  1232. return;
  1233. m_ThreadActivity = activity;
  1234. m_ThreadProgress = 0;
  1235. m_ThreadPhase = 0;
  1236. SetThreadPhaseCount(activity);
  1237. m_BackgroundThread = new Thread(BackgroundThread);
  1238. m_BackgroundThread.Start();
  1239. }
  1240. void CreateComparisonTable()
  1241. {
  1242. UpdateThreadNames();
  1243. // Set default sorting state
  1244. int sortedColumn = (int)ComparisonTable.MyColumns.AbsDiff;
  1245. bool sortAscending = false;
  1246. // Query last sorting state
  1247. if (m_ComparisonMulticolumnHeaderState != null)
  1248. {
  1249. if (m_ComparisonMulticolumnHeaderState.sortedColumnIndex >= 0)
  1250. {
  1251. sortedColumn = m_ComparisonMulticolumnHeaderState.sortedColumnIndex;
  1252. if (sortedColumn >= 0 && sortedColumn < m_ComparisonMulticolumnHeaderState.columns.Length)
  1253. sortAscending = m_ComparisonMulticolumnHeaderState.columns[sortedColumn].sortedAscending;
  1254. }
  1255. }
  1256. if (m_ComparisonTreeViewState == null)
  1257. m_ComparisonTreeViewState = new TreeViewState();
  1258. m_ComparisonMulticolumnHeaderState = ComparisonTable.CreateDefaultMultiColumnHeaderState(m_CompareModeFilter);
  1259. var multiColumnHeader = new MultiColumnHeader(m_ComparisonMulticolumnHeaderState);
  1260. multiColumnHeader.SetSorting(sortedColumn, sortAscending);
  1261. multiColumnHeader.ResizeToFit();
  1262. m_ComparisonTable = new ComparisonTable(m_ComparisonTreeViewState, multiColumnHeader, m_ProfileLeftView, m_ProfileRightView, m_Pairings, this, m_2D, UIColor.left, UIColor.right);
  1263. if (string.IsNullOrEmpty(m_SelectedMarker.name))
  1264. SelectPairing(0);
  1265. else
  1266. SelectPairingByName(m_SelectedMarker.name);
  1267. }
  1268. void CalculatePairingbuckets(ProfileAnalysis left, ProfileAnalysis right, List<MarkerPairing> pairings)
  1269. {
  1270. var leftMarkers = left.GetMarkers();
  1271. var rightMarkers = right.GetMarkers();
  1272. foreach (var pairing in pairings)
  1273. {
  1274. float min = float.MaxValue;
  1275. float max = 0.0f;
  1276. MarkerData leftMarker = null;
  1277. MarkerData rightMarker = null;
  1278. if (pairing.leftIndex >= 0)
  1279. {
  1280. leftMarker = leftMarkers[pairing.leftIndex];
  1281. max = Math.Max(max, leftMarker.msMax);
  1282. min = Math.Min(min, leftMarker.msMin);
  1283. }
  1284. if (pairing.rightIndex >= 0)
  1285. {
  1286. rightMarker = rightMarkers[pairing.rightIndex];
  1287. max = Math.Max(max, rightMarker.msMax);
  1288. min = Math.Min(min, rightMarker.msMin);
  1289. }
  1290. int countMin = int.MaxValue;
  1291. int countMax = 0;
  1292. if (leftMarker != null)
  1293. {
  1294. countMax = Math.Max(countMax, leftMarker.countMax);
  1295. countMin = Math.Min(countMin, leftMarker.countMin);
  1296. }
  1297. if (rightMarker != null)
  1298. {
  1299. countMax = Math.Max(countMax, rightMarker.countMax);
  1300. countMin = Math.Min(countMin, rightMarker.countMin);
  1301. }
  1302. if (leftMarker != null)
  1303. {
  1304. leftMarker.ComputeBuckets(min, max);
  1305. leftMarker.ComputeCountBuckets(countMin, countMax);
  1306. }
  1307. if (rightMarker != null)
  1308. {
  1309. rightMarker.ComputeBuckets(min, max);
  1310. rightMarker.ComputeCountBuckets(countMin, countMax);
  1311. }
  1312. }
  1313. }
  1314. bool CompareSync()
  1315. {
  1316. if (!m_ProfileLeftView.IsDataValid())
  1317. return false;
  1318. if (!m_ProfileRightView.IsDataValid())
  1319. return false;
  1320. List<string> threadUINamesNew;
  1321. List<string> threadNamesNew;
  1322. Dictionary<string, string> threadNameToUINameNew;
  1323. GetThreadNames(m_ProfileLeftView.data, m_ProfileRightView.data, out threadUINamesNew, out threadNamesNew, out threadNameToUINameNew);
  1324. List<string> threadSelection = GetLimitedThreadSelection(threadNamesNew, m_ThreadSelection);
  1325. System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
  1326. stopwatch.Start();
  1327. int updateDepthPhase = m_NewComparisonDataLoaded ? 2 : 0;
  1328. int fullLeftPhase = (m_FullCompareRequired && m_ProfileLeftView.selectedIndices.Count != m_ProfileLeftView.data.GetFrameCount()) ? 1 : 0;
  1329. int fullRightPhase = (m_FullCompareRequired && m_ProfileLeftView.selectedIndices.Count != m_ProfileLeftView.data.GetFrameCount()) ? 1 : 0;
  1330. m_ThreadPhases = 2 /*scan left and right*/ + updateDepthPhase + 2 /*fullLeftPhase and fullRightPhase*/ + 2 /*analyze left and right*/;
  1331. bool selfTimes = IsSelfTime();
  1332. // First scan just the frames
  1333. m_ThreadPhase = 0;
  1334. var leftAnalysisNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, null, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker);
  1335. m_ThreadPhase++;
  1336. var rightAnalysisNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, null, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker);
  1337. m_ThreadPhase++;
  1338. if (leftAnalysisNew == null || rightAnalysisNew == null)
  1339. {
  1340. stopwatch.Stop();
  1341. return false;
  1342. }
  1343. // Calculate the max frame time of the two scans
  1344. float timeScaleMax = Math.Max(leftAnalysisNew.GetFrameSummary().msMax, rightAnalysisNew.GetFrameSummary().msMax);
  1345. // Need to recalculate the depth difference when thread filters change
  1346. // For now do it always if the depth is auto and not 'all'
  1347. if (updateDepthPhase != 0)
  1348. {
  1349. var leftAnalysis = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, threadSelection, ProfileAnalyzer.kDepthAll, selfTimes, m_ParentMarker, timeScaleMax);
  1350. m_ThreadPhase++;
  1351. var rightAnalysis = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, threadSelection, ProfileAnalyzer.kDepthAll, selfTimes, m_ParentMarker, timeScaleMax);
  1352. m_ThreadPhase++;
  1353. var pairings = GeneratePairings(leftAnalysis, rightAnalysis);
  1354. if (m_DepthSliceUI.UpdateDepthForCompareSync(leftAnalysis, rightAnalysis, pairings, m_ProfileLeftView, m_ProfileRightView))
  1355. {
  1356. // New depth diff calculated to we need to do the full analysis
  1357. if (fullLeftPhase == 0)
  1358. fullLeftPhase = 1;
  1359. if (fullRightPhase == 0)
  1360. fullRightPhase = 1;
  1361. }
  1362. }
  1363. // Now process the markers and setup buckets using the overall max frame time
  1364. List<int> selection = new List<int>();
  1365. if (fullLeftPhase != 0)
  1366. {
  1367. selection.Clear();
  1368. for (int frameOffset = 0; frameOffset < m_ProfileLeftView.data.GetFrameCount(); frameOffset++)
  1369. {
  1370. selection.Add(m_ProfileLeftView.data.OffsetToDisplayFrame(frameOffset));
  1371. }
  1372. // We don't pass timeScaleMax as that is only for the selected region.
  1373. // Pass 0 to auto select full range
  1374. m_ProfileLeftView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, selection, threadSelection, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker, 0);
  1375. }
  1376. m_ThreadPhase++;
  1377. if (fullRightPhase != 0)
  1378. {
  1379. selection.Clear();
  1380. for (int frameOffset = 0; frameOffset < m_ProfileRightView.data.GetFrameCount(); frameOffset++)
  1381. {
  1382. selection.Add(m_ProfileRightView.data.OffsetToDisplayFrame(frameOffset));
  1383. }
  1384. // We don't pass timeScaleMax as that is only for the selected region.
  1385. // Pass 0 to auto select full range
  1386. m_ProfileRightView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, selection, threadSelection, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker, 0);
  1387. }
  1388. m_ThreadPhase++;
  1389. m_ProfileLeftView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker, timeScaleMax);
  1390. m_ThreadPhase++;
  1391. m_ProfileRightView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker, timeScaleMax);
  1392. m_ThreadPhase++;
  1393. m_TotalCombinedMarkerCountNew = GetTotalCombinedMarkerCount(m_ProfileLeftView.data, m_ProfileRightView.data);
  1394. m_PairingsNew = GeneratePairings(m_ProfileLeftView.analysisNew, m_ProfileRightView.analysisNew);
  1395. CalculatePairingbuckets(m_ProfileLeftView.analysisNew, m_ProfileRightView.analysisNew, m_PairingsNew);
  1396. stopwatch.Stop();
  1397. m_LastCompareTimeMilliseconds = stopwatch.ElapsedMilliseconds;
  1398. TimeSpan ts = stopwatch.Elapsed;
  1399. if (ts.Minutes > 0)
  1400. m_LastCompareTime = string.Format("Last compare time {0} mins {1} secs {2} ms ", ts.Minutes, ts.Seconds, ts.Milliseconds);
  1401. else if (ts.Seconds > 0)
  1402. m_LastCompareTime = string.Format("Last compare time {0} secs {1} ms ", ts.Seconds, ts.Milliseconds);
  1403. else
  1404. m_LastCompareTime = string.Format("Last compare time {0} ms ", ts.Milliseconds);
  1405. return true;
  1406. }
  1407. void Compare()
  1408. {
  1409. if (m_Async)
  1410. {
  1411. //m_comparisonTable = null;
  1412. //m_ProfileLeftView.analysis = null;
  1413. //m_ProfileRightView.analysis = null;
  1414. Pre2019DisableRecording();
  1415. BeginAsyncAction(ThreadActivity.Compare);
  1416. }
  1417. else
  1418. {
  1419. CompareSync();
  1420. UpdateAnalysisFromAsyncProcessing(m_ProfileLeftView, m_FullCompareRequired);
  1421. UpdateAnalysisFromAsyncProcessing(m_ProfileRightView, m_FullCompareRequired);
  1422. m_FullCompareRequired = false;
  1423. }
  1424. }
  1425. List<MarkerPairing> GetPairings()
  1426. {
  1427. return m_Pairings;
  1428. }
  1429. int GetUnsavedIndex(string path)
  1430. {
  1431. if (path == null)
  1432. return 0;
  1433. Regex unsavedRegExp = new Regex(@"^Unsaved[\s*]([\d]*)", RegexOptions.IgnoreCase);
  1434. Match match = unsavedRegExp.Match(path);
  1435. if (match.Length <= 0)
  1436. return 0;
  1437. return Int32.Parse(match.Groups[1].Value);
  1438. }
  1439. void PullFromProfiler(int firstFrame, int lastFrame, ProfileDataView view, FrameTimeGraph frameTimeGraph)
  1440. {
  1441. m_ProgressBar.InitProgressBar("Pulling Frames from Profiler", "Please wait...", lastFrame - firstFrame);
  1442. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  1443. ProfileData newProfileData = m_ProfilerWindowInterface.PullFromProfiler(firstFrame, lastFrame);
  1444. ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.Pull, analytic);
  1445. frameTimeGraph.Reset();
  1446. frameTimeGraph.SetData(GetFrameTimeData(newProfileData));
  1447. // Check if this is new data (rather than repulling the same data)
  1448. if (!newProfileData.IsSame(view.data))
  1449. {
  1450. if (view == m_ProfileSingleView)
  1451. m_NewDataLoaded = true;
  1452. else
  1453. m_NewComparisonDataLoaded = true;
  1454. }
  1455. // Update the path to use the same saved file name if this is the same data as another view
  1456. if (newProfileData.IsSame(m_ProfileLeftView.data))
  1457. {
  1458. view.path = m_ProfileLeftView.path;
  1459. }
  1460. else if (newProfileData.IsSame(m_ProfileRightView.data))
  1461. {
  1462. view.path = m_ProfileRightView.path;
  1463. }
  1464. else if (newProfileData.IsSame(m_ProfileSingleView.data))
  1465. {
  1466. view.path = m_ProfileSingleView.path;
  1467. }
  1468. else
  1469. {
  1470. int lastIndex = 0;
  1471. lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileSingleView.path));
  1472. lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileLeftView.path));
  1473. lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileRightView.path));
  1474. view.path = string.Format("Unsaved {0}", lastIndex + 1);
  1475. }
  1476. if (view.data != null && !TmpInUse(view, view.data.FilePath))
  1477. view.data.DeleteTmpFiles();
  1478. view.data = newProfileData;
  1479. view.SelectFullRange();
  1480. // Remove pairing if both left/right point at the same data
  1481. if (m_ProfileLeftView.path == m_ProfileRightView.path)
  1482. {
  1483. SetFrameTimeGraphPairing(false);
  1484. }
  1485. m_ProgressBar.ClearProgressBar();
  1486. }
  1487. void BackgroundThread()
  1488. {
  1489. try
  1490. {
  1491. switch (m_ThreadActivity)
  1492. {
  1493. case ThreadActivity.Analyze:
  1494. AnalyzeSync();
  1495. m_ThreadActivity = ThreadActivity.AnalyzeDone;
  1496. break;
  1497. case ThreadActivity.Compare:
  1498. CompareSync();
  1499. m_ThreadActivity = ThreadActivity.CompareDone;
  1500. break;
  1501. case ThreadActivity.AnalyzeDone:
  1502. break;
  1503. case ThreadActivity.CompareDone:
  1504. break;
  1505. case ThreadActivity.Load:
  1506. m_ThreadActivity = ProfileData.Load(m_Path, out m_ProfilerData) ? ThreadActivity.LoadDone : ThreadActivity.None;
  1507. break;
  1508. case ThreadActivity.LoadDone:
  1509. break;
  1510. default:
  1511. // m_threadActivity = ThreadActivity.None;
  1512. break;
  1513. }
  1514. }
  1515. catch (ThreadAbortException)
  1516. {
  1517. var activity = (m_ThreadActivity == ThreadActivity.Load) ? "Load" : "Analysis";
  1518. Debug.LogFormat("{0} failed due to a domain reload. Please try again.", activity);
  1519. }
  1520. }
  1521. void SelectFirstMarkerInTable()
  1522. {
  1523. // SelectMarkerByIndex(0) would only select the first one found, not the first in the sorted list
  1524. if (m_ProfileTable == null)
  1525. return;
  1526. var rows = m_ProfileTable.GetRows();
  1527. if (rows == null || rows.Count < 1)
  1528. return;
  1529. SelectMarkerByName(rows[0].displayName);
  1530. }
  1531. void CreateProfileTable()
  1532. {
  1533. if (m_ProfileTreeViewState == null)
  1534. m_ProfileTreeViewState = new TreeViewState();
  1535. // Set default sorting state
  1536. int sortedColumn = (int)ProfileTable.MyColumns.Median;
  1537. bool sortAscending = false;
  1538. // Query last sorting state
  1539. if (m_ProfileMulticolumnHeaderState != null)
  1540. {
  1541. if (m_ProfileMulticolumnHeaderState.sortedColumnIndex >= 0)
  1542. {
  1543. sortedColumn = m_ProfileMulticolumnHeaderState.sortedColumnIndex;
  1544. if (sortedColumn >= 0 && sortedColumn < m_ProfileMulticolumnHeaderState.columns.Length)
  1545. sortAscending = m_ProfileMulticolumnHeaderState.columns[sortedColumn].sortedAscending;
  1546. }
  1547. }
  1548. m_ProfileMulticolumnHeaderState = ProfileTable.CreateDefaultMultiColumnHeaderState(m_SingleModeFilter);
  1549. var multiColumnHeader = new MultiColumnHeader(m_ProfileMulticolumnHeaderState);
  1550. multiColumnHeader.SetSorting(sortedColumn, sortAscending);
  1551. multiColumnHeader.ResizeToFit();
  1552. m_ProfileTable = new ProfileTable(m_ProfileTreeViewState, multiColumnHeader, m_ProfileSingleView, this, m_2D, UIColor.bar);
  1553. if (string.IsNullOrEmpty(m_SelectedMarker.name))
  1554. SelectFirstMarkerInTable();
  1555. else
  1556. SelectMarkerByName(m_SelectedMarker.name);
  1557. UpdateThreadNames();
  1558. }
  1559. void AnalyzeSync()
  1560. {
  1561. if (!m_ProfileSingleView.IsDataValid())
  1562. return;
  1563. System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
  1564. stopwatch.Start();
  1565. List<string> threadUINamesNew;
  1566. List<string> threadNamesNew;
  1567. Dictionary<string, string> threadNameToUINameNew;
  1568. GetThreadNames(m_ProfileSingleView.data, out threadUINamesNew, out threadNamesNew, out threadNameToUINameNew);
  1569. List<string> threadSelection = GetLimitedThreadSelection(threadNamesNew, m_ThreadSelection);
  1570. int fullPhase = (m_FullAnalysisRequired && (m_ProfileSingleView.selectedIndices.Count != m_ProfileSingleView.data.GetFrameCount())) ? 1 : 0;
  1571. m_ThreadPhases = 1 + fullPhase;
  1572. bool selfTimes = IsSelfTime();
  1573. m_ThreadPhase = 0;
  1574. if (fullPhase == 1)
  1575. {
  1576. List<int> selection = new List<int>();
  1577. for (int frameOffset = 0; frameOffset < m_ProfileSingleView.data.GetFrameCount(); frameOffset++)
  1578. {
  1579. selection.Add(m_ProfileSingleView.data.OffsetToDisplayFrame(frameOffset));
  1580. }
  1581. m_ProfileSingleView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileSingleView.data, selection, threadSelection, m_DepthSliceUI.depthFilter, selfTimes, m_ParentMarker);
  1582. m_ThreadPhase++;
  1583. }
  1584. m_ProfileSingleView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileSingleView.data, m_ProfileSingleView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter, selfTimes, m_ParentMarker);
  1585. m_ThreadPhase++;
  1586. stopwatch.Stop();
  1587. m_LastAnalysisTimeMilliseconds = stopwatch.ElapsedMilliseconds;
  1588. TimeSpan ts = stopwatch.Elapsed;
  1589. if (ts.Minutes > 0)
  1590. m_LastAnalysisTime = string.Format("Last analysis time {0} mins {1} secs {2} ms ", ts.Minutes, ts.Seconds, ts.Milliseconds);
  1591. else if (ts.Seconds > 0)
  1592. m_LastAnalysisTime = string.Format("Last analysis time {0} secs {1} ms ", ts.Seconds, ts.Milliseconds);
  1593. else
  1594. m_LastAnalysisTime = string.Format("Last analysis time {0} ms ", ts.Milliseconds);
  1595. }
  1596. void Analyze()
  1597. {
  1598. if (m_EnableAnalysisProfiling)
  1599. {
  1600. m_AnalyzeInUpdatePhase = 1;
  1601. return;
  1602. }
  1603. if (m_Async)
  1604. {
  1605. //m_profileTable = null;
  1606. //m_ProfileSingleView.analysis = null;
  1607. Pre2019DisableRecording();
  1608. BeginAsyncAction(ThreadActivity.Analyze);
  1609. }
  1610. else
  1611. {
  1612. AnalyzeSync();
  1613. UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired);
  1614. m_FullAnalysisRequired = false;
  1615. }
  1616. }
  1617. void GetThreadNames(ProfileData profleData, out List<string> threadUINames, out List<string> threadFilters, out Dictionary<string, string> threadNameToUIName)
  1618. {
  1619. GetThreadNames(profleData, null, out threadUINames, out threadFilters, out threadNameToUIName);
  1620. }
  1621. public string GetUIThreadName(string threadNameWithIndex)
  1622. {
  1623. string threadName = "";
  1624. m_ThreadNameToUIName.TryGetValue(threadNameWithIndex, out threadName);
  1625. return threadName;
  1626. }
  1627. string GetFriendlyThreadName(string threadNameWithIndex, bool single)
  1628. {
  1629. if (string.IsNullOrEmpty(threadNameWithIndex))
  1630. return "";
  1631. var info = threadNameWithIndex.Split(':');
  1632. int threadGroupIndex = int.Parse(info[0]);
  1633. var threadName = info[1].Trim();
  1634. if (single) // Single instance of this thread name
  1635. {
  1636. return threadName;
  1637. }
  1638. else
  1639. {
  1640. // The original format was "Worker 0"
  1641. // The internal formatting is 1:Worker (1+original value).
  1642. // Hence the -1 here
  1643. return string.Format("{0} {1}", threadName, threadGroupIndex - 1);
  1644. }
  1645. }
  1646. internal int CompareUINames(string a, string b)
  1647. {
  1648. var aSpaceIndex = a.LastIndexOf(' ');
  1649. var bSpaceIndex = b.LastIndexOf(' ');
  1650. if (aSpaceIndex >= 0 && bSpaceIndex >= 0)
  1651. {
  1652. var aThreadName = a.Substring(0, aSpaceIndex);
  1653. var bThreadName = b.Substring(0, bSpaceIndex);
  1654. if (aThreadName == bThreadName)
  1655. {
  1656. var aThreadIndex = a.Substring(aSpaceIndex + 1);
  1657. var bThreadIndex = b.Substring(bSpaceIndex + 1);
  1658. if (aThreadIndex == "All" && bThreadIndex != "All")
  1659. return -1;
  1660. if (aThreadIndex != "All" && bThreadIndex == "All")
  1661. return 1;
  1662. int aGroupIndex;
  1663. if (int.TryParse(aThreadIndex, out aGroupIndex))
  1664. {
  1665. int bGroupIndex;
  1666. if (int.TryParse(bThreadIndex, out bGroupIndex))
  1667. {
  1668. return aGroupIndex.CompareTo(bGroupIndex);
  1669. }
  1670. }
  1671. }
  1672. }
  1673. return a.CompareTo(b);
  1674. }
  1675. void GetThreadNames(ProfileData leftData, ProfileData rightData, out List<string> threadUINames, out List<string> threadFilters, out Dictionary<string, string> threadNameToUIName)
  1676. {
  1677. List<string> threadNames = (leftData != null) ? new List<string>(leftData.GetThreadNames()) : new List<string>();
  1678. if (rightData != null)
  1679. {
  1680. foreach (var threadNameWithIndex in rightData.GetThreadNames())
  1681. {
  1682. if (!threadNames.Contains(threadNameWithIndex))
  1683. {
  1684. // TODO: Insert after last thread with same name (or at end)
  1685. threadNames.Add(threadNameWithIndex);
  1686. }
  1687. }
  1688. }
  1689. Dictionary<string, string> threadNamesDict = new Dictionary<string, string>();
  1690. for (int index = 0; index < threadNames.Count; index++)
  1691. {
  1692. var threadNameWithIndex = threadNames[index];
  1693. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  1694. if (threadIdentifier.index == 1)
  1695. {
  1696. if (threadNames.Contains(string.Format("2:{0}", threadIdentifier.name)))
  1697. {
  1698. var threadGroupIdentifier = new ThreadIdentifier(threadIdentifier);
  1699. threadGroupIdentifier.SetAll();
  1700. // First thread name of a group with the same name
  1701. // Add an 'all' selection
  1702. threadNamesDict[string.Format("{0} : All", threadIdentifier.name)] = threadGroupIdentifier.threadNameWithIndex;
  1703. // And add the first item too
  1704. threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, false)] = threadNameWithIndex;
  1705. }
  1706. else
  1707. {
  1708. // Single instance of this thread name
  1709. threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, true)] = threadNameWithIndex;
  1710. }
  1711. }
  1712. else
  1713. {
  1714. threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, false)] = threadNameWithIndex;
  1715. }
  1716. }
  1717. List<string> uiNames = new List<string>();
  1718. foreach (string uiName in threadNamesDict.Keys)
  1719. uiNames.Add(uiName);
  1720. uiNames.Sort(CompareUINames);
  1721. var allThreadIdentifier = new ThreadIdentifier();
  1722. allThreadIdentifier.SetName("All");
  1723. allThreadIdentifier.SetAll();
  1724. threadUINames = new List<string>();
  1725. threadFilters = new List<string>();
  1726. threadNameToUIName = new Dictionary<string, string>();
  1727. threadUINames.Add(allThreadIdentifier.name);
  1728. threadFilters.Add(allThreadIdentifier.threadNameWithIndex);
  1729. threadNameToUIName[allThreadIdentifier.name] = allThreadIdentifier.threadNameWithIndex;
  1730. foreach (string uiName in uiNames)
  1731. {
  1732. // Strip off the group name
  1733. // Note we don't do this in GetFriendlyThreadName else we would collapse the same named threads (in different groups) in the dict
  1734. string groupName;
  1735. string threadName = ProfileData.GetThreadNameWithoutGroup(uiName, out groupName);
  1736. string threadFilter = threadNamesDict[uiName];
  1737. threadUINames.Add(threadName);
  1738. threadFilters.Add(threadFilter);
  1739. threadNameToUIName[threadFilter] = threadName;
  1740. }
  1741. }
  1742. void UpdateThreadGroupSelection(List<string> threadNames, ThreadSelection threadSelection)
  1743. {
  1744. // Make sure all members of active groups are present
  1745. foreach (string threadGroupNameWithIndex in threadSelection.groups)
  1746. {
  1747. var threadGroupIdentifier = new ThreadIdentifier(threadGroupNameWithIndex);
  1748. if (threadGroupIdentifier.name == "All" && threadGroupIdentifier.index == ThreadIdentifier.kAll)
  1749. {
  1750. foreach (string threadNameWithIndex in threadNames)
  1751. {
  1752. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  1753. if (threadIdentifier.index != ThreadIdentifier.kAll)
  1754. {
  1755. if (!threadSelection.selection.Contains(threadNameWithIndex))
  1756. threadSelection.selection.Add(threadNameWithIndex);
  1757. }
  1758. }
  1759. }
  1760. else
  1761. {
  1762. foreach (string threadNameWithIndex in threadNames)
  1763. {
  1764. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  1765. if (threadIdentifier.name == threadGroupIdentifier.name &&
  1766. threadIdentifier.index != ThreadIdentifier.kAll)
  1767. {
  1768. if (!threadSelection.selection.Contains(threadNameWithIndex))
  1769. threadSelection.selection.Add(threadNameWithIndex);
  1770. }
  1771. }
  1772. }
  1773. }
  1774. }
  1775. List<string> GetLimitedThreadSelection(List<string> threadNames, ThreadSelection threadSelection)
  1776. {
  1777. List<string> limitedThreadSelection = new List<string>();
  1778. if (threadSelection.selection == null)
  1779. return limitedThreadSelection;
  1780. foreach (string threadNameWithIndex in threadSelection.selection)
  1781. {
  1782. if (threadNames.Contains(threadNameWithIndex))
  1783. limitedThreadSelection.Add(threadNameWithIndex);
  1784. }
  1785. // Make sure all members of active groups are present
  1786. foreach (string threadGroupNameWithIndex in threadSelection.groups)
  1787. {
  1788. var threadGroupIdentifier = new ThreadIdentifier(threadGroupNameWithIndex);
  1789. if (threadGroupIdentifier.name == "All" && threadGroupIdentifier.index == ThreadIdentifier.kAll)
  1790. {
  1791. foreach (string threadNameWithIndex in threadNames)
  1792. {
  1793. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  1794. if (threadIdentifier.index != ThreadIdentifier.kAll)
  1795. {
  1796. if (!limitedThreadSelection.Contains(threadNameWithIndex))
  1797. limitedThreadSelection.Add(threadNameWithIndex);
  1798. }
  1799. }
  1800. }
  1801. else
  1802. {
  1803. foreach (string threadNameWithIndex in threadNames)
  1804. {
  1805. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  1806. if (threadIdentifier.name == threadGroupIdentifier.name &&
  1807. threadIdentifier.index != ThreadIdentifier.kAll)
  1808. {
  1809. if (!limitedThreadSelection.Contains(threadNameWithIndex))
  1810. limitedThreadSelection.Add(threadNameWithIndex);
  1811. }
  1812. }
  1813. }
  1814. }
  1815. return limitedThreadSelection;
  1816. }
  1817. int ClampToRange(int value, int min, int max)
  1818. {
  1819. if (value < min)
  1820. value = min;
  1821. if (value > max)
  1822. value = max;
  1823. return value;
  1824. }
  1825. void GraphActive(bool active)
  1826. {
  1827. RequestRepaint();
  1828. }
  1829. void SetRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus)
  1830. {
  1831. if (inputStatus == FrameTimeGraph.State.Dragging)
  1832. return;
  1833. if (clickCount == 2)
  1834. {
  1835. if (selectedOffsets.Count > 0 && m_ProfileSingleView.inSyncWithProfilerData)
  1836. JumpToFrame(m_ProfileSingleView.data.OffsetToDisplayFrame(selectedOffsets[0]), m_ProfileSingleView.data, false);
  1837. }
  1838. else
  1839. {
  1840. m_ProfileSingleView.selectedIndices.Clear();
  1841. foreach (int offset in selectedOffsets)
  1842. {
  1843. m_ProfileSingleView.selectedIndices.Add(m_ProfileSingleView.data.OffsetToDisplayFrame(offset));
  1844. }
  1845. // Keep indices sorted
  1846. m_ProfileSingleView.selectedIndices.Sort();
  1847. m_RequestAnalysis = true;
  1848. }
  1849. }
  1850. internal void ClearSelection()
  1851. {
  1852. if (m_ActiveTab == ActiveTab.Summary)
  1853. {
  1854. m_ProfileSingleView.ClearSelection();
  1855. m_RequestAnalysis = true;
  1856. }
  1857. if (m_ActiveTab == ActiveTab.Compare)
  1858. {
  1859. m_ProfileLeftView.ClearSelection();
  1860. m_ProfileRightView.ClearSelection();
  1861. m_RequestCompare = true;
  1862. }
  1863. }
  1864. internal void SelectAllFrames()
  1865. {
  1866. if (m_ActiveTab == ActiveTab.Summary)
  1867. {
  1868. m_ProfileSingleView.SelectFullRange();
  1869. m_RequestAnalysis = true;
  1870. }
  1871. if (m_ActiveTab == ActiveTab.Compare)
  1872. {
  1873. m_ProfileLeftView.SelectFullRange();
  1874. m_ProfileRightView.SelectFullRange();
  1875. m_RequestCompare = true;
  1876. }
  1877. }
  1878. internal void SelectFramesContainingMarker(string markerName, bool inSelection)
  1879. {
  1880. if (m_ActiveTab == ActiveTab.Summary)
  1881. {
  1882. if (m_ProfileSingleView.SelectAllFramesContainingMarker(markerName, inSelection))
  1883. {
  1884. m_RequestAnalysis = true;
  1885. }
  1886. }
  1887. if (m_ActiveTab == ActiveTab.Compare)
  1888. {
  1889. if (m_ProfileLeftView.SelectAllFramesContainingMarker(markerName, inSelection))
  1890. {
  1891. m_RequestCompare = true;
  1892. }
  1893. if (m_ProfileRightView.SelectAllFramesContainingMarker(markerName, inSelection))
  1894. {
  1895. m_RequestCompare = true;
  1896. }
  1897. }
  1898. }
  1899. static List<string> GetNameFilters(string nameFilter)
  1900. {
  1901. List<string> nameFilters = new List<string>();
  1902. if (string.IsNullOrEmpty(nameFilter))
  1903. return nameFilters;
  1904. // Get all quoted strings, without the quotes
  1905. MatchCollection matches = quotedStringWithoutQuotes.Matches(nameFilter);
  1906. foreach (Match match in matches)
  1907. {
  1908. var theData = match.Groups[1].Value;
  1909. nameFilters.Add(theData);
  1910. }
  1911. // Get a new string with the quoted strings removed
  1912. string remaining = quotedString.Replace(nameFilter, "");
  1913. // Get all the remaining strings (that are space separated)
  1914. matches = stringWithoutWhiteSpace.Matches(remaining);
  1915. foreach (Match match in matches)
  1916. {
  1917. string theData = match.Groups[1].Value;
  1918. nameFilters.Add(theData);
  1919. }
  1920. return nameFilters;
  1921. }
  1922. internal List<string> GetNameFilters()
  1923. {
  1924. return GetNameFilters(m_NameFilter);
  1925. }
  1926. internal List<string> GetNameExcludes()
  1927. {
  1928. return GetNameFilters(m_NameExclude);
  1929. }
  1930. internal bool NameInFilterList(string name, List<string> nameFilters)
  1931. {
  1932. switch (m_NameFilterOperation)
  1933. {
  1934. default:
  1935. //case NameFilterOperation.All:
  1936. return NameInAllFilterList(name, nameFilters);
  1937. case NameFilterOperation.Any:
  1938. return NameInAnyFilterList(name, nameFilters);
  1939. }
  1940. }
  1941. internal bool NameInExcludeList(string name, List<string> nameFilters)
  1942. {
  1943. switch (m_NameExcludeOperation)
  1944. {
  1945. default:
  1946. //case NameFilterOperation.All:
  1947. return NameInAllFilterList(name, nameFilters);
  1948. case NameFilterOperation.Any:
  1949. return NameInAnyFilterList(name, nameFilters);
  1950. }
  1951. }
  1952. internal bool NameInAllFilterList(string name, List<string> nameFilters)
  1953. {
  1954. foreach (string subString in nameFilters)
  1955. {
  1956. // As soon as name doesn't match one in the list then return false
  1957. if (name.IndexOf(subString, StringComparison.OrdinalIgnoreCase) < 0)
  1958. return false;
  1959. }
  1960. // Name is matching all the filters in the list
  1961. return true;
  1962. }
  1963. bool NameInAnyFilterList(string name, List<string> nameFilters)
  1964. {
  1965. foreach (string subString in nameFilters)
  1966. {
  1967. // As soon as names matches one in the list then return true
  1968. if (name.IndexOf(subString, StringComparison.OrdinalIgnoreCase) >= 0)
  1969. return true;
  1970. }
  1971. return false;
  1972. }
  1973. static string FilterWithQuotes(string markerName)
  1974. {
  1975. return markerName.Contains(" ") ? string.Format("\"{0}\"", markerName) : markerName;
  1976. }
  1977. static void AddFilter(ref string filter, string quotedMarkerName)
  1978. {
  1979. if (string.IsNullOrEmpty(filter))
  1980. filter = quotedMarkerName;
  1981. else
  1982. filter = string.Format("{0} {1}", filter, quotedMarkerName);
  1983. }
  1984. static bool AddFilter(List<string> nameFilters, ref string filter, string markerName)
  1985. {
  1986. bool justAdded = false;
  1987. string quotedMarkerName = FilterWithQuotes(markerName);
  1988. if (!nameFilters.Contains(quotedMarkerName))
  1989. {
  1990. AddFilter(ref filter, quotedMarkerName);
  1991. justAdded = true;
  1992. }
  1993. return justAdded;
  1994. }
  1995. internal void AddToIncludeFilter(string markerName)
  1996. {
  1997. if (AddFilter(GetNameFilters(), ref m_NameFilter, markerName))
  1998. UpdateMarkerTable();
  1999. // Remove from exclude list if in the include list
  2000. RemoveFromExcludeFilter(markerName);
  2001. }
  2002. internal void AddToExcludeFilter(string markerName)
  2003. {
  2004. if (AddFilter(GetNameExcludes(), ref m_NameExclude, markerName))
  2005. UpdateMarkerTable();
  2006. // Remove from include list if in the include list
  2007. RemoveFromIncludeFilter(markerName);
  2008. }
  2009. internal void RemoveFromFilter(string markerName, List<string> nameFilters, ref string newFilters)
  2010. {
  2011. if (nameFilters.Count == 0)
  2012. return;
  2013. string nameFilterString = "";
  2014. bool updated = false;
  2015. foreach (string filter in nameFilters)
  2016. {
  2017. if (string.Compare(filter, markerName, StringComparison.CurrentCultureIgnoreCase) != 0)
  2018. AddFilter(ref nameFilterString, FilterWithQuotes(filter));
  2019. else
  2020. updated = true;
  2021. }
  2022. if (updated)
  2023. {
  2024. newFilters = nameFilterString;
  2025. UpdateMarkerTable();
  2026. }
  2027. }
  2028. internal void RemoveFromIncludeFilter(string markerName)
  2029. {
  2030. RemoveFromFilter(markerName, GetNameFilters(), ref m_NameFilter);
  2031. }
  2032. internal void RemoveFromExcludeFilter(string markerName)
  2033. {
  2034. RemoveFromFilter(markerName, GetNameExcludes(), ref m_NameExclude);
  2035. }
  2036. internal void SetAsParentMarkerFilter(string markerName)
  2037. {
  2038. if (markerName != m_ParentMarker)
  2039. {
  2040. m_ParentMarker = markerName;
  2041. UpdateActiveTab(true);
  2042. }
  2043. }
  2044. float GetFilenameWidth(string path)
  2045. {
  2046. if (path == null)
  2047. return 0f;
  2048. string filename = System.IO.Path.GetFileName(path);
  2049. GUIContent content = new GUIContent(filename, path);
  2050. Vector2 size = GUI.skin.label.CalcSize(content);
  2051. return size.x;
  2052. }
  2053. void ShowFilename(string path)
  2054. {
  2055. if (path != null)
  2056. {
  2057. string filename = System.IO.Path.GetFileNameWithoutExtension(path);
  2058. GUIContent content = new GUIContent(filename, path);
  2059. Vector2 size = GUI.skin.label.CalcSize(content);
  2060. float width = Math.Min(size.x, 200f);
  2061. EditorGUILayout.LabelField(content, GUILayout.MaxWidth(width));
  2062. }
  2063. }
  2064. void DrawLoadSave()
  2065. {
  2066. EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(300), GUILayout.ExpandWidth(false));
  2067. GUIStyle buttonStyle = GUI.skin.button;
  2068. bool lastEnabled = GUI.enabled;
  2069. bool isAnalysisRunning = IsAnalysisRunning();
  2070. GUI.enabled = !isAnalysisRunning;
  2071. if (GUILayout.Button("Load", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50)))
  2072. Load();
  2073. GUI.enabled = !isAnalysisRunning && (m_ProfileSingleView.IsDataValid());
  2074. if (GUILayout.Button("Save", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50)))
  2075. Save(m_ProfileSingleView);
  2076. GUI.enabled = lastEnabled;
  2077. ShowFilename(m_ProfileSingleView.path);
  2078. EditorGUILayout.EndHorizontal();
  2079. }
  2080. void ShowSelectedMarker()
  2081. {
  2082. bool valid = true;
  2083. if (m_ActiveTab == ActiveTab.Summary)
  2084. {
  2085. if (IsAnalysisValid())
  2086. {
  2087. valid = false;
  2088. List<MarkerData> markers = m_ProfileSingleView.analysis.GetMarkers();
  2089. if (markers != null)
  2090. {
  2091. int markerAt = m_SelectedMarker.id;
  2092. if (markerAt >= 0 && markerAt < markers.Count)
  2093. {
  2094. valid = true;
  2095. }
  2096. }
  2097. }
  2098. }
  2099. else if (m_ActiveTab == ActiveTab.Compare)
  2100. {
  2101. if (IsAnalysisValid())
  2102. {
  2103. valid = false;
  2104. List<MarkerData> leftMarkers = m_ProfileLeftView.analysis.GetMarkers();
  2105. List<MarkerData> rightMarkers = m_ProfileRightView.analysis.GetMarkers();
  2106. int pairingAt = m_SelectedPairing;
  2107. if (leftMarkers != null && rightMarkers != null && m_Pairings != null)
  2108. {
  2109. if (pairingAt >= 0 && pairingAt < m_Pairings.Count)
  2110. {
  2111. valid = true;
  2112. }
  2113. }
  2114. }
  2115. }
  2116. if (valid)
  2117. {
  2118. DrawSelectedText(m_SelectedMarker.name);
  2119. }
  2120. else
  2121. {
  2122. var markerInThread = m_SelectedMarker.id == -1 && !string.IsNullOrEmpty(m_SelectedMarker.threadName);
  2123. var threadText = markerInThread ?
  2124. string.Format(" (Selected in: {0}{1}{2})",
  2125. m_SelectedMarker.threadGroupName,
  2126. string.IsNullOrEmpty(m_SelectedMarker.threadGroupName) ? "" : ".",
  2127. m_SelectedMarker.threadName) :
  2128. null;
  2129. string text = string.Format("{0}{1} not in selection", m_SelectedMarker.name, threadText);
  2130. GUIContent content = new GUIContent(text, text);
  2131. Vector2 size = GUI.skin.label.CalcSize(content);
  2132. Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxWidth(size.x), GUILayout.Height(size.y));
  2133. if (Event.current.type == EventType.Repaint)
  2134. {
  2135. GUI.Label(rect, content);
  2136. }
  2137. }
  2138. }
  2139. internal bool AllSelected()
  2140. {
  2141. if (m_ActiveTab == ActiveTab.Summary)
  2142. {
  2143. if (m_ProfileSingleView.AllSelected())
  2144. return true;
  2145. }
  2146. if (m_ActiveTab == ActiveTab.Compare)
  2147. {
  2148. if (m_ProfileLeftView.AllSelected() && m_ProfileRightView.AllSelected())
  2149. return true;
  2150. }
  2151. return false;
  2152. }
  2153. internal bool HasSelection()
  2154. {
  2155. if (m_ActiveTab == ActiveTab.Summary)
  2156. {
  2157. if (m_ProfileSingleView.HasSelection())
  2158. return true;
  2159. }
  2160. if (m_ActiveTab == ActiveTab.Compare)
  2161. {
  2162. if (m_ProfileLeftView.HasSelection())
  2163. return true;
  2164. if (m_ProfileRightView.HasSelection())
  2165. return true;
  2166. }
  2167. return false;
  2168. }
  2169. internal int GetRemappedUIFrameIndex(int frameIndex, ProfileDataView context)
  2170. {
  2171. if (context.inSyncWithProfilerData)
  2172. return RemapFrameIndex(frameIndex, context.data.FrameIndexOffset);
  2173. else
  2174. return k_ProfileDataDefaultDisplayOffset + context.data.DisplayFrameToOffset(frameIndex);
  2175. }
  2176. int GetRemappedUIFirstFrameOffset(ProfileDataView context)
  2177. {
  2178. if (context.inSyncWithProfilerData)
  2179. return RemapFrameIndex(context.data.OffsetToDisplayFrame(0), context.data.FrameIndexOffset);
  2180. else
  2181. return context.data.OffsetToDisplayFrame(0);
  2182. }
  2183. int GetRemappedUIFirstFrameDisplayOffset(ProfileDataView context)
  2184. {
  2185. if (context.inSyncWithProfilerData)
  2186. return RemapFrameIndex(context.data.OffsetToDisplayFrame(0), context.data.FrameIndexOffset);
  2187. else
  2188. return k_ProfileDataDefaultDisplayOffset;
  2189. }
  2190. static readonly ProfilerMarkerAbstracted m_DrawFrameTimeGraphProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawFrameTimeGraph");
  2191. void DrawFrameTimeGraph(float height)
  2192. {
  2193. using (m_DrawFrameTimeGraphProfilerMarker.Auto())
  2194. {
  2195. GUI.SetNextControlName("FrameTimeGraph");
  2196. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(height));
  2197. if (m_ProfileSingleView.IsDataValid())
  2198. {
  2199. if (!m_FrameTimeGraph.HasData())
  2200. m_FrameTimeGraph.SetData(GetFrameTimeData(m_ProfileSingleView.data));
  2201. if (!m_ProfileSingleView.HasValidSelection())
  2202. m_ProfileSingleView.SelectFullRange();
  2203. List<int> selectedOffsets = new List<int>();
  2204. foreach (int index in m_ProfileSingleView.selectedIndices)
  2205. {
  2206. selectedOffsets.Add(m_ProfileSingleView.data.DisplayFrameToOffset(index));
  2207. }
  2208. float yRange = m_FrameTimeGraph.GetDataRange();
  2209. int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileSingleView);
  2210. int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileSingleView);
  2211. bool enabled = !IsAnalysisRunning();
  2212. m_FrameTimeGraph.SetEnabled(enabled);
  2213. m_FrameTimeGraph.Draw(rect, m_ProfileSingleView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping,
  2214. m_SelectedMarker.name, 0, m_ProfileSingleView.analysisFull);
  2215. EditorGUILayout.BeginHorizontal();
  2216. GUILayout.FlexibleSpace();
  2217. ShowSelectedMarker();
  2218. EditorGUILayout.EndHorizontal();
  2219. }
  2220. else
  2221. {
  2222. GUI.Label(rect, Styles.dataMissing, m_StyleUpperLeft);
  2223. }
  2224. }
  2225. }
  2226. void DrawParentFilter()
  2227. {
  2228. EditorGUILayout.BeginHorizontal();
  2229. EditorGUILayout.LabelField(Styles.parentMarker, GUILayout.Width(100));
  2230. if (!string.IsNullOrEmpty(m_ParentMarker))
  2231. {
  2232. bool lastEnabled = GUI.enabled;
  2233. bool enabled = !IsAnalysisRunning();
  2234. GUI.enabled = enabled;
  2235. if (GUILayout.Button("Clear", EditorStyles.miniButton, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth)))
  2236. {
  2237. SetAsParentMarkerFilter("");
  2238. }
  2239. GUI.enabled = lastEnabled;
  2240. DrawSelectedText(m_ParentMarker);
  2241. }
  2242. else
  2243. {
  2244. EditorGUILayout.LabelField(Styles.selectParentMarker);
  2245. }
  2246. EditorGUILayout.EndHorizontal();
  2247. }
  2248. internal void SetThreadSelection(ThreadSelection threadSelection)
  2249. {
  2250. m_ThreadSelectionNew = new ThreadSelection(threadSelection);
  2251. UpdateActiveTab(true);
  2252. }
  2253. string CalculateSelectedThreadsSummary()
  2254. {
  2255. if (m_ThreadSelection.selection == null || m_ThreadSelection.selection.Count == 0)
  2256. return "None";
  2257. // Count all threads in a group
  2258. var threadDict = new Dictionary<string, int>();
  2259. var threadSelectionDict = new Dictionary<string, int>();
  2260. foreach (var threadNameWithIndex in m_ThreadNames)
  2261. {
  2262. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  2263. if (threadIdentifier.index == ThreadIdentifier.kAll)
  2264. continue;
  2265. int count;
  2266. if (threadDict.TryGetValue(threadIdentifier.name, out count))
  2267. threadDict[threadIdentifier.name] = count + 1;
  2268. else
  2269. threadDict[threadIdentifier.name] = 1;
  2270. threadSelectionDict[threadIdentifier.name] = 0;
  2271. }
  2272. // Count all the threads we have 'selected' in a group
  2273. foreach (var threadNameWithIndex in m_ThreadSelection.selection)
  2274. {
  2275. var threadIdentifier = new ThreadIdentifier(threadNameWithIndex);
  2276. if (threadDict.ContainsKey(threadIdentifier.name) &&
  2277. threadSelectionDict.ContainsKey(threadIdentifier.name) &&
  2278. threadIdentifier.index <= threadDict[threadIdentifier.name])
  2279. {
  2280. // Selected thread valid and in the thread list
  2281. // and also within the range of valid threads for this data set
  2282. threadSelectionDict[threadIdentifier.name]++;
  2283. }
  2284. }
  2285. // Count all thread groups where we have 'selected all the threads'
  2286. int threadsSelected = 0;
  2287. foreach (var threadName in threadDict.Keys)
  2288. {
  2289. if (threadSelectionDict[threadName] != threadDict[threadName])
  2290. continue;
  2291. threadsSelected++;
  2292. }
  2293. // If we've just added all the thread names we have everything selected
  2294. // Note we don't compare against the m_ThreadNames directly as this contains the 'all' versions
  2295. if (threadsSelected == threadDict.Keys.Count)
  2296. return "All";
  2297. // Add all the individual threads were we haven't already added the group
  2298. List<string> threads = new List<string>();
  2299. foreach (var threadName in threadSelectionDict.Keys)
  2300. {
  2301. int selectionCount = threadSelectionDict[threadName];
  2302. if (selectionCount <= 0)
  2303. continue;
  2304. int threadCount = threadDict[threadName];
  2305. if (threadCount == 1)
  2306. threads.Add(threadName);
  2307. else if (selectionCount != threadCount)
  2308. threads.Add(string.Format("{0} ({1} of {2})", threadName, selectionCount, threadCount));
  2309. else
  2310. threads.Add(string.Format("{0} (All)", threadName));
  2311. }
  2312. // Maintain alphabetical order
  2313. threads.Sort(CompareUINames);
  2314. if (threads.Count == 0)
  2315. return "None";
  2316. string threadsSelectedText = string.Join(", ", threads.ToArray());
  2317. return threadsSelectedText;
  2318. }
  2319. string GetSelectedThreadsSummary()
  2320. {
  2321. return m_ThreadSelectionSummary;
  2322. }
  2323. void DrawThreadFilter(ProfileData profileData)
  2324. {
  2325. EditorGUILayout.BeginHorizontal();
  2326. EditorGUILayout.LabelField(Styles.threadFilter, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth));
  2327. if (profileData != null)
  2328. {
  2329. if (m_ThreadNames.Count > 0)
  2330. {
  2331. bool lastEnabled = GUI.enabled;
  2332. bool enabled = !IsAnalysisRunning() && !ThreadSelectionWindow.IsOpen();
  2333. GUI.enabled = enabled;
  2334. if (GUILayout.Button(Styles.threadFilterSelect, GUILayout.Width(LayoutSize.FilterOptionsEnumWidth)))
  2335. {
  2336. Vector2 windowPosition = new Vector2(Event.current.mousePosition.x + LayoutSize.FilterOptionsEnumWidth, Event.current.mousePosition.y + GUI.skin.label.lineHeight);
  2337. Vector2 screenPosition = GUIUtility.GUIToScreenPoint(windowPosition);
  2338. ThreadSelectionWindow.Open(screenPosition.x, screenPosition.y, this, m_ThreadSelection, m_ThreadNames, m_ThreadUINames);
  2339. EditorGUIUtility.ExitGUI();
  2340. }
  2341. GUI.enabled = lastEnabled;
  2342. ShowSelectedThreads();
  2343. GUILayout.FlexibleSpace();
  2344. }
  2345. }
  2346. EditorGUILayout.EndHorizontal();
  2347. }
  2348. void DrawSelectedText(string text)
  2349. {
  2350. if (text == null)
  2351. return;
  2352. #if UNITY_2019_1_OR_NEWER
  2353. GUIStyle treeViewSelectionStyle = "TV Selection";
  2354. GUIStyle backgroundStyle = new GUIStyle(treeViewSelectionStyle);
  2355. GUIStyle treeViewLineStyle = "TV Line";
  2356. GUIStyle textStyle = new GUIStyle(treeViewLineStyle);
  2357. #else
  2358. GUIStyle textStyle = GUI.skin.label;
  2359. #endif
  2360. GUIContent content = new GUIContent(text, text);
  2361. Vector2 size = textStyle.CalcSize(content);
  2362. Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxWidth(size.x), GUILayout.Height(size.y));
  2363. if (Event.current.type == EventType.Repaint)
  2364. {
  2365. #if UNITY_2019_1_OR_NEWER
  2366. backgroundStyle.Draw(rect, false, false, true, true);
  2367. #endif
  2368. GUI.Label(rect, content, textStyle);
  2369. }
  2370. }
  2371. void ShowSelectedThreads()
  2372. {
  2373. string threadsSelected = GetSelectedThreadsSummary();
  2374. DrawSelectedText(threadsSelected);
  2375. }
  2376. void DrawUnitFilter()
  2377. {
  2378. EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth));
  2379. EditorGUILayout.LabelField(Styles.unitFilter, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth));
  2380. bool lastEnabled = GUI.enabled;
  2381. bool enabled = !IsAnalysisRunning();
  2382. GUI.enabled = enabled;
  2383. //Units units = (Units)EditorGUILayout.EnumPopup(m_DisplayUnits.Units, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth));
  2384. Units units = (Units)EditorGUILayout.Popup((int)m_DisplayUnits.Units, m_UnitNames, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth));
  2385. GUI.enabled = lastEnabled;
  2386. if (units != m_DisplayUnits.Units)
  2387. {
  2388. SetUnits(units);
  2389. m_FrameTimeGraph.SetUnits(m_DisplayUnits.Units);
  2390. m_LeftFrameTimeGraph.SetUnits(m_DisplayUnits.Units);
  2391. m_RightFrameTimeGraph.SetUnits(m_DisplayUnits.Units);
  2392. UpdateMarkerTable();
  2393. }
  2394. EditorGUILayout.EndHorizontal();
  2395. }
  2396. bool IsSelfTime()
  2397. {
  2398. return (m_TimingOption == TimingOptions.TimingOption.Self) ? true : false;
  2399. }
  2400. void DrawTimingFilter()
  2401. {
  2402. EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth));
  2403. EditorGUILayout.LabelField(Styles.timingFilter, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth));
  2404. bool lastEnabled = GUI.enabled;
  2405. bool enabled = !IsAnalysisRunning();
  2406. GUI.enabled = enabled;
  2407. var timingOption = (TimingOptions.TimingOption)EditorGUILayout.Popup((int)m_TimingOption, TimingOptions.TimingOptionNames, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth));
  2408. GUI.enabled = lastEnabled;
  2409. if (timingOption != m_TimingOption)
  2410. {
  2411. m_TimingOption = timingOption;
  2412. UpdateActiveTab(true, true);
  2413. }
  2414. EditorGUILayout.EndHorizontal();
  2415. }
  2416. internal string GetDisplayUnits()
  2417. {
  2418. return m_DisplayUnits.Postfix();
  2419. }
  2420. internal string ToDisplayUnits(float ms, bool showUnits = false, int limitToDigits = 5, bool showFullValueWhenBelowZero = false)
  2421. {
  2422. return m_DisplayUnits.ToString(ms, showUnits, limitToDigits, showFullValueWhenBelowZero);
  2423. }
  2424. internal string ToDisplayUnits(double ms, bool showUnits = false, int limitToDigits = 5, bool showFullValueWhenBelowZero = false)
  2425. {
  2426. return m_DisplayUnits.ToString((float)ms, showUnits, limitToDigits, showFullValueWhenBelowZero);
  2427. }
  2428. internal string ToTooltipDisplayUnits(float ms, bool showUnits = false, int frameIndex = -1)
  2429. {
  2430. return m_DisplayUnits.ToTooltipString(ms, showUnits, frameIndex);
  2431. }
  2432. internal GUIContent ToDisplayUnitsWithTooltips(float ms, bool showUnits = false, int frameIndex = -1)
  2433. {
  2434. return m_DisplayUnits.ToGUIContentWithTooltips(ms, showUnits, 5, frameIndex);
  2435. }
  2436. void SetUnits(Units units)
  2437. {
  2438. m_DisplayUnits = new DisplayUnits(units);
  2439. }
  2440. void UpdateActiveTab(bool fullAnalysisRequired = false, bool markOtherDirty = true)
  2441. {
  2442. switch (m_ActiveTab)
  2443. {
  2444. case ActiveTab.Summary:
  2445. m_RequestAnalysis = true;
  2446. m_FullAnalysisRequired = fullAnalysisRequired;
  2447. break;
  2448. case ActiveTab.Compare:
  2449. m_RequestCompare = true;
  2450. m_FullCompareRequired = fullAnalysisRequired;
  2451. break;
  2452. }
  2453. if (markOtherDirty)
  2454. m_OtherTabDirty = true;
  2455. }
  2456. void UpdateMarkerTable(bool markOtherDirty = true)
  2457. {
  2458. switch (m_ActiveTab)
  2459. {
  2460. case ActiveTab.Summary:
  2461. if (m_ProfileTable != null)
  2462. m_ProfileTable.Reload();
  2463. break;
  2464. case ActiveTab.Compare:
  2465. if (m_ComparisonTable != null)
  2466. m_ComparisonTable.Reload();
  2467. break;
  2468. }
  2469. if (markOtherDirty)
  2470. m_OtherTableDirty = true;
  2471. }
  2472. void DrawNameFilter()
  2473. {
  2474. EditorGUILayout.BeginHorizontal();
  2475. EditorGUILayout.LabelField(Styles.nameFilter, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth));
  2476. NameFilterOperation lastNameFilterOperation = m_NameFilterOperation;
  2477. bool lastEnabled = GUI.enabled;
  2478. bool enabled = !IsAnalysisRunning();
  2479. GUI.enabled = enabled;
  2480. m_NameFilterOperation = (NameFilterOperation)EditorGUILayout.Popup((int)m_NameFilterOperation, Styles.nameFilterOperation, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth));
  2481. GUI.enabled = lastEnabled;
  2482. if (m_NameFilterOperation != lastNameFilterOperation)
  2483. {
  2484. UpdateMarkerTable();
  2485. }
  2486. string lastFilter = m_NameFilter;
  2487. GUI.enabled = enabled;
  2488. GUI.SetNextControlName("NameFilter");
  2489. m_NameFilter = EditorGUILayout.TextField(m_NameFilter, GUILayout.MinWidth(200 - LayoutSize.FilterOptionsEnumWidth));
  2490. GUI.enabled = lastEnabled;
  2491. if (m_NameFilter != lastFilter)
  2492. {
  2493. UpdateMarkerTable();
  2494. }
  2495. EditorGUILayout.LabelField(Styles.nameExclude, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth));
  2496. NameFilterOperation lastNameExcludeOperation = m_NameExcludeOperation;
  2497. GUI.enabled = enabled;
  2498. m_NameExcludeOperation = (NameFilterOperation)EditorGUILayout.Popup((int)m_NameExcludeOperation, Styles.nameFilterOperation, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth));
  2499. GUI.enabled = lastEnabled;
  2500. if (m_NameExcludeOperation != lastNameExcludeOperation)
  2501. {
  2502. UpdateMarkerTable();
  2503. }
  2504. string lastExclude = m_NameExclude;
  2505. GUI.enabled = enabled;
  2506. GUI.SetNextControlName("ExcludeFilter");
  2507. m_NameExclude = EditorGUILayout.TextField(m_NameExclude, GUILayout.MinWidth(200 - LayoutSize.FilterOptionsEnumWidth));
  2508. GUI.enabled = lastEnabled;
  2509. if (m_NameExclude != lastExclude)
  2510. {
  2511. UpdateMarkerTable();
  2512. }
  2513. EditorGUILayout.EndHorizontal();
  2514. }
  2515. internal void SetMode(MarkerColumnFilter.Mode newMode)
  2516. {
  2517. m_SingleModeFilter.mode = newMode;
  2518. m_CompareModeFilter.mode = newMode;
  2519. if (m_ProfileTable != null)
  2520. m_ProfileTable.SetMode(m_SingleModeFilter);
  2521. if (m_ComparisonTable != null)
  2522. m_ComparisonTable.SetMode(m_CompareModeFilter);
  2523. }
  2524. internal void SetSingleModeColumns(int[] visibleColumns)
  2525. {
  2526. // If selecting the columns manually then override the currently stored selection with the current
  2527. m_ProfileMulticolumnHeaderState.visibleColumns = visibleColumns;
  2528. m_SingleModeFilter.mode = MarkerColumnFilter.Mode.Custom;
  2529. m_SingleModeFilter.visibleColumns = visibleColumns;
  2530. }
  2531. internal void SetComparisonModeColumns(int[] visibleColumns)
  2532. {
  2533. // If selecting the columns manually then override the currently stored selection with the current
  2534. m_ComparisonMulticolumnHeaderState.visibleColumns = visibleColumns;
  2535. m_CompareModeFilter.mode = MarkerColumnFilter.Mode.Custom;
  2536. m_CompareModeFilter.visibleColumns = visibleColumns;
  2537. }
  2538. void DrawMarkerColumnFilter()
  2539. {
  2540. EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth));
  2541. EditorGUILayout.LabelField(Styles.markerColumns, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth));
  2542. bool lastEnabled = GUI.enabled;
  2543. bool enabled = !IsAnalysisRunning();
  2544. GUI.enabled = enabled;
  2545. var filterMode = m_ActiveTab == ActiveTab.Summary ? m_SingleModeFilter : m_CompareModeFilter;
  2546. var oldMode = filterMode.mode;
  2547. filterMode.mode = (MarkerColumnFilter.Mode)EditorGUILayout.IntPopup((int)filterMode.mode, MarkerColumnFilter.ModeNames, MarkerColumnFilter.ModeValues, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth));
  2548. if (filterMode.mode != oldMode)
  2549. {
  2550. if (m_ActiveTab == ActiveTab.Summary && m_ProfileTable != null)
  2551. m_ProfileTable.SetMode(filterMode);
  2552. else if (m_ActiveTab == ActiveTab.Compare && m_ComparisonTable != null)
  2553. m_ComparisonTable.SetMode(filterMode);
  2554. }
  2555. GUI.enabled = lastEnabled;
  2556. EditorGUILayout.EndHorizontal();
  2557. }
  2558. enum InDataSet
  2559. {
  2560. Left,
  2561. Both,
  2562. Right
  2563. };
  2564. int GetCombinedThreadCount(out int matchingCount, out int uniqueLeft, out int uniqueRight)
  2565. {
  2566. var threads = new Dictionary<string, InDataSet>();
  2567. foreach (var threadName in m_ProfileLeftView.data.GetThreadNames())
  2568. {
  2569. threads[threadName] = InDataSet.Left;
  2570. }
  2571. foreach (var threadName in m_ProfileRightView.data.GetThreadNames())
  2572. {
  2573. if (threads.ContainsKey(threadName))
  2574. threads[threadName] = InDataSet.Both;
  2575. else
  2576. threads[threadName] = InDataSet.Right;
  2577. }
  2578. matchingCount = 0;
  2579. uniqueLeft = 0;
  2580. uniqueRight = 0;
  2581. int total = 0;
  2582. foreach (var thread in threads)
  2583. {
  2584. switch (thread.Value)
  2585. {
  2586. case InDataSet.Left:
  2587. uniqueLeft++;
  2588. break;
  2589. case InDataSet.Both:
  2590. matchingCount++;
  2591. break;
  2592. case InDataSet.Right:
  2593. uniqueRight++;
  2594. break;
  2595. }
  2596. total++;
  2597. }
  2598. return total;
  2599. }
  2600. void DrawMarkerCount()
  2601. {
  2602. if (!IsAnalysisValid())
  2603. return;
  2604. if (m_ActiveTab == ActiveTab.Summary)
  2605. {
  2606. int markersCount = m_ProfileSingleView.analysis.GetFrameSummary().totalMarkers;
  2607. int filteredMarkersCount = (m_ProfileTable != null) ? m_ProfileTable.GetRows().Count : 0;
  2608. var content = new GUIContent(
  2609. String.Format("{0} of {1} markers", filteredMarkersCount, markersCount),
  2610. "Number of markers in the filtered set, compared to the total in the data set");
  2611. Vector2 size = GUI.skin.label.CalcSize(content);
  2612. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y));
  2613. EditorGUI.LabelField(rect, content);
  2614. }
  2615. if (m_ActiveTab == ActiveTab.Compare)
  2616. {
  2617. int markersCount = m_TotalCombinedMarkerCount;
  2618. int filteredMarkersCount = (m_ComparisonTable != null) ? m_ComparisonTable.GetRows().Count : 0;
  2619. var content = new GUIContent(
  2620. String.Format("{0} of {1} markers", filteredMarkersCount, markersCount),
  2621. "Number of markers in the filtered set, compared to total unique markers in the combined data sets");
  2622. Vector2 size = GUI.skin.label.CalcSize(content);
  2623. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y));
  2624. EditorGUI.LabelField(rect, content);
  2625. }
  2626. }
  2627. string GetThreadCountToolTipUnion(int allThreadsCount, int matchingCount)
  2628. {
  2629. return String.Format(
  2630. "Total\n{0} Union : Combined over both data sets\n{1} Intersection : Matching in both data sets",
  2631. allThreadsCount,
  2632. matchingCount
  2633. );
  2634. }
  2635. string GetThreadCountToolTipDifference(int allThreadsCount, int matchingCount, int uniqueLeft, int uniqueRight)
  2636. {
  2637. return String.Format(
  2638. "Difference\n{0}\n{1} Unique to left\n{2} Unique to right",
  2639. allThreadsCount - matchingCount,
  2640. uniqueLeft,
  2641. uniqueRight);
  2642. }
  2643. string GetThreadCountToolTip(int allThreadsCount, int matchingCount, int uniqueLeft, int uniqueRight)
  2644. {
  2645. return String.Format(
  2646. "{0}\n\n{1}",
  2647. GetThreadCountToolTipUnion(allThreadsCount, matchingCount),
  2648. GetThreadCountToolTipDifference(allThreadsCount, matchingCount, uniqueLeft, uniqueRight)
  2649. );
  2650. }
  2651. void DrawThreadCount()
  2652. {
  2653. if (!IsAnalysisValid())
  2654. return;
  2655. if (m_ActiveTab == ActiveTab.Summary)
  2656. {
  2657. int allThreadsCount = m_ProfileSingleView.data.GetThreadNames().Count;
  2658. List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection);
  2659. int selectedThreads = threadSelection.Count;
  2660. var content = new GUIContent(
  2661. String.Format("{0} of {1} threads", selectedThreads, allThreadsCount),
  2662. "Number of threads in the filtered set, compared to the total in the data set");
  2663. Vector2 size = GUI.skin.label.CalcSize(content);
  2664. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y));
  2665. EditorGUI.LabelField(rect, content);
  2666. }
  2667. if (m_ActiveTab == ActiveTab.Compare)
  2668. {
  2669. int matchingCount, uniqueLeft, uniqueRight;
  2670. int allThreadsCount = GetCombinedThreadCount(out matchingCount, out uniqueLeft, out uniqueRight);
  2671. List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection);
  2672. int selectedThreads = threadSelection.Count;
  2673. string partialTooltip = GetThreadCountToolTip(allThreadsCount, matchingCount, uniqueLeft, uniqueRight);
  2674. var content = new GUIContent(
  2675. String.Format("{0} of {1} threads", selectedThreads, allThreadsCount),
  2676. String.Format("Number of threads in the filtered set, compared to total unique threads in the combined data sets\n\n{0}", partialTooltip)
  2677. );
  2678. Vector2 size = GUI.skin.label.CalcSize(content);
  2679. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y));
  2680. EditorGUI.LabelField(rect, content);
  2681. }
  2682. }
  2683. static readonly ProfilerMarkerAbstracted m_DrawAnalysisOptionsProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawAnalysisOptions");
  2684. void DrawAnalysisOptions()
  2685. {
  2686. using (m_DrawAnalysisOptionsProfilerMarker.Auto())
  2687. {
  2688. EditorGUILayout.BeginVertical(GUI.skin.box);
  2689. bool lastShowFilters = m_ShowFilters;
  2690. m_ShowFilters = BoldFoldout(m_ShowFilters, Styles.filters);
  2691. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  2692. if (m_ShowFilters)
  2693. {
  2694. DrawNameFilter();
  2695. EditorGUILayout.BeginHorizontal();
  2696. DrawThreadFilter(m_ProfileSingleView.data);
  2697. EditorGUILayout.EndHorizontal();
  2698. EditorGUILayout.BeginHorizontal();
  2699. m_DepthSliceUI.DrawDepthFilter(IsAnalysisRunning(), m_ActiveTab == ActiveTab.Summary,
  2700. m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView);
  2701. DrawTimingFilter();
  2702. EditorGUILayout.EndHorizontal();
  2703. EditorGUILayout.BeginHorizontal();
  2704. DrawParentFilter();
  2705. DrawUnitFilter();
  2706. EditorGUILayout.EndHorizontal();
  2707. EditorGUILayout.BeginHorizontal();
  2708. bool lastEnabled = GUI.enabled;
  2709. GUI.enabled = !IsAnalysisRunning();
  2710. if (GUILayout.Button(new GUIContent("Analyze", m_LastAnalysisTime), GUILayout.Width(100)))
  2711. m_RequestAnalysis = true;
  2712. GUI.enabled = lastEnabled;
  2713. DrawMarkerCount();
  2714. EditorGUILayout.LabelField(",", GUILayout.Width(10), GUILayout.ExpandWidth(false));
  2715. DrawThreadCount();
  2716. GUILayout.FlexibleSpace();
  2717. DrawMarkerColumnFilter();
  2718. EditorGUILayout.EndHorizontal();
  2719. }
  2720. if (m_ShowFilters != lastShowFilters)
  2721. {
  2722. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Filters,
  2723. analytic.GetDurationInSeconds(), m_ShowFilters);
  2724. }
  2725. EditorGUILayout.EndVertical();
  2726. }
  2727. }
  2728. internal bool IsAnalysisRunning()
  2729. {
  2730. return m_ThreadActivity != ThreadActivity.None;
  2731. }
  2732. internal bool IsLoading()
  2733. {
  2734. return m_ThreadActivity == ThreadActivity.Load;
  2735. }
  2736. float GetProgress()
  2737. {
  2738. // We return the value from the update loop so the data doesn't change over the time onGui is called for layout and repaint
  2739. // m_ThreadProgress ranges from 0 to 100.
  2740. return m_ThreadProgress * 0.01f;
  2741. }
  2742. bool IsAnalysisValid(bool checkFrameCount = false)
  2743. {
  2744. switch (m_ActiveTab)
  2745. {
  2746. case ActiveTab.Summary:
  2747. if (!m_ProfileSingleView.IsDataValid())
  2748. return false;
  2749. if (m_ProfileSingleView.analysis == null)
  2750. return false;
  2751. if (checkFrameCount)
  2752. {
  2753. if (m_ProfileSingleView.analysis.GetFrameSummary().frames.Count <= 0)
  2754. return false;
  2755. }
  2756. break;
  2757. case ActiveTab.Compare:
  2758. if (!m_ProfileLeftView.IsDataValid())
  2759. return false;
  2760. if (!m_ProfileRightView.IsDataValid())
  2761. return false;
  2762. if (m_ProfileLeftView.analysis == null)
  2763. return false;
  2764. if (m_ProfileRightView.analysis == null)
  2765. return false;
  2766. if (checkFrameCount)
  2767. {
  2768. if (m_ProfileLeftView.analysis.GetFrameSummary().frames.Count <= 0)
  2769. return false;
  2770. if (m_ProfileRightView.analysis.GetFrameSummary().frames.Count <= 0)
  2771. return false;
  2772. }
  2773. break;
  2774. }
  2775. //if (IsAnalysisRunning())
  2776. // return false;
  2777. return true;
  2778. }
  2779. void DrawProgress(Rect rect)
  2780. {
  2781. if (IsAnalysisRunning())
  2782. {
  2783. EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(140));
  2784. float x = 0;
  2785. float y = 0;
  2786. float width = rect.width;
  2787. float height = k_ProgressBarHeight;
  2788. if (m_2D.DrawStart(width, height, Draw2D.Origin.TopLeft))
  2789. {
  2790. float barLength = width * GetProgress();
  2791. m_2D.DrawFilledBox(x, y, barLength, height, UIColor.white);
  2792. m_2D.DrawEnd();
  2793. }
  2794. EditorGUILayout.EndHorizontal();
  2795. }
  2796. else
  2797. {
  2798. EditorGUILayout.BeginVertical();
  2799. GUILayout.Space(k_ProgressBarHeight);
  2800. EditorGUILayout.EndVertical();
  2801. }
  2802. }
  2803. void DrawPullButton(Color color, ProfileDataView view, FrameTimeGraph frameTimeGraph)
  2804. {
  2805. bool lastEnabled = GUI.enabled;
  2806. GUI.enabled = !IsAnalysisRunning();
  2807. GUIContent content;
  2808. if (!IsProfilerWindowOpen())
  2809. {
  2810. content = Styles.pullOpen;
  2811. GUI.enabled = false;
  2812. }
  2813. else if (m_ProfilerFirstFrameIndex == 0 && m_ProfilerLastFrameIndex == 0)
  2814. {
  2815. content = Styles.pullRange;
  2816. GUI.enabled = false;
  2817. }
  2818. /*
  2819. // Commented out so we can capture even if recording
  2820. else if (m_ProfilerWindowInterface.IsRecording())
  2821. {
  2822. content = Styles.pullRecording;
  2823. GUI.enabled = false;
  2824. }
  2825. */
  2826. else
  2827. {
  2828. content = Styles.pull;
  2829. }
  2830. Color oldColor = GUI.backgroundColor;
  2831. GUI.backgroundColor = color;
  2832. bool pull = GUILayout.Button(content, GUILayout.Width(100));
  2833. GUI.backgroundColor = oldColor;
  2834. if (pull)
  2835. {
  2836. PullFromProfiler(m_ProfilerFirstFrameIndex, m_ProfilerLastFrameIndex, view, frameTimeGraph);
  2837. UpdateActiveTab(true, false);
  2838. }
  2839. GUI.enabled = lastEnabled;
  2840. }
  2841. static readonly ProfilerMarkerAbstracted m_DrawFilesLoadedProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawFilesLoaded");
  2842. void DrawFilesLoaded()
  2843. {
  2844. using (m_DrawFilesLoadedProfilerMarker.Auto())
  2845. {
  2846. var boxStyle = GUI.skin.box;
  2847. var rect = EditorGUILayout.BeginVertical(boxStyle);
  2848. if (m_ActiveTab == ActiveTab.Summary)
  2849. {
  2850. EditorGUILayout.BeginHorizontal(GUILayout.Height(100 + GUI.skin.label.lineHeight +
  2851. (2 * (GUI.skin.label.margin.vertical +
  2852. GUI.skin.label.padding.vertical))));
  2853. float filenameWidth = GetFilenameWidth(m_ProfileSingleView.path);
  2854. filenameWidth = Math.Min(filenameWidth, 200);
  2855. EditorGUILayout.BeginVertical(GUILayout.MaxWidth(100 + filenameWidth),
  2856. GUILayout.ExpandWidth(false));
  2857. DrawPullButton(GUI.backgroundColor, m_ProfileSingleView, m_FrameTimeGraph);
  2858. DrawLoadSave();
  2859. EditorGUILayout.EndVertical();
  2860. EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
  2861. DrawFrameTimeGraph(100);
  2862. EditorGUILayout.EndVertical();
  2863. EditorGUILayout.EndHorizontal();
  2864. }
  2865. if (m_ActiveTab == ActiveTab.Compare)
  2866. {
  2867. DrawComparisonLoadSave();
  2868. }
  2869. rect.width -= boxStyle.margin.right;
  2870. DrawProgress(rect);
  2871. EditorGUILayout.EndVertical();
  2872. }
  2873. }
  2874. void ShowHelp()
  2875. {
  2876. EditorGUILayout.BeginVertical(GUI.skin.box);
  2877. m_HelpScroll = EditorGUILayout.BeginScrollView(m_HelpScroll, GUILayout.ExpandHeight(true));
  2878. #if UNITY_2019_3_OR_NEWER
  2879. GUILayout.TextArea(Styles.helpText);
  2880. #else
  2881. GUIStyle helpStyle = new GUIStyle(EditorStyles.textField);
  2882. helpStyle.wordWrap = true;
  2883. EditorGUILayout.LabelField(Styles.helpText, helpStyle);
  2884. #endif
  2885. EditorGUILayout.EndScrollView();
  2886. EditorGUILayout.EndVertical();
  2887. }
  2888. static readonly ProfilerMarkerAbstracted m_DrawAnalysisProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawAnalysis");
  2889. static readonly ProfilerMarkerAbstracted m_TopNMarkersProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.TopNMarkers");
  2890. static readonly ProfilerMarkerAbstracted m_DrawMarkerTableProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawMarkerTable");
  2891. void DrawAnalysis()
  2892. {
  2893. using (m_DrawAnalysisProfilerMarker.Auto())
  2894. {
  2895. EditorGUILayout.BeginHorizontal();
  2896. EditorGUILayout.BeginVertical();
  2897. DrawFilesLoaded();
  2898. if (m_ProfileSingleView.IsDataValid() && m_ProfileSingleView.data.GetFrameCount() > 0)
  2899. {
  2900. DrawAnalysisOptions();
  2901. if (IsAnalysisValid())
  2902. {
  2903. EditorGUILayout.BeginVertical(GUI.skin.box);
  2904. string title = string.Format("Top {0} markers on median frame", m_TopNBars);
  2905. GUIContent markersTitle = new GUIContent(title, Styles.topMarkersTooltip);
  2906. bool lastShowTopMarkers = m_ShowTopNMarkers;
  2907. m_ShowTopNMarkers = BoldFoldout(m_ShowTopNMarkers, markersTitle);
  2908. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  2909. if (m_ShowTopNMarkers)
  2910. {
  2911. using (m_TopNMarkersProfilerMarker.Auto())
  2912. {
  2913. m_TopMarkers.SetData(m_ProfileSingleView, m_DepthSliceUI.depthFilter, GetNameFilters(),
  2914. GetNameExcludes(), m_TimingOption, m_ThreadSelection.selection.Count);
  2915. EditorGUILayout.BeginVertical(GUILayout.Height(20));
  2916. EditorGUILayout.BeginHorizontal();
  2917. FrameSummary frameSummary = m_ProfileSingleView.analysis.GetFrameSummary();
  2918. if (frameSummary.count > 0)
  2919. DrawFrameIndexButton(frameSummary.medianFrameIndex, m_ProfileSingleView);
  2920. else
  2921. GUILayout.Label("", GUILayout.MinWidth(50));
  2922. Rect rect = EditorGUILayout.GetControlRect(GUILayout.ExpandWidth(true),
  2923. GUILayout.ExpandHeight(true));
  2924. float range = m_TopMarkers.GetTopMarkerTimeRange();
  2925. m_TopMarkers.Draw(rect, UIColor.bar, m_TopNBars, range, UIColor.barBackground,
  2926. Color.black, Color.white, true, true);
  2927. EditorGUILayout.EndHorizontal();
  2928. EditorGUILayout.EndVertical();
  2929. EditorGUILayout.BeginVertical(GUILayout.Height(20));
  2930. GUILayout.Label(m_DepthSliceUI.GetUIInfo(false));
  2931. EditorGUILayout.EndVertical();
  2932. }
  2933. }
  2934. if (m_ShowTopNMarkers != lastShowTopMarkers)
  2935. {
  2936. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.TopTen,
  2937. analytic.GetDurationInSeconds(), m_ShowTopNMarkers);
  2938. }
  2939. EditorGUILayout.EndVertical();
  2940. if (m_ProfileTable != null)
  2941. {
  2942. m_ShowMarkerTable = BoldFoldout(m_ShowMarkerTable, Styles.profileTable);
  2943. if (m_ShowMarkerTable)
  2944. {
  2945. using (m_DrawMarkerTableProfilerMarker.Auto())
  2946. {
  2947. Rect r = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true));
  2948. float scrollBarWidth = GUI.skin.verticalScrollbar.fixedWidth +
  2949. GUI.skin.verticalScrollbar.border.horizontal +
  2950. GUI.skin.verticalScrollbar.margin.horizontal +
  2951. GUI.skin.verticalScrollbar.padding.horizontal;
  2952. scrollBarWidth += LayoutSize.ScrollBarPadding;
  2953. //offset vertically to get correct clipping behaviour
  2954. Rect clipRect = new Rect(r.x, m_ProfileTable.state.scrollPos.y,
  2955. r.width - scrollBarWidth,
  2956. r.height -
  2957. (m_ProfileTable.multiColumnHeader.height + GUI.skin.box.padding.top) -
  2958. (m_ProfileTable.ShowingHorizontalScroll
  2959. ? (scrollBarWidth - LayoutSize.ScrollBarPadding)
  2960. : 0));
  2961. m_2D.SetClipRect(clipRect);
  2962. m_ProfileTable.OnGUI(r);
  2963. m_2D.ClearClipRect();
  2964. }
  2965. }
  2966. }
  2967. }
  2968. }
  2969. else
  2970. {
  2971. ShowHelp();
  2972. }
  2973. EditorGUILayout.EndVertical();
  2974. EditorGUILayout.BeginVertical(GUILayout.Width(LayoutSize.WidthRHS));
  2975. GUILayout.Space(4);
  2976. DrawFrameSummary();
  2977. DrawThreadSummary();
  2978. DrawSelected();
  2979. EditorGUILayout.EndVertical();
  2980. EditorGUILayout.EndHorizontal();
  2981. }
  2982. }
  2983. void SetRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus, ProfileDataView mainData, List<int> selectedIndices)
  2984. {
  2985. if (inputStatus == FrameTimeGraph.State.Dragging)
  2986. return;
  2987. var data = mainData.data;
  2988. if (clickCount == 2)
  2989. {
  2990. if (mainData.inSyncWithProfilerData)
  2991. {
  2992. int index = data.OffsetToDisplayFrame(selectedOffsets[0]);
  2993. JumpToFrame(index, mainData.data, false);
  2994. }
  2995. }
  2996. else
  2997. {
  2998. selectedIndices.Clear();
  2999. foreach (int offset in selectedOffsets)
  3000. {
  3001. selectedIndices.Add(data.OffsetToDisplayFrame(offset));
  3002. }
  3003. // Keep indices sorted
  3004. selectedIndices.Sort();
  3005. m_RequestCompare = true;
  3006. }
  3007. }
  3008. void SetLeftRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus)
  3009. {
  3010. SetRange(selectedOffsets, clickCount, inputStatus, m_ProfileLeftView, m_ProfileLeftView.selectedIndices);
  3011. }
  3012. void SetRightRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus)
  3013. {
  3014. SetRange(selectedOffsets, clickCount, inputStatus, m_ProfileRightView, m_ProfileRightView.selectedIndices);
  3015. }
  3016. void DrawComparisonLoadSaveButton(Color color, ProfileDataView view, FrameTimeGraph frameTimeGraph, ActiveView activeView)
  3017. {
  3018. bool lastEnabled = GUI.enabled;
  3019. bool isAnalysisRunning = IsAnalysisRunning();
  3020. EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(300), GUILayout.ExpandWidth(false));
  3021. GUIStyle buttonStyle = GUI.skin.button;
  3022. Color oldColor = GUI.backgroundColor;
  3023. GUI.backgroundColor = color;
  3024. GUI.enabled = !isAnalysisRunning;
  3025. bool load = GUILayout.Button("Load", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50));
  3026. GUI.enabled = lastEnabled;
  3027. GUI.backgroundColor = oldColor;
  3028. if (load)
  3029. {
  3030. m_Path = EditorUtility.OpenFilePanel("Load profile analyzer data file", "", "pdata");
  3031. if (m_Path.Length != 0)
  3032. {
  3033. m_ActiveLoadingView = activeView;
  3034. Pre2019DisableRecording();
  3035. BeginAsyncAction(ThreadActivity.Load);
  3036. }
  3037. GUIUtility.ExitGUI();
  3038. }
  3039. GUI.backgroundColor = color;
  3040. GUI.enabled = !isAnalysisRunning && view.IsDataValid();
  3041. bool save = GUILayout.Button("Save", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50));
  3042. GUI.enabled = lastEnabled;
  3043. GUI.backgroundColor = oldColor;
  3044. if (save)
  3045. {
  3046. Save(view, true);
  3047. }
  3048. ShowFilename(view.path);
  3049. EditorGUILayout.EndHorizontal();
  3050. }
  3051. float GetComparisonYRange()
  3052. {
  3053. float yRangeLeft = m_ProfileLeftView.IsDataValid() ? m_LeftFrameTimeGraph.GetDataRange() : 0f;
  3054. float yRangeRight = m_ProfileRightView.IsDataValid() ? m_RightFrameTimeGraph.GetDataRange() : 0f;
  3055. float yRange = Math.Max(yRangeLeft, yRangeRight);
  3056. return yRange;
  3057. }
  3058. void SetFrameTimeGraphPairing(bool paired)
  3059. {
  3060. if (paired != m_FrameTimeGraphsPaired)
  3061. {
  3062. m_FrameTimeGraphsPaired = paired;
  3063. m_LeftFrameTimeGraph.PairWith(m_FrameTimeGraphsPaired ? m_RightFrameTimeGraph : null);
  3064. }
  3065. }
  3066. void DrawComparisonLoadSave()
  3067. {
  3068. int leftFrames = m_ProfileLeftView.IsDataValid() ? m_ProfileLeftView.data.GetFrameCount() : 0;
  3069. int rightFrames = m_ProfileRightView.IsDataValid() ? m_ProfileRightView.data.GetFrameCount() : 0;
  3070. int maxFrames = Math.Max(leftFrames, rightFrames);
  3071. float yRange = GetComparisonYRange();
  3072. EditorGUILayout.BeginHorizontal(GUILayout.Height(100 + GUI.skin.label.lineHeight + (2 * (GUI.skin.label.margin.vertical + GUI.skin.label.padding.vertical))));
  3073. float leftFilenameWidth = GetFilenameWidth(m_ProfileLeftView.path);
  3074. float rightFilenameWidth = GetFilenameWidth(m_ProfileRightView.path);
  3075. float filenameWidth = Math.Max(leftFilenameWidth, rightFilenameWidth);
  3076. filenameWidth = Math.Min(filenameWidth, 200);
  3077. EditorGUILayout.BeginVertical(GUILayout.MaxWidth(100 + filenameWidth), GUILayout.ExpandWidth(false));
  3078. DrawPullButton(UIColor.left, m_ProfileLeftView, m_LeftFrameTimeGraph);
  3079. DrawComparisonLoadSaveButton(UIColor.left, m_ProfileLeftView, m_LeftFrameTimeGraph, ActiveView.Left);
  3080. DrawPullButton(UIColor.right, m_ProfileRightView, m_RightFrameTimeGraph);
  3081. DrawComparisonLoadSaveButton(UIColor.right, m_ProfileRightView, m_RightFrameTimeGraph, ActiveView.Right);
  3082. EditorGUILayout.EndVertical();
  3083. bool lastEnabled = GUI.enabled;
  3084. bool enabled = !IsAnalysisRunning();
  3085. EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
  3086. GUI.SetNextControlName("LeftFrameTimeGraph");
  3087. Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(50));
  3088. if (m_ProfileLeftView.IsDataValid())
  3089. {
  3090. if (!m_LeftFrameTimeGraph.HasData())
  3091. m_LeftFrameTimeGraph.SetData(GetFrameTimeData(m_ProfileLeftView.data));
  3092. if (!m_ProfileLeftView.HasValidSelection())
  3093. m_ProfileLeftView.SelectFullRange();
  3094. List<int> selectedOffsets = new List<int>();
  3095. foreach (int index in m_ProfileLeftView.selectedIndices)
  3096. {
  3097. selectedOffsets.Add(m_ProfileLeftView.data.DisplayFrameToOffset(index));
  3098. }
  3099. int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileLeftView);
  3100. int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileLeftView);
  3101. m_LeftFrameTimeGraph.SetEnabled(enabled);
  3102. m_LeftFrameTimeGraph.Draw(rect, m_ProfileLeftView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping, m_SelectedMarker.name, maxFrames, m_ProfileLeftView.analysisFull);
  3103. }
  3104. else
  3105. {
  3106. GUI.Label(rect, Styles.comparisonDataMissing, m_StyleUpperLeft);
  3107. }
  3108. GUI.SetNextControlName("RightFrameTimeGraph");
  3109. rect = EditorGUILayout.GetControlRect(GUILayout.Height(50));
  3110. if (m_ProfileRightView.IsDataValid())
  3111. {
  3112. if (!m_RightFrameTimeGraph.HasData())
  3113. m_RightFrameTimeGraph.SetData(GetFrameTimeData(m_ProfileRightView.data));
  3114. if (!m_ProfileRightView.HasValidSelection())
  3115. m_ProfileRightView.SelectFullRange();
  3116. List<int> selectedOffsets = new List<int>();
  3117. foreach (int index in m_ProfileRightView.selectedIndices)
  3118. {
  3119. selectedOffsets.Add(m_ProfileRightView.data.DisplayFrameToOffset(index));
  3120. }
  3121. int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileRightView);
  3122. int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileRightView);
  3123. m_RightFrameTimeGraph.SetEnabled(enabled);
  3124. m_RightFrameTimeGraph.Draw(rect, m_ProfileRightView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping, m_SelectedMarker.name, maxFrames, m_ProfileRightView.analysisFull);
  3125. }
  3126. else
  3127. {
  3128. GUI.Label(rect, Styles.comparisonDataMissing, m_StyleUpperLeft);
  3129. }
  3130. EditorGUILayout.BeginHorizontal();
  3131. if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid() && m_ProfileLeftView.data.GetFrameCount() > 0 && m_ProfileRightView.data.GetFrameCount() > 0)
  3132. {
  3133. GUIStyle lockButtonStyle = "IN LockButton";
  3134. GUIStyle style = new GUIStyle(lockButtonStyle);
  3135. style.padding.left = 20;
  3136. //bool paired = GUILayout.Toggle(m_frameTimeGraphsPaired, Styles.graphPairing, style);
  3137. GUI.enabled = enabled;
  3138. bool paired = EditorGUILayout.ToggleLeft(Styles.graphPairing, m_FrameTimeGraphsPaired, style, GUILayout.MaxWidth(200));
  3139. GUI.enabled = lastEnabled;
  3140. SetFrameTimeGraphPairing(paired);
  3141. GUILayout.FlexibleSpace();
  3142. ShowSelectedMarker();
  3143. }
  3144. EditorGUILayout.EndHorizontal();
  3145. EditorGUILayout.EndVertical();
  3146. EditorGUILayout.EndHorizontal();
  3147. }
  3148. void DrawComparisonHistogram(float height, float minValue, float maxValue, int bucketCount, int[] leftBuckets, int[] rightBuckets, int leftCount, int rightCount, bool leftValid, bool rightValid, DisplayUnits displayUnits)
  3149. {
  3150. Histogram histogram = new Histogram(m_2D, displayUnits.Units);
  3151. float width = LayoutSize.HistogramWidth;
  3152. float min = minValue;
  3153. float max = maxValue;
  3154. float spacing = 2;
  3155. float range = max - min;
  3156. // bucketCount = (range == 0f) ? 1 : bucketCount;
  3157. float x = (spacing / 2);
  3158. float y = 0;
  3159. float w = ((width + spacing) / bucketCount) - spacing;
  3160. float h = height;
  3161. histogram.DrawStart(width);
  3162. if (m_2D.DrawStart(width, height, Draw2D.Origin.BottomLeft))
  3163. {
  3164. float bucketWidth = (range / bucketCount);
  3165. Rect rect = GUILayoutUtility.GetLastRect();
  3166. histogram.DrawBackground(width, height, bucketCount, min, max, spacing);
  3167. if (!IsAnalysisRunning())
  3168. {
  3169. for (int bucketAt = 0; bucketAt < bucketCount; bucketAt++)
  3170. {
  3171. float leftBarCount = leftValid ? leftBuckets[bucketAt] : 0;
  3172. float rightBarCount = rightValid ? rightBuckets[bucketAt] : 0;
  3173. float leftBarHeight = leftValid ? ((h * leftBarCount) / leftCount) : 0;
  3174. float rightBarHeight = rightValid ? ((h * rightBarCount) / rightCount) : 0;
  3175. if (leftBarCount > 0) // Make sure we always slow a small bar if non zero
  3176. leftBarHeight = Mathf.Max(1.0f, leftBarHeight);
  3177. if (rightBarCount > 0) // Make sure we always slow a small bar if non zero
  3178. rightBarHeight = Mathf.Max(1.0f, rightBarHeight);
  3179. if ((int)rightBarHeight == (int)leftBarHeight)
  3180. {
  3181. m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.both);
  3182. }
  3183. else if (rightBarHeight > leftBarHeight)
  3184. {
  3185. m_2D.DrawFilledBox(x, y, w, rightBarHeight, UIColor.right);
  3186. m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.both);
  3187. }
  3188. else
  3189. {
  3190. m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.left);
  3191. m_2D.DrawFilledBox(x, y, w, rightBarHeight, UIColor.both);
  3192. }
  3193. float bucketStart = min + (bucketAt * bucketWidth);
  3194. float bucketEnd = bucketStart + bucketWidth;
  3195. string tooltip = string.Format(
  3196. "{0}-{1}\nLeft: {2} {3}\nRight: {4} {5}\n\nBar width: {6}",
  3197. displayUnits.ToTooltipString(bucketStart, false),
  3198. displayUnits.ToTooltipString(bucketEnd, true),
  3199. leftBarCount, leftBarCount == 1 ? "frame" : "frames",
  3200. rightBarCount, rightBarCount == 1 ? "frame" : "frames",
  3201. displayUnits.ToTooltipString(bucketWidth, true));
  3202. GUI.Label(new Rect(rect.x + x, rect.y + y, w, h),
  3203. new GUIContent("", tooltip)
  3204. );
  3205. x += w;
  3206. x += spacing;
  3207. }
  3208. }
  3209. m_2D.DrawEnd();
  3210. }
  3211. histogram.DrawEnd(width, min, max, spacing);
  3212. }
  3213. void DrawComparisonFrameSummary()
  3214. {
  3215. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  3216. bool lastShowFrameSummary = m_ShowFrameSummary;
  3217. m_ShowFrameSummary = BoldFoldout(m_ShowFrameSummary, Styles.frameSummary);
  3218. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3219. if (m_ShowFrameSummary)
  3220. {
  3221. EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present
  3222. if (IsAnalysisValid())
  3223. {
  3224. var leftFrameSummary = m_ProfileLeftView.analysis.GetFrameSummary();
  3225. var rightFrameSummary = m_ProfileRightView.analysis.GetFrameSummary();
  3226. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  3227. m_Columns.Draw4("", "Left", "Right", "Diff");
  3228. int diff = rightFrameSummary.count - leftFrameSummary.count;
  3229. m_Columns.Draw4(Styles.frameCount, GetFrameCountText(m_ProfileLeftView), GetFrameCountText(m_ProfileRightView), new GUIContent(diff.ToString(), ""));
  3230. m_Columns.Draw3(Styles.frameStart, GetFirstFrameText(m_ProfileLeftView), GetFirstFrameText(m_ProfileRightView));
  3231. m_Columns.Draw3(Styles.frameEnd, GetLastFrameText(m_ProfileLeftView), GetLastFrameText(m_ProfileRightView));
  3232. m_Columns.Draw(0, "");
  3233. string units = GetDisplayUnits();
  3234. m_Columns.Draw4("", units, units, units);
  3235. Draw4DiffMs(Styles.max, leftFrameSummary.msMax, leftFrameSummary.maxFrameIndex, rightFrameSummary.msMax, rightFrameSummary.maxFrameIndex);
  3236. Draw4DiffMs(Styles.upperQuartile, leftFrameSummary.msUpperQuartile, rightFrameSummary.msUpperQuartile);
  3237. Draw4DiffMs(Styles.median, leftFrameSummary.msMedian, leftFrameSummary.medianFrameIndex, rightFrameSummary.msMedian, rightFrameSummary.medianFrameIndex);
  3238. Draw4DiffMs(Styles.mean, leftFrameSummary.msMean, rightFrameSummary.msMean);
  3239. Draw4DiffMs(Styles.lowerQuartile, leftFrameSummary.msLowerQuartile, rightFrameSummary.msLowerQuartile);
  3240. Draw4DiffMs(Styles.min, leftFrameSummary.msMin, leftFrameSummary.minFrameIndex, rightFrameSummary.msMin, rightFrameSummary.minFrameIndex);
  3241. GUIStyle style = GUI.skin.label;
  3242. GUILayout.Space(style.lineHeight);
  3243. EditorGUILayout.BeginHorizontal();
  3244. int leftBucketCount = leftFrameSummary.buckets.Length;
  3245. int rightBucketCount = rightFrameSummary.buckets.Length;
  3246. float msFrameMax = Math.Max(leftFrameSummary.msMax, rightFrameSummary.msMax);
  3247. float yRange = msFrameMax;
  3248. if (leftBucketCount != rightBucketCount)
  3249. {
  3250. Debug.Log("Error left frame summary bucket count doesn't equal right summary");
  3251. }
  3252. else
  3253. {
  3254. DrawComparisonHistogram(40, 0, yRange, leftBucketCount, leftFrameSummary.buckets, rightFrameSummary.buckets, leftFrameSummary.count, rightFrameSummary.count, true, true, m_DisplayUnits);
  3255. }
  3256. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units);
  3257. float plotWidth = 40 + GUI.skin.box.padding.horizontal;
  3258. float plotHeight = 40;
  3259. plotWidth /= 2.0f;
  3260. boxAndWhiskerPlot.Draw(plotWidth, plotHeight, leftFrameSummary.msMin, leftFrameSummary.msLowerQuartile,
  3261. leftFrameSummary.msMedian, leftFrameSummary.msUpperQuartile, leftFrameSummary.msMax, 0, yRange,
  3262. UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft);
  3263. boxAndWhiskerPlot.Draw(plotWidth, plotHeight, rightFrameSummary.msMin, rightFrameSummary.msLowerQuartile,
  3264. rightFrameSummary.msMedian, rightFrameSummary.msUpperQuartile, rightFrameSummary.msMax, 0, yRange,
  3265. UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight);
  3266. boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, 0, yRange,
  3267. "Min frame time for selected frames in the 2 data sets",
  3268. "Max frame time for selected frames in the 2 data sets");
  3269. EditorGUILayout.EndHorizontal();
  3270. }
  3271. else
  3272. {
  3273. EditorGUILayout.LabelField("No analysis data selected");
  3274. }
  3275. EditorGUILayout.EndVertical();
  3276. }
  3277. if (m_ShowFrameSummary != lastShowFrameSummary)
  3278. {
  3279. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Frames, analytic.GetDurationInSeconds(), m_ShowFrameSummary);
  3280. }
  3281. EditorGUILayout.EndVertical();
  3282. }
  3283. void ShowThreadRange()
  3284. {
  3285. EditorGUILayout.BeginHorizontal();
  3286. m_Columns.Draw(0, Styles.threadGraphScale);
  3287. m_ThreadRange = (ThreadRange)EditorGUILayout.Popup((int)m_ThreadRange, Styles.threadRanges, GUILayout.Width(160));
  3288. EditorGUILayout.EndHorizontal();
  3289. }
  3290. float GetThreadTimeRange(ProfileAnalysis profileAnalysis)
  3291. {
  3292. if (profileAnalysis == null)
  3293. return 0.0f;
  3294. var frameSummary = profileAnalysis.GetFrameSummary();
  3295. float range = frameSummary.msMax;
  3296. switch (m_ThreadRange)
  3297. {
  3298. case ThreadRange.Median:
  3299. range = frameSummary.msMedian;
  3300. break;
  3301. case ThreadRange.UpperQuartile:
  3302. range = frameSummary.msUpperQuartile;
  3303. break;
  3304. case ThreadRange.Max:
  3305. range = frameSummary.msMax;
  3306. break;
  3307. }
  3308. return range;
  3309. }
  3310. int GetThreadSelectionCount(out int leftSelectionCount, out int rightSelectionCount)
  3311. {
  3312. List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection);
  3313. leftSelectionCount = 0;
  3314. foreach (var threadName in m_ProfileLeftView.data.GetThreadNames())
  3315. {
  3316. if (threadSelection.Contains(threadName))
  3317. {
  3318. leftSelectionCount++;
  3319. }
  3320. }
  3321. rightSelectionCount = 0;
  3322. foreach (var threadName in m_ProfileRightView.data.GetThreadNames())
  3323. {
  3324. if (threadSelection.Contains(threadName))
  3325. {
  3326. rightSelectionCount++;
  3327. }
  3328. }
  3329. return threadSelection.Count;
  3330. }
  3331. void DrawComparisonThreadSummary()
  3332. {
  3333. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  3334. bool lastShowThreadSummary = m_ShowThreadSummary;
  3335. m_ShowThreadSummary = BoldFoldout(m_ShowThreadSummary, Styles.threadSummary);
  3336. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3337. if (m_ShowThreadSummary)
  3338. {
  3339. EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present
  3340. if (IsAnalysisValid())
  3341. {
  3342. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  3343. EditorGUILayout.BeginHorizontal();
  3344. m_Columns.Draw4("", "Left", "Right", "Total");
  3345. EditorGUILayout.EndHorizontal();
  3346. int matchingCount, uniqueLeft, uniqueRight;
  3347. int allThreadsCount = GetCombinedThreadCount(out matchingCount, out uniqueLeft, out uniqueRight);
  3348. EditorGUILayout.BeginHorizontal();
  3349. m_Columns.Draw(0, "Total Count : ");
  3350. m_Columns.Draw(1, new GUIContent(m_ProfileLeftView.data.GetThreadCount().ToString(), "Total threads in left data set"));
  3351. m_Columns.Draw(2, new GUIContent(m_ProfileRightView.data.GetThreadCount().ToString(), "Total threads in right data set"));
  3352. string tooltip = GetThreadCountToolTipUnion(allThreadsCount, matchingCount);
  3353. m_Columns.Draw(3, new GUIContent(allThreadsCount.ToString(), tooltip));
  3354. EditorGUILayout.EndHorizontal();
  3355. EditorGUILayout.BeginHorizontal();
  3356. m_Columns.Draw(0, "Unique Count : ");
  3357. m_Columns.Draw(1, new GUIContent(uniqueLeft.ToString(), "Unique to left data set"));
  3358. m_Columns.Draw(2, new GUIContent(uniqueRight.ToString(), "Unique to right data set"));
  3359. tooltip = GetThreadCountToolTipDifference(allThreadsCount, matchingCount, uniqueLeft, uniqueRight);
  3360. m_Columns.Draw(3, new GUIContent((allThreadsCount - matchingCount).ToString(), tooltip));
  3361. EditorGUILayout.EndHorizontal();
  3362. int leftSelectionCount, rightSelectionCount;
  3363. int selectedThreads = GetThreadSelectionCount(out leftSelectionCount, out rightSelectionCount);
  3364. EditorGUILayout.BeginHorizontal();
  3365. m_Columns.Draw(0, "Selected : ");
  3366. m_Columns.Draw(1, new GUIContent(leftSelectionCount.ToString(), "Left selected"));
  3367. m_Columns.Draw(2, new GUIContent(rightSelectionCount.ToString(), "Right selected"));
  3368. m_Columns.Draw(3, new GUIContent(selectedThreads.ToString(), "Total selected"));
  3369. EditorGUILayout.EndHorizontal();
  3370. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2 + LayoutSize.WidthColumn3, 0);
  3371. ShowThreadRange();
  3372. float width = 100;
  3373. float height = GUI.skin.label.lineHeight;
  3374. float xAxisMin = 0.0f;
  3375. float xAxisMax = GetThreadTimeRange(m_ProfileLeftView.analysis);
  3376. m_Columns.Draw3(Styles.emptyString, Styles.median, Styles.thread);
  3377. m_ThreadScroll = EditorGUILayout.BeginScrollView(m_ThreadScroll, GUIStyle.none, GUI.skin.verticalScrollbar);
  3378. Rect clipRect = new Rect(m_ThreadScroll.x, m_ThreadScroll.y, m_ComparisonThreadsAreaRect.width, m_ComparisonThreadsAreaRect.height);
  3379. m_2D.SetClipRect(clipRect);
  3380. for (int i = 0; i < m_ThreadUINames.Count; i++)
  3381. {
  3382. string threadNameWithIndex = m_ThreadNames[i];
  3383. bool include = ProfileAnalyzer.MatchThreadFilter(threadNameWithIndex, m_ThreadSelection.selection);
  3384. if (!include)
  3385. continue;
  3386. ThreadData threadLeft = m_ProfileLeftView.analysis.GetThreadByName(threadNameWithIndex);
  3387. ThreadData threadRight = m_ProfileRightView.analysis.GetThreadByName(threadNameWithIndex);
  3388. ThreadData thread = threadLeft != null ? threadLeft : threadRight;
  3389. if (thread == null)
  3390. continue;
  3391. bool singleThread = thread.threadsInGroup > 1 ? false : true;
  3392. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units);
  3393. EditorGUILayout.BeginHorizontal();
  3394. if (threadLeft != null)
  3395. boxAndWhiskerPlot.DrawHorizontal(width, height, threadLeft.msMin, threadLeft.msLowerQuartile, threadLeft.msMedian, threadLeft.msUpperQuartile, threadLeft.msMax, xAxisMin, xAxisMax, UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft, GUI.skin.label);
  3396. else
  3397. EditorGUILayout.LabelField(Styles.noThread, GUILayout.Width(width));
  3398. m_Columns.Draw(1, (threadLeft != null) ? ToDisplayUnitsWithTooltips(threadLeft.msMedian) : Styles.noThread);
  3399. m_Columns.Draw(2, GetThreadNameWithGroupTooltip(thread.threadNameWithIndex, singleThread));
  3400. EditorGUILayout.EndHorizontal();
  3401. EditorGUILayout.BeginHorizontal();
  3402. if (threadRight != null)
  3403. boxAndWhiskerPlot.DrawHorizontal(width, height, threadRight.msMin, threadRight.msLowerQuartile, threadRight.msMedian, threadRight.msUpperQuartile, threadRight.msMax, xAxisMin, xAxisMax, UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight, GUI.skin.label);
  3404. else
  3405. EditorGUILayout.LabelField(Styles.noThread, GUILayout.Width(width));
  3406. m_Columns.Draw(1, (threadRight != null) ? ToDisplayUnitsWithTooltips(threadRight.msMedian) : Styles.noThread);
  3407. m_Columns.Draw(2, "");
  3408. EditorGUILayout.EndHorizontal();
  3409. }
  3410. m_2D.ClearClipRect();
  3411. EditorGUILayout.EndScrollView();
  3412. if (Event.current.type == EventType.Repaint)
  3413. {
  3414. // This value is not valid at layout phase
  3415. m_ComparisonThreadsAreaRect = GUILayoutUtility.GetLastRect();
  3416. }
  3417. }
  3418. else
  3419. {
  3420. EditorGUILayout.LabelField("No analysis data selected");
  3421. }
  3422. EditorGUILayout.EndVertical();
  3423. }
  3424. if (m_ShowThreadSummary != lastShowThreadSummary)
  3425. {
  3426. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Threads, analytic.GetDurationInSeconds(), m_ShowThreadSummary);
  3427. }
  3428. EditorGUILayout.EndVertical();
  3429. }
  3430. static readonly ProfilerMarkerAbstracted m_DrawCompareOptionsProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawCompareOptions");
  3431. void DrawCompareOptions()
  3432. {
  3433. using (m_DrawCompareOptionsProfilerMarker.Auto())
  3434. {
  3435. EditorGUILayout.BeginVertical(GUI.skin.box);
  3436. bool lastShowFilters = m_ShowFilters;
  3437. m_ShowFilters = BoldFoldout(m_ShowFilters, Styles.filters);
  3438. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3439. if (m_ShowFilters)
  3440. {
  3441. DrawNameFilter();
  3442. EditorGUILayout.BeginHorizontal();
  3443. DrawThreadFilter(m_ProfileLeftView.data);
  3444. EditorGUILayout.EndHorizontal();
  3445. EditorGUILayout.BeginHorizontal();
  3446. m_DepthSliceUI.DrawDepthFilter(IsAnalysisRunning(), m_ActiveTab == ActiveTab.Summary,
  3447. m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView);
  3448. DrawTimingFilter();
  3449. EditorGUILayout.EndHorizontal();
  3450. EditorGUILayout.BeginHorizontal();
  3451. DrawParentFilter();
  3452. DrawUnitFilter();
  3453. EditorGUILayout.EndHorizontal();
  3454. EditorGUILayout.BeginHorizontal();
  3455. if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid())
  3456. {
  3457. bool lastEnabled = GUI.enabled;
  3458. GUI.enabled = !IsAnalysisRunning();
  3459. if (GUILayout.Button(new GUIContent("Compare", m_LastCompareTime), GUILayout.Width(100)))
  3460. m_RequestCompare = true;
  3461. GUI.enabled = lastEnabled;
  3462. }
  3463. DrawMarkerCount();
  3464. EditorGUILayout.LabelField(",", GUILayout.Width(10), GUILayout.ExpandWidth(false));
  3465. DrawThreadCount();
  3466. GUILayout.FlexibleSpace();
  3467. DrawMarkerColumnFilter();
  3468. EditorGUILayout.EndHorizontal();
  3469. }
  3470. if (m_ShowFilters != lastShowFilters)
  3471. {
  3472. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Filters,
  3473. analytic.GetDurationInSeconds(), m_ShowFilters);
  3474. }
  3475. EditorGUILayout.EndVertical();
  3476. }
  3477. }
  3478. static readonly ProfilerMarkerAbstracted m_DrawComparisonProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawComparison");
  3479. static readonly ProfilerMarkerAbstracted m_DrawComparisonTableProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawComparisonTable");
  3480. void DrawComparison()
  3481. {
  3482. using (m_DrawComparisonProfilerMarker.Auto())
  3483. {
  3484. EditorGUILayout.BeginHorizontal();
  3485. EditorGUILayout.BeginVertical();
  3486. DrawFilesLoaded();
  3487. if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid() &&
  3488. m_ProfileLeftView.data.GetFrameCount() > 0 && m_ProfileRightView.data.GetFrameCount() > 0)
  3489. {
  3490. DrawCompareOptions();
  3491. if (m_ComparisonTable != null)
  3492. {
  3493. EditorGUILayout.BeginVertical(GUI.skin.box);
  3494. string title = string.Format("Top {0} markers on median frames", m_TopNBars);
  3495. GUIContent markersTitle = new GUIContent(title, Styles.topMarkersTooltip);
  3496. bool lastShowTopMarkers = m_ShowTopNMarkers;
  3497. m_ShowTopNMarkers = BoldFoldout(m_ShowTopNMarkers, markersTitle);
  3498. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3499. if (m_ShowTopNMarkers)
  3500. {
  3501. using (m_TopNMarkersProfilerMarker.Auto())
  3502. {
  3503. EditorGUILayout.BeginVertical(GUILayout.Height(40));
  3504. Rect rect = EditorGUILayout.GetControlRect(GUILayout.ExpandWidth(true),
  3505. GUILayout.ExpandHeight(true));
  3506. rect.height = rect.height / 2;
  3507. var nameFilters = GetNameFilters();
  3508. var nameExcludes = GetNameExcludes();
  3509. m_TopMarkersLeft.SetData(m_ProfileLeftView, m_DepthSliceUI.depthFilter1, nameFilters,
  3510. nameExcludes, m_TimingOption, m_ThreadSelection.selection.Count);
  3511. m_TopMarkersRight.SetData(m_ProfileRightView, m_DepthSliceUI.depthFilter2, nameFilters,
  3512. nameExcludes, m_TimingOption, m_ThreadSelection.selection.Count);
  3513. float leftRange = m_TopMarkersLeft.GetTopMarkerTimeRange();
  3514. float rightRange = m_TopMarkersRight.GetTopMarkerTimeRange();
  3515. if (m_TopTenDisplay == TopTenDisplay.LongestTime)
  3516. {
  3517. float max = Math.Max(leftRange, rightRange);
  3518. leftRange = max;
  3519. rightRange = max;
  3520. }
  3521. int leftMedian = 0;
  3522. int rightMedian = 0;
  3523. if (m_ProfileLeftView.analysis != null)
  3524. {
  3525. FrameSummary frameSummary = m_ProfileLeftView.analysis.GetFrameSummary();
  3526. if (frameSummary.count > 0)
  3527. leftMedian = frameSummary.medianFrameIndex;
  3528. }
  3529. if (m_ProfileRightView.analysis != null)
  3530. {
  3531. FrameSummary frameSummary = m_ProfileRightView.analysis.GetFrameSummary();
  3532. if (frameSummary.count > 0)
  3533. rightMedian = frameSummary.medianFrameIndex;
  3534. }
  3535. int maxMedian = Math.Max(leftMedian, rightMedian);
  3536. Rect frameIndexRect = new Rect(rect);
  3537. Vector2 size =
  3538. GUI.skin.button.CalcSize(new GUIContent(string.Format("{0}", maxMedian)));
  3539. frameIndexRect.width =
  3540. Math.Max(size.x, 50); // DrawFrameIndexButton should always be at least 50 wide
  3541. if (leftMedian != 0f)
  3542. DrawFrameIndexButton(frameIndexRect, leftMedian, m_ProfileLeftView);
  3543. else
  3544. GUI.Label(frameIndexRect, "");
  3545. float padding = 2;
  3546. rect.x += frameIndexRect.width + padding;
  3547. rect.width -= frameIndexRect.width;
  3548. m_TopMarkersLeft.Draw(rect, UIColor.left, m_TopNBars, leftRange, UIColor.barBackground,
  3549. Color.black, Color.white, true, true);
  3550. rect.y += rect.height;
  3551. frameIndexRect.y += rect.height;
  3552. if (rightMedian != 0f)
  3553. DrawFrameIndexButton(frameIndexRect, rightMedian, m_ProfileRightView);
  3554. else
  3555. GUI.Label(frameIndexRect, "");
  3556. m_TopMarkersRight.Draw(rect, UIColor.right, m_TopNBars, rightRange,
  3557. UIColor.barBackground,
  3558. Color.black, Color.white, true, true);
  3559. EditorGUILayout.EndVertical();
  3560. EditorGUILayout.BeginHorizontal();
  3561. GUILayout.Label(m_DepthSliceUI.GetUIInfo(true), GUILayout.ExpandWidth(true));
  3562. GUILayout.Label(Styles.topMarkerRatio, GUILayout.ExpandWidth(false));
  3563. m_TopTenDisplay = (TopTenDisplay)EditorGUILayout.Popup((int)m_TopTenDisplay, Styles.topTenDisplayOptions, GUILayout.MaxWidth(100));
  3564. EditorGUILayout.EndHorizontal();
  3565. }
  3566. }
  3567. if (m_ShowTopNMarkers != lastShowTopMarkers)
  3568. {
  3569. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(
  3570. ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(),
  3571. m_ShowTopNMarkers);
  3572. }
  3573. EditorGUILayout.EndVertical();
  3574. if (m_ComparisonTable != null)
  3575. {
  3576. m_ShowMarkerTable = BoldFoldout(m_ShowMarkerTable, Styles.comparisonTable);
  3577. if (m_ShowMarkerTable)
  3578. {
  3579. using (m_DrawComparisonTableProfilerMarker.Auto())
  3580. {
  3581. Rect r = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true));
  3582. float scrollBarWidth = GUI.skin.verticalScrollbar.fixedWidth +
  3583. GUI.skin.verticalScrollbar.border.horizontal +
  3584. GUI.skin.verticalScrollbar.margin.horizontal +
  3585. GUI.skin.verticalScrollbar.padding.horizontal;
  3586. scrollBarWidth += LayoutSize.ScrollBarPadding;
  3587. //offset vertically to get correct clipping behaviour
  3588. Rect clipRect = new Rect(r.x, m_ComparisonTable.state.scrollPos.y,
  3589. r.width - scrollBarWidth,
  3590. r.height - (m_ComparisonTable.multiColumnHeader.height + GUI.skin.box.padding.top) -
  3591. (m_ComparisonTable.ShowingHorizontalScroll
  3592. ? (scrollBarWidth - LayoutSize.ScrollBarPadding)
  3593. : 0));
  3594. m_2D.SetClipRect(clipRect);
  3595. m_ComparisonTable.OnGUI(r);
  3596. m_2D.ClearClipRect();
  3597. }
  3598. }
  3599. }
  3600. }
  3601. }
  3602. else
  3603. {
  3604. ShowHelp();
  3605. }
  3606. EditorGUILayout.EndVertical();
  3607. EditorGUILayout.BeginVertical(GUILayout.Width(LayoutSize.WidthRHS));
  3608. GUILayout.Space(4);
  3609. DrawComparisonFrameSummary();
  3610. DrawComparisonThreadSummary();
  3611. DrawComparisonSelected();
  3612. EditorGUILayout.EndVertical();
  3613. EditorGUILayout.EndHorizontal();
  3614. }
  3615. }
  3616. bool BoldFoldout(bool toggle, GUIContent content)
  3617. {
  3618. GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout);
  3619. foldoutStyle.fontStyle = FontStyle.Bold;
  3620. return EditorGUILayout.Foldout(toggle, content, true, foldoutStyle);
  3621. }
  3622. void DrawComparisonSelectedStats(MarkerData leftMarker, MarkerData rightMarker)
  3623. {
  3624. GUIStyle style = GUI.skin.label;
  3625. string units = GetDisplayUnits();
  3626. m_Columns.Draw4("", units, units, units);
  3627. Draw4DiffMs(Styles.max, MarkerData.GetMsMax(leftMarker), MarkerData.GetMaxFrameIndex(leftMarker), MarkerData.GetMsMax(rightMarker), MarkerData.GetMaxFrameIndex(rightMarker));
  3628. Draw4DiffMs(Styles.upperQuartile, MarkerData.GetMsUpperQuartile(leftMarker), MarkerData.GetMsUpperQuartile(rightMarker));
  3629. Draw4DiffMs(Styles.median, MarkerData.GetMsMedian(leftMarker), MarkerData.GetMedianFrameIndex(leftMarker), MarkerData.GetMsMedian(rightMarker), MarkerData.GetMedianFrameIndex(rightMarker));
  3630. Draw4DiffMs(Styles.mean, MarkerData.GetMsMean(leftMarker), MarkerData.GetMsMean(rightMarker));
  3631. Draw4DiffMs(Styles.lowerQuartile, MarkerData.GetMsLowerQuartile(leftMarker), MarkerData.GetMsLowerQuartile(rightMarker));
  3632. Draw4DiffMs(Styles.min, MarkerData.GetMsMin(leftMarker), MarkerData.GetMinFrameIndex(leftMarker), MarkerData.GetMsMin(rightMarker), MarkerData.GetMinFrameIndex(rightMarker));
  3633. GUILayout.Space(style.lineHeight);
  3634. Draw4DiffMs(Styles.individualMax, MarkerData.GetMsMaxIndividual(leftMarker), MarkerData.GetMsMaxIndividual(rightMarker));
  3635. Draw4DiffMs(Styles.individualMin, MarkerData.GetMsMinIndividual(leftMarker), MarkerData.GetMsMinIndividual(rightMarker));
  3636. }
  3637. void DrawComparisonSelected()
  3638. {
  3639. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  3640. GUIStyle style = GUI.skin.label;
  3641. bool lastMarkerSummary = m_ShowMarkerSummary;
  3642. m_ShowMarkerSummary = BoldFoldout(m_ShowMarkerSummary, Styles.markerSummary);
  3643. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3644. if (m_ShowMarkerSummary)
  3645. {
  3646. EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present
  3647. if (IsAnalysisValid())
  3648. {
  3649. List<MarkerData> leftMarkers = m_ProfileLeftView.analysis.GetMarkers();
  3650. List<MarkerData> rightMarkers = m_ProfileRightView.analysis.GetMarkers();
  3651. int pairingAt = m_SelectedPairing;
  3652. if (leftMarkers != null && rightMarkers != null && m_Pairings != null)
  3653. {
  3654. if (pairingAt >= 0 && pairingAt < m_Pairings.Count)
  3655. {
  3656. m_MarkerSummaryScroll = GUILayout.BeginScrollView(m_MarkerSummaryScroll, GUIStyle.none, GUI.skin.verticalScrollbar);
  3657. Rect clipRect = new Rect(m_MarkerSummaryScroll.x, m_MarkerSummaryScroll.y, LayoutSize.WidthRHS, 500);
  3658. m_2D.SetClipRect(clipRect);
  3659. EditorGUILayout.BeginVertical();
  3660. var pairing = m_Pairings[pairingAt];
  3661. var leftMarker = (pairing.leftIndex >= 0 && pairing.leftIndex < leftMarkers.Count) ? leftMarkers[pairing.leftIndex] : null;
  3662. var rightMarker = (pairing.rightIndex >= 0 && pairing.rightIndex < rightMarkers.Count) ? rightMarkers[pairing.rightIndex] : null;
  3663. EditorGUILayout.LabelField(pairing.name,
  3664. GUILayout.MaxWidth(LayoutSize.WidthRHS -
  3665. (GUI.skin.box.padding.horizontal + GUI.skin.box.margin.horizontal)));
  3666. DrawComparisonFrameRatio(leftMarker, rightMarker);
  3667. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  3668. EditorGUILayout.BeginHorizontal();
  3669. m_Columns.Draw(0, Styles.firstFrame);
  3670. if (leftMarker != null)
  3671. DrawFrameIndexButton(leftMarker.firstFrameIndex, m_ProfileLeftView);
  3672. else
  3673. m_Columns.Draw(1, Styles.emptyString);
  3674. if (rightMarker != null)
  3675. DrawFrameIndexButton(rightMarker.firstFrameIndex, m_ProfileRightView);
  3676. else
  3677. m_Columns.Draw(2, Styles.emptyString);
  3678. EditorGUILayout.EndHorizontal();
  3679. DrawTopComparison(leftMarker, rightMarker);
  3680. GUILayout.Space(style.lineHeight);
  3681. EditorGUILayout.BeginHorizontal();
  3682. int leftBucketCount = leftMarker != null ? leftMarker.buckets.Length : 0;
  3683. int rightBucketCount = rightMarker != null ? rightMarker.buckets.Length : 0;
  3684. float leftMin = MarkerData.GetMsMin(leftMarker);
  3685. float rightMin = MarkerData.GetMsMin(rightMarker);
  3686. float leftMax = MarkerData.GetMsMax(leftMarker);
  3687. float rightMax = MarkerData.GetMsMax(rightMarker);
  3688. int[] leftBuckets = leftMarker != null ? leftMarker.buckets : new int[0];
  3689. int[] rightBuckets = rightMarker != null ? rightMarker.buckets : new int[0];
  3690. Units units = m_DisplayUnits.Units;
  3691. string unitName = "marker time";
  3692. if (DisplayCount())
  3693. {
  3694. units = Units.Count;
  3695. unitName = "count";
  3696. leftBucketCount = leftMarker != null ? leftMarker.countBuckets.Length : 0;
  3697. rightBucketCount = rightMarker != null ? rightMarker.countBuckets.Length : 0;
  3698. leftMin = MarkerData.GetCountMin(leftMarker);
  3699. rightMin = MarkerData.GetCountMin(rightMarker);
  3700. leftMax = MarkerData.GetCountMax(leftMarker);
  3701. rightMax = MarkerData.GetCountMax(rightMarker);
  3702. leftBuckets = leftMarker != null ? leftMarker.countBuckets : new int[0];
  3703. rightBuckets = rightMarker != null ? rightMarker.countBuckets : new int[0];
  3704. }
  3705. DisplayUnits displayUnits = new DisplayUnits(units);
  3706. float minValue;
  3707. float maxValue;
  3708. if (leftMarker != null && rightMarker != null)
  3709. {
  3710. minValue = Math.Min(leftMin, rightMin);
  3711. maxValue = Math.Max(leftMax, rightMax);
  3712. }
  3713. else if (leftMarker != null)
  3714. {
  3715. minValue = leftMin;
  3716. maxValue = leftMax;
  3717. }
  3718. else // Either valid or 0
  3719. {
  3720. minValue = rightMin;
  3721. maxValue = rightMax;
  3722. }
  3723. if (leftBucketCount > 0 && rightBucketCount > 0 && leftBucketCount != rightBucketCount)
  3724. {
  3725. Debug.Log("Error - number of buckets doesn't match in the left and right marker analysis");
  3726. }
  3727. else
  3728. {
  3729. int bucketCount = Math.Max(leftBucketCount, rightBucketCount);
  3730. int leftFrameCount = MarkerData.GetPresentOnFrameCount(leftMarker);
  3731. int rightFrameCount = MarkerData.GetPresentOnFrameCount(rightMarker);
  3732. DrawComparisonHistogram(100, minValue, maxValue, bucketCount, leftBuckets, rightBuckets, leftFrameCount, rightFrameCount, leftMarker != null, rightMarker != null, displayUnits);
  3733. }
  3734. float plotWidth = 40 + GUI.skin.box.padding.horizontal;
  3735. float plotHeight = 100;
  3736. plotWidth /= 2.0f;
  3737. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, units);
  3738. DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileLeftView.analysis, leftMarker, minValue, maxValue,
  3739. UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft);
  3740. DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileRightView.analysis, rightMarker, minValue, maxValue,
  3741. UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight);
  3742. boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, minValue, maxValue,
  3743. string.Format("Min {0} for selected frames in the 2 data sets", unitName),
  3744. string.Format("Max {0} for selected frames in the 2 data sets", unitName));
  3745. EditorGUILayout.EndHorizontal();
  3746. GUILayout.Space(style.lineHeight);
  3747. DrawComparisonSelectedStats(leftMarker, rightMarker);
  3748. EditorGUILayout.EndVertical();
  3749. m_2D.ClearClipRect();
  3750. GUILayout.EndScrollView();
  3751. }
  3752. else
  3753. {
  3754. EditorGUILayout.LabelField("Marker not in selection");
  3755. }
  3756. }
  3757. }
  3758. else
  3759. {
  3760. EditorGUILayout.LabelField("No marker data selected");
  3761. }
  3762. EditorGUILayout.EndVertical();
  3763. }
  3764. if (m_ShowMarkerSummary != lastMarkerSummary)
  3765. {
  3766. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(), m_ShowMarkerSummary);
  3767. }
  3768. EditorGUILayout.EndVertical();
  3769. }
  3770. void SelectTab(ActiveTab newTab)
  3771. {
  3772. m_NextActiveTab = newTab;
  3773. }
  3774. static readonly ProfilerMarkerAbstracted m_DrawToolbarProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawToolbar");
  3775. void DrawToolbar()
  3776. {
  3777. using (m_DrawToolbarProfilerMarker.Auto())
  3778. {
  3779. EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
  3780. EditorGUILayout.LabelField("Mode:", GUILayout.Width(40));
  3781. ActiveTab newTab = (ActiveTab)GUILayout.Toolbar((int)m_ActiveTab, new string[] {"Single", "Compare"},
  3782. EditorStyles.toolbarButton, GUILayout.ExpandWidth(false));
  3783. if (newTab != m_ActiveTab)
  3784. {
  3785. SelectTab(newTab);
  3786. }
  3787. //GUILayout.FlexibleSpace();
  3788. EditorGUILayout.Separator();
  3789. bool lastEnabled = GUI.enabled;
  3790. bool enabled = GUI.enabled;
  3791. if (m_ProfileSingleView.IsDataValid() ||
  3792. (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid()))
  3793. GUI.enabled = true;
  3794. else
  3795. GUI.enabled = false;
  3796. if (GUILayout.Button(Styles.export, EditorStyles.toolbarButton, GUILayout.Width(50)))
  3797. {
  3798. Vector2 windowPosition = new Vector2(Event.current.mousePosition.x,
  3799. Event.current.mousePosition.y + GUI.skin.label.lineHeight);
  3800. Vector2 screenPosition = GUIUtility.GUIToScreenPoint(windowPosition);
  3801. ProfileAnalyzerExportWindow.Open(screenPosition.x, screenPosition.y, m_ProfileSingleView,
  3802. m_ProfileLeftView, m_ProfileRightView, this);
  3803. EditorGUIUtility.ExitGUI();
  3804. }
  3805. GUI.enabled = lastEnabled;
  3806. bool profilerOpen = IsProfilerWindowOpen();
  3807. if (!profilerOpen)
  3808. {
  3809. if (GUILayout.Toggle(profilerOpen, "Open Profiler Window", EditorStyles.toolbarButton,
  3810. GUILayout.ExpandWidth(false)) == true)
  3811. {
  3812. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3813. m_ProfilerWindowInterface.OpenProfilerOrUseExisting();
  3814. ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.OpenProfiler,
  3815. analytic);
  3816. EditorGUIUtility.ExitGUI();
  3817. }
  3818. }
  3819. else
  3820. {
  3821. if (GUILayout.Toggle(profilerOpen, "Close Profiler Window", EditorStyles.toolbarButton,
  3822. GUILayout.ExpandWidth(false)) == false)
  3823. {
  3824. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  3825. m_ProfilerWindowInterface.CloseProfiler();
  3826. ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.CloseProfiler,
  3827. analytic);
  3828. EditorGUIUtility.ExitGUI();
  3829. }
  3830. }
  3831. EditorGUILayout.Separator();
  3832. GUILayout.FlexibleSpace();
  3833. EditorGUILayout.EndHorizontal();
  3834. }
  3835. }
  3836. void SetupStyles()
  3837. {
  3838. if (!m_StylesSetup)
  3839. {
  3840. m_StyleMiddleRight = new GUIStyle(GUI.skin.label);
  3841. m_StyleMiddleRight.alignment = TextAnchor.MiddleRight;
  3842. m_StyleUpperLeft = new GUIStyle(GUI.skin.label);
  3843. m_StyleUpperLeft.alignment = TextAnchor.UpperLeft;
  3844. m_StylesSetup = true;
  3845. }
  3846. }
  3847. static readonly ProfilerMarkerAbstracted m_DrawProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.Draw");
  3848. void Draw()
  3849. {
  3850. // Make sure we start enabled (in case something overrode it last frame)
  3851. GUI.enabled = true;
  3852. using (m_DrawProfilerMarker.Auto())
  3853. {
  3854. SetupStyles();
  3855. EditorGUILayout.BeginVertical();
  3856. DrawToolbar();
  3857. switch (m_ActiveTab)
  3858. {
  3859. case ActiveTab.Summary:
  3860. DrawAnalysis();
  3861. break;
  3862. case ActiveTab.Compare:
  3863. DrawComparison();
  3864. break;
  3865. }
  3866. EditorGUILayout.EndVertical();
  3867. }
  3868. }
  3869. int FindSelectionByName(List<MarkerData> markers, string name)
  3870. {
  3871. int index = 0;
  3872. foreach (var marker in markers)
  3873. {
  3874. if (marker.name == name)
  3875. return index;
  3876. index++;
  3877. }
  3878. return -1; // not found
  3879. }
  3880. /// <summary>
  3881. /// Select marker to focus on
  3882. /// </summary>
  3883. /// <param name="name">Name of the marker</param>
  3884. // Version 1.0 of the package exposed this API so we can't remove it until we increment the major package version.
  3885. public void SelectMarker(string name)
  3886. {
  3887. SelectMarker(name, null, null);
  3888. }
  3889. void SelectMarker(string name, string threadGroupName = null, string threadName = null)
  3890. {
  3891. switch (m_ActiveTab)
  3892. {
  3893. case ActiveTab.Summary:
  3894. SelectMarkerByName(name, threadGroupName, threadName);
  3895. break;
  3896. case ActiveTab.Compare:
  3897. SelectPairingByName(name, threadGroupName, threadName);
  3898. break;
  3899. }
  3900. }
  3901. void UpdateSelectedMarkerName(string markerName)
  3902. {
  3903. m_SelectedMarker.name = markerName;
  3904. // only update the Profiler Window if it wasn't updated successfully with this marker yet.
  3905. if (m_LastMarkerSuccesfullySyncedWithProfilerWindow == markerName)
  3906. return;
  3907. var updatedSelectedSampleSuccesfully = false;
  3908. if (m_ProfilerWindowInterface.IsReady() && !m_SelectionEventFromProfilerWindowInProgress && m_ThreadSelection.selection != null && m_ThreadSelection.selection.Count > 0)
  3909. {
  3910. updatedSelectedSampleSuccesfully = m_ProfilerWindowInterface.SetProfilerWindowMarkerName(markerName, m_ThreadSelection.selection);
  3911. }
  3912. if (updatedSelectedSampleSuccesfully)
  3913. m_LastMarkerSuccesfullySyncedWithProfilerWindow = markerName;
  3914. }
  3915. internal void SelectMarkerByIndex(int index, string markerNameFallback = null, string threadGroupName = null, string threadName = null)
  3916. {
  3917. if (m_ProfileSingleView == null || m_ProfileSingleView.analysis == null)
  3918. return;
  3919. // Check if this marker is in the 'filtered' list
  3920. var markers = m_ProfileSingleView.analysis.GetMarkers();
  3921. if (markers.Count <= 0)
  3922. return;
  3923. bool valid = true;
  3924. if (index >= 0 && index < markers.Count)
  3925. {
  3926. var marker = markers[index];
  3927. var nameFilters = GetNameFilters();
  3928. if (nameFilters.Count > 0)
  3929. {
  3930. if (!NameInFilterList(marker.name, nameFilters))
  3931. valid = false;
  3932. }
  3933. var nameExcludes = GetNameExcludes();
  3934. if (nameExcludes.Count > 0)
  3935. {
  3936. if (NameInExcludeList(marker.name, nameExcludes))
  3937. valid = false;
  3938. }
  3939. }
  3940. m_SelectedMarker.id = index;
  3941. if (m_ProfileTable != null)
  3942. {
  3943. List<int> selection = new List<int>();
  3944. if (index >= 0 && valid)
  3945. selection.Add(index);
  3946. m_ProfileTable.SetSelection(selection, TreeViewSelectionOptions.RevealAndFrame);
  3947. }
  3948. var markerName = GetMarkerName(index);
  3949. if (index == -1 && !string.IsNullOrEmpty(markerNameFallback))
  3950. {
  3951. markerName = markerNameFallback;
  3952. if (!string.IsNullOrEmpty(threadName))
  3953. {
  3954. m_SelectedMarker.threadGroupName = threadGroupName;
  3955. m_SelectedMarker.threadName = threadName;
  3956. }
  3957. else
  3958. {
  3959. m_SelectedMarker.threadGroupName = null;
  3960. m_SelectedMarker.threadName = null;
  3961. }
  3962. }
  3963. else
  3964. {
  3965. m_SelectedMarker.threadGroupName = null;
  3966. m_SelectedMarker.threadName = null;
  3967. }
  3968. if (markerName != null)
  3969. UpdateSelectedMarkerName(markerName);
  3970. }
  3971. /// <summary>
  3972. /// Get currently selected marker
  3973. /// </summary>
  3974. /// <returns>Name of currently selected marker, or null if none selected</returns>
  3975. public string GetSelectedMarkerName()
  3976. {
  3977. switch (m_ActiveTab)
  3978. {
  3979. case ActiveTab.Summary:
  3980. return GetMarkerName(m_SelectedMarker.id);
  3981. case ActiveTab.Compare:
  3982. return GetPairingName(m_SelectedPairing);
  3983. }
  3984. return null;
  3985. }
  3986. string GetMarkerName(int index)
  3987. {
  3988. if (m_ProfileSingleView.analysis == null)
  3989. return null;
  3990. var marker = m_ProfileSingleView.analysis.GetMarker(index);
  3991. if (marker == null)
  3992. return null;
  3993. return marker.name;
  3994. }
  3995. void SelectMarkerByName(string markerName, string threadGroupName = null, string threadName = null)
  3996. {
  3997. int index = (m_ProfileSingleView.analysis != null) ? m_ProfileSingleView.analysis.GetMarkerIndexByName(markerName) : -1;
  3998. SelectMarkerByIndex(index, markerName, threadGroupName, threadName);
  3999. }
  4000. internal void SelectPairing(int index, string threadGroupName = null, string threadName = null)
  4001. {
  4002. if (m_Pairings == null || m_Pairings.Count == 0)
  4003. return;
  4004. // Check if this marker is in the 'filtered' list
  4005. bool valid = true;
  4006. if (index >= 0 && index < m_Pairings.Count)
  4007. {
  4008. var pairing = m_Pairings[index];
  4009. var nameFilters = GetNameFilters();
  4010. if (nameFilters.Count > 0)
  4011. {
  4012. if (!NameInFilterList(pairing.name, nameFilters))
  4013. valid = false;
  4014. }
  4015. var nameExcludes = GetNameExcludes();
  4016. if (nameExcludes.Count > 0)
  4017. {
  4018. if (NameInExcludeList(pairing.name, nameExcludes))
  4019. valid = false;
  4020. }
  4021. }
  4022. m_SelectedPairing = index;
  4023. if (m_ComparisonTable != null)
  4024. {
  4025. List<int> selection = new List<int>();
  4026. if (index >= 0 && valid)
  4027. selection.Add(index);
  4028. m_ComparisonTable.SetSelection(selection, TreeViewSelectionOptions.RevealAndFrame);
  4029. }
  4030. var markerName = GetPairingName(index);
  4031. if (markerName != null)
  4032. UpdateSelectedMarkerName(markerName);
  4033. }
  4034. string GetPairingName(int index)
  4035. {
  4036. if (m_Pairings == null)
  4037. return null;
  4038. if (index < 0 || index >= m_Pairings.Count)
  4039. return null;
  4040. return m_Pairings[index].name;
  4041. }
  4042. void SelectPairingByName(string pairingName, string threadGroupName = null, string threadName = null)
  4043. {
  4044. if (m_Pairings != null && pairingName != null)
  4045. {
  4046. for (int index = 0; index < m_Pairings.Count; index++)
  4047. {
  4048. var pairing = m_Pairings[index];
  4049. if (pairing.name == pairingName)
  4050. {
  4051. SelectPairing(index, threadGroupName, threadName);
  4052. return;
  4053. }
  4054. }
  4055. }
  4056. SelectPairing(-1, threadGroupName, threadName);
  4057. }
  4058. GUIContent GetFrameCountText(ProfileDataView context)
  4059. {
  4060. var frameSummary = context.analysis.GetFrameSummary();
  4061. string text;
  4062. string tooltip;
  4063. if (frameSummary.first == frameSummary.last)
  4064. {
  4065. text = string.Format("{0}", frameSummary.count);
  4066. tooltip = "";
  4067. }
  4068. else
  4069. {
  4070. int rangeSize = (1 + (frameSummary.last - frameSummary.first));
  4071. if (frameSummary.count == rangeSize)
  4072. {
  4073. text = string.Format("{0}", frameSummary.count);
  4074. tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence"
  4075. , frameSummary.count
  4076. , GetRemappedUIFrameIndex(frameSummary.first, context)
  4077. , GetRemappedUIFrameIndex(frameSummary.last, context));
  4078. }
  4079. else
  4080. {
  4081. text = string.Format("{0}*", frameSummary.count);
  4082. var ranges = RangesText(context);
  4083. tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges);
  4084. }
  4085. }
  4086. return new GUIContent(text, tooltip);
  4087. }
  4088. string RangesText(ProfileDataView context)
  4089. {
  4090. var sortedFrames = context.analysis.GetFrameSummary().frames.OrderBy(x => x.frameIndex).ToArray();
  4091. var ranges = "";
  4092. int lastAdded = GetRemappedUIFrameIndex(sortedFrames[0].frameIndex, context);
  4093. ranges += lastAdded;
  4094. for (int n = 1; n < sortedFrames.Length; ++n)
  4095. {
  4096. if (sortedFrames[n].frameIndex == (sortedFrames[n - 1].frameIndex + 1)) continue;
  4097. int nIdx = GetRemappedUIFrameIndex(sortedFrames[n].frameIndex, context);
  4098. int pNIdx = GetRemappedUIFrameIndex(sortedFrames[n - 1].frameIndex, context);
  4099. if (lastAdded == pNIdx)
  4100. {
  4101. ranges += ", " + nIdx;
  4102. }
  4103. else
  4104. {
  4105. ranges += "-" + pNIdx + ", " + nIdx;
  4106. }
  4107. lastAdded = nIdx;
  4108. }
  4109. int remappedLastFrame = GetRemappedUIFrameIndex(sortedFrames.Last().frameIndex, context);
  4110. if (lastAdded == remappedLastFrame)
  4111. return ranges;
  4112. ranges += "-" + remappedLastFrame;
  4113. return ranges;
  4114. }
  4115. GUIContent GetFirstFrameText(ProfileDataView context)
  4116. {
  4117. var frameSummary = context.analysis.GetFrameSummary();
  4118. string text;
  4119. string tooltip;
  4120. if (frameSummary.count == 0)
  4121. {
  4122. text = "";
  4123. tooltip = "";
  4124. }
  4125. else if (frameSummary.first == frameSummary.last)
  4126. {
  4127. int remappedFrame = GetRemappedUIFrameIndex(frameSummary.first, context);
  4128. text = string.Format("{0}", remappedFrame);
  4129. tooltip = string.Format("Frame {0} selected", remappedFrame);
  4130. }
  4131. else
  4132. {
  4133. int rangeSize = (1 + (frameSummary.last - frameSummary.first));
  4134. if (frameSummary.count == rangeSize)
  4135. {
  4136. int remappedFirstFrame = GetRemappedUIFrameIndex(frameSummary.first, context);
  4137. text = string.Format("{0}", remappedFirstFrame);
  4138. tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence"
  4139. , frameSummary.count
  4140. , remappedFirstFrame
  4141. , GetRemappedUIFrameIndex(frameSummary.last, context));
  4142. }
  4143. else
  4144. {
  4145. text = string.Format("{0}*", GetRemappedUIFrameIndex(frameSummary.first, context));
  4146. var ranges = RangesText(context);
  4147. tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges);
  4148. }
  4149. }
  4150. return new GUIContent(text, tooltip);
  4151. }
  4152. GUIContent GetLastFrameText(ProfileDataView context)
  4153. {
  4154. var frameSummary = context.analysis.GetFrameSummary();
  4155. string text;
  4156. string tooltip;
  4157. if (frameSummary.count == 0)
  4158. {
  4159. text = "";
  4160. tooltip = "";
  4161. }
  4162. else if (frameSummary.first == frameSummary.last)
  4163. {
  4164. text = "";
  4165. tooltip = string.Format("Frame {0} selected", GetRemappedUIFrameIndex(frameSummary.first, context), context);
  4166. }
  4167. else
  4168. {
  4169. int rangeSize = (1 + (frameSummary.last - frameSummary.first));
  4170. if (frameSummary.count == rangeSize)
  4171. {
  4172. int remappedLastFrame = GetRemappedUIFrameIndex(frameSummary.last, context);
  4173. text = string.Format("{0}", remappedLastFrame);
  4174. tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence",
  4175. frameSummary.count,
  4176. GetRemappedUIFrameIndex(frameSummary.first, context),
  4177. remappedLastFrame);
  4178. }
  4179. else
  4180. {
  4181. text = string.Format("{0}*", GetRemappedUIFrameIndex(frameSummary.last, context));
  4182. var ranges = RangesText(context);
  4183. tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges);;
  4184. }
  4185. }
  4186. return new GUIContent(text, tooltip);
  4187. }
  4188. void DrawFrameSummary()
  4189. {
  4190. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  4191. bool lastShowFrameSummary = m_ShowFrameSummary;
  4192. m_ShowFrameSummary = BoldFoldout(m_ShowFrameSummary, Styles.frameSummary);
  4193. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  4194. if (m_ShowFrameSummary)
  4195. {
  4196. EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present
  4197. if (IsAnalysisValid())
  4198. {
  4199. var frameSummary = m_ProfileSingleView.analysis.GetFrameSummary();
  4200. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4201. m_Columns.Draw(0, "");
  4202. m_Columns.Draw2(Styles.frameCount, GetFrameCountText(m_ProfileSingleView));
  4203. EditorGUILayout.BeginHorizontal();
  4204. m_Columns.Draw(0, Styles.frameStart);
  4205. GUIContent firstFrameTextContent = GetFirstFrameText(m_ProfileSingleView);
  4206. m_Columns.Draw(1, firstFrameTextContent);
  4207. if (firstFrameTextContent.text != "")
  4208. DrawFrameIndexButton(frameSummary.first, m_ProfileSingleView);
  4209. EditorGUILayout.EndHorizontal();
  4210. EditorGUILayout.BeginHorizontal();
  4211. m_Columns.Draw(0, Styles.frameEnd);
  4212. GUIContent lastFrameTextContent = GetLastFrameText(m_ProfileSingleView);
  4213. m_Columns.Draw(1, lastFrameTextContent);
  4214. if (lastFrameTextContent.text != "")
  4215. DrawFrameIndexButton(frameSummary.last, m_ProfileSingleView);
  4216. EditorGUILayout.EndHorizontal();
  4217. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4218. m_Columns.Draw(0, "");
  4219. m_Columns.Draw3("", GetDisplayUnits(), "Frame");
  4220. Draw3LabelMsFrame(Styles.max, frameSummary.msMax, frameSummary.maxFrameIndex, m_ProfileSingleView);
  4221. Draw2LabelMs(Styles.upperQuartile, frameSummary.msUpperQuartile);
  4222. Draw3LabelMsFrame(Styles.median, frameSummary.msMedian, frameSummary.medianFrameIndex, m_ProfileSingleView);
  4223. Draw2LabelMs(Styles.mean, frameSummary.msMean);
  4224. Draw2LabelMs(Styles.lowerQuartile, frameSummary.msLowerQuartile);
  4225. Draw3LabelMsFrame(Styles.min, frameSummary.msMin, frameSummary.minFrameIndex, m_ProfileSingleView);
  4226. GUIStyle style = GUI.skin.label;
  4227. GUILayout.Space(style.lineHeight);
  4228. EditorGUILayout.BeginHorizontal();
  4229. Histogram histogram = new Histogram(m_2D, m_DisplayUnits.Units);
  4230. histogram.Draw(LayoutSize.HistogramWidth, 40, frameSummary.buckets, frameSummary.count, 0, frameSummary.msMax, UIColor.bar);
  4231. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units);
  4232. float plotWidth = 40 + GUI.skin.box.padding.horizontal;
  4233. float plotHeight = 40;
  4234. boxAndWhiskerPlot.Draw(plotWidth, plotHeight, frameSummary.msMin, frameSummary.msLowerQuartile, frameSummary.msMedian, frameSummary.msUpperQuartile, frameSummary.msMax, 0, frameSummary.msMax, UIColor.standardLine, UIColor.standardLine);
  4235. boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, frameSummary.msMin, frameSummary.msMax,
  4236. "Min frame time for selected frames",
  4237. "Max frame time for selected frames");
  4238. EditorGUILayout.EndHorizontal();
  4239. }
  4240. else
  4241. {
  4242. EditorGUILayout.LabelField("No analysis data selected");
  4243. }
  4244. EditorGUILayout.EndVertical();
  4245. }
  4246. if (m_ShowFrameSummary != lastShowFrameSummary)
  4247. {
  4248. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Frames, analytic.GetDurationInSeconds(), m_ShowFrameSummary);
  4249. }
  4250. EditorGUILayout.EndVertical();
  4251. }
  4252. GUIContent GetThreadNameWithGroupTooltip(string threadNameWithIndex, bool singleThread)
  4253. {
  4254. string friendlyThreadName = GetFriendlyThreadName(threadNameWithIndex, singleThread);
  4255. string groupName;
  4256. friendlyThreadName = ProfileData.GetThreadNameWithoutGroup(friendlyThreadName, out groupName);
  4257. if (groupName == "")
  4258. return new GUIContent(friendlyThreadName, string.Format("{0}", friendlyThreadName));
  4259. else
  4260. return new GUIContent(friendlyThreadName, string.Format("{0}\n{1}", friendlyThreadName, groupName));
  4261. }
  4262. void DrawThreadSummary()
  4263. {
  4264. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  4265. bool lastShowThreadSummary = m_ShowThreadSummary;
  4266. m_ShowThreadSummary = BoldFoldout(m_ShowThreadSummary, Styles.threadSummary);
  4267. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  4268. if (m_ShowThreadSummary)
  4269. {
  4270. EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present
  4271. if (IsAnalysisValid())
  4272. {
  4273. float xAxisMin = 0.0f;
  4274. float xAxisMax = GetThreadTimeRange(m_ProfileSingleView.analysis);
  4275. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4276. EditorGUILayout.BeginHorizontal();
  4277. m_Columns.Draw4("", "", "", "");
  4278. EditorGUILayout.EndHorizontal();
  4279. EditorGUILayout.BeginHorizontal();
  4280. m_Columns.Draw(0, "Total Count : ");
  4281. m_Columns.Draw(1, m_ProfileSingleView.data.GetThreadCount().ToString());
  4282. EditorGUILayout.EndHorizontal();
  4283. List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection);
  4284. int selectedThreads = threadSelection.Count;
  4285. EditorGUILayout.BeginHorizontal();
  4286. m_Columns.Draw(0, "Selected : ");
  4287. m_Columns.Draw(1, selectedThreads.ToString());
  4288. EditorGUILayout.EndHorizontal();
  4289. ShowThreadRange();
  4290. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2 + LayoutSize.WidthColumn3, 0);
  4291. m_Columns.Draw3("", "Median", "Thread");
  4292. m_ThreadScroll = EditorGUILayout.BeginScrollView(m_ThreadScroll, GUIStyle.none, GUI.skin.verticalScrollbar);
  4293. Rect clipRect = new Rect(m_ThreadScroll.x, m_ThreadScroll.y, m_ThreadsAreaRect.width, m_ThreadsAreaRect.height);
  4294. m_2D.SetClipRect(clipRect);
  4295. for (int i = 0; i < m_ThreadUINames.Count; i++)
  4296. {
  4297. string threadNameWithIndex = m_ThreadNames[i];
  4298. if (!threadNameWithIndex.Contains(":"))
  4299. continue; // Ignore 'All'
  4300. bool include = ProfileAnalyzer.MatchThreadFilter(threadNameWithIndex, m_ThreadSelection.selection);
  4301. if (!include)
  4302. continue;
  4303. ThreadData thread = m_ProfileSingleView.analysis.GetThreadByName(threadNameWithIndex);
  4304. if (thread == null) // May be the 'all' field
  4305. continue;
  4306. bool singleThread = thread.threadsInGroup > 1 ? false : true;
  4307. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units);
  4308. EditorGUILayout.BeginHorizontal();
  4309. boxAndWhiskerPlot.DrawHorizontal(100, GUI.skin.label.lineHeight, thread.msMin, thread.msLowerQuartile, thread.msMedian, thread.msUpperQuartile, thread.msMax, xAxisMin, xAxisMax, UIColor.bar, UIColor.barBackground, GUI.skin.label);
  4310. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(thread.msMedian));
  4311. m_Columns.Draw(2, GetThreadNameWithGroupTooltip(thread.threadNameWithIndex, singleThread));
  4312. EditorGUILayout.EndHorizontal();
  4313. }
  4314. m_2D.ClearClipRect();
  4315. EditorGUILayout.EndScrollView();
  4316. if (Event.current.type == EventType.Repaint)
  4317. {
  4318. // This value is not valid at layout phase
  4319. m_ThreadsAreaRect = GUILayoutUtility.GetLastRect();
  4320. }
  4321. }
  4322. else
  4323. {
  4324. EditorGUILayout.LabelField("No analysis data selected");
  4325. }
  4326. EditorGUILayout.EndVertical();
  4327. }
  4328. if (m_ShowThreadSummary != lastShowThreadSummary)
  4329. {
  4330. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Threads, analytic.GetDurationInSeconds(), m_ShowThreadSummary);
  4331. }
  4332. EditorGUILayout.EndVertical();
  4333. }
  4334. void DrawHistogramForMarker(Histogram histogram, MarkerData marker)
  4335. {
  4336. if (DisplayCount())
  4337. histogram.Draw(LayoutSize.HistogramWidth, 100, marker.countBuckets, marker.presentOnFrameCount, marker.countMin, marker.countMax, UIColor.bar);
  4338. else
  4339. histogram.Draw(LayoutSize.HistogramWidth, 100, marker.buckets, marker.presentOnFrameCount, marker.msMin, marker.msMax, UIColor.bar);
  4340. }
  4341. internal bool IsProfilerWindowOpen()
  4342. {
  4343. return m_ProfilerWindowInterface.IsReady();
  4344. }
  4345. /// <summary>
  4346. /// Used to remap frame indices when the loaded range in the profiler does not match the range present in the Profile Analyzer capture.
  4347. /// This happens when we reload data into the Profiler Window as the index range becomes 1 -> n+1
  4348. /// </summary>
  4349. /// <param name="frameIndex">target frame index</param>
  4350. /// <param name="frameIndexOffset">capture frameIndex offset</param>
  4351. /// <returns></returns>
  4352. internal int RemapFrameIndex(int frameIndex, int frameIndexOffset)
  4353. {
  4354. if (m_ProfilerFirstFrameIndex == 1 && frameIndex > frameIndexOffset)
  4355. return frameIndex - frameIndexOffset;
  4356. else
  4357. return frameIndex;
  4358. }
  4359. internal void JumpToFrame(int frameIndex, ProfileData frameContext, bool reportErrors = true)
  4360. {
  4361. if (!m_ProfilerWindowInterface.IsReady())
  4362. return;
  4363. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  4364. m_ProfilerWindowInterface.JumpToFrame(RemapFrameIndex(frameIndex, frameContext.FrameIndexOffset));
  4365. if (!string.IsNullOrEmpty(m_SelectedMarker.name))
  4366. m_ProfilerWindowInterface.SetProfilerWindowMarkerName(m_SelectedMarker.name, m_ThreadSelection.selection);
  4367. ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.JumpToFrame, analytic);
  4368. }
  4369. internal float DrawFrameIndexButton(int frameIndex, ProfileDataView frameContext)
  4370. {
  4371. float defaultWidth = 50f;
  4372. if (frameIndex < 0)
  4373. return defaultWidth;
  4374. bool enabled = GUI.enabled;
  4375. if (!IsProfilerWindowOpen() || !frameContext.inSyncWithProfilerData)
  4376. GUI.enabled = false;
  4377. var remappedIndex = GetRemappedUIFrameIndex(frameIndex, frameContext);
  4378. var content = new GUIContent(string.Format("{0}", remappedIndex), string.Format("Jump to frame {0} in the Unity Profiler", remappedIndex));
  4379. Vector2 size = GUI.skin.button.CalcSize(content);
  4380. //float height = size.y;
  4381. float maxWidth = Math.Max(defaultWidth, size.x);
  4382. if (GUILayout.Button(content, GUILayout.MinWidth(defaultWidth), GUILayout.MaxWidth(maxWidth)))
  4383. {
  4384. JumpToFrame(frameIndex, frameContext.data);
  4385. }
  4386. GUI.enabled = enabled;
  4387. return maxWidth;
  4388. }
  4389. internal void DrawFrameIndexButton(Rect rect, int frameIndex, ProfileDataView frameContext)
  4390. {
  4391. if (frameIndex < 0)
  4392. return;
  4393. bool enabled = GUI.enabled;
  4394. if (!IsProfilerWindowOpen() || !frameContext.inSyncWithProfilerData)
  4395. GUI.enabled = false;
  4396. // Clamp to max height to match other buttons
  4397. // And centre vertically if needed
  4398. var remappedIndex = GetRemappedUIFrameIndex(frameIndex, frameContext);
  4399. var content = new GUIContent(string.Format("{0}", remappedIndex), string.Format("Jump to frame {0} in the Unity Profiler", remappedIndex));
  4400. Vector2 size = GUI.skin.button.CalcSize(content);
  4401. float height = size.y; // was 14
  4402. rect.y += (rect.height - height) / 2;
  4403. rect.height = Math.Min(rect.height, height);
  4404. if (GUI.Button(rect, content))
  4405. {
  4406. JumpToFrame(frameIndex, frameContext.data);
  4407. }
  4408. GUI.enabled = enabled;
  4409. }
  4410. void Draw3LabelMsFrame(GUIContent col1, float ms, int frameIndex, ProfileDataView frameContext)
  4411. {
  4412. EditorGUILayout.BeginHorizontal();
  4413. m_Columns.Draw(0, col1);
  4414. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(ms));
  4415. DrawFrameIndexButton(frameIndex, frameContext);
  4416. EditorGUILayout.EndHorizontal();
  4417. }
  4418. void Draw2LabelMs(GUIContent col1, float ms)
  4419. {
  4420. EditorGUILayout.BeginHorizontal();
  4421. m_Columns.Draw(0, col1);
  4422. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(ms));
  4423. EditorGUILayout.EndHorizontal();
  4424. }
  4425. void Draw4DiffMs(GUIContent col1, float msLeft, float msRight)
  4426. {
  4427. EditorGUILayout.BeginHorizontal();
  4428. m_Columns.Draw(0, col1);
  4429. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(msLeft));
  4430. m_Columns.Draw(2, ToDisplayUnitsWithTooltips(msRight));
  4431. m_Columns.Draw(3, ToDisplayUnitsWithTooltips(msRight - msLeft));
  4432. EditorGUILayout.EndHorizontal();
  4433. }
  4434. void Draw4DiffMs(GUIContent col1, float msLeft, int frameIndexLeft, float msRight, int frameIndexRight)
  4435. {
  4436. EditorGUILayout.BeginHorizontal();
  4437. m_Columns.Draw(0, col1);
  4438. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(msLeft, false, frameIndexLeft));
  4439. m_Columns.Draw(2, ToDisplayUnitsWithTooltips(msRight, false, frameIndexRight));
  4440. m_Columns.Draw(3, ToDisplayUnitsWithTooltips(msRight - msLeft));
  4441. EditorGUILayout.EndHorizontal();
  4442. }
  4443. void Draw4Ms(GUIContent col1, float value2, float value3, float value4)
  4444. {
  4445. EditorGUILayout.BeginHorizontal();
  4446. m_Columns.Draw(0, col1);
  4447. m_Columns.Draw(1, ToDisplayUnitsWithTooltips(value2));
  4448. m_Columns.Draw(2, ToDisplayUnitsWithTooltips(value3));
  4449. m_Columns.Draw(3, ToDisplayUnitsWithTooltips(value4));
  4450. EditorGUILayout.EndHorizontal();
  4451. }
  4452. void DrawBoxAndWhiskerPlotForMarker(BoxAndWhiskerPlot boxAndWhiskerPlot, float width, float height, ProfileAnalysis analysis, MarkerData marker, float yAxisStart, float yAxisEnd, Color color, Color colorBackground)
  4453. {
  4454. if (marker == null)
  4455. {
  4456. boxAndWhiskerPlot.Draw(width, height, 0, 0, 0, 0, 0, yAxisStart, yAxisEnd, color, colorBackground);
  4457. return;
  4458. }
  4459. if (DisplayCount())
  4460. boxAndWhiskerPlot.Draw(width, height, marker.countMin, marker.countLowerQuartile, marker.countMedian, marker.countUpperQuartile, marker.countMax, yAxisStart, yAxisEnd, color, colorBackground);
  4461. else
  4462. boxAndWhiskerPlot.Draw(width, height, marker.msMin, marker.msLowerQuartile, marker.msMedian, marker.msUpperQuartile, marker.msMax, yAxisStart, yAxisEnd, color, colorBackground);
  4463. }
  4464. void DrawBoxAndWhiskerPlotHorizontalForMarker(BoxAndWhiskerPlot boxAndWhiskerPlot, float width, float height, ProfileAnalysis analysis, MarkerData marker, float yAxisStart, float yAxisEnd, Color color, Color colorBackground)
  4465. {
  4466. boxAndWhiskerPlot.DrawHorizontal(width, height, marker.msMin, marker.msLowerQuartile, marker.msMedian, marker.msUpperQuartile, marker.msMax, yAxisStart, yAxisEnd, color, colorBackground);
  4467. }
  4468. void DrawFrameRatio(MarkerData marker)
  4469. {
  4470. var frameSummary = m_ProfileSingleView.analysis.GetFrameSummary();
  4471. GUIStyle style = GUI.skin.label;
  4472. float w = LayoutSize.WidthColumn0;
  4473. float h = style.lineHeight;
  4474. float ySpacing = 2;
  4475. float barHeight = h - ySpacing;
  4476. EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2));
  4477. float barMax = frameSummary.msMean;
  4478. float barValue = marker.msMean;
  4479. string text = "Mean frame contribution";
  4480. Units units = m_DisplayUnits.Units;
  4481. if (DisplayCount())
  4482. {
  4483. units = Units.Count;
  4484. barMax = frameSummary.markerCountMaxMean;
  4485. barValue = marker.countMean;
  4486. text = "Mean count";
  4487. }
  4488. DisplayUnits displayUnits = new DisplayUnits(units);
  4489. float barLength = Math.Min((w * barValue) / barMax, w);
  4490. EditorGUILayout.LabelField(text);
  4491. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4492. m_Columns.Draw2("", "");
  4493. EditorGUILayout.BeginHorizontal();
  4494. // NOTE: This can effect the whole width of the region its inside
  4495. // Not clear why
  4496. if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style))
  4497. {
  4498. m_2D.DrawFilledBox(0, ySpacing, barLength, barHeight, UIColor.bar);
  4499. m_2D.DrawFilledBox(barLength, ySpacing, w - barLength, barHeight, UIColor.barBackground);
  4500. m_2D.DrawEnd();
  4501. Rect rect = GUILayoutUtility.GetLastRect();
  4502. string tooltip = string.Format("{0}", displayUnits.ToString(barValue, true, 5));
  4503. GUI.Label(rect, new GUIContent("", tooltip));
  4504. }
  4505. EditorGUILayout.LabelField(ShowPercent((100 * barValue) / barMax), GUILayout.MaxWidth(50));
  4506. EditorGUILayout.EndHorizontal();
  4507. EditorGUILayout.EndVertical();
  4508. }
  4509. GUIContent ShowPercent(float percent)
  4510. {
  4511. string text;
  4512. string tooltip;
  4513. if (percent >= 999.95f)
  4514. {
  4515. text = string.Format("{0:f0}%", percent);
  4516. tooltip = string.Format("{0:f2}%", percent);
  4517. }
  4518. else if (percent >= 99.995f)
  4519. {
  4520. text = string.Format("{0:f1}%", percent);
  4521. tooltip = string.Format("{0:f2}%", percent);
  4522. }
  4523. else
  4524. {
  4525. text = string.Format("{0:f2}%", percent);
  4526. tooltip = text;
  4527. }
  4528. return new GUIContent(text, tooltip);
  4529. }
  4530. void DrawComparisonFrameRatio(MarkerData leftMarker, MarkerData rightMarker)
  4531. {
  4532. var leftFrameSummary = m_ProfileLeftView.analysis.GetFrameSummary();
  4533. var rightFrameSummary = m_ProfileRightView.analysis.GetFrameSummary();
  4534. GUIStyle style = GUI.skin.label;
  4535. float w = LayoutSize.WidthColumn0;
  4536. float h = style.lineHeight;
  4537. float ySpacing = 2;
  4538. float barHeight = (h - ySpacing) / 2;
  4539. EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2));
  4540. float leftBarValue = MarkerData.GetMsMean(leftMarker);
  4541. float rightBarValue = MarkerData.GetMsMean(rightMarker);
  4542. float leftBarMax = leftFrameSummary.msMean;
  4543. float rightBarMax = rightFrameSummary.msMean;
  4544. string text = "Mean frame contribution";
  4545. Units units = m_DisplayUnits.Units;
  4546. if (DisplayCount())
  4547. {
  4548. units = Units.Count;
  4549. leftBarValue = MarkerData.GetCountMean(leftMarker);
  4550. rightBarValue = MarkerData.GetCountMean(rightMarker);
  4551. leftBarMax = leftFrameSummary.markerCountMaxMean;
  4552. rightBarMax = rightFrameSummary.markerCountMaxMean;
  4553. text = "Mean count";
  4554. }
  4555. DisplayUnits displayUnits = new DisplayUnits(units);
  4556. float leftBarLength = (leftBarMax > 0) ? (w * leftBarValue) / leftBarMax : 0f;
  4557. leftBarLength = Math.Min(leftBarLength, w);
  4558. float rightBarLength = (rightBarMax > 0) ? (w * rightBarValue) / rightBarMax : 0f;
  4559. rightBarLength = Math.Min(rightBarLength, w);
  4560. EditorGUILayout.LabelField(text);
  4561. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4562. m_Columns.Draw4("", "Left", "Right", "Diff");
  4563. EditorGUILayout.BeginHorizontal();
  4564. if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style))
  4565. {
  4566. m_2D.DrawFilledBox(0, ySpacing, w, h - ySpacing, UIColor.barBackground);
  4567. m_2D.DrawFilledBox(0, ySpacing, leftBarLength, barHeight, UIColor.left);
  4568. m_2D.DrawFilledBox(0, ySpacing + barHeight, rightBarLength, barHeight, UIColor.right);
  4569. m_2D.DrawEnd();
  4570. Rect rect = GUILayoutUtility.GetLastRect();
  4571. string tooltip = string.Format("Left: {0}\nRight: {1}", displayUnits.ToTooltipString(leftBarValue, true), displayUnits.ToTooltipString(rightBarValue, true));
  4572. GUI.Label(rect, new GUIContent("", tooltip));
  4573. }
  4574. float leftPercentage = leftBarMax > 0 ? (100 * leftBarValue) / leftBarMax : 0f;
  4575. float rightPercentage = rightBarMax > 0 ? (100 * rightBarValue) / rightBarMax : 0f;
  4576. EditorGUILayout.LabelField(ShowPercent(leftPercentage), GUILayout.Width(LayoutSize.WidthColumn1));
  4577. EditorGUILayout.LabelField(ShowPercent(rightPercentage), GUILayout.Width(LayoutSize.WidthColumn2));
  4578. if (leftMarker != null && rightMarker != null)
  4579. EditorGUILayout.LabelField(ShowPercent(rightPercentage - leftPercentage), GUILayout.Width(LayoutSize.WidthColumn3));
  4580. EditorGUILayout.EndHorizontal();
  4581. EditorGUILayout.EndVertical();
  4582. }
  4583. void DrawTopComparison(MarkerData leftMarker, MarkerData rightMarker)
  4584. {
  4585. GUIStyle style = GUI.skin.label;
  4586. float w = LayoutSize.WidthColumn0;
  4587. float h = style.lineHeight;
  4588. float ySpacing = 2;
  4589. float barHeight = (h - ySpacing) / 2;
  4590. EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2));
  4591. bool showCount = DisplayCount();
  4592. float leftMax = MarkerData.GetMsMax(leftMarker);
  4593. float rightMax = MarkerData.GetMsMax(rightMarker);
  4594. Units units = m_DisplayUnits.Units;
  4595. if (showCount)
  4596. {
  4597. units = Units.Count;
  4598. leftMax = MarkerData.GetCountMax(leftMarker);
  4599. rightMax = MarkerData.GetCountMax(rightMarker);
  4600. }
  4601. DisplayUnits displayUnits = new DisplayUnits(units);
  4602. TopMarkerList topMarkerList = new TopMarkerList(m_2D, units,
  4603. LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3,
  4604. UIColor.bar, UIColor.barBackground, DrawFrameIndexButton);
  4605. m_TopNumber = topMarkerList.DrawTopNumber(m_TopNumber, m_TopStrings, m_TopValues);
  4606. float barMax = Math.Max(leftMax, rightMax);
  4607. List<FrameTime> leftFrames = leftMarker != null ? topMarkerList.GetTopN(leftMarker, m_TopNumber, showCount) : new List<FrameTime>();
  4608. List<FrameTime> rightFrames = rightMarker != null ? topMarkerList.GetTopN(rightMarker, m_TopNumber, showCount) : new List<FrameTime>();
  4609. FrameTime zeroFrameTime = new FrameTime(-1, 0.0f, 0);
  4610. for (int i = 0; i < m_TopNumber; i++)
  4611. {
  4612. bool leftValid = i < leftFrames.Count;
  4613. bool rightValid = i < rightFrames.Count;
  4614. FrameTime leftFrameTime = leftValid ? leftFrames[i] : zeroFrameTime;
  4615. FrameTime rightFrameTime = rightValid ? rightFrames[i] : zeroFrameTime;
  4616. float leftBarValue = showCount ? leftFrameTime.count : leftFrameTime.ms;
  4617. float rightBarValue = showCount ? rightFrameTime.count : rightFrameTime.ms;
  4618. float leftBarLength = Math.Min((w * leftBarValue) / barMax, w);
  4619. float rightBarLength = Math.Min((w * rightBarValue) / barMax, w);
  4620. EditorGUILayout.BeginHorizontal();
  4621. if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style))
  4622. {
  4623. if (leftValid || rightValid)
  4624. {
  4625. m_2D.DrawFilledBox(0, ySpacing, w, h - ySpacing, UIColor.barBackground);
  4626. m_2D.DrawFilledBox(0, ySpacing, leftBarLength, barHeight, UIColor.left);
  4627. m_2D.DrawFilledBox(0, ySpacing + barHeight, rightBarLength, barHeight, UIColor.right);
  4628. }
  4629. m_2D.DrawEnd();
  4630. Rect rect = GUILayoutUtility.GetLastRect();
  4631. string leftContent = leftValid ? displayUnits.ToTooltipString(leftBarValue, true, leftFrameTime.frameIndex) : "None";
  4632. string rightContent = rightValid ? displayUnits.ToTooltipString(rightBarValue, true, rightFrameTime.frameIndex) : "None";
  4633. GUI.Label(rect, new GUIContent("", string.Format("Left:\t{0}\nRight:\t{1}", leftContent, rightContent)));
  4634. }
  4635. EditorGUILayout.LabelField(leftValid ? displayUnits.ToGUIContentWithTooltips(leftBarValue, frameIndex: leftFrameTime.frameIndex) : Styles.emptyString, GUILayout.Width(LayoutSize.WidthColumn1));
  4636. EditorGUILayout.LabelField(rightValid ? displayUnits.ToGUIContentWithTooltips(rightBarValue, frameIndex: rightFrameTime.frameIndex) : Styles.emptyString, GUILayout.Width(LayoutSize.WidthColumn2));
  4637. if (leftValid || rightValid)
  4638. EditorGUILayout.LabelField(displayUnits.ToGUIContentWithTooltips(rightBarValue - leftBarValue), GUILayout.Width(LayoutSize.WidthColumn3));
  4639. EditorGUILayout.EndHorizontal();
  4640. }
  4641. EditorGUILayout.EndVertical();
  4642. }
  4643. void DrawSelectedStats(MarkerData marker, ProfileDataView markerContext)
  4644. {
  4645. GUIStyle style = GUI.skin.label;
  4646. m_Columns.Draw3("", GetDisplayUnits(), "Frame");
  4647. Draw3LabelMsFrame(Styles.max, marker.msMax, marker.maxFrameIndex, markerContext);
  4648. Draw2LabelMs(Styles.upperQuartile, marker.msUpperQuartile);
  4649. Draw3LabelMsFrame(Styles.median, marker.msMedian, marker.medianFrameIndex, markerContext);
  4650. Draw2LabelMs(Styles.mean, marker.msMean);
  4651. Draw2LabelMs(Styles.lowerQuartile, marker.msLowerQuartile);
  4652. Draw3LabelMsFrame(Styles.min, marker.msMin, marker.minFrameIndex, markerContext);
  4653. GUILayout.Space(style.lineHeight);
  4654. Draw3LabelMsFrame(Styles.individualMax, marker.msMaxIndividual,
  4655. marker.maxIndividualFrameIndex, markerContext);
  4656. Draw3LabelMsFrame(Styles.individualMin, marker.msMinIndividual,
  4657. marker.minIndividualFrameIndex, markerContext);
  4658. }
  4659. void DrawSelected()
  4660. {
  4661. EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS));
  4662. bool lastMarkerSummary = m_ShowMarkerSummary;
  4663. m_ShowMarkerSummary = BoldFoldout(m_ShowMarkerSummary, Styles.markerSummary);
  4664. var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
  4665. if (m_ShowMarkerSummary)
  4666. {
  4667. if (IsAnalysisValid())
  4668. {
  4669. List<MarkerData> markers = m_ProfileSingleView.analysis.GetMarkers();
  4670. if (markers != null)
  4671. {
  4672. int markerAt = m_SelectedMarker.id;
  4673. if (markerAt >= 0 && markerAt < markers.Count)
  4674. {
  4675. var marker = markers[markerAt];
  4676. m_MarkerSummaryScroll = GUILayout.BeginScrollView(m_MarkerSummaryScroll, GUIStyle.none, GUI.skin.verticalScrollbar);
  4677. Rect clipRect = new Rect(m_MarkerSummaryScroll.x, m_MarkerSummaryScroll.y, LayoutSize.WidthRHS, 500);
  4678. m_2D.SetClipRect(clipRect);
  4679. EditorGUILayout.BeginVertical();
  4680. EditorGUILayout.LabelField(marker.name,
  4681. GUILayout.MaxWidth(LayoutSize.WidthRHS -
  4682. (GUI.skin.box.padding.horizontal + GUI.skin.box.margin.horizontal)));
  4683. DrawFrameRatio(marker);
  4684. m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3);
  4685. EditorGUILayout.BeginHorizontal();
  4686. m_Columns.Draw(0, Styles.firstFrame);
  4687. m_Columns.Draw(1, Styles.emptyString);
  4688. DrawFrameIndexButton(marker.firstFrameIndex, m_ProfileSingleView);
  4689. EditorGUILayout.EndHorizontal();
  4690. GUIStyle style = GUI.skin.label;
  4691. float min = marker.msMin;
  4692. float max = marker.msMax;
  4693. string fieldString = "marker time";
  4694. Units units = m_DisplayUnits.Units;
  4695. if (DisplayCount())
  4696. {
  4697. min = marker.countMin;
  4698. max = marker.countMax;
  4699. fieldString = "count";
  4700. units = Units.Count;
  4701. }
  4702. TopMarkerList topMarkerList = new TopMarkerList(m_2D, units,
  4703. LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3,
  4704. UIColor.bar, UIColor.barBackground, DrawFrameIndexButton);
  4705. m_TopNumber = topMarkerList.Draw(marker, m_ProfileSingleView, m_TopNumber, m_TopStrings, m_TopValues);
  4706. GUILayout.Space(style.lineHeight);
  4707. float plotWidth = 40 + GUI.skin.box.padding.horizontal;
  4708. float plotHeight = 100;
  4709. EditorGUILayout.BeginHorizontal();
  4710. Histogram histogram = new Histogram(m_2D, units);
  4711. DrawHistogramForMarker(histogram, marker);
  4712. BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, units);
  4713. DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileSingleView.analysis, marker,
  4714. min, max, UIColor.standardLine, UIColor.boxAndWhiskerBoxColor);
  4715. boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, min, max,
  4716. string.Format("Min {0} for selected frames", fieldString),
  4717. string.Format("Max {0} for selected frames", fieldString));
  4718. EditorGUILayout.EndHorizontal();
  4719. GUILayout.Space(style.lineHeight);
  4720. DrawSelectedStats(marker, m_ProfileSingleView);
  4721. EditorGUILayout.EndVertical();
  4722. m_2D.ClearClipRect();
  4723. GUILayout.EndScrollView();
  4724. }
  4725. else
  4726. {
  4727. EditorGUILayout.LabelField("Marker not in selection");
  4728. }
  4729. }
  4730. }
  4731. else
  4732. {
  4733. EditorGUILayout.LabelField("No marker data selected");
  4734. }
  4735. }
  4736. if (m_ShowMarkerSummary != lastMarkerSummary)
  4737. {
  4738. ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(), m_ShowMarkerSummary);
  4739. }
  4740. EditorGUILayout.EndVertical();
  4741. }
  4742. internal static bool FileInTempDir(string filePath)
  4743. {
  4744. return Directory.Exists(TmpDir) && Directory.GetFiles(TmpDir).Contains(filePath);
  4745. }
  4746. }
  4747. }