| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file glGraphicsStateGuardian_src.cxx
- * @author drose
- * @date 1999-02-02
- * @author fperazzi, PandaSE
- * @date 2010-05-05
- * get_supports_cg_profile)
- */
- #include "config_putil.h"
- #include "displayRegion.h"
- #include "renderBuffer.h"
- #include "geom.h"
- #include "geomVertexData.h"
- #include "geomTriangles.h"
- #include "geomTristrips.h"
- #include "geomTrifans.h"
- #include "geomLines.h"
- #include "geomLinestrips.h"
- #include "geomPoints.h"
- #include "geomVertexReader.h"
- #include "graphicsWindow.h"
- #include "lens.h"
- #include "perspectiveLens.h"
- #include "directionalLight.h"
- #include "pointLight.h"
- #include "spotlight.h"
- #include "planeNode.h"
- #include "fog.h"
- #include "clockObject.h"
- #include "string_utils.h"
- #include "nodePath.h"
- #include "dcast.h"
- #include "pvector.h"
- #include "vector_string.h"
- #include "string_utils.h"
- #include "pnmImage.h"
- #include "config_gobj.h"
- #include "lightMutexHolder.h"
- #include "indirectLess.h"
- #include "pStatTimer.h"
- #include "load_prc_file.h"
- #include "bamCache.h"
- #include "bamCacheRecord.h"
- #include "alphaTestAttrib.h"
- #include "clipPlaneAttrib.h"
- #include "cullFaceAttrib.h"
- #include "depthOffsetAttrib.h"
- #include "depthWriteAttrib.h"
- #include "fogAttrib.h"
- #include "lightAttrib.h"
- #include "logicOpAttrib.h"
- #include "materialAttrib.h"
- #include "rescaleNormalAttrib.h"
- #include "scissorAttrib.h"
- #include "shadeModelAttrib.h"
- #include "stencilAttrib.h"
- #include "graphicsEngine.h"
- #include "shaderGenerator.h"
- #include "samplerState.h"
- #include "displayInformation.h"
- #if defined(HAVE_CG) && !defined(OPENGLES)
- #include <Cg/cgGL.h>
- #endif
- #include <algorithm>
- using std::dec;
- using std::endl;
- using std::hex;
- using std::max;
- using std::min;
- using std::string;
- TypeHandle CLP(GraphicsStateGuardian)::_type_handle;
- PStatCollector CLP(GraphicsStateGuardian)::_load_display_list_pcollector("Draw:Transfer data:Display lists");
- PStatCollector CLP(GraphicsStateGuardian)::_primitive_batches_display_list_pcollector("Primitive batches:Display lists");
- PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists");
- PStatCollector CLP(GraphicsStateGuardian)::_vertices_immediate_pcollector("Vertices:Immediate mode");
- PStatCollector CLP(GraphicsStateGuardian)::_memory_barrier_pcollector("Draw:Memory barriers");
- PStatCollector CLP(GraphicsStateGuardian)::_vertex_array_update_pcollector("Draw:Update arrays");
- PStatCollector CLP(GraphicsStateGuardian)::_texture_update_pcollector("Draw:Update texture");
- PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO");
- PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors");
- PStatCollector CLP(GraphicsStateGuardian)::_check_residency_pcollector("*:PStats:Check residency");
- // The following noop functions are assigned to the corresponding glext
- // function pointers in the class, in case the functions are not defined by
- // the GL, just so it will always be safe to call the extension functions.
- static void APIENTRY
- null_glPointParameterfv(GLenum, const GLfloat *) {
- }
- #ifdef OPENGLES_1
- // OpenGL ES 1 doesn't support this, period. Might as well macro it.
- #define _glDrawRangeElements(mode, start, end, count, type, indices) \
- glDrawElements(mode, count, type, indices)
- #else
- static void APIENTRY
- null_glDrawRangeElements(GLenum mode, GLuint start, GLuint end,
- GLsizei count, GLenum type, const GLvoid *indices) {
- // If we don't support glDrawRangeElements(), just use the original
- // glDrawElements() instead.
- glDrawElements(mode, count, type, indices);
- }
- #endif
- #if defined(OPENGLES) && !defined(OPENGLES_1)
- static void APIENTRY
- null_glVertexAttrib4dv(GLuint index, const GLdouble *v) {
- GLfloat vf[4] = {(GLfloat)v[0], (GLfloat)v[1], (GLfloat)v[2], (GLfloat)v[3]};
- glVertexAttrib4fv(index, vf);
- }
- #endif
- static void APIENTRY
- null_glActiveTexture(GLenum gl_texture_stage) {
- // If we don't support multitexture, we'd better not try to request a
- // texture beyond the first texture stage.
- nassertv(gl_texture_stage == GL_TEXTURE0);
- }
- #ifdef OPENGLES_2
- #define _glBlendEquation glBlendEquation
- #define _glBlendEquationSeparate glBlendEquationSeparate
- #define _glBlendFuncSeparate glBlendFuncSeparate
- #define _glBlendColor glBlendColor
- #else
- static void APIENTRY
- null_glBlendEquation(GLenum) {
- }
- static void APIENTRY
- null_glBlendFuncSeparate(GLenum src, GLenum dest, GLenum, GLenum) {
- glBlendFunc(src, dest);
- }
- static void APIENTRY
- null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
- }
- #endif
- #ifndef OPENGLES_1
- // We have a default shader that will be applied when there isn't any shader
- // applied (e.g. if it failed to compile). We need this because OpenGL ES
- // 2.x and OpenGL 3.2+ core don't have a fixed-function pipeline. This
- // default shader just applies a single texture, which is good enough for
- // drawing GUIs and such.
- static const string default_vshader =
- #ifndef OPENGLES
- #ifdef __APPLE__ // Apple's GL 3.2 contexts require at least GLSL 1.50.
- "#version 150\n"
- #else
- "#version 130\n"
- #endif
- "in vec4 p3d_Vertex;\n"
- "in vec4 p3d_Color;\n"
- "in vec2 p3d_MultiTexCoord0;\n"
- "out vec2 texcoord;\n"
- "out vec4 color;\n"
- #else
- "#version 100\n"
- "precision mediump float;\n"
- "attribute vec4 p3d_Vertex;\n"
- "attribute vec4 p3d_Color;\n"
- "attribute vec2 p3d_MultiTexCoord0;\n"
- "varying vec2 texcoord;\n"
- "varying lowp vec4 color;\n"
- #endif
- "uniform mat4 p3d_ModelViewProjectionMatrix;\n"
- "uniform vec4 p3d_ColorScale;\n"
- "void main(void) {\n"
- " gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n"
- " texcoord = p3d_MultiTexCoord0;\n"
- " color = p3d_Color * p3d_ColorScale;\n"
- "}\n";
- #ifndef OPENGLES
- // This version of the shader is used if vertices-float64 is enabled.
- static const string default_vshader_fp64 =
- #ifdef __APPLE__
- "#version 150\n"
- #else
- "#version 130\n"
- #endif
- "#extension GL_ARB_vertex_attrib_64bit : require\n"
- "#extension GL_ARB_gpu_shader_fp64 : require\n"
- "in dvec3 p3d_Vertex;\n"
- "in vec4 p3d_Color;\n"
- "in dvec2 p3d_MultiTexCoord0;\n"
- "out vec2 texcoord;\n"
- "out vec4 color;\n"
- "uniform mat4 p3d_ModelViewMatrix;\n"
- "uniform mat4 p3d_ProjectionMatrix;\n"
- "uniform vec4 p3d_ColorScale;\n"
- "void main(void) {\n" // Apply proj & modelview in two steps, more precise
- " gl_Position = vec4(dmat4(p3d_ProjectionMatrix) * (dmat4(p3d_ModelViewMatrix) * dvec4(p3d_Vertex, 1)));\n"
- " texcoord = vec2(p3d_MultiTexCoord0);\n"
- " color = p3d_Color * p3d_ColorScale;\n"
- "}\n";
- // Same as above, but for OpenGL 4.1.
- static const string default_vshader_fp64_gl41 =
- "#version 410\n"
- "in dvec3 p3d_Vertex;\n"
- "in vec4 p3d_Color;\n"
- "in dvec2 p3d_MultiTexCoord0;\n"
- "out vec2 texcoord;\n"
- "out vec4 color;\n"
- "uniform mat4 p3d_ModelViewMatrix;\n"
- "uniform mat4 p3d_ProjectionMatrix;\n"
- "uniform vec4 p3d_ColorScale;\n"
- "void main(void) {\n" // Apply proj & modelview in two steps, more precise
- " gl_Position = vec4(dmat4(p3d_ProjectionMatrix) * (dmat4(p3d_ModelViewMatrix) * dvec4(p3d_Vertex, 1)));\n"
- " texcoord = vec2(p3d_MultiTexCoord0);\n"
- " color = p3d_Color * p3d_ColorScale;\n"
- "}\n";
- #endif
- static const string default_fshader =
- #ifndef OPENGLES
- #ifdef __APPLE__ // Apple's GL 3.2 contexts require at least GLSL 1.50.
- "#version 150\n"
- #else
- "#version 130\n"
- #endif
- "in vec2 texcoord;\n"
- "in vec4 color;\n"
- "out vec4 p3d_FragColor;\n"
- "uniform sampler2D p3d_Texture0;\n"
- "uniform vec4 p3d_TexAlphaOnly;\n"
- #else
- "#version 100\n"
- "precision mediump float;\n"
- "varying vec2 texcoord;\n"
- "varying lowp vec4 color;\n"
- "uniform lowp sampler2D p3d_Texture0;\n"
- "uniform lowp vec4 p3d_TexAlphaOnly;\n"
- #endif
- "void main(void) {\n"
- #ifndef OPENGLES
- " p3d_FragColor = texture(p3d_Texture0, texcoord);\n"
- " p3d_FragColor += p3d_TexAlphaOnly;\n" // Hack for text rendering
- " p3d_FragColor *= color;\n"
- #else
- " gl_FragColor = texture2D(p3d_Texture0, texcoord);\n"
- " gl_FragColor += p3d_TexAlphaOnly;\n" // Hack for text rendering
- " gl_FragColor *= color;\n"
- #endif
- "}\n";
- #endif
- /**
- * Recopies the given array of pixels, converting from BGR to RGB arrangement.
- */
- static void
- uchar_bgr_to_rgb(unsigned char *dest, const unsigned char *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[2];
- dest[1] = source[1];
- dest[2] = source[0];
- dest += 3;
- source += 3;
- }
- }
- /**
- * Recopies the given array of pixels, converting from luminance to RGB
- * arrangement.
- */
- static void
- uchar_l_to_rgb(unsigned char *dest, const unsigned char *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[0];
- dest[1] = source[0];
- dest[2] = source[0];
- dest += 3;
- source += 1;
- }
- }
- /**
- * Recopies the given array of pixels, converting from BGRA to RGBA
- * arrangement.
- */
- static void
- uchar_bgra_to_rgba(unsigned char *dest, const unsigned char *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[2];
- dest[1] = source[1];
- dest[2] = source[0];
- dest[3] = source[3];
- dest += 4;
- source += 4;
- }
- }
- /**
- * Recopies the given array of pixels, converting from luminance-alpha to RGBA
- * arrangement.
- */
- static void
- uchar_la_to_rgba(unsigned char *dest, const unsigned char *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[0];
- dest[1] = source[0];
- dest[2] = source[0];
- dest[3] = source[1];
- dest += 4;
- source += 2;
- }
- }
- /**
- * Recopies the given array of pixels, converting from BGR to RGB arrangement.
- */
- static void
- ushort_bgr_to_rgb(unsigned short *dest, const unsigned short *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[2];
- dest[1] = source[1];
- dest[2] = source[0];
- dest += 3;
- source += 3;
- }
- }
- /**
- * Recopies the given array of pixels, converting from luminance to RGB
- * arrangement.
- */
- static void
- ushort_l_to_rgb(unsigned short *dest, const unsigned short *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[0];
- dest[1] = source[0];
- dest[2] = source[0];
- dest += 3;
- source += 1;
- }
- }
- /**
- * Recopies the given array of pixels, converting from BGRA to RGBA
- * arrangement.
- */
- static void
- ushort_bgra_to_rgba(unsigned short *dest, const unsigned short *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[2];
- dest[1] = source[1];
- dest[2] = source[0];
- dest[3] = source[3];
- dest += 4;
- source += 4;
- }
- }
- /**
- * Recopies the given array of pixels, converting from luminance-alpha to RGBA
- * arrangement.
- */
- static void
- ushort_la_to_rgba(unsigned short *dest, const unsigned short *source,
- int num_pixels) {
- for (int i = 0; i < num_pixels; i++) {
- dest[0] = source[0];
- dest[1] = source[0];
- dest[2] = source[0];
- dest[3] = source[1];
- dest += 4;
- source += 2;
- }
- }
- /**
- * Reverses the order of the components within the image, to convert (for
- * instance) GL_BGR to GL_RGB. Returns the byte pointer representing the
- * converted image, or the original image if it is unchanged.
- *
- * new_image must be supplied; it is the PTA_uchar that will be used to hold
- * the converted image if required. It will be modified only if the
- * conversion is necessary, in which case the data will be stored there, and
- * this pointer will be returned. If the conversion is not necessary, this
- * pointer will be left unchanged.
- */
- static const unsigned char *
- fix_component_ordering(PTA_uchar &new_image,
- const unsigned char *orig_image, size_t orig_image_size,
- GLenum external_format, Texture *tex) {
- const unsigned char *result = orig_image;
- switch (external_format) {
- case GL_RGB:
- if (tex->get_num_components() == 1) {
- new_image = PTA_uchar::empty_array(orig_image_size * 3);
- uchar_l_to_rgb(new_image, orig_image, orig_image_size);
- result = new_image;
- break;
- }
- switch (tex->get_component_type()) {
- case Texture::T_unsigned_byte:
- case Texture::T_byte:
- new_image = PTA_uchar::empty_array(orig_image_size);
- uchar_bgr_to_rgb(new_image, orig_image, orig_image_size / 3);
- result = new_image;
- break;
- case Texture::T_unsigned_short:
- case Texture::T_short:
- new_image = PTA_uchar::empty_array(orig_image_size);
- ushort_bgr_to_rgb((unsigned short *)new_image.p(),
- (const unsigned short *)orig_image,
- orig_image_size / 6);
- result = new_image;
- break;
- default:
- break;
- }
- break;
- case GL_RGBA:
- if (tex->get_num_components() == 2) {
- new_image = PTA_uchar::empty_array(orig_image_size * 2);
- uchar_la_to_rgba(new_image, orig_image, orig_image_size / 2);
- result = new_image;
- break;
- }
- switch (tex->get_component_type()) {
- case Texture::T_unsigned_byte:
- case Texture::T_byte:
- new_image = PTA_uchar::empty_array(orig_image_size);
- uchar_bgra_to_rgba(new_image, orig_image, orig_image_size / 4);
- result = new_image;
- break;
- case Texture::T_unsigned_short:
- case Texture::T_short:
- new_image = PTA_uchar::empty_array(orig_image_size);
- ushort_bgra_to_rgba((unsigned short *)new_image.p(),
- (const unsigned short *)orig_image,
- orig_image_size / 8);
- result = new_image;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return result;
- }
- // #--- Zhao Nov2011
- string CLP(GraphicsStateGuardian)::get_driver_vendor() { return _gl_vendor; }
- string CLP(GraphicsStateGuardian)::get_driver_renderer() { return _gl_renderer; }
- string CLP(GraphicsStateGuardian)::get_driver_version() { return _gl_version; }
- int CLP(GraphicsStateGuardian)::get_driver_version_major() { return _gl_version_major; }
- int CLP(GraphicsStateGuardian)::get_driver_version_minor() { return _gl_version_minor; }
- int CLP(GraphicsStateGuardian)::get_driver_shader_version_major() { return _gl_shadlang_ver_major; }
- int CLP(GraphicsStateGuardian)::get_driver_shader_version_minor() { return _gl_shadlang_ver_minor; }
- /**
- *
- */
- CLP(GraphicsStateGuardian)::
- CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) :
- GraphicsStateGuardian(gl_coordinate_system, engine, pipe),
- _renderbuffer_residency(get_prepared_objects()->get_name(), "renderbuffer")
- {
- _error_count = 0;
- _last_error_check = -1.0;
- // calling glGetError() forces a sync, this turns it on if you want to.
- _check_errors = gl_check_errors;
- _force_flush = gl_force_flush;
- _gl_shadlang_ver_major = 0;
- _gl_shadlang_ver_minor = 0;
- // Let's say we have a core profile, to be checked later (Otherwise, if we are
- // wrong the user may ask for some non-available functions)
- #ifndef OPENGLES
- _core_profile = true;
- #endif
- // Hack. Turn on the flag that we turned off at a higher level, since we
- // know this works properly in OpenGL, and we want the performance benefit
- // it gives us.
- _prepared_objects->_support_released_buffer_cache = true;
- // Assume that we will get a hardware-accelerated context, unless the window
- // tells us otherwise.
- _is_hardware = true;
- _scissor_enabled = false;
- _scissor_attrib_active = false;
- _white_texture = 0;
- #ifndef OPENGLES
- _shader_point_size = false;
- #endif
- #ifdef HAVE_CG
- _cg_context = 0;
- #endif
- #ifdef DO_PSTATS
- if (gl_finish) {
- GLCAT.warning()
- << "The config variable gl-finish is set to true. This may have a substantial negative impact on your render performance.\n";
- }
- #endif // DO_PSTATS
- }
- /**
- *
- */
- CLP(GraphicsStateGuardian)::
- ~CLP(GraphicsStateGuardian)() {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "GLGraphicsStateGuardian " << this << " destructing\n";
- }
- close_gsg();
- }
- /**
- * This is called by the GL if an error occurs, if gl_debug has been enabled
- * (and the driver supports the GL_ARB_debug_output extension).
- */
- void CLP(GraphicsStateGuardian)::
- debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, GLvoid *userParam) {
- // Determine how to map the severity level.
- NotifySeverity level;
- switch (severity) {
- case GL_DEBUG_SEVERITY_HIGH:
- level = NS_error;
- break;
- case GL_DEBUG_SEVERITY_MEDIUM:
- if (type == GL_DEBUG_TYPE_PERFORMANCE) {
- // Performance warnings should really be "info".
- level = NS_info;
- } else {
- level = NS_warning;
- }
- break;
- case GL_DEBUG_SEVERITY_LOW:
- level = NS_info;
- break;
- case GL_DEBUG_SEVERITY_NOTIFICATION:
- level = NS_debug;
- break;
- default:
- level = NS_fatal; //???
- break;
- }
- string msg_str(message, length);
- GLCAT.out(level) << msg_str << "\n";
- #ifndef NDEBUG
- if (level >= gl_debug_abort_level.get_value()) {
- abort();
- }
- #endif
- }
- /**
- * Resets all internal state as if the gsg were newly created.
- */
- void CLP(GraphicsStateGuardian)::
- reset() {
- _last_error_check = -1.0;
- free_pointers();
- GraphicsStateGuardian::reset();
- // Build _inv_state_mask as a mask of 1's where we don't care, and 0's where
- // we do care, about the state. _inv_state_mask =
- // RenderState::SlotMask::all_on();
- _inv_state_mask.clear_bit(ShaderAttrib::get_class_slot());
- _inv_state_mask.clear_bit(AlphaTestAttrib::get_class_slot());
- _inv_state_mask.clear_bit(AntialiasAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ClipPlaneAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ColorAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ColorScaleAttrib::get_class_slot());
- _inv_state_mask.clear_bit(CullFaceAttrib::get_class_slot());
- _inv_state_mask.clear_bit(DepthOffsetAttrib::get_class_slot());
- _inv_state_mask.clear_bit(DepthTestAttrib::get_class_slot());
- _inv_state_mask.clear_bit(DepthWriteAttrib::get_class_slot());
- _inv_state_mask.clear_bit(RenderModeAttrib::get_class_slot());
- _inv_state_mask.clear_bit(RescaleNormalAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ShadeModelAttrib::get_class_slot());
- _inv_state_mask.clear_bit(TransparencyAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ColorBlendAttrib::get_class_slot());
- _inv_state_mask.clear_bit(LogicOpAttrib::get_class_slot());
- _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
- _inv_state_mask.clear_bit(TexGenAttrib::get_class_slot());
- _inv_state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
- _inv_state_mask.clear_bit(MaterialAttrib::get_class_slot());
- _inv_state_mask.clear_bit(LightAttrib::get_class_slot());
- _inv_state_mask.clear_bit(StencilAttrib::get_class_slot());
- _inv_state_mask.clear_bit(FogAttrib::get_class_slot());
- _inv_state_mask.clear_bit(ScissorAttrib::get_class_slot());
- // Output the vendor and version strings.
- query_gl_version();
- if (_gl_version_major == 0) {
- // Couldn't get GL. Fail.
- mark_new();
- return;
- }
- // Save the extensions tokens.
- _extensions.clear();
- // In OpenGL (ES) 3.0 and later, glGetString(GL_EXTENSIONS) is deprecated.
- #ifndef OPENGLES_1
- if (_gl_version_major >= 3) {
- PFNGLGETSTRINGIPROC _glGetStringi =
- (PFNGLGETSTRINGIPROC)get_extension_func("glGetStringi");
- if (_glGetStringi != nullptr) {
- GLint n = 0;
- glGetIntegerv(GL_NUM_EXTENSIONS, &n);
- for (GLint i = 0; i < n; ++i) {
- const char *extension = (const char *)_glGetStringi(GL_EXTENSIONS, i);
- _extensions.insert(string(extension));
- }
- } else {
- GLCAT.error() << "glGetStringi is not available!\n";
- save_extensions((const char *)glGetString(GL_EXTENSIONS));
- }
- } else
- #endif
- {
- save_extensions((const char *)glGetString(GL_EXTENSIONS));
- }
- get_extra_extensions();
- // This needs access to the extensions, so put this after save_extensions.
- query_glsl_version();
- #ifndef OPENGLES
- // Determine whether this OpenGL context has compatibility features.
- bool core_profile = false;
- if (_gl_version_major >= 3) {
- if (_gl_version_major > 3 || _gl_version_minor >= 2) {
- // OpenGL 3.2 has a built-in way to check this.
- GLint profile_mask = 0;
- glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile_mask);
- if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT) {
- core_profile = true;
- } else if (profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
- core_profile = false;
- } else {
- core_profile = !has_extension("GL_ARB_compatibility");
- }
- } else {
- // OpenGL 3.0/3.1.
- GLint flags = 0;
- glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
- if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
- core_profile = true;
- }
- }
- }
- if (GLCAT.is_debug()) {
- if (core_profile) {
- GLCAT.debug() << "Using core profile\n";
- } else {
- GLCAT.debug() << "Using compatibility profile\n";
- }
- }
- _core_profile = core_profile;
- #elif defined(OPENGLES_1)
- static const bool core_profile = false;
- #else
- static const bool core_profile = true;
- #endif
- // Print out a list of all extensions.
- report_extensions();
- // Check if we are running under a profiling tool such as apitrace.
- #if !defined(NDEBUG) && !defined(OPENGLES_1)
- if (has_extension("GL_EXT_debug_marker")) {
- _glPushGroupMarker = (PFNGLPUSHGROUPMARKEREXTPROC)
- get_extension_func("glPushGroupMarkerEXT");
- _glPopGroupMarker = (PFNGLPOPGROUPMARKEREXTPROC)
- get_extension_func("glPopGroupMarkerEXT");
- // Start a group right away.
- push_group_marker("reset");
- } else {
- _glPushGroupMarker = nullptr;
- _glPopGroupMarker = nullptr;
- }
- #endif
- // Initialize OpenGL debugging output first, if enabled and supported.
- _supports_debug = false;
- _use_object_labels = false;
- if (gl_debug) {
- PFNGLDEBUGMESSAGECALLBACKPROC_P _glDebugMessageCallback;
- PFNGLDEBUGMESSAGECONTROLPROC _glDebugMessageControl;
- if (is_at_least_gl_version(4, 3) || has_extension("GL_KHR_debug")) {
- #ifdef OPENGLES
- _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC_P)
- get_extension_func("glDebugMessageCallbackKHR");
- _glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)
- get_extension_func("glDebugMessageControlKHR");
- _glObjectLabel = (PFNGLOBJECTLABELPROC)
- get_extension_func("glObjectLabelKHR");
- #else
- _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC_P)
- get_extension_func("glDebugMessageCallback");
- _glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)
- get_extension_func("glDebugMessageControl");
- _glObjectLabel = (PFNGLOBJECTLABELPROC)
- get_extension_func("glObjectLabel");
- #endif
- glEnable(GL_DEBUG_OUTPUT); // Not supported in ARB version
- _supports_debug = true;
- _use_object_labels = gl_debug_object_labels;
- #ifndef OPENGLES
- } else if (has_extension("GL_ARB_debug_output")) {
- _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC_P)
- get_extension_func("glDebugMessageCallbackARB");
- _glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)
- get_extension_func("glDebugMessageControlARB");
- _supports_debug = true;
- #endif
- } else {
- _supports_debug = false;
- }
- if (_supports_debug) {
- // Set the categories we want to listen to.
- _glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH,
- 0, nullptr, GLCAT.is_error());
- _glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM,
- 0, nullptr, GLCAT.is_warning());
- _glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW,
- 0, nullptr, GLCAT.is_info());
- _glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
- 0, nullptr, GLCAT.is_debug());
- // Enable the callback.
- _glDebugMessageCallback((GLDEBUGPROC_P) &debug_callback, (void*)this);
- if (gl_debug_synchronous) {
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- }
- GLCAT.info() << "gl-debug enabled.\n";
- } else {
- GLCAT.warning() << "gl-debug enabled, but NOT supported.\n";
- }
- } else {
- // However, still check if it is supported.
- _supports_debug = is_at_least_gl_version(4, 3)
- || has_extension("GL_KHR_debug")
- || has_extension("GL_ARB_debug_output");
- if (GLCAT.is_debug()) {
- if (_supports_debug) {
- GLCAT.debug() << "gl-debug supported, but NOT enabled.\n";
- } else {
- GLCAT.debug() << "gl-debug disabled and unsupported.\n";
- }
- }
- }
- _supported_geom_rendering =
- #ifndef OPENGLES
- Geom::GR_render_mode_wireframe | Geom::GR_render_mode_point |
- #endif
- Geom::GR_indexed_point |
- Geom::GR_point | Geom::GR_point_uniform_size |
- Geom::GR_indexed_other |
- Geom::GR_triangle_strip | Geom::GR_triangle_fan |
- Geom::GR_line_strip |
- Geom::GR_flat_last_vertex;
- #ifndef OPENGLES
- if (_supports_geometry_shaders) {
- _supported_geom_rendering |= Geom::GR_adjacency;
- }
- #endif
- _supports_point_parameters = false;
- #ifdef OPENGLES_1
- _glPointParameterfv = glPointParameterfv;
- #elif defined(OPENGLES)
- // Other OpenGL ES versions don't support point parameters.
- #else
- if (is_at_least_gl_version(1, 4)) {
- _supports_point_parameters = true;
- _glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)
- get_extension_func("glPointParameterfv");
- } else if (has_extension("GL_ARB_point_parameters")) {
- _supports_point_parameters = true;
- _glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)
- get_extension_func("glPointParameterfvARB");
- }
- if (_supports_point_parameters) {
- if (_glPointParameterfv == nullptr) {
- GLCAT.warning()
- << "glPointParameterfv advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_point_parameters = false;
- }
- }
- if (_supports_point_parameters) {
- _supported_geom_rendering |= Geom::GR_point_perspective | Geom::GR_point_scale;
- } else {
- _glPointParameterfv = null_glPointParameterfv;
- }
- #endif // !OPENGLES_2
- #if defined(OPENGLES_2)
- // OpenGL ES 2 doesn't have point sprites.
- _supports_point_sprite = false;
- #elif defined(OPENGLES_1)
- _supports_point_sprite = has_extension("GL_OES_point_sprite");
- #else
- _supports_point_sprite = is_at_least_gl_version(2, 0) ||
- has_extension("GL_ARB_point_sprite");
- #endif
- if (_supports_point_sprite) {
- // It appears that the point_sprite extension doesn't support texture
- // transforms on the generated texture coordinates. How inconsistent.
- // Because of this, we don't advertise GR_point_sprite_tex_matrix.
- _supported_geom_rendering |= Geom::GR_point_sprite;
- }
- // Determine whether we support wide lines (and how wide they can be).
- {
- GLfloat aliased_range[2] = {1.0f, 1.0f};
- glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliased_range);
- _max_line_width = aliased_range[1];
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- #ifndef OPENGLES
- GLfloat range[2] = {1.0f, 1.0f};
- glGetFloatv(GL_LINE_WIDTH_RANGE, range);
- _max_line_width = std::max(_max_line_width, range[1]);
- #endif
- GLfloat smooth_range[2] = {1.0f, 1.0f};
- glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smooth_range);
- _max_line_width = std::max(_max_line_width, smooth_range[1]);
- }
- #endif
- }
- #ifdef OPENGLES_1
- // OpenGL ES 1.0 does not support primitive restart indices.
- #elif defined(OPENGLES)
- if (gl_support_primitive_restart_index && is_at_least_gles_version(3, 0)) {
- glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
- _supported_geom_rendering |= Geom::GR_strip_cut_index;
- }
- #else
- _explicit_primitive_restart = false;
- _glPrimitiveRestartIndex = nullptr;
- if (gl_support_primitive_restart_index) {
- if ((is_at_least_gl_version(4, 3) || has_extension("GL_ARB_ES3_compatibility")) &&
- _gl_renderer.substr(0, 7) != "Gallium") {
- // As long as we enable this, OpenGL will always use the highest
- // possible index for a numeric type as strip cut index, which coincides
- // with our convention. This saves us a call to glPrimitiveRestartIndex
- // ... of course, though, the Gallium driver bugs out here. See also:
- // https://www.panda3d.org/forums/viewtopic.php?f=5&t=17512
- glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
- _supported_geom_rendering |= Geom::GR_strip_cut_index;
- } else if (is_at_least_gl_version(3, 1)) {
- // We have to use an explicit primitive restart enableindex.
- _explicit_primitive_restart = true;
- _supported_geom_rendering |= Geom::GR_strip_cut_index;
- _glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)
- get_extension_func("glPrimitiveRestartIndex");
- }
- }
- #endif
- #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION)
- if (has_fixed_function_pipeline() && is_at_least_gl_version(1, 4)) {
- _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)
- get_extension_func("glSecondaryColorPointer");
- } else if (has_extension("GL_EXT_secondary_color")) {
- _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)
- get_extension_func("glSecondaryColorPointerEXT");
- }
- #endif
- #ifndef OPENGLES_1
- _glDrawRangeElements = null_glDrawRangeElements;
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 0)) {
- _glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)
- get_extension_func("glDrawRangeElements");
- }
- #else
- if (is_at_least_gl_version(1, 2)) {
- _glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)
- get_extension_func("glDrawRangeElements");
- } else if (has_extension("GL_EXT_draw_range_elements")) {
- _glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)
- get_extension_func("glDrawRangeElementsEXT");
- }
- #endif
- if (_glDrawRangeElements == nullptr) {
- GLCAT.warning()
- << "glDrawRangeElements advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _glDrawRangeElements = null_glDrawRangeElements;
- }
- #endif // !OPENGLES_1
- _supports_3d_texture = false;
- #ifndef OPENGLES_1
- if (is_at_least_gl_version(1, 2) || is_at_least_gles_version(3, 0)) {
- _supports_3d_texture = true;
- _glTexImage3D = (PFNGLTEXIMAGE3DPROC_P)
- get_extension_func("glTexImage3D");
- _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
- get_extension_func("glTexSubImage3D");
- _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
- get_extension_func("glCopyTexSubImage3D");
- #ifndef OPENGLES
- } else if (has_extension("GL_EXT_texture3D")) {
- _supports_3d_texture = true;
- _glTexImage3D = (PFNGLTEXIMAGE3DPROC_P)
- get_extension_func("glTexImage3DEXT");
- _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
- get_extension_func("glTexSubImage3DEXT");
- _glCopyTexSubImage3D = nullptr;
- if (has_extension("GL_EXT_copy_texture")) {
- _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
- get_extension_func("glCopyTexSubImage3DEXT");
- }
- #else
- } else if (has_extension("GL_OES_texture_3D")) {
- _supports_3d_texture = true;
- _glTexImage3D = (PFNGLTEXIMAGE3DPROC_P)
- get_extension_func("glTexImage3DOES");
- _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
- get_extension_func("glTexSubImage3DOES");
- _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
- get_extension_func("glCopyTexSubImage3DOES");
- _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DOES)
- get_extension_func("glFramebufferTexture3DOES");
- #endif
- }
- if (_supports_3d_texture) {
- if (_glTexImage3D == nullptr || _glTexSubImage3D == nullptr) {
- GLCAT.warning()
- << "3-D textures advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_3d_texture = false;
- }
- }
- #endif // !OPENGLES_1
- _supports_tex_storage = false;
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 0)) {
- #else
- if (is_at_least_gl_version(4, 2) || has_extension("GL_ARB_texture_storage")) {
- #endif
- _supports_tex_storage = true;
- _glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)
- get_extension_func("glTexStorage1D");
- _glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)
- get_extension_func("glTexStorage2D");
- _glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)
- get_extension_func("glTexStorage3D");
- }
- #ifdef OPENGLES
- else if (has_extension("GL_EXT_texture_storage")) {
- _supports_tex_storage = true;
- _glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)
- get_extension_func("glTexStorage1DEXT");
- _glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)
- get_extension_func("glTexStorage2DEXT");
- _glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)
- get_extension_func("glTexStorage3DEXT");
- }
- #endif
- if (_supports_tex_storage) {
- if (_glTexStorage1D == nullptr || _glTexStorage2D == nullptr || _glTexStorage3D == nullptr) {
- GLCAT.warning()
- << "Immutable texture storage advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_tex_storage = false;
- }
- }
- _supports_clear_texture = false;
- #ifndef OPENGLES
- if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_clear_texture")) {
- _glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)
- get_extension_func("glClearTexImage");
- if (_glClearTexImage == nullptr) {
- GLCAT.warning()
- << "GL_ARB_clear_texture advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- } else {
- _supports_clear_texture = true;
- }
- }
- #endif
- _supports_clear_buffer = false;
- #ifndef OPENGLES
- if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_clear_buffer_object")) {
- _glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)
- get_extension_func("glClearBufferData");
- if (_glClearBufferData == nullptr) {
- GLCAT.warning()
- << "GL_ARB_clear_buffer_object advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- } else {
- _supports_clear_buffer = true;
- }
- }
- #endif
- _supports_2d_texture_array = false;
- #ifndef OPENGLES_1
- if (_gl_version_major >= 3) {
- _supports_2d_texture_array = true;
- _glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)
- get_extension_func("glFramebufferTextureLayer");
- #ifndef OPENGLES
- } else if (has_extension("GL_EXT_texture_array")) {
- _supports_2d_texture_array = true;
- _glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)
- get_extension_func("glFramebufferTextureLayerEXT");
- #endif
- }
- if (_supports_2d_texture_array && _glFramebufferTextureLayer == nullptr) {
- GLCAT.warning()
- << "Texture arrays advertised as supported by OpenGL runtime, but could not get pointer to glFramebufferTextureLayer function.\n";
- }
- #endif // !OPENGLES_1
- #ifdef OPENGLES_2
- _supports_cube_map = true;
- #else
- _supports_cube_map =
- is_at_least_gl_version(1, 3) ||
- has_extension("GL_ARB_texture_cube_map") ||
- has_extension("GL_OES_texture_cube_map");
- #endif
- #ifndef OPENGLES
- if (_supports_cube_map && gl_cube_map_seamless) {
- if (is_at_least_gl_version(3, 2) || has_extension("GL_ARB_seamless_cube_map")) {
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
- }
- }
- #endif
- #ifndef OPENGLES
- _supports_cube_map_array = is_at_least_gl_version(4, 0) ||
- has_extension("GL_ARB_texture_cube_map_array");
- #endif
- #ifndef OPENGLES
- if (is_at_least_gl_version(3, 1)) {
- _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBuffer");
- _supports_buffer_texture = true;
- } else if (has_extension("GL_ARB_texture_buffer_object")) {
- _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBufferARB");
- _supports_buffer_texture = true;
- }
- #endif
- #ifdef OPENGLES
- _supports_texture_srgb =
- is_at_least_gles_version(3, 0) || has_extension("GL_EXT_sRGB");
- #else
- _supports_texture_srgb =
- is_at_least_gl_version(2, 1) || has_extension("GL_EXT_texture_sRGB");
- #endif
- #ifdef OPENGLES
- _supports_compressed_texture = true;
- // Supported in the core. 1D textures are not supported by OpenGL ES.
- _glCompressedTexImage1D = nullptr;
- _glCompressedTexImage2D = glCompressedTexImage2D;
- _glCompressedTexSubImage1D = nullptr;
- _glCompressedTexSubImage2D = glCompressedTexSubImage2D;
- _glGetCompressedTexImage = nullptr;
- _glCompressedTexImage3D = nullptr;
- _glCompressedTexSubImage3D = nullptr;
- #ifdef OPENGLES_2
- if (_supports_3d_texture) {
- _glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)
- get_extension_func("glCompressedTexImage3DOES");
- _glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)
- get_extension_func("glCompressedTexSubImageOES");
- }
- #endif
- #else
- if (is_at_least_gl_version(1, 3)) {
- _supports_compressed_texture = true;
- _glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)
- get_extension_func("glCompressedTexImage1D");
- _glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
- get_extension_func("glCompressedTexImage2D");
- _glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)
- get_extension_func("glCompressedTexImage3D");
- _glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)
- get_extension_func("glCompressedTexSubImage1D");
- _glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)
- get_extension_func("glCompressedTexSubImage2D");
- _glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)
- get_extension_func("glCompressedTexSubImage3D");
- _glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
- get_extension_func("glGetCompressedTexImage");
- } else if (has_extension("GL_ARB_texture_compression")) {
- _supports_compressed_texture = true;
- _glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)
- get_extension_func("glCompressedTexImage1DARB");
- _glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
- get_extension_func("glCompressedTexImage2DARB");
- _glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)
- get_extension_func("glCompressedTexImage3DARB");
- _glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)
- get_extension_func("glCompressedTexSubImage1DARB");
- _glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)
- get_extension_func("glCompressedTexSubImage2DARB");
- _glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)
- get_extension_func("glCompressedTexSubImage3DARB");
- _glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
- get_extension_func("glGetCompressedTexImageARB");
- } else {
- _supports_compressed_texture = false;
- }
- if (_supports_compressed_texture) {
- if (_glCompressedTexImage1D == nullptr ||
- _glCompressedTexImage2D == nullptr ||
- _glCompressedTexImage3D == nullptr ||
- _glCompressedTexSubImage1D == nullptr ||
- _glCompressedTexSubImage2D == nullptr ||
- _glCompressedTexSubImage3D == nullptr ||
- _glGetCompressedTexImage == nullptr) {
- GLCAT.warning()
- << "Compressed textures advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_compressed_texture = false;
- }
- }
- #endif
- if (_supports_compressed_texture) {
- #ifndef OPENGLES
- _compressed_texture_formats.set_bit(Texture::CM_on);
- #endif
- GLint num_compressed_formats = 0;
- glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_compressed_formats);
- GLint *formats = (GLint *)alloca(num_compressed_formats * sizeof(GLint));
- glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
- for (int i = 0; i < num_compressed_formats; ++i) {
- switch (formats[i]) {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- _compressed_texture_formats.set_bit(Texture::CM_dxt1);
- break;
- #ifdef OPENGLES
- case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
- case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
- _compressed_texture_formats.set_bit(Texture::CM_pvr1_2bpp);
- break;
- case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
- case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
- _compressed_texture_formats.set_bit(Texture::CM_pvr1_4bpp);
- break;
- #else
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- _compressed_texture_formats.set_bit(Texture::CM_dxt3);
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- _compressed_texture_formats.set_bit(Texture::CM_dxt5);
- break;
- case GL_COMPRESSED_RGB_FXT1_3DFX:
- case GL_COMPRESSED_RGBA_FXT1_3DFX:
- _compressed_texture_formats.set_bit(Texture::CM_fxt1);
- break;
- #endif
- case GL_COMPRESSED_R11_EAC:
- case GL_COMPRESSED_RG11_EAC:
- _compressed_texture_formats.set_bit(Texture::CM_eac);
- break;
- case GL_COMPRESSED_RGB8_ETC2:
- case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
- case GL_COMPRESSED_RGBA8_ETC2_EAC:
- _compressed_texture_formats.set_bit(Texture::CM_etc1);
- _compressed_texture_formats.set_bit(Texture::CM_etc2);
- break;
- #ifdef OPENGLES
- case GL_ETC1_RGB8_OES:
- _compressed_texture_formats.set_bit(Texture::CM_etc1);
- break;
- #endif
- default:
- break;
- }
- }
- #ifndef OPENGLES
- // The OpenGL spec states that these are not reported by the above
- // mechanism, so we have to check for the extension ourselves.
- if (is_at_least_gl_version(3, 0) ||
- has_extension("GL_ARB_texture_compression_rgtc") ||
- has_extension("GL_EXT_texture_compression_rgtc")) {
- _compressed_texture_formats.set_bit(Texture::CM_rgtc);
- }
- #endif
- }
- #ifdef OPENGLES
- // Note that these extensions only offer support for GL_BGRA, not GL_BGR.
- _supports_bgr = has_extension("GL_EXT_texture_format_BGRA8888") ||
- has_extension("GL_APPLE_texture_format_BGRA8888");
- _supports_bgra_read = has_extension("GL_EXT_read_format_bgra");
- #else
- // In regular OpenGL, we have both GL_BGRA and GL_BGR.
- _supports_bgr =
- is_at_least_gl_version(1, 2) || has_extension("GL_EXT_bgra");
- _supports_bgra_read = _supports_bgr;
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- #ifdef OPENGLES_1
- _supports_rescale_normal = true;
- #else
- _supports_rescale_normal =
- has_fixed_function_pipeline() && gl_support_rescale_normal &&
- (is_at_least_gl_version(1, 2) || has_extension("GL_EXT_rescale_normal"));
- #endif
- #ifndef OPENGLES
- _use_separate_specular_color =
- has_fixed_function_pipeline() && gl_separate_specular_color &&
- (is_at_least_gl_version(1, 2) || has_extension("GL_EXT_separate_specular_color"));
- #endif
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef OPENGLES
- _supports_packed_dabc = false;
- _supports_packed_ufloat = false;
- #else
- _supports_packed_dabc = is_at_least_gl_version(3, 2) ||
- has_extension("GL_ARB_vertex_array_bgra") ||
- has_extension("GL_EXT_vertex_array_bgra");
- _supports_packed_ufloat = is_at_least_gl_version(4, 4) ||
- has_extension("GL_ARB_vertex_type_10f_11f_11f_rev");
- #endif
- #ifdef OPENGLES
- //TODO
- _supports_multisample = false;
- #else
- _supports_multisample =
- is_at_least_gl_version(1, 3) || has_extension("GL_ARB_multisample");
- #endif
- #ifdef OPENGLES_1
- _supports_generate_mipmap = is_at_least_gles_version(1, 1);
- #elif defined(OPENGLES)
- _supports_generate_mipmap = true;
- #else
- _supports_generate_mipmap = is_at_least_gl_version(1, 4) ||
- has_extension("GL_SGIS_generate_mipmap");
- #endif
- #ifdef OPENGLES_1
- _supports_tex_non_pow2 = false;
- #elif defined(OPENGLES)
- _supports_tex_non_pow2 = is_at_least_gles_version(3, 0) ||
- has_extension("GL_OES_texture_npot");
- #else
- _supports_tex_non_pow2 = is_at_least_gl_version(2, 0) ||
- has_extension("GL_ARB_texture_non_power_of_two");
- #endif
- #ifndef OPENGLES_2
- bool supports_multitexture = false;
- if (is_at_least_gl_version(1, 3) || is_at_least_gles_version(1, 1)) {
- supports_multitexture = true;
- _glActiveTexture = (PFNGLACTIVETEXTUREPROC)
- get_extension_func("glActiveTexture");
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
- get_extension_func("glClientActiveTexture");
- }
- #endif
- #ifdef SUPPORT_IMMEDIATE_MODE
- _glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)
- get_extension_func("glMultiTexCoord1f");
- _glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)
- get_extension_func("glMultiTexCoord2f");
- _glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)
- get_extension_func("glMultiTexCoord3f");
- _glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)
- get_extension_func("glMultiTexCoord4f");
- _glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)
- get_extension_func("glMultiTexCoord1d");
- _glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)
- get_extension_func("glMultiTexCoord2d");
- _glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)
- get_extension_func("glMultiTexCoord3d");
- _glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)
- get_extension_func("glMultiTexCoord4d");
- #endif
- } else if (has_extension("GL_ARB_multitexture")) {
- supports_multitexture = true;
- _glActiveTexture = (PFNGLACTIVETEXTUREPROC)
- get_extension_func("glActiveTextureARB");
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
- get_extension_func("glClientActiveTextureARB");
- }
- #endif
- #ifdef SUPPORT_IMMEDIATE_MODE
- _glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)
- get_extension_func("glMultiTexCoord1fARB");
- _glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)
- get_extension_func("glMultiTexCoord2fARB");
- _glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)
- get_extension_func("glMultiTexCoord3fARB");
- _glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)
- get_extension_func("glMultiTexCoord4fARB");
- _glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)
- get_extension_func("glMultiTexCoord1dARB");
- _glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)
- get_extension_func("glMultiTexCoord2dARB");
- _glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)
- get_extension_func("glMultiTexCoord3dARB");
- _glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)
- get_extension_func("glMultiTexCoord4dARB");
- #endif
- } else {
- supports_multitexture = false;
- }
- if (supports_multitexture) {
- if (_glActiveTexture == nullptr
- #ifdef SUPPORT_FIXED_FUNCTION
- || (has_fixed_function_pipeline() && _glClientActiveTexture == nullptr)
- #endif
- #ifdef SUPPORT_IMMEDIATE_MODE
- || GLf(_glMultiTexCoord1) == nullptr || GLf(_glMultiTexCoord2) == nullptr
- || GLf(_glMultiTexCoord3) == nullptr || GLf(_glMultiTexCoord4) == nullptr
- #endif
- ) {
- GLCAT.warning()
- << "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- supports_multitexture = false;
- }
- }
- if (!supports_multitexture) {
- // Replace with dummy no-op functions.
- _glActiveTexture = null_glActiveTexture;
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (!supports_multitexture || !has_fixed_function_pipeline()) {
- _glClientActiveTexture = null_glActiveTexture;
- }
- #endif
- #endif // OPENGLES_2
- #ifdef OPENGLES_1
- _supports_depth_texture = false;
- _supports_depth_stencil = has_extension("GL_OES_packed_depth_stencil");
- _supports_depth24 = has_extension("GL_OES_depth24");
- _supports_depth32 = has_extension("GL_OES_depth32");
- _supports_luminance_texture = true;
- #elif defined(OPENGLES)
- if (is_at_least_gles_version(3, 0)) {
- _supports_depth_texture = true;
- _supports_depth_stencil = true;
- _supports_depth24 = true;
- _supports_depth32 = true;
- } else {
- if (has_extension("GL_ANGLE_depth_texture")) {
- // This extension provides both depth textures and depth-stencil support.
- _supports_depth_texture = true;
- _supports_depth_stencil = true;
- } else if (has_extension("GL_OES_depth_texture")) {
- _supports_depth_texture = true;
- _supports_depth_stencil = has_extension("GL_OES_packed_depth_stencil");
- }
- _supports_depth24 = has_extension("GL_OES_depth24");
- _supports_depth32 = has_extension("GL_OES_depth32");
- }
- _supports_luminance_texture = true;
- #else
- _supports_depth_texture = (is_at_least_gl_version(1, 4) ||
- has_extension("GL_ARB_depth_texture"));
- _supports_depth_stencil = (is_at_least_gl_version(3, 0) ||
- has_extension("GL_ARB_framebuffer_object") ||
- has_extension("GL_EXT_packed_depth_stencil"));
- // OpenGL 3 deprecates luminance, luminance-alpha and alpha textures.
- _supports_luminance_texture = !core_profile;
- #endif
- #ifdef OPENGLES_2
- if (gl_support_shadow_filter && _supports_depth_texture &&
- (is_at_least_gles_version(3, 0) || has_extension("GL_EXT_shadow_samplers"))) {
- _supports_shadow_filter = true;
- }
- #else
- if (gl_support_shadow_filter &&
- _supports_depth_texture &&
- (is_at_least_gl_version(1, 4) || has_extension("GL_ARB_shadow")) &&
- (is_at_least_gl_version(2, 0) || has_extension("GL_ARB_fragment_program_shadow"))) {
- _supports_shadow_filter = true;
- }
- #endif
- // Actually, we can't keep forever disabling ARB_shadow on ATI cards, since
- // they do work correctly now. Maybe there is some feature level we can
- // check somewhere?
- /*if (_gl_vendor.substr(0,3)=="ATI") {
- // ATI drivers have never provided correct shadow support.
- _supports_shadow_filter = false;
- }*/
- _supports_texture_combine = false;
- _supports_texture_saved_result = false;
- _supports_texture_dot3 = false;
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- _supports_texture_combine =
- is_at_least_gl_version(1, 3) ||
- is_at_least_gles_version(1, 1) ||
- has_extension("GL_ARB_texture_env_combine");
- #ifdef OPENGLES_1
- _supports_texture_saved_result =
- has_extension("GL_OES_texture_env_crossbar");
- #else
- _supports_texture_saved_result =
- is_at_least_gl_version(1, 4) ||
- has_extension("GL_ARB_texture_env_crossbar");
- #endif
- _supports_texture_dot3 =
- is_at_least_gl_version(1, 3) ||
- is_at_least_gles_version(1, 1) ||
- has_extension("GL_ARB_texture_env_dot3");
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef OPENGLES_2
- _supports_buffers = true;
- _glGenBuffers = glGenBuffers;
- _glBindBuffer = glBindBuffer;
- _glBufferData = glBufferData;
- _glBufferSubData = glBufferSubData;
- _glDeleteBuffers = glDeleteBuffers;
- #else
- _supports_buffers = false;
- if (is_at_least_gl_version(1, 5) || is_at_least_gles_version(1, 1)) {
- _supports_buffers = true;
- _glGenBuffers = (PFNGLGENBUFFERSPROC)
- get_extension_func("glGenBuffers");
- _glBindBuffer = (PFNGLBINDBUFFERPROC)
- get_extension_func("glBindBuffer");
- _glBufferData = (PFNGLBUFFERDATAPROC)
- get_extension_func("glBufferData");
- _glBufferSubData = (PFNGLBUFFERSUBDATAPROC)
- get_extension_func("glBufferSubData");
- _glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
- get_extension_func("glDeleteBuffers");
- #ifndef OPENGLES
- _glMapBuffer = (PFNGLMAPBUFFERPROC)
- get_extension_func("glMapBuffer");
- _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
- get_extension_func("glUnmapBuffer");
- _glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)
- get_extension_func("glGetBufferSubData");
- #endif
- }
- #ifndef OPENGLES_1
- else if (has_extension("GL_ARB_vertex_buffer_object")) {
- _supports_buffers = true;
- _glGenBuffers = (PFNGLGENBUFFERSPROC)
- get_extension_func("glGenBuffersARB");
- _glBindBuffer = (PFNGLBINDBUFFERPROC)
- get_extension_func("glBindBufferARB");
- _glBufferData = (PFNGLBUFFERDATAPROC)
- get_extension_func("glBufferDataARB");
- _glBufferSubData = (PFNGLBUFFERSUBDATAPROC)
- get_extension_func("glBufferSubDataARB");
- _glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
- get_extension_func("glDeleteBuffersARB");
- _glMapBuffer = (PFNGLMAPBUFFERPROC)
- get_extension_func("glMapBufferARB");
- _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
- get_extension_func("glUnmapBufferARB");
- _glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)
- get_extension_func("glGetBufferSubDataARB");
- }
- #endif // OPENGLES_1
- if (_supports_buffers) {
- if (_glGenBuffers == nullptr || _glBindBuffer == nullptr ||
- _glBufferData == nullptr || _glBufferSubData == nullptr ||
- _glDeleteBuffers == nullptr) {
- GLCAT.warning()
- << "Buffers advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_buffers = false;
- }
- }
- #endif
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 0)) {
- _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)
- get_extension_func("glMapBufferRange");
- } else if (has_extension("GL_EXT_map_buffer_range")) {
- _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)
- get_extension_func("glMapBufferRangeEXT");
- } else {
- _glMapBufferRange = nullptr;
- }
- #else
- // Check for various advanced buffer management features.
- if (is_at_least_gl_version(3, 0) || has_extension("GL_ARB_map_buffer_range")) {
- _glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)
- get_extension_func("glMapBufferRange");
- } else {
- _glMapBufferRange = nullptr;
- }
- if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_buffer_storage")) {
- _glBufferStorage = (PFNGLBUFFERSTORAGEPROC)
- get_extension_func("glBufferStorage");
- if (_glBufferStorage != nullptr) {
- _supports_buffer_storage = true;
- } else {
- GLCAT.warning()
- << "Buffer storage advertised as supported by OpenGL runtime, but "
- "could not get pointers to extension function.\n";
- }
- } else {
- _supports_buffer_storage = false;
- }
- #endif
- _supports_vao = false;
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 0)) {
- #else
- if (is_at_least_gl_version(3, 0) || has_extension("GL_ARB_vertex_array_object")) {
- #endif
- _supports_vao = true;
- _glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)
- get_extension_func("glBindVertexArray");
- _glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)
- get_extension_func("glDeleteVertexArrays");
- _glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)
- get_extension_func("glGenVertexArrays");
- #ifdef OPENGLES
- } else if (has_extension("GL_OES_vertex_array_object")) {
- _supports_vao = true;
- _glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)
- get_extension_func("glBindVertexArrayOES");
- _glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)
- get_extension_func("glDeleteVertexArraysOES");
- _glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)
- get_extension_func("glGenVertexArraysOES");
- #endif
- }
- if (_supports_vao) {
- if (_glBindVertexArray == nullptr || _glDeleteVertexArrays == nullptr ||
- _glGenVertexArrays == nullptr) {
- GLCAT.warning()
- << "Vertex array objects advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_vao = false;
- }
- }
- // Check for GLSL support.
- #if defined(OPENGLES_1)
- _supports_glsl = false;
- _supports_geometry_shaders = false;
- _supports_tessellation_shaders = false;
- #elif defined(OPENGLES)
- _supports_glsl = true;
- _supports_geometry_shaders = false;
- _supports_tessellation_shaders = false;
- #else
- _supports_glsl = (_gl_shadlang_ver_major >= 1);
- _supports_tessellation_shaders = is_at_least_gl_version(4, 0) || has_extension("GL_ARB_tessellation_shader");
- if (is_at_least_gl_version(3, 2)) {
- _supports_geometry_shaders = true;
- _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
- get_extension_func("glFramebufferTexture");
- } else if (has_extension("GL_ARB_geometry_shader4")) {
- _supports_geometry_shaders = true;
- _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
- get_extension_func("glFramebufferTextureARB");
- _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
- get_extension_func("glProgramParameteriARB");
- } else if (has_extension("GL_EXT_geometry_shader4")) {
- _supports_geometry_shaders = true;
- _glFramebufferTexture = nullptr;
- _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
- get_extension_func("glProgramParameteriEXT");
- } else {
- _supports_geometry_shaders = false;
- _glFramebufferTexture = nullptr;
- }
- #endif
- _shader_caps._supports_glsl = _supports_glsl;
- // Check for support for other types of shaders that can be used by Cg.
- _supports_basic_shaders = false;
- #if defined(HAVE_CG) && !defined(OPENGLES)
- if (has_extension("GL_ARB_vertex_program") &&
- has_extension("GL_ARB_fragment_program")) {
- _supports_basic_shaders = true;
- _shader_caps._active_vprofile = (int)CG_PROFILE_ARBVP1;
- _shader_caps._active_fprofile = (int)CG_PROFILE_ARBFP1;
- _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN; // No geometry shader if only using basic
- if (basic_shaders_only) {
- // We're happy with ARB programs, thanks.
- } else if (has_extension("GL_NV_gpu_program5")) {
- _shader_caps._active_vprofile = (int)CG_PROFILE_GP5VP;
- _shader_caps._active_fprofile = (int)CG_PROFILE_GP5FP;
- _shader_caps._active_gprofile = (int)CG_PROFILE_GP5GP;
- } else if (has_extension("GL_NV_gpu_program4")) {
- _shader_caps._active_vprofile = (int)CG_PROFILE_GP4VP;
- _shader_caps._active_fprofile = (int)CG_PROFILE_GP4FP;
- _shader_caps._active_gprofile = (int)CG_PROFILE_GP4GP;
- } else if (has_extension("GL_NV_vertex_program3") &&
- has_extension("GL_NV_fragment_program2")) {
- _shader_caps._active_vprofile = (int)CG_PROFILE_VP40;
- _shader_caps._active_fprofile = (int)CG_PROFILE_FP40;
- _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
- } else if (has_extension("GL_NV_vertex_program2") &&
- has_extension("GL_NV_fragment_program")) {
- _shader_caps._active_vprofile = (int)CG_PROFILE_VP30;
- _shader_caps._active_fprofile = (int)CG_PROFILE_FP30;
- _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
- } else if (has_extension("GL_NV_vertex_program1_1") &&
- has_extension("GL_NV_texture_shader2") &&
- has_extension("GL_NV_register_combiners2")) {
- _shader_caps._active_vprofile = (int)CG_PROFILE_VP20;
- _shader_caps._active_fprofile = (int)CG_PROFILE_FP20;
- _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
- } else if (_supports_glsl) {
- // This is what will be available to non-NVIDIA cards. It is the last
- // option since it is slower to compile GLSL than the other options.
- _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
- _shader_caps._active_fprofile = (int)CG_PROFILE_GLSLF;
- if (_supports_geometry_shaders) {
- _shader_caps._active_gprofile = (int)CG_PROFILE_GLSLG;
- }
- }
- _shader_caps._ultimate_vprofile = (int)CG_PROFILE_GLSLV;
- _shader_caps._ultimate_fprofile = (int)CG_PROFILE_GLSLF;
- _shader_caps._ultimate_gprofile = (int)CG_PROFILE_GLSLG;
- // Bug workaround for radeons.
- if (_shader_caps._active_fprofile == CG_PROFILE_ARBFP1) {
- if (has_extension("GL_ATI_draw_buffers")) {
- _shader_caps._bug_list.insert(Shader::SBUG_ati_draw_buffers);
- }
- }
- Shader::set_default_caps(_shader_caps);
- } else if (_supports_glsl) {
- // No, but we do support GLSL...
- _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
- _shader_caps._active_fprofile = (int)CG_PROFILE_GLSLF;
- if (_supports_geometry_shaders) {
- _shader_caps._active_gprofile = (int)CG_PROFILE_GLSLG;
- } else {
- _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
- }
- }
- #endif // HAVE_CG
- _supports_compute_shaders = false;
- #ifndef OPENGLES_1
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 1)) {
- #else
- if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_compute_shader")) {
- #endif
- _glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)
- get_extension_func("glDispatchCompute");
- if (_glDispatchCompute != nullptr) {
- _supports_compute_shaders = true;
- }
- }
- #endif // !OPENGLES_1
- #ifndef OPENGLES
- if (_supports_glsl) {
- _glAttachShader = (PFNGLATTACHSHADERPROC)
- get_extension_func("glAttachShader");
- _glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)
- get_extension_func("glBindAttribLocation");
- _glCompileShader = (PFNGLCOMPILESHADERPROC)
- get_extension_func("glCompileShader");
- _glCreateProgram = (PFNGLCREATEPROGRAMPROC)
- get_extension_func("glCreateProgram");
- _glCreateShader = (PFNGLCREATESHADERPROC)
- get_extension_func("glCreateShader");
- _glDeleteProgram = (PFNGLDELETEPROGRAMPROC)
- get_extension_func("glDeleteProgram");
- _glDeleteShader = (PFNGLDELETESHADERPROC)
- get_extension_func("glDeleteShader");
- _glDetachShader = (PFNGLDETACHSHADERPROC)
- get_extension_func("glDetachShader");
- _glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)
- get_extension_func("glDisableVertexAttribArray");
- _glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)
- get_extension_func("glEnableVertexAttribArray");
- _glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)
- get_extension_func("glGetActiveAttrib");
- _glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)
- get_extension_func("glGetActiveUniform");
- _glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)
- get_extension_func("glGetAttribLocation");
- _glGetProgramiv = (PFNGLGETPROGRAMIVPROC)
- get_extension_func("glGetProgramiv");
- _glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)
- get_extension_func("glGetProgramInfoLog");
- _glGetShaderiv = (PFNGLGETSHADERIVPROC)
- get_extension_func("glGetShaderiv");
- _glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)
- get_extension_func("glGetShaderInfoLog");
- _glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)
- get_extension_func("glGetUniformLocation");
- _glLinkProgram = (PFNGLLINKPROGRAMPROC)
- get_extension_func("glLinkProgram");
- _glShaderSource = (PFNGLSHADERSOURCEPROC_P)
- get_extension_func("glShaderSource");
- _glUseProgram = (PFNGLUSEPROGRAMPROC)
- get_extension_func("glUseProgram");
- _glUniform4f = (PFNGLUNIFORM4FPROC)
- get_extension_func("glUniform4f");
- _glUniform1i = (PFNGLUNIFORM1IPROC)
- get_extension_func("glUniform1i");
- _glUniform1fv = (PFNGLUNIFORM1FVPROC)
- get_extension_func("glUniform1fv");
- _glUniform2fv = (PFNGLUNIFORM2FVPROC)
- get_extension_func("glUniform2fv");
- _glUniform3fv = (PFNGLUNIFORM3FVPROC)
- get_extension_func("glUniform3fv");
- _glUniform4fv = (PFNGLUNIFORM4FVPROC)
- get_extension_func("glUniform4fv");
- _glUniform1iv = (PFNGLUNIFORM1IVPROC)
- get_extension_func("glUniform1iv");
- _glUniform2iv = (PFNGLUNIFORM2IVPROC)
- get_extension_func("glUniform2iv");
- _glUniform3iv = (PFNGLUNIFORM3IVPROC)
- get_extension_func("glUniform3iv");
- _glUniform4iv = (PFNGLUNIFORM4IVPROC)
- get_extension_func("glUniform4iv");
- _glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)
- get_extension_func("glUniformMatrix3fv");
- _glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)
- get_extension_func("glUniformMatrix4fv");
- _glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)
- get_extension_func("glValidateProgram");
- _glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)
- get_extension_func("glVertexAttrib4fv");
- _glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)
- get_extension_func("glVertexAttrib4dv");
- _glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)
- get_extension_func("glVertexAttribPointer");
- if (is_at_least_gl_version(3, 0)) {
- _glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)
- get_extension_func("glBindFragDataLocation");
- _glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)
- get_extension_func("glVertexAttribIPointer");
- _glUniform1uiv = (PFNGLUNIFORM1UIVPROC)
- get_extension_func("glUniform1uiv");
- _glUniform2uiv = (PFNGLUNIFORM2UIVPROC)
- get_extension_func("glUniform2uiv");
- _glUniform3uiv = (PFNGLUNIFORM3UIVPROC)
- get_extension_func("glUniform3uiv");
- _glUniform4uiv = (PFNGLUNIFORM4UIVPROC)
- get_extension_func("glUniform4uiv");
- } else if (has_extension("GL_EXT_gpu_shader4")) {
- _glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)
- get_extension_func("glBindFragDataLocationEXT");
- _glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)
- get_extension_func("glVertexAttribIPointerEXT");
- _glUniform1uiv = (PFNGLUNIFORM1UIVPROC)
- get_extension_func("glUniform1uivEXT");
- _glUniform2uiv = (PFNGLUNIFORM2UIVPROC)
- get_extension_func("glUniform2uivEXT");
- _glUniform3uiv = (PFNGLUNIFORM3UIVPROC)
- get_extension_func("glUniform3uivEXT");
- _glUniform4uiv = (PFNGLUNIFORM4UIVPROC)
- get_extension_func("glUniform4uivEXT");
- } else {
- _glBindFragDataLocation = nullptr;
- _glVertexAttribIPointer = nullptr;
- }
- if (is_at_least_gl_version(4, 1) ||
- has_extension("GL_ARB_vertex_attrib_64bit")) {
- _glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)
- get_extension_func("glVertexAttribLPointer");
- } else {
- _glVertexAttribLPointer = nullptr;
- }
- if (_supports_tessellation_shaders) {
- _glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)
- get_extension_func("glPatchParameteri");
- }
- } else if (_supports_basic_shaders) {
- // We don't support GLSL, but we support ARB programs.
- _glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)
- get_extension_func("glDisableVertexAttribArrayARB");
- _glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)
- get_extension_func("glEnableVertexAttribArrayARB");
- _glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)
- get_extension_func("glVertexAttrib4fvARB");
- _glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)
- get_extension_func("glVertexAttrib4dvARB");
- _glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)
- get_extension_func("glVertexAttribPointerARB");
- _glBindFragDataLocation = nullptr;
- _glVertexAttribIPointer = nullptr;
- _glVertexAttribLPointer = nullptr;
- }
- #endif
- #ifdef OPENGLES_2
- _glAttachShader = glAttachShader;
- _glBindAttribLocation = glBindAttribLocation;
- _glCompileShader = glCompileShader;
- _glCreateProgram = glCreateProgram;
- _glCreateShader = glCreateShader;
- _glDeleteProgram = glDeleteProgram;
- _glDeleteShader = glDeleteShader;
- _glDetachShader = glDetachShader;
- _glDisableVertexAttribArray = glDisableVertexAttribArray;
- _glEnableVertexAttribArray = glEnableVertexAttribArray;
- _glGetActiveAttrib = glGetActiveAttrib;
- _glGetActiveUniform = glGetActiveUniform;
- _glGetAttribLocation = glGetAttribLocation;
- _glGetProgramiv = glGetProgramiv;
- _glGetProgramInfoLog = glGetProgramInfoLog;
- _glGetShaderiv = glGetShaderiv;
- _glGetShaderInfoLog = glGetShaderInfoLog;
- _glGetUniformLocation = glGetUniformLocation;
- _glLinkProgram = glLinkProgram;
- _glShaderSource = (PFNGLSHADERSOURCEPROC_P) glShaderSource;
- _glUseProgram = glUseProgram;
- _glUniform4f = glUniform4f;
- _glUniform1i = glUniform1i;
- _glUniform1fv = glUniform1fv;
- _glUniform2fv = glUniform2fv;
- _glUniform3fv = glUniform3fv;
- _glUniform4fv = glUniform4fv;
- _glUniform1iv = glUniform1iv;
- _glUniform2iv = glUniform2iv;
- _glUniform3iv = glUniform3iv;
- _glUniform4iv = glUniform4iv;
- _glUniformMatrix3fv = glUniformMatrix3fv;
- _glUniformMatrix4fv = glUniformMatrix4fv;
- _glValidateProgram = glValidateProgram;
- _glVertexAttrib4fv = glVertexAttrib4fv;
- _glVertexAttrib4dv = null_glVertexAttrib4dv;
- _glVertexAttribPointer = glVertexAttribPointer;
- _glVertexAttribLPointer = nullptr;
- if (is_at_least_gles_version(3, 0)) {
- _glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)
- get_extension_func("glVertexAttribIPointer");
- } else {
- _glVertexAttribIPointer = nullptr;
- }
- if (has_extension("GL_EXT_blend_func_extended")) {
- _glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)
- get_extension_func("glBindFragDataLocationEXT");
- } else {
- _glBindFragDataLocation = nullptr;
- }
- #endif
- #ifndef OPENGLES_1
- _use_vertex_attrib_binding = false;
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 1)) {
- #else
- if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_vertex_attrib_binding")) {
- #endif
- _glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)
- get_extension_func("glBindVertexBuffer");
- _glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)
- get_extension_func("glVertexAttribFormat");
- _glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)
- get_extension_func("glVertexAttribIFormat");
- _glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)
- get_extension_func("glVertexAttribBinding");
- _glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)
- get_extension_func("glVertexBindingDivisor");
- #ifndef OPENGLES
- _glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)
- get_extension_func("glVertexAttribLFormat");
- #endif
- if (gl_fixed_vertex_attrib_locations) {
- _use_vertex_attrib_binding = true;
- }
- }
- #endif
- // We need to have a default shader to apply in case something didn't happen
- // to have a shader applied, or if it failed to compile. This default
- // shader just outputs a red color, indicating that something went wrong.
- #ifndef OPENGLES_1
- if (_default_shader == nullptr && !has_fixed_function_pipeline()) {
- #ifndef OPENGLES
- bool use_float64 = vertices_float64;
- if (use_float64 && is_at_least_gl_version(4, 1)) {
- _default_shader = Shader::make(Shader::SL_GLSL, default_vshader_fp64_gl41, default_fshader);
- } else if (use_float64 && has_extension("GL_ARB_vertex_attrib_64bit")) {
- _default_shader = Shader::make(Shader::SL_GLSL, default_vshader_fp64, default_fshader);
- } else
- #endif
- {
- _default_shader = Shader::make(Shader::SL_GLSL, default_vshader, default_fshader);
- }
- }
- #endif
- #ifndef OPENGLES_1
- // Check for uniform buffers.
- #ifdef OPENGLES
- if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
- #else
- if (is_at_least_gles_version(3, 0)) {
- #endif
- _supports_uniform_buffers = true;
- _glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)
- get_extension_func("glGetActiveUniformsiv");
- _glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)
- get_extension_func("glGetActiveUniformBlockiv");
- _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
- get_extension_func("glGetActiveUniformBlockName");
- } else {
- _supports_uniform_buffers = false;
- }
- #ifndef OPENGLES
- // Check for SSBOs.
- if (is_at_least_gl_version(4, 3) || has_extension("ARB_shader_storage_buffer_object")) {
- _supports_shader_buffers = true;
- _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)
- get_extension_func("glGetProgramInterfaceiv");
- _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)
- get_extension_func("glGetProgramResourceName");
- _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)
- get_extension_func("glGetProgramResourceiv");
- } else
- #endif
- {
- _supports_shader_buffers = false;
- }
- if (_supports_uniform_buffers || _supports_shader_buffers) {
- _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
- get_extension_func("glBindBufferBase");
- }
- #endif
- // Check whether we support geometry instancing and instanced vertex
- // attribs.
- #if defined(OPENGLES_1)
- _supports_vertex_attrib_divisor = false;
- _supports_geometry_instancing = false;
- #elif defined(OPENGLES)
- if (is_at_least_gles_version(3, 0)) {
- // OpenGL ES 3 has all of this in the core.
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisor");
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstanced");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstanced");
- _supports_vertex_attrib_divisor = true;
- _supports_geometry_instancing = true;
- } else if (has_extension("GL_ANGLE_instanced_arrays")) {
- // This extension has both things in one.
- #ifdef __EMSCRIPTEN__
- // Work around bug - it doesn't allow ANGLE suffix in getProcAddress.
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisor");
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstanced");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstanced");
- #else
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisorANGLE");
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstancedANGLE");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstancedANGLE");
- #endif
- _supports_vertex_attrib_divisor = true;
- _supports_geometry_instancing = true;
- } else {
- // Check separately for geometry instancing and instanced attribs.
- if (has_extension("GL_EXT_draw_instanced")) {
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstancedEXT");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstancedEXT");
- _supports_geometry_instancing = true;
- } else if (has_extension("GL_NV_draw_instanced")) {
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstancedNV");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstancedNV");
- _supports_geometry_instancing = true;
- } else {
- _supports_geometry_instancing = false;
- }
- if (has_extension("GL_EXT_instanced_arrays")) {
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisorEXT");
- _supports_vertex_attrib_divisor = true;
- } else if (has_extension("GL_NV_instanced_arrays")) {
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisorNV");
- _supports_vertex_attrib_divisor = true;
- } else {
- _supports_vertex_attrib_divisor = false;
- }
- }
- #else
- if (is_at_least_gl_version(3, 3)) {
- // This feature is in OpenGL core as of 3.3.
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisor");
- _supports_vertex_attrib_divisor = true;
- } else if (has_extension("GL_ARB_instanced_arrays")) {
- _glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)
- get_extension_func("glVertexAttribDivisorARB");
- _supports_vertex_attrib_divisor = true;
- } else {
- _supports_vertex_attrib_divisor = false;
- }
- // Some drivers expose one extension, some expose the other.
- if (is_at_least_gl_version(3, 1)) {
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstanced");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstanced");
- _supports_geometry_instancing = true;
- } else if (has_extension("GL_ARB_draw_instanced")) {
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstancedARB");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstancedARB");
- _supports_geometry_instancing = true;
- } else if (has_extension("GL_EXT_draw_instanced")) {
- _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
- get_extension_func("glDrawArraysInstancedEXT");
- _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
- get_extension_func("glDrawElementsInstancedEXT");
- _supports_geometry_instancing = true;
- } else {
- _glDrawElementsInstanced = 0;
- _glDrawArraysInstanced = 0;
- _supports_geometry_instancing = false;
- }
- #endif
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing) {
- if (_glDrawArraysInstanced == nullptr || _glDrawElementsInstanced == nullptr) {
- GLCAT.warning()
- << "Geometry instancing advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_geometry_instancing = false;
- }
- }
- if (_supports_vertex_attrib_divisor) {
- if (_glVertexAttribDivisor == nullptr) {
- GLCAT.warning()
- << "Instanced vertex arrays advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_vertex_attrib_divisor = false;
- }
- }
- #endif
- // Check if we support indirect draw.
- _supports_indirect_draw = false;
- #ifndef OPENGLES_1
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 1)) {
- #else
- if (is_at_least_gl_version(4, 0) || has_extension("GL_ARB_draw_indirect")) {
- #endif
- _glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)
- get_extension_func("glDrawArraysIndirect");
- _glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)
- get_extension_func("glDrawElementsIndirect");
- if (_glDrawArraysIndirect == nullptr || _glDrawElementsIndirect == nullptr) {
- GLCAT.warning()
- << "Indirect draw advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- } else {
- _supports_indirect_draw = true;
- }
- }
- #endif
- #ifdef OPENGLES_1
- _supports_framebuffer_multisample = false;
- _supports_framebuffer_blit = false;
- if (has_extension("GL_OES_framebuffer_object")) {
- _supports_framebuffer_object = true;
- _glIsRenderbuffer = (PFNGLISRENDERBUFFEROESPROC)
- get_extension_func("glIsRenderbufferOES");
- _glBindRenderbuffer = (PFNGLBINDRENDERBUFFEROESPROC)
- get_extension_func("glBindRenderbufferOES");
- _glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSOESPROC)
- get_extension_func("glDeleteRenderbuffersOES");
- _glGenRenderbuffers = (PFNGLGENRENDERBUFFERSOESPROC)
- get_extension_func("glGenRenderbuffersOES");
- _glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEOESPROC)
- get_extension_func("glRenderbufferStorageOES");
- _glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVOESPROC)
- get_extension_func("glGetRenderbufferParameterivOES");
- _glIsFramebuffer = (PFNGLISFRAMEBUFFEROESPROC)
- get_extension_func("glIsFramebufferOES");
- _glBindFramebuffer = (PFNGLBINDFRAMEBUFFEROESPROC)
- get_extension_func("glBindFramebufferOES");
- _glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSOESPROC)
- get_extension_func("glDeleteFramebuffersOES");
- _glGenFramebuffers = (PFNGLGENFRAMEBUFFERSOESPROC)
- get_extension_func("glGenFramebuffersOES");
- _glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSOESPROC)
- get_extension_func("glCheckFramebufferStatusOES");
- _glFramebufferTexture1D = nullptr;
- _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DOESPROC)
- get_extension_func("glFramebufferTexture2DOES");
- _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEROESPROC)
- get_extension_func("glFramebufferRenderbufferOES");
- _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC)
- get_extension_func("glGetFramebufferAttachmentParameterivOES");
- _glGenerateMipmap = (PFNGLGENERATEMIPMAPOESPROC)
- get_extension_func("glGenerateMipmapOES");
- } else {
- _supports_framebuffer_object = false;
- _glGenerateMipmap = nullptr;
- }
- #elif defined(OPENGLES)
- // In OpenGL ES 2.x, FBO's are supported in the core.
- _supports_framebuffer_object = true;
- _glIsRenderbuffer = glIsRenderbuffer;
- _glBindRenderbuffer = glBindRenderbuffer;
- _glDeleteRenderbuffers = glDeleteRenderbuffers;
- _glGenRenderbuffers = glGenRenderbuffers;
- _glRenderbufferStorage = glRenderbufferStorage;
- _glGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
- _glIsFramebuffer = glIsFramebuffer;
- _glBindFramebuffer = glBindFramebuffer;
- _glDeleteFramebuffers = glDeleteFramebuffers;
- _glGenFramebuffers = glGenFramebuffers;
- _glCheckFramebufferStatus = glCheckFramebufferStatus;
- _glFramebufferTexture1D = nullptr;
- _glFramebufferTexture2D = glFramebufferTexture2D;
- _glFramebufferRenderbuffer = glFramebufferRenderbuffer;
- _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
- _glGenerateMipmap = glGenerateMipmap;
- if (is_at_least_gles_version(3, 0)) {
- _supports_framebuffer_multisample = true;
- _supports_framebuffer_blit = true;
- _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
- get_extension_func("glRenderbufferStorageMultisample");
- _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
- get_extension_func("glBlitFramebuffer");
- } else {
- if (has_extension("GL_ANGLE_framebuffer_multisample")) {
- _supports_framebuffer_multisample = true;
- _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEANGLEPROC)
- get_extension_func("glRenderbufferStorageMultisampleANGLE");
- } else {
- _supports_framebuffer_multisample = false;
- }
- if (has_extension("GL_ANGLE_framebuffer_blit")) {
- _supports_framebuffer_blit = true;
- _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERANGLEPROC)
- get_extension_func("glBlitFramebufferANGLE");
- } else {
- _supports_framebuffer_blit = false;
- }
- }
- #else // Desktop OpenGL case.
- if (is_at_least_gl_version(3, 0) || has_extension("GL_ARB_framebuffer_object")) {
- _supports_framebuffer_object = true;
- _supports_framebuffer_multisample = true;
- _supports_framebuffer_blit = true;
- _glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)
- get_extension_func("glIsRenderbuffer");
- _glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)
- get_extension_func("glBindRenderbuffer");
- _glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)
- get_extension_func("glDeleteRenderbuffers");
- _glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)
- get_extension_func("glGenRenderbuffers");
- _glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)
- get_extension_func("glRenderbufferStorage");
- _glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)
- get_extension_func("glGetRenderbufferParameteriv");
- _glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)
- get_extension_func("glIsFramebuffer");
- _glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)
- get_extension_func("glBindFramebuffer");
- _glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)
- get_extension_func("glDeleteFramebuffers");
- _glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)
- get_extension_func("glGenFramebuffers");
- _glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)
- get_extension_func("glCheckFramebufferStatus");
- _glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)
- get_extension_func("glFramebufferTexture1D");
- _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)
- get_extension_func("glFramebufferTexture2D");
- _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)
- get_extension_func("glFramebufferTexture3D");
- _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)
- get_extension_func("glFramebufferRenderbuffer");
- _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)
- get_extension_func("glGetFramebufferAttachmentParameteriv");
- _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)
- get_extension_func("glGenerateMipmap");
- _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
- get_extension_func("glRenderbufferStorageMultisample");
- _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
- get_extension_func("glBlitFramebuffer");
- } else if (has_extension("GL_EXT_framebuffer_object")) {
- _supports_framebuffer_object = true;
- _glIsRenderbuffer = (PFNGLISRENDERBUFFEREXTPROC)
- get_extension_func("glIsRenderbufferEXT");
- _glBindRenderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
- get_extension_func("glBindRenderbufferEXT");
- _glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
- get_extension_func("glDeleteRenderbuffersEXT");
- _glGenRenderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
- get_extension_func("glGenRenderbuffersEXT");
- _glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
- get_extension_func("glRenderbufferStorageEXT");
- _glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)
- get_extension_func("glGetRenderbufferParameterivEXT");
- _glIsFramebuffer = (PFNGLISFRAMEBUFFEREXTPROC)
- get_extension_func("glIsFramebufferEXT");
- _glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
- get_extension_func("glBindFramebufferEXT");
- _glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
- get_extension_func("glDeleteFramebuffersEXT");
- _glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
- get_extension_func("glGenFramebuffersEXT");
- _glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
- get_extension_func("glCheckFramebufferStatusEXT");
- _glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)
- get_extension_func("glFramebufferTexture1DEXT");
- _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
- get_extension_func("glFramebufferTexture2DEXT");
- _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)
- get_extension_func("glFramebufferTexture3DEXT");
- _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
- get_extension_func("glFramebufferRenderbufferEXT");
- _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)
- get_extension_func("glGetFramebufferAttachmentParameterivEXT");
- _glGenerateMipmap = (PFNGLGENERATEMIPMAPEXTPROC)
- get_extension_func("glGenerateMipmapEXT");
- if (has_extension("GL_EXT_framebuffer_multisample")) {
- _supports_framebuffer_multisample = true;
- _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
- get_extension_func("glRenderbufferStorageMultisampleEXT");
- } else {
- _supports_framebuffer_multisample = false;
- }
- if (has_extension("GL_EXT_framebuffer_blit")) {
- _supports_framebuffer_blit = true;
- _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
- get_extension_func("glBlitFramebufferEXT");
- } else {
- _supports_framebuffer_blit = false;
- }
- } else {
- _supports_framebuffer_object = false;
- _supports_framebuffer_multisample = false;
- _supports_framebuffer_blit = false;
- _glGenerateMipmap = nullptr;
- }
- #endif
- #ifndef OPENGLES
- if (is_at_least_gl_version(4, 5) || has_extension("GL_ARB_direct_state_access")) {
- _glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)
- get_extension_func("glGenerateTextureMipmap");
- _supports_dsa = true;
- } else {
- _supports_dsa = false;
- }
- #endif
- #ifndef OPENGLES_1
- // Do we support empty framebuffer objects?
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 1)) {
- #else
- if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_framebuffer_no_attachments")) {
- #endif
- _glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)
- get_extension_func("glFramebufferParameteri");
- _supports_empty_framebuffer = true;
- } else {
- _supports_empty_framebuffer = false;
- }
- #endif // !OPENGLES_1
- #ifndef OPENGLES
- _supports_framebuffer_multisample_coverage_nv = false;
- if (_supports_framebuffer_multisample &&
- has_extension("GL_NV_framebuffer_multisample_coverage")) {
- _supports_framebuffer_multisample_coverage_nv = true;
- _glRenderbufferStorageMultisampleCoverage = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)
- get_extension_func("glRenderbufferStorageMultisampleCoverageNV");
- }
- #endif
- #if defined(OPENGLES_1)
- _glDrawBuffers = nullptr;
- _max_color_targets = 1;
- #elif defined(OPENGLES_2)
- if (is_at_least_gles_version(3, 0)) {
- _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
- get_extension_func("glDrawBuffers");
- } else if (has_extension("GL_EXT_draw_buffers")) {
- _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
- get_extension_func("glDrawBuffersEXT");
- } else if (has_extension("GL_NV_draw_buffers")) {
- _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
- get_extension_func("glDrawBuffersNV");
- } else {
- _glDrawBuffers = nullptr;
- }
- #else
- if (is_at_least_gl_version(2, 0)) {
- _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
- get_extension_func("glDrawBuffers");
- } else if (has_extension("GL_ARB_draw_buffers")) {
- _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
- get_extension_func("glDrawBuffersARB");
- } else {
- _glDrawBuffers = nullptr;
- }
- #endif
- #if defined(OPENGLES) && !defined(OPENGLES_1)
- if (is_at_least_gles_version(3, 0)) {
- _glReadBuffer = (PFNGLREADBUFFERPROC)
- get_extension_func("glReadBuffer");
- } else if (has_extension("GL_NV_read_buffer")) {
- _glReadBuffer = (PFNGLREADBUFFERPROC)
- get_extension_func("glReadBufferNV");
- } else {
- _glReadBuffer = nullptr;
- }
- #endif
- #ifndef OPENGLES_1
- _max_color_targets = 1;
- if (_glDrawBuffers != nullptr) {
- GLint max_draw_buffers = 0;
- glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
- _max_color_targets = max_draw_buffers;
- }
- #endif // !OPENGLES_1
- #ifndef OPENGLES_1
- if (_gl_version_major >= 3) {
- _glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)
- get_extension_func("glClearBufferfv");
- _glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)
- get_extension_func("glClearBufferiv");
- _glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)
- get_extension_func("glClearBufferfi");
- } else {
- _glClearBufferfv = nullptr;
- _glClearBufferiv = nullptr;
- _glClearBufferfi = nullptr;
- }
- #endif // !OPENGLES
- #ifndef OPENGLES
- _supports_viewport_arrays = false;
- if (is_at_least_gl_version(4, 1) || has_extension("GL_ARB_viewport_array")) {
- _glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)
- get_extension_func("glViewportArrayv");
- _glScissorArrayv = (PFNGLSCISSORARRAYVPROC)
- get_extension_func("glScissorArrayv");
- _glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)
- get_extension_func("glDepthRangeArrayv");
- if (_glViewportArrayv == nullptr || _glScissorArrayv == nullptr || _glDepthRangeArrayv == nullptr) {
- GLCAT.warning()
- << "Viewport arrays advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- } else {
- _supports_viewport_arrays = true;
- }
- }
- #endif // !OPENGLES
- _max_fb_samples = 0;
- if (_supports_framebuffer_multisample) {
- GLint max_samples;
- glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_samples);
- _max_fb_samples = max_samples;
- }
- _supports_occlusion_query = false;
- #ifndef OPENGLES
- if (gl_support_occlusion_query) {
- if (is_at_least_gl_version(1, 5)) {
- _supports_occlusion_query = true;
- _glGenQueries = (PFNGLGENQUERIESPROC)
- get_extension_func("glGenQueries");
- _glBeginQuery = (PFNGLBEGINQUERYPROC)
- get_extension_func("glBeginQuery");
- _glEndQuery = (PFNGLENDQUERYPROC)
- get_extension_func("glEndQuery");
- _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
- get_extension_func("glDeleteQueries");
- _glGetQueryiv = (PFNGLGETQUERYIVPROC)
- get_extension_func("glGetQueryiv");
- _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
- get_extension_func("glGetQueryObjectuiv");
- } else if (has_extension("GL_ARB_occlusion_query")) {
- _supports_occlusion_query = true;
- _glGenQueries = (PFNGLGENQUERIESPROC)
- get_extension_func("glGenQueriesARB");
- _glBeginQuery = (PFNGLBEGINQUERYPROC)
- get_extension_func("glBeginQueryARB");
- _glEndQuery = (PFNGLENDQUERYPROC)
- get_extension_func("glEndQueryARB");
- _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
- get_extension_func("glDeleteQueriesARB");
- _glGetQueryiv = (PFNGLGETQUERYIVPROC)
- get_extension_func("glGetQueryivARB");
- _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
- get_extension_func("glGetQueryObjectuivARB");
- }
- }
- if (_supports_occlusion_query) {
- if (_glGenQueries == nullptr || _glBeginQuery == nullptr ||
- _glEndQuery == nullptr || _glDeleteQueries == nullptr ||
- _glGetQueryiv == nullptr || _glGetQueryObjectuiv == nullptr) {
- GLCAT.warning()
- << "Occlusion queries advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
- _supports_occlusion_query = false;
- } else {
- GLint num_bits;
- _glGetQueryiv(GL_SAMPLES_PASSED, GL_QUERY_COUNTER_BITS, &num_bits);
- if (num_bits == 0) {
- _supports_occlusion_query = false;
- }
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Occlusion query counter provides " << num_bits << " bits.\n";
- }
- }
- }
- #endif // !OPENGLES
- _supports_timer_query = false;
- #if defined(DO_PSTATS) && !defined(OPENGLES)
- if (is_at_least_gl_version(3, 3) || has_extension("GL_ARB_timer_query")) {
- _supports_timer_query = true;
- _glQueryCounter = (PFNGLQUERYCOUNTERPROC)
- get_extension_func("glQueryCounter");
- _glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)
- get_extension_func("glGetQueryObjecti64v");
- _glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)
- get_extension_func("glGetQueryObjectui64v");
- _glGetInteger64v = (PFNGLGETINTEGER64VPROC)
- get_extension_func("glGetInteger64v");
- }
- #endif
- #ifdef OPENGLES_1
- // In OpenGL ES 1, blending is supported via extensions.
- if (has_extension("GL_OES_blend_subtract")) {
- _glBlendEquation = (PFNGLBLENDEQUATIONPROC)
- get_extension_func("glBlendEquationOES");
- if (_glBlendEquation == nullptr) {
- _glBlendEquation = null_glBlendEquation;
- GLCAT.warning()
- << "BlendEquationOES advertised as supported by OpenGL ES runtime, but "
- "could not get pointer to extension function.\n";
- }
- } else {
- _glBlendEquation = null_glBlendEquation;
- }
- if (has_extension("GL_OES_blend_equation_separate")) {
- _glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEOESPROC)
- get_extension_func("glBlendEquationSeparateOES");
- if (_glBlendEquation == nullptr) {
- _supports_blend_equation_separate = false;
- GLCAT.warning()
- << "BlendEquationSeparateOES advertised as supported by OpenGL ES "
- "runtime, but could not get pointer to extension function.\n";
- } else {
- _supports_blend_equation_separate = true;
- }
- } else {
- _supports_blend_equation_separate = false;
- _glBlendEquationSeparate = nullptr;
- }
- if (has_extension("GL_OES_blend_func_separate")) {
- _glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEOESPROC)
- get_extension_func("glBlendFuncSeparateOES");
- if (_glBlendFuncSeparate == nullptr) {
- _glBlendFuncSeparate = null_glBlendFuncSeparate;
- GLCAT.warning()
- << "BlendFuncSeparateOES advertised as supported by OpenGL ES runtime, but "
- "could not get pointer to extension function.\n";
- }
- } else {
- _glBlendFuncSeparate = null_glBlendFuncSeparate;
- }
- #elif defined(OPENGLES)
- // In OpenGL ES 2.x and above, this is supported in the core.
- _supports_blend_equation_separate = false;
- #else
- if (is_at_least_gl_version(1, 2)) {
- _glBlendEquation = (PFNGLBLENDEQUATIONPROC)
- get_extension_func("glBlendEquation");
- } else if (has_extension("GL_EXT_blend_minmax")) {
- _glBlendEquation = (PFNGLBLENDEQUATIONPROC)
- get_extension_func("glBlendEquationEXT");
- } else {
- _glBlendEquation = null_glBlendEquation;
- }
- if (_glBlendEquation == nullptr) {
- _glBlendEquation = null_glBlendEquation;
- GLCAT.warning()
- << "BlendEquation advertised as supported by OpenGL runtime, but could "
- "not get pointer to extension function.\n";
- }
- if (is_at_least_gl_version(2, 0)) {
- _supports_blend_equation_separate = true;
- _glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)
- get_extension_func("glBlendEquationSeparate");
- } else if (has_extension("GL_EXT_blend_equation_separate")) {
- _supports_blend_equation_separate = true;
- _glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEEXTPROC)
- get_extension_func("glBlendEquationSeparateEXT");
- } else {
- _supports_blend_equation_separate = false;
- _glBlendEquationSeparate = nullptr;
- }
- if (_supports_blend_equation_separate && _glBlendEquationSeparate == nullptr) {
- _supports_blend_equation_separate = false;
- GLCAT.warning()
- << "BlendEquationSeparate advertised as supported by OpenGL runtime, "
- "but could not get pointer to extension function.\n";
- }
- if (is_at_least_gl_version(1, 4)) {
- _glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)
- get_extension_func("glBlendFuncSeparate");
- } else if (has_extension("GL_EXT_blend_func_separate")) {
- _glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEEXTPROC)
- get_extension_func("glBlendFuncSeparateEXT");
- } else {
- _glBlendFuncSeparate = null_glBlendFuncSeparate;
- }
- if (_glBlendFuncSeparate == nullptr) {
- _glBlendFuncSeparate = null_glBlendFuncSeparate;
- GLCAT.warning()
- << "BlendFuncSeparate advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- }
- #endif
- // In OpenGL ES 2.x, this is supported in the core. In 1.x, not at all.
- #ifndef OPENGLES
- _glBlendColor = nullptr;
- bool supports_blend_color = false;
- if (is_at_least_gl_version(1, 2)) {
- supports_blend_color = true;
- _glBlendColor = (PFNGLBLENDCOLORPROC)
- get_extension_func("glBlendColor");
- } else if (has_extension("GL_EXT_blend_color")) {
- supports_blend_color = true;
- _glBlendColor = (PFNGLBLENDCOLORPROC)
- get_extension_func("glBlendColorEXT");
- }
- if (supports_blend_color && _glBlendColor == nullptr) {
- GLCAT.warning()
- << "BlendColor advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- }
- if (_glBlendColor == nullptr) {
- _glBlendColor = null_glBlendColor;
- }
- #endif
- #ifdef OPENGLES_1
- // OpenGL ES 1 doesn't support dual-source blending.
- #elif defined(OPENGLES)
- _supports_dual_source_blending = has_extension("GL_EXT_blend_func_extended");
- #else
- _supports_dual_source_blending =
- is_at_least_gl_version(3, 3) || has_extension("GL_ARB_blend_func_extended");
- #endif
- #ifdef OPENGLES
- _edge_clamp = GL_CLAMP_TO_EDGE;
- #else
- _edge_clamp = GL_CLAMP;
- if (is_at_least_gl_version(1, 2) || is_at_least_gles_version(1, 1) ||
- has_extension("GL_SGIS_texture_edge_clamp")) {
- _edge_clamp = GL_CLAMP_TO_EDGE;
- }
- #endif
- _border_clamp = _edge_clamp;
- #ifndef OPENGLES
- if (gl_support_clamp_to_border &&
- (is_at_least_gl_version(1, 3) ||
- has_extension("GL_ARB_texture_border_clamp"))) {
- _border_clamp = GL_CLAMP_TO_BORDER;
- }
- #endif
- #ifdef OPENGLES_1
- _mirror_repeat = GL_REPEAT;
- if (has_extension("GL_OES_texture_mirrored_repeat")) {
- _mirror_repeat = GL_MIRRORED_REPEAT;
- }
- #elif defined(OPENGLES)
- // OpenGL 2.x and above support this in the core.
- _mirror_repeat = GL_MIRRORED_REPEAT;
- #else
- _mirror_repeat = GL_REPEAT;
- if (is_at_least_gl_version(1, 4) ||
- has_extension("GL_ARB_texture_mirrored_repeat")) {
- _mirror_repeat = GL_MIRRORED_REPEAT;
- }
- #endif
- _mirror_clamp = _edge_clamp;
- _mirror_edge_clamp = _edge_clamp;
- _mirror_border_clamp = _border_clamp;
- #ifndef OPENGLES
- if (has_extension("GL_EXT_texture_mirror_clamp")) {
- _mirror_clamp = GL_MIRROR_CLAMP_EXT;
- _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT;
- _mirror_border_clamp = GL_MIRROR_CLAMP_TO_BORDER_EXT;
- } else if (is_at_least_gl_version(4, 4) ||
- has_extension("GL_ARB_texture_mirror_clamp_to_edge")) {
- _mirror_clamp = GL_MIRROR_CLAMP_TO_EDGE;
- _mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE;
- }
- #endif
- #ifdef OPENGLES
- _supports_texture_lod = is_at_least_gles_version(3, 0);
- _supports_texture_lod_bias = false;
- #else
- _supports_texture_lod = false;
- _supports_texture_lod_bias = false;
- if (gl_support_texture_lod &&
- (is_at_least_gl_version(1, 2) || has_extension("GL_SGIS_texture_lod"))) {
- _supports_texture_lod = true;
- if (is_at_least_gl_version(1, 4) || has_extension("GL_EXT_texture_lod_bias")) {
- _supports_texture_lod_bias = true;
- }
- }
- #endif
- #ifdef OPENGLES
- _supports_texture_max_level = is_at_least_gles_version(3, 0) ||
- has_extension("GL_APPLE_texture_max_level");
- #else
- _supports_texture_max_level = is_at_least_gl_version(1, 2);
- #endif
- if (_supports_multisample) {
- GLint sample_buffers = 0;
- glGetIntegerv(GL_SAMPLE_BUFFERS, &sample_buffers);
- if (sample_buffers != 1) {
- // Even if the API supports multisample, we might have ended up with a
- // framebuffer that doesn't have any multisample bits. (It's also
- // possible the graphics card doesn't provide any framebuffers with
- // multisample.) In this case, we don't really support the multisample
- // API's, since they won't do anything.
- _supports_multisample = false;
- }
- }
- GLint max_texture_size = 0;
- GLint max_3d_texture_size = 0;
- GLint max_2d_texture_array_layers = 0;
- GLint max_cube_map_size = 0;
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
- _max_texture_dimension = max_texture_size;
- if (_supports_3d_texture) {
- #ifndef OPENGLES_1
- glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
- #endif
- _max_3d_texture_dimension = max_3d_texture_size;
- } else {
- _max_3d_texture_dimension = 0;
- }
- #ifndef OPENGLES_1
- if (_supports_2d_texture_array) {
- glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_2d_texture_array_layers);
- _max_2d_texture_array_layers = max_2d_texture_array_layers;
- }
- #endif
- if (_supports_cube_map) {
- glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_size);
- _max_cube_map_dimension = max_cube_map_size;
- } else {
- _max_cube_map_dimension = 0;
- }
- #ifndef OPENGLES
- if (_supports_buffer_texture) {
- GLint max_buffer_texture_size = 0;
- glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max_buffer_texture_size);
- _max_buffer_texture_size = max_buffer_texture_size;
- } else {
- _max_buffer_texture_size = 0;
- }
- #endif // !OPENGLES
- #ifndef OPENGLES
- GLint max_elements_vertices = 0, max_elements_indices = 0;
- if (is_at_least_gl_version(1, 2) || has_extension("GL_EXT_draw_range_elements")) {
- glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices);
- glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &max_elements_indices);
- if (max_elements_vertices > 0) {
- _max_vertices_per_array = max_elements_vertices;
- }
- if (max_elements_indices > 0) {
- _max_vertices_per_primitive = max_elements_indices;
- }
- }
- #endif // OPENGLES
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "max texture dimension = " << _max_texture_dimension
- << ", max 3d texture = " << _max_3d_texture_dimension
- << ", max 2d texture array = " << max_2d_texture_array_layers
- << ", max cube map = " << _max_cube_map_dimension << "\n";
- #ifndef OPENGLES
- GLCAT.debug()
- << "max_elements_vertices = " << max_elements_vertices
- << ", max_elements_indices = " << max_elements_indices << "\n";
- #endif
- if (_supports_buffers) {
- if (vertex_buffers) {
- GLCAT.debug()
- << "vertex buffer objects are supported.\n";
- } else {
- GLCAT.debug()
- << "vertex buffer objects are supported (but not enabled).\n";
- }
- } else {
- GLCAT.debug()
- << "vertex buffer objects are NOT supported.\n";
- }
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (!vertex_arrays) {
- GLCAT.debug()
- << "immediate mode commands will be used instead of vertex arrays.\n";
- }
- #endif
- if (!_supports_compressed_texture) {
- GLCAT.debug()
- << "Texture compression is not supported.\n";
- } else {
- GLint num_compressed_formats = 0;
- glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &num_compressed_formats);
- if (num_compressed_formats == 0) {
- GLCAT.debug()
- << "No specific compressed texture formats are supported.\n";
- } else {
- #ifndef NDEBUG
- GLCAT.debug()
- << "Supported compressed texture formats:\n";
- GLint *formats = (GLint *)alloca(num_compressed_formats * sizeof(GLint));
- glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
- for (int i = 0; i < num_compressed_formats; ++i) {
- const char *format_str = get_compressed_format_string(formats[i]);
- if (format_str != nullptr) {
- GLCAT.debug(false) << " " << format_str << '\n';
- } else {
- GLCAT.debug(false)
- << " Unknown compressed format 0x" << hex << formats[i]
- << dec << "\n";
- }
- }
- #endif
- }
- }
- }
- _active_texture_stage = -1;
- _num_active_texture_stages = 0;
- // Check availability of anisotropic texture filtering.
- _supports_anisotropy = false;
- _max_anisotropy = 1.0;
- if (is_at_least_gl_version(4, 6) ||
- has_extension("GL_EXT_texture_filter_anisotropic") ||
- has_extension("GL_ARB_texture_filter_anisotropic")) {
- GLfloat max_anisotropy;
- glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
- _max_anisotropy = (PN_stdfloat)max_anisotropy;
- _supports_anisotropy = true;
- }
- // Check availability of image read/write functionality in shaders.
- _max_image_units = 0;
- #ifndef OPENGLES_1
- #ifdef OPENGLES
- if (is_at_least_gl_version(3, 1)) {
- #else
- if (is_at_least_gl_version(4, 2) || has_extension("GL_ARB_shader_image_load_store")) {
- #endif
- _glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)
- get_extension_func("glBindImageTexture");
- _glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)
- get_extension_func("glMemoryBarrier");
- glGetIntegerv(GL_MAX_IMAGE_UNITS, &_max_image_units);
- #ifndef OPENGLES
- } else if (has_extension("GL_EXT_shader_image_load_store")) {
- _glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)
- get_extension_func("glBindImageTextureEXT");
- _glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)
- get_extension_func("glMemoryBarrierEXT");
- glGetIntegerv(GL_MAX_IMAGE_UNITS_EXT, &_max_image_units);
- #endif
- } else {
- _glBindImageTexture = nullptr;
- _glMemoryBarrier = nullptr;
- }
- #endif // !OPENGLES_1
- _supports_sampler_objects = false;
- #ifndef OPENGLES_1
- if (gl_support_sampler_objects &&
- #ifdef OPENGLES
- is_at_least_gles_version(3, 0)) {
- #else
- (is_at_least_gl_version(3, 3) || has_extension("GL_ARB_sampler_objects"))) {
- #endif
- _glGenSamplers = (PFNGLGENSAMPLERSPROC) get_extension_func("glGenSamplers");
- _glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) get_extension_func("glDeleteSamplers");
- _glBindSampler = (PFNGLBINDSAMPLERPROC) get_extension_func("glBindSampler");
- _glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) get_extension_func("glSamplerParameteri");
- _glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) get_extension_func("glSamplerParameteriv");
- _glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) get_extension_func("glSamplerParameterf");
- _glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) get_extension_func("glSamplerParameterfv");
- if (_glGenSamplers == nullptr || _glDeleteSamplers == nullptr ||
- _glBindSampler == nullptr || _glSamplerParameteri == nullptr ||
- _glSamplerParameteriv == nullptr || _glSamplerParameterf == nullptr ||
- _glSamplerParameterfv == nullptr) {
- GLCAT.warning()
- << "GL_ARB_sampler_objects advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- } else {
- _supports_sampler_objects = true;
- }
- }
- #endif // !OPENGLES_1
- // Check availability of multi-bind functions.
- _supports_multi_bind = false;
- #ifndef OPENGLES
- if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_multi_bind")) {
- _glBindTextures = (PFNGLBINDTEXTURESPROC)
- get_extension_func("glBindTextures");
- _glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)
- get_extension_func("glBindImageTextures");
- if (_supports_sampler_objects) {
- _glBindSamplers = (PFNGLBINDSAMPLERSPROC)
- get_extension_func("glBindSamplers");
- }
- if (_use_vertex_attrib_binding) {
- _glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)
- get_extension_func("glBindVertexBuffers");
- }
- if (_glBindTextures != nullptr && _glBindImageTextures != nullptr) {
- _supports_multi_bind = true;
- } else {
- GLCAT.warning()
- << "ARB_multi_bind advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- }
- }
- #endif // !OPENGLES
- #ifndef OPENGLES_1
- #ifdef OPENGLES
- if (is_at_least_gl_version(3, 0)) {
- #else
- if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_internalformat_query2")) {
- #endif
- _glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)
- get_extension_func("glGetInternalformativ");
- if (_glGetInternalformativ == nullptr) {
- GLCAT.warning()
- << "ARB_internalformat_query2 advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- }
- }
- #endif // !OPENGLES_1
- _supports_bindless_texture = false;
- #ifndef OPENGLES
- if (has_extension("GL_ARB_bindless_texture")) {
- _glGetTextureHandle = (PFNGLGETTEXTUREHANDLEPROC)
- get_extension_func("glGetTextureHandleARB");
- _glGetTextureSamplerHandle = (PFNGLGETTEXTURESAMPLERHANDLEPROC)
- get_extension_func("glGetTextureSamplerHandleARB");
- _glMakeTextureHandleResident = (PFNGLMAKETEXTUREHANDLERESIDENTPROC)
- get_extension_func("glMakeTextureHandleResidentARB");
- _glUniformHandleui64 = (PFNGLUNIFORMHANDLEUI64PROC)
- get_extension_func("glUniformHandleui64ARB");
- if (_glGetTextureHandle == nullptr || _glMakeTextureHandleResident == nullptr ||
- _glUniformHandleui64 == nullptr) {
- GLCAT.warning()
- << "GL_ARB_bindless_texture advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
- } else {
- _supports_bindless_texture = true;
- }
- }
- #endif // !OPENGLES
- #ifndef OPENGLES_1
- _supports_get_program_binary = false;
- _program_binary_formats.clear();
- #ifdef OPENGLES
- if (is_at_least_gles_version(3, 0)) {
- #else
- if (is_at_least_gl_version(4, 1) || has_extension("GL_ARB_get_program_binary")) {
- #endif
- _glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)
- get_extension_func("glGetProgramBinary");
- _glProgramBinary = (PFNGLPROGRAMBINARYPROC)
- get_extension_func("glProgramBinary");
- _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
- get_extension_func("glProgramParameteri");
- GLint num_binary_formats = 0;
- if (_glGetProgramBinary != nullptr &&
- _glProgramBinary != nullptr &&
- _glProgramParameteri != nullptr) {
- glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_binary_formats);
- }
- if (num_binary_formats > 0) {
- _supports_get_program_binary = true;
- GLenum *binary_formats = (GLenum *)alloca(sizeof(GLenum) * num_binary_formats);
- glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, (GLint *)binary_formats);
- for (int i = 0; i < num_binary_formats; ++i) {
- _program_binary_formats.insert(binary_formats[i]);
- }
- }
- }
- #endif // !OPENGLES_1
- report_my_gl_errors();
- if (core_profile) {
- // TODO: better detection mechanism?
- _supports_stencil = support_stencil;
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- else if (support_stencil) {
- GLint num_stencil_bits;
- glGetIntegerv(GL_STENCIL_BITS, &num_stencil_bits);
- _supports_stencil = (num_stencil_bits != 0);
- }
- #endif
- #ifdef OPENGLES_1
- _supports_stencil_wrap = has_extension("GL_OES_stencil_wrap");
- #elif defined(OPENGLES)
- _supports_stencil_wrap = true;
- #else
- _supports_stencil_wrap = is_at_least_gl_version(1, 4) ||
- has_extension("GL_EXT_stencil_wrap");
- #endif
- _supports_two_sided_stencil = false;
- #ifndef OPENGLES
- //TODO: support the two-sided stencil functions that ended up in core.
- if (has_extension("GL_EXT_stencil_two_side")) {
- _glActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)
- get_extension_func("glActiveStencilFaceEXT");
- _supports_two_sided_stencil = true;
- } else {
- _glActiveStencilFaceEXT = 0;
- }
- #endif
- // Ensure the initial state is what we say it should be (in some cases, we
- // don't want the GL default settings; in others, we have to force the point
- // with some drivers that aren't strictly compliant w.r.t. initial
- // settings).
- glFrontFace(GL_CCW);
- #ifndef OPENGLES_2
- glDisable(GL_LINE_SMOOTH);
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- glDisable(GL_POINT_SMOOTH);
- }
- #endif
- #ifndef OPENGLES
- glDisable(GL_POLYGON_SMOOTH);
- #endif // OPENGLES
- #ifndef OPENGLES_2
- if (_supports_multisample) {
- glDisable(GL_MULTISAMPLE);
- }
- #endif
- // Set depth range from zero to one if requested.
- #ifndef OPENGLES
- _use_depth_zero_to_one = false;
- _use_remapped_depth_range = false;
- if (gl_depth_zero_to_one) {
- if (is_at_least_gl_version(4, 5) || has_extension("GL_ARB_clip_control")) {
- PFNGLCLIPCONTROLPROC pglClipControl =
- (PFNGLCLIPCONTROLPROC)get_extension_func("glClipControl");
- if (pglClipControl != nullptr) {
- pglClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
- _use_depth_zero_to_one = true;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Set zero-to-one depth using glClipControl\n";
- }
- }
- }/* else if (has_extension("GL_NV_depth_buffer_float")) {
- // Alternatively, all GeForce 8+ and even some AMD drivers support this
- // extension, which (unlike the core glDepthRange, which clamps its
- // input parameters) can compensate for the built-in depth remapping.
- _glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)get_extension_func("glDepthRangedNV");
- if (_glDepthRangedNV != nullptr) {
- _glDepthRangedNV(-1.0, 1.0);
- _use_depth_zero_to_one = true;
- _use_remapped_depth_range = true;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Set zero-to-one depth using glDepthRangedNV\n";
- }
- }
- }*/
- if (!_use_depth_zero_to_one) {
- GLCAT.warning()
- << "Zero-to-one depth was requested, but driver does not support it.\n";
- }
- }
- #endif
- // Set up all the enableddisabled flags to GL's known initial values:
- // everything off.
- _multisample_mode = 0;
- _line_smooth_enabled = false;
- _point_smooth_enabled = false;
- _polygon_smooth_enabled = false;
- _stencil_test_enabled = false;
- _blend_enabled = false;
- _depth_test_enabled = false;
- _fog_enabled = false;
- _alpha_test_enabled = false;
- _polygon_offset_enabled = false;
- _flat_shade_model = false;
- _decal_level = 0;
- _active_color_write_mask = ColorWriteAttrib::C_all;
- _tex_gen_point_sprite = false;
- #ifndef OPENGLES_1
- _enabled_vertex_attrib_arrays.clear();
- memset(_vertex_attrib_divisors, 0, sizeof(GLuint) * 32);
- #endif
- // Dither is on by default in GL; let's turn it off
- glDisable(GL_DITHER);
- _dithering_enabled = false;
- #ifndef OPENGLES_1
- _current_shader = nullptr;
- _current_shader_context = nullptr;
- _vertex_array_shader = nullptr;
- _vertex_array_shader_context = nullptr;
- _texture_binding_shader = nullptr;
- _texture_binding_shader_context = nullptr;
- #endif
- // Count the max number of lights
- _max_lights = 0;
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- GLint max_lights = 0;
- glGetIntegerv(GL_MAX_LIGHTS, &max_lights);
- _max_lights = max_lights;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "max lights = " << _max_lights << "\n";
- }
- }
- #endif
- // Count the max number of clipping planes
- _max_clip_planes = 0;
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- GLint max_clip_planes = 0;
- glGetIntegerv(GL_MAX_CLIP_PLANES, &max_clip_planes);
- _max_clip_planes = max_clip_planes;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "max clip planes = " << _max_clip_planes << "\n";
- }
- }
- #endif
- _max_texture_stages = 1;
- #ifdef SUPPORT_FIXED_FUNCTION
- if (supports_multitexture && has_fixed_function_pipeline()) {
- GLint max_texture_stages = 0;
- glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_stages);
- _max_texture_stages = max_texture_stages;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "max texture stages = " << _max_texture_stages << "\n";
- }
- }
- #endif
- _current_vbuffer_index = 0;
- _current_ibuffer_index = 0;
- _current_vao_index = 0;
- _current_fbo = 0;
- _auto_antialias_mode = false;
- _render_mode = RenderModeAttrib::M_filled;
- _point_size = 1.0f;
- _point_perspective = false;
- #ifndef OPENGLES
- _current_vertex_buffers.clear();
- _current_vertex_format.clear();
- memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
- _current_sbuffer_index = 0;
- _current_sbuffer_base.clear();
- #endif
- report_my_gl_errors();
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- if (gl_cheap_textures) {
- GLCAT.info()
- << "Setting glHint() for fastest textures.\n";
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
- }
- // Use per-vertex fog if per-pixel fog requires SW renderer
- glHint(GL_FOG_HINT, GL_DONT_CARE);
- }
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- GLint num_red_bits = 0;
- glGetIntegerv(GL_RED_BITS, &num_red_bits);
- if (num_red_bits < 8) {
- glEnable(GL_DITHER);
- _dithering_enabled = true;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "frame buffer depth = " << num_red_bits
- << " bits/channel, enabling dithering\n";
- }
- }
- }
- #endif
- _error_count = 0;
- report_my_gl_errors();
- #ifndef OPENGLES_1
- if (GLCAT.is_debug()) {
- if (_supports_get_program_binary) {
- GLCAT.debug()
- << "Supported program binary formats:\n";
- GLCAT.debug() << " ";
- pset<GLenum>::const_iterator it;
- for (it = _program_binary_formats.begin();
- it != _program_binary_formats.end(); ++it) {
- char number[16];
- sprintf(number, "0x%04X", *it);
- GLCAT.debug(false) << " " << number << "";
- }
- GLCAT.debug(false) << "\n";
- } else {
- GLCAT.debug() << "No program binary formats supported.\n";
- }
- }
- #endif
- // Do we guarantee that we can apply the color scale via a shader? We set
- // this false if there is a chance that the fixed-function pipeline is used.
- _runtime_color_scale = !has_fixed_function_pipeline();
- #ifndef OPENGLES
- if (_gl_shadlang_ver_major >= 4 || has_extension("GL_NV_gpu_program5")) {
- // gp5fp - OpenGL fragment profile for GeForce 400 Series and up
- _shader_model = SM_50;
- } else if (_gl_shadlang_ver_major >= 3 ||
- has_extension("GL_NV_gpu_program4")) {
- // gp4fp - OpenGL fragment profile for G8x (GeForce 8xxx and up)
- _shader_model = SM_40;
- } else if (has_extension("GL_NV_fragment_program2")) {
- // fp40 - OpenGL fragment profile for NV4x (GeForce 6xxx and 7xxx Series,
- // NV4x-based Quadro FX, etc.)
- _shader_model = SM_30;
- } else if (has_extension("GL_NV_fragment_program")) {
- // fp30 - OpenGL fragment profile for NV3x (GeForce FX, Quadro FX, etc.)
- _shader_model = SM_2X;
- } else if (_gl_shadlang_ver_major >= 1 ||
- has_extension("GL_ARB_fragment_program")) {
- // This OpenGL profile corresponds to the per-fragment functionality
- // introduced by GeForce FX and other DirectX 9 GPUs.
- _shader_model = SM_20;
- } else if (has_extension("GL_NV_texture_shader2")) {
- // fp20 - OpenGL fragment profile for NV2x (GeForce3, GeForce4 Ti, Quadro
- // DCC, etc.)
- _shader_model = SM_11;
- } else {
- // No shader support
- _shader_model = SM_00;
- }
- // DisplayInformation may have better shader model detection
- {
- GraphicsPipe *pipe;
- DisplayInformation *display_information;
- pipe = this->get_pipe();
- if (pipe) {
- display_information = pipe->get_display_information ();
- if (display_information) {
- if (display_information->get_shader_model() > _shader_model) {
- _shader_model = display_information->get_shader_model();
- }
- }
- }
- }
- _auto_detect_shader_model = _shader_model;
- if (GLCAT.is_debug()) {
- #ifdef HAVE_CG
- #if CG_VERSION_NUM >= 2200
- GLCAT.debug() << "Supported Cg profiles:\n";
- int num_profiles = cgGetNumSupportedProfiles();
- for (int i = 0; i < num_profiles; ++i) {
- CGprofile profile = cgGetSupportedProfile(i);
- if (cgGLIsProfileSupported(profile)) {
- GLCAT.debug() << " " << cgGetProfileString(profile) << "\n";
- }
- }
- #endif // CG_VERSION_NUM >= 2200
- #if CG_VERSION_NUM >= 3100
- GLCAT.debug() << "Cg GLSL version = "
- << cgGLGetGLSLVersionString(cgGLDetectGLSLVersion()) << "\n";
- #endif
- GLCAT.debug()
- << "Cg latest vertex profile = "
- << cgGetProfileString(cgGLGetLatestProfile(CG_GL_VERTEX)) << "\n";
- GLCAT.debug()
- << "Cg latest fragment profile = "
- << cgGetProfileString(cgGLGetLatestProfile(CG_GL_FRAGMENT)) << "\n";
- #if CG_VERSION_NUM >= 2000
- GLCAT.debug()
- << "Cg latest geometry profile = "
- << cgGetProfileString(cgGLGetLatestProfile(CG_GL_GEOMETRY)) << "\n";
- #endif
- GLCAT.debug() << "basic-shaders-only " << basic_shaders_only << "\n";
- GLCAT.debug()
- << "Cg active vertex profile = "
- << cgGetProfileString((CGprofile)_shader_caps._active_vprofile) << "\n";
- GLCAT.debug()
- << "Cg active fragment profile = "
- << cgGetProfileString((CGprofile)_shader_caps._active_fprofile) << "\n";
- GLCAT.debug()
- << "Cg active geometry profile = "
- << cgGetProfileString((CGprofile)_shader_caps._active_gprofile) << "\n";
- #endif // HAVE_CG
- GLCAT.debug() << "shader model = " << _shader_model << "\n";
- }
- #endif // !OPENGLES
- // OpenGL core profile requires a VAO to be bound. It's a bit silly,
- // because we can just bind a VAO and then forget about it.
- #if !defined(OPENGLES)
- if (core_profile) {
- if (_supports_vao) {
- _glGenVertexArrays(1, &_current_vao_index);
- _glBindVertexArray(_current_vao_index);
- } else {
- GLCAT.error()
- << "Core profile enabled, but vertex array objects not supported!\n";
- }
- }
- #endif
- pop_group_marker();
- // Now that the GSG has been initialized, make it available for
- // optimizations.
- add_gsg(this);
- }
- /**
- * Force the graphics card to finish drawing before returning. !!!!!HACK
- * WARNING!!!! glfinish does not actually wait for the graphics card to finish
- * drawing only for draw calls to finish. Thus flip may not happene
- * immediately. Instead we read a single pixel from the framebuffer. This
- * forces the graphics card to finish drawing the frame before returning.
- */
- void CLP(GraphicsStateGuardian)::
- finish() {
- // Rather than call glfinish which returns immediately if draw commands have
- // been submitted, we will read a single pixel from the frame. That will
- // force the graphics card to finish drawing before it is called
- char data[4];
- glReadPixels(0,0,1,1,GL_RGBA,GL_UNSIGNED_BYTE,&data);
- // glFinish();
- }
- /**
- * Clears the framebuffer within the current DisplayRegion, according to the
- * flags indicated by the given DrawableRegion object.
- *
- * This does not set the DisplayRegion first. You should call
- * prepare_display_region() to specify the region you wish the clear operation
- * to apply to.
- */
- void CLP(GraphicsStateGuardian)::
- clear(DrawableRegion *clearable) {
- report_my_gl_errors();
- if (!clearable->is_any_clear_active()) {
- return;
- }
- // XXX rdb: Is this line really necessary? Could we perhaps just reset the
- // color write mask and other relevant attributes?
- set_state_and_transform(RenderState::make_empty(), _internal_transform);
- int mask = 0;
- #ifndef OPENGLES_1
- if (_current_fbo != 0 && _glClearBufferfv != nullptr) {
- // We can use glClearBuffer to clear all the color attachments, which
- // protects us from the overhead of having to call set_draw_buffer for
- // every single attachment.
- int index = 0;
- if (_current_properties->get_color_bits() > 0) {
- if (_current_properties->is_stereo()) {
- // Clear both left and right attachments.
- if (clearable->get_clear_active(GraphicsOutput::RTP_color)) {
- LColorf v = LCAST(float, clearable->get_clear_value(GraphicsOutput::RTP_color));
- _glClearBufferfv(GL_COLOR, index, v.get_data());
- _glClearBufferfv(GL_COLOR, index + 1, v.get_data());
- }
- index += 2;
- } else {
- if (clearable->get_clear_active(GraphicsOutput::RTP_color)) {
- LColorf v = LCAST(float, clearable->get_clear_value(GraphicsOutput::RTP_color));
- _glClearBufferfv(GL_COLOR, index, v.get_data());
- }
- ++index;
- }
- }
- for (int i = 0; i < _current_properties->get_aux_rgba(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_rgba_0 + i;
- if (clearable->get_clear_active(layerid)) {
- LColorf v = LCAST(float, clearable->get_clear_value(layerid));
- _glClearBufferfv(GL_COLOR, index, v.get_data());
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_hrgba(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_hrgba_0 + i;
- if (clearable->get_clear_active(layerid)) {
- LColorf v = LCAST(float, clearable->get_clear_value(layerid));
- _glClearBufferfv(GL_COLOR, index, v.get_data());
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_float(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_float_0 + i;
- if (clearable->get_clear_active(layerid)) {
- LColorf v = LCAST(float, clearable->get_clear_value(layerid));
- _glClearBufferfv(GL_COLOR, index, v.get_data());
- }
- ++index;
- }
- } else
- #endif
- {
- if (_current_properties->get_aux_mask() != 0) {
- for (int i = 0; i < _current_properties->get_aux_rgba(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_rgba_0 + i;
- int layerbit = RenderBuffer::T_aux_rgba_0 << i;
- if (clearable->get_clear_active(layerid)) {
- LColor v = clearable->get_clear_value(layerid);
- glClearColor(v[0], v[1], v[2], v[3]);
- set_draw_buffer(layerbit);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- }
- for (int i = 0; i < _current_properties->get_aux_hrgba(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_hrgba_0 + i;
- int layerbit = RenderBuffer::T_aux_hrgba_0 << i;
- if (clearable->get_clear_active(layerid)) {
- LColor v = clearable->get_clear_value(layerid);
- glClearColor(v[0], v[1], v[2], v[3]);
- set_draw_buffer(layerbit);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- }
- for (int i = 0; i < _current_properties->get_aux_float(); ++i) {
- int layerid = GraphicsOutput::RTP_aux_float_0 + i;
- int layerbit = RenderBuffer::T_aux_float_0 << i;
- if (clearable->get_clear_active(layerid)) {
- LColor v = clearable->get_clear_value(layerid);
- glClearColor(v[0], v[1], v[2], v[3]);
- set_draw_buffer(layerbit);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- }
- // In the past, it was possible to set the draw buffer once in
- // prepare_display_region and then forget about it. Now, with aux
- // layers, it is necessary to occasionally change the draw buffer. In
- // time, I think there will need to be a draw buffer attrib. Until
- // then, this little hack to put things back the way they were after
- // prepare_display_region will do.
- set_draw_buffer(_draw_buffer_type);
- }
- if (_current_properties->get_color_bits() > 0) {
- if (clearable->get_clear_color_active()) {
- LColor v = clearable->get_clear_color();
- glClearColor(v[0], v[1], v[2], v[3]);
- clear_color_write_mask();
- _state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
- mask |= GL_COLOR_BUFFER_BIT;
- }
- }
- }
- if (clearable->get_clear_depth_active()) {
- #ifdef OPENGLES
- glClearDepthf(clearable->get_clear_depth());
- #else
- glClearDepth(clearable->get_clear_depth());
- #endif // OPENGLES
- #ifdef GSG_VERBOSE
- GLCAT.spam()
- << "glDepthMask(GL_TRUE)" << endl;
- #endif
- glDepthMask(GL_TRUE);
- _state_mask.clear_bit(DepthWriteAttrib::get_class_slot());
- mask |= GL_DEPTH_BUFFER_BIT;
- }
- if (_supports_stencil && clearable->get_clear_stencil_active()) {
- glStencilMask(~0);
- glClearStencil(clearable->get_clear_stencil());
- mask |= GL_STENCIL_BUFFER_BIT;
- }
- if (mask != 0) {
- glClear(mask);
- if (GLCAT.is_spam()) {
- string clear_flags;
- if (mask & GL_COLOR_BUFFER_BIT) {
- clear_flags += " | GL_COLOR_BUFFER_BIT";
- }
- if (mask & GL_DEPTH_BUFFER_BIT) {
- clear_flags += " | GL_DEPTH_BUFFER_BIT";
- }
- if (mask & GL_STENCIL_BUFFER_BIT) {
- clear_flags += " | GL_STENCIL_BUFFER_BIT";
- }
- #ifndef OPENGLES
- if (mask & GL_ACCUM_BUFFER_BIT) {
- clear_flags += " | GL_ACCUM_BUFFER_BIT";
- }
- #endif
- GLCAT.spam() << "glClear(" << (clear_flags.c_str() + 3) << ")\n";
- }
- }
- report_my_gl_errors();
- }
- /**
- * Prepare a display region for rendering (set up scissor region and viewport)
- */
- void CLP(GraphicsStateGuardian)::
- prepare_display_region(DisplayRegionPipelineReader *dr) {
- nassertv(dr != nullptr);
- GraphicsStateGuardian::prepare_display_region(dr);
- int l, b, w, h;
- dr->get_region_pixels(l, b, w, h);
- _viewport_x = l;
- _viewport_y = b;
- _viewport_width = w;
- _viewport_height = h;
- GLint x = GLint(l);
- GLint y = GLint(b);
- GLsizei width = GLsizei(w);
- GLsizei height = GLsizei(h);
- _draw_buffer_type = dr->get_object()->get_draw_buffer_type() & _current_properties->get_buffer_mask() & _stereo_buffer_mask;
- _draw_buffer_type |= _current_properties->get_aux_mask();
- set_draw_buffer(_draw_buffer_type);
- int count = dr->get_num_regions();
- if (dr->get_scissor_enabled()) {
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glEnable(GL_SCISSOR_TEST)\n";
- }
- glEnable(GL_SCISSOR_TEST);
- _scissor_enabled = true;
- _scissor_array.resize(count);
- } else {
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glDisable(GL_SCISSOR_TEST)\n";
- }
- glDisable(GL_SCISSOR_TEST);
- _scissor_enabled = false;
- _scissor_array.clear();
- }
- _scissor_attrib_active = false;
- #ifndef OPENGLES
- if (_supports_viewport_arrays) {
- GLfloat *viewports = (GLfloat *)alloca(sizeof(GLfloat) * 4 * count);
- // We store the scissor regions in a vector since we may need to switch
- // back to it in do_issue_scissor.
- for (int i = 0; i < count; ++i) {
- LVecBase4i sr;
- dr->get_region_pixels(i, sr[0], sr[1], sr[2], sr[3]);
- GLfloat *vr = viewports + i * 4;
- vr[0] = (GLfloat) sr[0];
- vr[1] = (GLfloat) sr[1];
- vr[2] = (GLfloat) sr[2];
- vr[3] = (GLfloat) sr[3];
- if (_scissor_enabled) {
- _scissor_array[i] = sr;
- }
- }
- _glViewportArrayv(0, count, viewports);
- if (_scissor_enabled) {
- _glScissorArrayv(0, count, _scissor_array[0].get_data());
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glViewportArrayv(0, " << count << ",";
- for (int i = 0; i < count; ++i) {
- GLfloat *vr = viewports + i * 4;
- GLCAT.spam(false) << " [" << vr[0] << " " << vr[1] << " " << vr[2] << " " << vr[3] << "]";
- }
- GLCAT.spam(false) << ")\n";
- if (_scissor_enabled) {
- GLCAT.spam()
- << "glScissorArrayv(0, " << count << ",";
- for (int i = 0; i < count; ++i) {
- const LVecBase4i &sr = _scissor_array[i];
- GLCAT.spam(false) << " [" << sr << "]";
- }
- GLCAT.spam(false) << ")\n";
- }
- }
- } else
- #endif // OPENGLES
- {
- glViewport(x, y, width, height);
- if (_scissor_enabled) {
- glScissor(x, y, width, height);
- _scissor_array.resize(1);
- _scissor_array[0].set(x, y, width, height);
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glViewport(" << x << ", " << y << ", " << width << ", " << height << ")\n";
- if (dr->get_scissor_enabled()) {
- GLCAT.spam()
- << "glScissor(" << x << ", " << y << ", " << width << ", " << height << ")\n";
- }
- }
- }
- report_my_gl_errors();
- }
- /**
- * Resets any non-standard graphics state that might give a callback apoplexy.
- * Some drivers require that the graphics state be restored to neutral before
- * performing certain operations. In OpenGL, for instance, this closes any
- * open vertex buffers.
- */
- void CLP(GraphicsStateGuardian)::
- clear_before_callback() {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- disable_standard_vertex_arrays();
- }
- #endif
- #ifndef OPENGLES_1
- if (_vertex_array_shader_context != 0) {
- _vertex_array_shader_context->disable_shader_vertex_arrays();
- _vertex_array_shader = nullptr;
- _vertex_array_shader_context = nullptr;
- }
- #endif
- unbind_buffers();
- // Some callbacks may quite reasonably assume that the active texture stage
- // is still set to stage 0. CEGUI, in particular, makes this assumption.
- set_active_texture_stage(0);
- #ifdef SUPPORT_FIXED_FUNCTION
- _glClientActiveTexture(GL_TEXTURE0);
- #endif
- // It's also quite reasonable to presume there aren't any funny color write
- // mask settings active.
- clear_color_write_mask();
- // Clear the bound sampler object, so that we do not inadvertently override
- // the callback's desired sampler settings.
- #ifndef OPENGLES_1
- if (_supports_sampler_objects) {
- _glBindSampler(0, 0);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindSampler(0, 0)\n";
- }
- }
- #endif
- }
- /**
- * Given a lens, calculates the appropriate projection matrix for use with
- * this gsg. Note that the projection matrix depends a lot upon the
- * coordinate system of the rendering API.
- *
- * The return value is a TransformState if the lens is acceptable, NULL if it
- * is not.
- */
- CPT(TransformState) CLP(GraphicsStateGuardian)::
- calc_projection_mat(const Lens *lens) {
- if (lens == nullptr) {
- return nullptr;
- }
- if (!lens->is_linear()) {
- return nullptr;
- }
- // The projection matrix must always be right-handed Y-up, even if our
- // coordinate system of choice is otherwise, because certain GL calls
- // (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of a coordinate
- // system. Sigh. In order to implement a Z-up (or other arbitrary)
- // coordinate system, we'll use a Y-up projection matrix, and store the
- // conversion to our coordinate system of choice in the modelview matrix.
- LMatrix4 result =
- LMatrix4::convert_mat(_internal_coordinate_system,
- lens->get_coordinate_system()) *
- lens->get_projection_mat(_current_stereo_channel);
- #ifndef OPENGLES
- if (_use_depth_zero_to_one) {
- // If we requested that the OpenGL NDC Z goes from zero to one like in
- // Direct3D, we need to scale the projection matrix, which assumes -1..1.
- static const LMatrix4 rescale_mat
- (1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 0.5, 0,
- 0, 0, 0.5, 1);
- result *= rescale_mat;
- }
- #endif
- if (_scene_setup->get_inverted()) {
- // If the scene is supposed to be inverted, then invert the projection
- // matrix.
- result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
- }
- return TransformState::make_mat(result);
- }
- /**
- * Makes the current lens (whichever lens was most recently specified with
- * set_scene()) active, so that it will transform future rendered geometry.
- * Normally this is only called from the draw process, and usually it is
- * called by set_scene().
- *
- * The return value is true if the lens is acceptable, false if it is not.
- */
- bool CLP(GraphicsStateGuardian)::
- prepare_lens() {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glMatrixMode(GL_PROJECTION): " << _projection_mat->get_mat() << endl;
- }
- glMatrixMode(GL_PROJECTION);
- call_glLoadMatrix(_projection_mat->get_mat());
- report_my_gl_errors();
- do_point_size();
- }
- #endif
- return true;
- }
- /**
- * Called before each frame is rendered, to allow the GSG a chance to do any
- * internal cleanup before beginning the frame.
- *
- * The return value is true if successful (in which case the frame will be
- * drawn and end_frame() will be called later), or false if unsuccessful (in
- * which case nothing will be drawn and end_frame() will not be called).
- */
- bool CLP(GraphicsStateGuardian)::
- begin_frame(Thread *current_thread) {
- if (!GraphicsStateGuardian::begin_frame(current_thread)) {
- return false;
- }
- _renderbuffer_residency.begin_frame(current_thread);
- report_my_gl_errors();
- #ifdef DO_PSTATS
- _vertices_display_list_pcollector.clear_level();
- _vertices_immediate_pcollector.clear_level();
- _primitive_batches_display_list_pcollector.clear_level();
- #endif
- #ifndef NDEBUG
- _show_texture_usage = false;
- if (gl_show_texture_usage) {
- // When this is true, then every other second, we show the usage textures
- // instead of the real textures.
- double now = ClockObject::get_global_clock()->get_frame_time();
- int this_second = (int)floor(now);
- if (this_second & 1) {
- _show_texture_usage = true;
- _show_texture_usage_index = this_second >> 1;
- int max_size = gl_show_texture_usage_max_size;
- if (max_size != _show_texture_usage_max_size) {
- // Remove the cache of usage textures; we've changed the max size.
- UsageTextures::iterator ui;
- for (ui = _usage_textures.begin();
- ui != _usage_textures.end();
- ++ui) {
- GLuint index = (*ui).second;
- glDeleteTextures(1, &index);
- }
- _usage_textures.clear();
- _show_texture_usage_max_size = max_size;
- }
- }
- }
- #endif // NDEBUG
- #ifdef DO_PSTATS
- /*if (_supports_timer_query) {
- // Measure the difference between the OpenGL clock and the PStats clock.
- GLint64 time_ns;
- _glGetInteger64v(GL_TIMESTAMP, &time_ns);
- _timer_delta = time_ns * -0.000000001;
- _timer_delta += PStatClient::get_global_pstats()->get_real_time();
- }*/
- #endif
- #ifndef OPENGLES
- if (_current_properties->get_srgb_color()) {
- glEnable(GL_FRAMEBUFFER_SRGB);
- }
- #endif
- report_my_gl_errors();
- return true;
- }
- /**
- * Called between begin_frame() and end_frame() to mark the beginning of
- * drawing commands for a "scene" (usually a particular DisplayRegion) within
- * a frame. All 3-D drawing commands, except the clear operation, must be
- * enclosed within begin_scene() .. end_scene().
- *
- * The return value is true if successful (in which case the scene will be
- * drawn and end_scene() will be called later), or false if unsuccessful (in
- * which case nothing will be drawn and end_scene() will not be called).
- */
- bool CLP(GraphicsStateGuardian)::
- begin_scene() {
- return GraphicsStateGuardian::begin_scene();
- }
- /**
- * Called between begin_frame() and end_frame() to mark the end of drawing
- * commands for a "scene" (usually a particular DisplayRegion) within a frame.
- * All 3-D drawing commands, except the clear operation, must be enclosed
- * within begin_scene() .. end_scene().
- */
- void CLP(GraphicsStateGuardian)::
- end_scene() {
- GraphicsStateGuardian::end_scene();
- _dlights.clear();
- report_my_gl_errors();
- }
- /**
- * Called after each frame is rendered, to allow the GSG a chance to do any
- * internal cleanup after rendering the frame, and before the window flips.
- */
- void CLP(GraphicsStateGuardian)::
- end_frame(Thread *current_thread) {
- report_my_gl_errors();
- #ifndef OPENGLES
- if (_current_properties->get_srgb_color()) {
- glDisable(GL_FRAMEBUFFER_SRGB);
- }
- #endif
- #ifdef DO_PSTATS
- // Check for textures, etc., that are no longer resident. These calls might
- // be measurably expensive, and they don't have any benefit unless we are
- // actually viewing PStats, so don't do them unless we're connected. That
- // will just mean that we'll count everything as resident until the user
- // connects PStats, at which point it will then correct the assessment. No
- // harm done.
- if (has_fixed_function_pipeline() && PStatClient::is_connected()) {
- PStatTimer timer(_check_residency_pcollector);
- check_nonresident_texture(_prepared_objects->_texture_residency.get_inactive_resident());
- check_nonresident_texture(_prepared_objects->_texture_residency.get_active_resident());
- // OpenGL provides no methods for querying whether a buffer object (vertex
- // buffer) is resident. In fact, the API appears geared towards the
- // assumption that such buffers are always resident. OK.
- }
- #endif
- #ifndef OPENGLES_1
- // This breaks shaders across multiple regions.
- if (_vertex_array_shader_context != 0) {
- _vertex_array_shader_context->disable_shader_vertex_arrays();
- _vertex_array_shader = nullptr;
- _vertex_array_shader_context = nullptr;
- }
- if (_texture_binding_shader_context != 0) {
- _texture_binding_shader_context->disable_shader_texture_bindings();
- _texture_binding_shader = nullptr;
- _texture_binding_shader_context = nullptr;
- }
- if (_current_shader_context != 0) {
- _current_shader_context->unbind();
- _current_shader = nullptr;
- _current_shader_context = nullptr;
- }
- _state_shader = nullptr;
- #endif
- // Respecify the active texture next frame, for good measure.
- _active_texture_stage = -1;
- // Calling glFlush() at the end of the frame is particularly necessary if
- // this is a single-buffered visual, so that the frame will be finished
- // drawing before we return to the application. It's not clear what effect
- // this has on our total frame time. if (_force_flush ||
- // _current_properties->is_single_buffered()) { gl_flush(); }
- maybe_gl_finish();
- GraphicsStateGuardian::end_frame(current_thread);
- _renderbuffer_residency.end_frame(current_thread);
- // Flush any PCollectors specific to this kind of GSG.
- _primitive_batches_display_list_pcollector.flush_level();
- _vertices_display_list_pcollector.flush_level();
- _vertices_immediate_pcollector.flush_level();
- // Now is a good time to delete any pending display lists.
- #ifndef OPENGLES
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline() && display_lists) {
- LightMutexHolder holder(_lock);
- if (!_deleted_display_lists.empty()) {
- DeletedNames::iterator ddli;
- for (ddli = _deleted_display_lists.begin();
- ddli != _deleted_display_lists.end();
- ++ddli) {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "releasing display list index " << (int)(*ddli) << "\n";
- }
- glDeleteLists((*ddli), 1);
- }
- _deleted_display_lists.clear();
- }
- }
- #endif
- // And deleted queries, too, unless we're using query timers in which case
- // we'll need to reuse lots of them.
- if (_supports_occlusion_query && !get_timer_queries_active()) {
- LightMutexHolder holder(_lock);
- if (!_deleted_queries.empty()) {
- if (GLCAT.is_spam()) {
- DeletedNames::iterator dqi;
- for (dqi = _deleted_queries.begin();
- dqi != _deleted_queries.end();
- ++dqi) {
- GLCAT.spam()
- << "releasing query index " << (int)(*dqi) << "\n";
- }
- }
- _glDeleteQueries(_deleted_queries.size(), &_deleted_queries[0]);
- _deleted_queries.clear();
- }
- }
- #endif // OPENGLES
- #ifndef NDEBUG
- if (_check_errors || (_supports_debug && gl_debug)) {
- report_my_gl_errors();
- } else {
- // If _check_errors is false, we still want to check for errors once every
- // second, so that we know if anything went wrong at all.
- double current = ClockObject::get_global_clock()->get_frame_time();
- if (current - _last_error_check >= 1.0) {
- _last_error_check = current;
- PStatTimer timer(_check_error_pcollector);
- GLenum error_code = glGetError();
- if (error_code != GL_NO_ERROR) {
- int error_count = 0;
- do {
- ++error_count;
- GLCAT.error()
- << "GL error 0x" << hex << error_code << dec << " : "
- << get_error_string(error_code) << "\n";
- error_code = glGetError();
- } while (error_code != GL_NO_ERROR);
- if (error_count == 1) {
- GLCAT.error()
- << "An OpenGL error has occurred.";
- } else {
- GLCAT.error()
- << error_count << " OpenGL errors have occurred.";
- }
- if (_supports_debug) {
- GLCAT.error(false) << " Set gl-debug #t "
- << "in your PRC file to display more information.\n";
- } else {
- GLCAT.error(false) << " Set gl-check-errors #t "
- << "in your PRC file to display more information.\n";
- }
- _error_count += error_count;
- if (_error_count >= gl_max_errors) {
- panic_deactivate();
- }
- }
- }
- }
- #endif
- // Add in a newline to the spam output for improved legibility.
- if (GLCAT.is_spam()) {
- GLCAT.spam(false) << endl;
- }
- }
- /**
- * Called before a sequence of draw_primitive() functions are called, this
- * should prepare the vertex data for rendering. It returns true if the
- * vertices are ok, false to abort this group of primitives.
- */
- bool CLP(GraphicsStateGuardian)::
- begin_draw_primitives(const GeomPipelineReader *geom_reader,
- const GeomVertexDataPipelineReader *data_reader,
- bool force) {
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifndef OPENGLES_1
- if (!has_fixed_function_pipeline()) {
- // We can't draw without a shader bound in OpenGL ES 2. This shouldn't
- // happen anyway unless the default shader failed to compile somehow.
- if (_current_shader_context == nullptr) {
- return false;
- }
- }
- #endif
- if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) {
- return false;
- }
- nassertr(_data_reader != nullptr, false);
- _geom_display_list = 0;
- if (_auto_antialias_mode) {
- switch (geom_reader->get_primitive_type()) {
- case GeomPrimitive::PT_polygons:
- case GeomPrimitive::PT_patches:
- setup_antialias_polygon();
- break;
- case GeomPrimitive::PT_points:
- setup_antialias_point();
- break;
- case GeomPrimitive::PT_lines:
- setup_antialias_line();
- break;
- case GeomPrimitive::PT_none:
- break;
- }
- int transparency_slot = TransparencyAttrib::get_class_slot();
- int color_write_slot = ColorWriteAttrib::get_class_slot();
- int color_blend_slot = ColorBlendAttrib::get_class_slot();
- if (!_state_mask.get_bit(transparency_slot) ||
- !_state_mask.get_bit(color_write_slot) ||
- !_state_mask.get_bit(color_blend_slot)) {
- do_issue_blending();
- _state_mask.set_bit(transparency_slot);
- _state_mask.set_bit(color_write_slot);
- _state_mask.set_bit(color_blend_slot);
- }
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline() && _data_reader->is_vertex_transformed()) {
- // If the vertex data claims to be already transformed into clip
- // coordinates, wipe out the current projection and modelview matrix (so
- // we don't attempt to transform it again).
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- }
- #endif
- #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) // Display lists not supported by OpenGL ES.
- if (has_fixed_function_pipeline() &&
- /*geom_reader->get_usage_hint() == Geom::UH_static &&*/
- _data_reader->get_usage_hint() == Geom::UH_static &&
- display_lists) {
- // If the geom claims to be totally static, try to build it into a display
- // list.
- // Before we compile or call a display list, make sure the current buffers
- // are unbound, or the nVidia drivers may crash.
- unbind_buffers();
- GeomContext *gc = geom_reader->prepare_now(get_prepared_objects(), this);
- nassertr(gc != nullptr, false);
- CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
- //const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger);
- UpdateSeq modified = max(geom_reader->get_modified(), _data_reader->get_modified());
- if (ggc->get_display_list(_geom_display_list, nullptr, modified)) {
- // If it hasn't been modified, just play the display list again.
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "calling display list " << (int)_geom_display_list << "\n";
- }
- glCallList(_geom_display_list);
- #ifdef DO_PSTATS
- _vertices_display_list_pcollector.add_level(ggc->_num_verts);
- _primitive_batches_display_list_pcollector.add_level(1);
- #endif
- // And now we don't need to do anything else for this geom.
- _geom_display_list = 0;
- end_draw_primitives();
- return false;
- }
- // Since we start this collector explicitly, we have to be sure to stop it
- // again.
- _load_display_list_pcollector.start();
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "compiling display list " << (int)_geom_display_list << "\n";
- }
- // If it has been modified, or this is the first time, then we need to
- // build the display list up.
- if (gl_compile_and_execute) {
- glNewList(_geom_display_list, GL_COMPILE_AND_EXECUTE);
- } else {
- glNewList(_geom_display_list, GL_COMPILE);
- }
- #ifdef DO_PSTATS
- // Count up the number of vertices used by primitives in the Geom, for
- // PStats reporting.
- ggc->_num_verts = 0;
- for (int i = 0; i < geom_reader->get_num_primitives(); i++) {
- ggc->_num_verts += geom_reader->get_primitive(i)->get_num_vertices();
- }
- #endif
- }
- #endif // OPENGLES
- // Enable the appropriate vertex arrays, and disable any extra vertex arrays
- // used by the previous rendering mode.
- #ifdef SUPPORT_IMMEDIATE_MODE
- _use_sender = !vertex_arrays;
- #endif
- #ifndef OPENGLES_1
- if (_use_vertex_attrib_binding) {
- const GeomVertexFormat *format = data_reader->get_format();
- if (format != _current_vertex_format) {
- update_shader_vertex_format(format);
- }
- }
- #endif
- {
- // PStatGPUTimer timer(this, _vertex_array_update_pcollector);
- #ifdef OPENGLES_1
- if (!update_standard_vertex_arrays(force)) {
- return false;
- }
- #else
- if (_current_shader_context == 0) {
- // No shader.
- if (_vertex_array_shader_context != 0) {
- _vertex_array_shader_context->disable_shader_vertex_arrays();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline() && !update_standard_vertex_arrays(force)) {
- return false;
- }
- #endif
- } else {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- // Shader.
- if (_vertex_array_shader_context == 0 ||
- _vertex_array_shader_context->uses_standard_vertex_arrays()) {
- // Previous shader used standard arrays.
- if (_current_shader_context->uses_standard_vertex_arrays()) {
- // So does the current, so update them.
- if (!update_standard_vertex_arrays(force)) {
- return false;
- }
- } else {
- // The current shader does not, so disable them entirely.
- disable_standard_vertex_arrays();
- }
- }
- #ifdef HAVE_CG
- else if (_vertex_array_shader_context->is_of_type(CLP(CgShaderContext)::get_class_type())) {
- // The previous shader was a Cg shader, which can leave a messy
- // situation.
- _vertex_array_shader_context->disable_shader_vertex_arrays();
- }
- #endif
- }
- #endif // SUPPORT_FIXED_FUNCTION
- // Now update the vertex arrays for the current shader.
- if (!_current_shader_context->
- update_shader_vertex_arrays(_vertex_array_shader_context, force)) {
- return false;
- }
- }
- _vertex_array_shader = _current_shader;
- _vertex_array_shader_context = _current_shader_context;
- #endif // OPENGLES_1
- }
- report_my_gl_errors();
- return true;
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Disables any unneeded vertex arrays that were previously enabled, and
- * enables any vertex arrays that are needed that were not previously enabled
- * (or, sets up an immediate-mode sender). Called only from
- * begin_draw_primitives. Used only when the standard (non-shader) pipeline
- * is about to be used - glShaderContexts are responsible for setting up their
- * own vertex arrays.
- */
- bool CLP(GraphicsStateGuardian)::
- update_standard_vertex_arrays(bool force) {
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- // We must use immediate mode to render primitives.
- _sender.clear();
- _sender.add_column(_data_reader, InternalName::get_normal(),
- nullptr, nullptr, GLPf(Normal3), nullptr);
- #ifndef NDEBUG
- if (_show_texture_usage) {
- // In show_texture_usage mode, all colors are white, so as not to
- // contaminate the texture color.
- GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
- } else
- #endif // NDEBUG
- if (!_sender.add_column(_data_reader, InternalName::get_color(),
- nullptr, nullptr, GLPf(Color3), GLPf(Color4))) {
- // If we didn't have a color column, the item color is white.
- GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
- }
- // Now set up each of the active texture coordinate stages--or at least
- // those for which we're not generating texture coordinates automatically.
- int max_stage_index = _target_texture->get_num_on_ff_stages();
- int stage_index = 0;
- while (stage_index < max_stage_index) {
- TextureStage *stage = _target_texture->get_on_ff_stage(stage_index);
- if (!_target_tex_gen->has_gen_texcoord_stage(stage)) {
- // This stage is not one of the stages that doesn't need texcoords
- // issued for it.
- const InternalName *name = stage->get_texcoord_name();
- if (stage_index == 0) {
- // Use the original functions for stage 0, in case we don't support
- // multitexture.
- _sender.add_column(_data_reader, name,
- GLPf(TexCoord1), GLPf(TexCoord2),
- GLPf(TexCoord3), GLPf(TexCoord4));
- } else {
- // Other stages require the multitexture functions.
- _sender.add_texcoord_column(_data_reader, name, stage_index,
- GLf(_glMultiTexCoord1), GLf(_glMultiTexCoord2),
- GLf(_glMultiTexCoord3), GLf(_glMultiTexCoord4));
- }
- }
- ++stage_index;
- }
- // Be sure also to disable any texture stages we had enabled before.
- while (stage_index < _last_max_stage_index) {
- _glClientActiveTexture(GL_TEXTURE0 + stage_index);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- ++stage_index;
- }
- _last_max_stage_index = max_stage_index;
- // We must add vertex last, because glVertex3f() is the key function call
- // that actually issues the vertex.
- _sender.add_column(_data_reader, InternalName::get_vertex(),
- nullptr, GLPf(Vertex2), GLPf(Vertex3), GLPf(Vertex4));
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- // We may use vertex arrays or buffers to render primitives.
- const GeomVertexArrayDataHandle *array_reader;
- const unsigned char *client_pointer;
- int num_values;
- Geom::NumericType numeric_type;
- int start;
- int stride;
- if (_data_reader->get_normal_info(array_reader, numeric_type,
- start, stride)) {
- if (!setup_array_data(client_pointer, array_reader, force)) {
- return false;
- }
- glNormalPointer(get_numeric_type(numeric_type), stride,
- client_pointer + start);
- glEnableClientState(GL_NORMAL_ARRAY);
- } else {
- glDisableClientState(GL_NORMAL_ARRAY);
- }
- #ifndef NDEBUG
- if (_show_texture_usage) {
- // In show_texture_usage mode, all colors are white, so as not to
- // contaminate the texture color.
- glDisableClientState(GL_COLOR_ARRAY);
- GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
- } else
- #endif // NDEBUG
- if (_vertex_colors_enabled &&
- _data_reader->get_color_info(array_reader, num_values, numeric_type,
- start, stride)) {
- if (!setup_array_data(client_pointer, array_reader, force)) {
- return false;
- }
- if (numeric_type == Geom::NT_packed_dabc) {
- glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE,
- stride, client_pointer + start);
- } else {
- glColorPointer(num_values, get_numeric_type(numeric_type),
- stride, client_pointer + start);
- }
- glEnableClientState(GL_COLOR_ARRAY);
- } else {
- glDisableClientState(GL_COLOR_ARRAY);
- // Since we don't have per-vertex color, the implicit color is white.
- if (_color_scale_via_lighting) {
- GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
- } else {
- LColor color = _scene_graph_color;
- color.componentwise_mult(_current_color_scale);
- GLPf(Color4)(color[0], color[1], color[2], color[3]);
- }
- }
- // Now set up each of the active texture coordinate stages--or at least
- // those for which we're not generating texture coordinates automatically.
- int max_stage_index = _target_texture->get_num_on_ff_stages();
- int stage_index = 0;
- while (stage_index < max_stage_index) {
- _glClientActiveTexture(GL_TEXTURE0 + stage_index);
- TextureStage *stage = _target_texture->get_on_ff_stage(stage_index);
- if (!_target_tex_gen->has_gen_texcoord_stage(stage)) {
- // This stage is not one of the stages that doesn't need texcoords
- // issued for it.
- const InternalName *name = stage->get_texcoord_name();
- if (_data_reader->get_array_info(name, array_reader, num_values,
- numeric_type, start, stride)) {
- // The vertex data does have texcoords for this stage.
- if (!setup_array_data(client_pointer, array_reader, force)) {
- return false;
- }
- glTexCoordPointer(num_values, get_numeric_type(numeric_type),
- stride, client_pointer + start);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- } else {
- // The vertex data doesn't have texcoords for this stage (even
- // though they're needed).
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
- } else {
- // No texcoords are needed for this stage.
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
- ++stage_index;
- }
- // Be sure also to disable any texture stages we had enabled before.
- while (stage_index < _last_max_stage_index) {
- _glClientActiveTexture(GL_TEXTURE0 + stage_index);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- ++stage_index;
- }
- _last_max_stage_index = max_stage_index;
- // There's no requirement that we add vertices last, but we do anyway.
- if (_data_reader->get_vertex_info(array_reader, num_values, numeric_type,
- start, stride)) {
- if (!setup_array_data(client_pointer, array_reader, force)) {
- return false;
- }
- glVertexPointer(num_values, get_numeric_type(numeric_type),
- stride, client_pointer + start);
- glEnableClientState(GL_VERTEX_ARRAY);
- }
- }
- return true;
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Ensures the vertex and array buffers are no longer bound. Some graphics
- * drivers crash if these are left bound indiscriminantly.
- */
- void CLP(GraphicsStateGuardian)::
- unbind_buffers() {
- if (_current_vbuffer_index != 0) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding vertex buffer\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, 0);
- _current_vbuffer_index = 0;
- }
- if (_current_ibuffer_index != 0) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding index buffer\n";
- }
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- _current_ibuffer_index = 0;
- }
- #ifndef OPENGLES
- if (_current_vertex_buffers.size() > 1 && _supports_multi_bind) {
- _glBindVertexBuffers(0, _current_vertex_buffers.size(), nullptr, nullptr, nullptr);
- } else {
- for (size_t i = 0; i < _current_vertex_buffers.size(); ++i) {
- if (_current_vertex_buffers[i] != 0) {
- _glBindVertexBuffer((GLuint)i, 0, 0, 0);
- }
- }
- }
- _current_vertex_buffers.clear();
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- disable_standard_vertex_arrays();
- }
- #endif
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Used to disable all the standard vertex arrays that are currently enabled.
- * glShaderContexts are responsible for setting up their own vertex arrays,
- * but before they can do so, the standard vertex arrays need to be disabled
- * to get them "out of the way." Called only from begin_draw_primitives.
- */
- void CLP(GraphicsStateGuardian)::
- disable_standard_vertex_arrays() {
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) return;
- #endif
- glDisableClientState(GL_NORMAL_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
- GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f);
- for (int stage_index=0; stage_index < _last_max_stage_index; stage_index++) {
- _glClientActiveTexture(GL_TEXTURE0 + stage_index);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- }
- _last_max_stage_index = 0;
- glDisableClientState(GL_VERTEX_ARRAY);
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifndef OPENGLES_1
- /**
- * Updates the vertex format used by the shader. This is still an
- * experimental feature.
- */
- void CLP(GraphicsStateGuardian)::
- update_shader_vertex_format(const GeomVertexFormat *format) {
- size_t num_columns = format->get_num_columns();
- for (size_t ci = 0; ci < num_columns; ++ci) {
- GLuint binding = format->get_array_with(ci);
- const GeomVertexColumn *column = format->get_column(ci);
- // Needs improvement, obviously.
- const InternalName *name = column->get_name();
- GLuint loc;
- if (name == InternalName::get_vertex()) {
- loc = 0;
- } else if (name == InternalName::get_transform_weight()) {
- loc = 1;
- } else if (name == InternalName::get_normal()) {
- loc = 2;
- } else if (name == InternalName::get_color()) {
- loc = 3;
- } else if (name == InternalName::get_transform_index()) {
- loc = 7;
- } else if (name == InternalName::get_texcoord()) {
- loc = 8;
- } else {
- // Not yet supported, ignore for now. This system will be improved.
- continue;
- }
- if (_vertex_attrib_columns[loc] != nullptr &&
- _vertex_attrib_columns[loc]->compare_to(*column) == 0) {
- continue;
- }
- _vertex_attrib_columns[loc] = column;
- GLuint offset = column->get_start();
- GLenum type = get_numeric_type(column->get_numeric_type());
- GLboolean normalized = (column->get_contents() == GeomEnums::C_color);
- GLint size = column->get_num_values();
- if (column->get_numeric_type() == GeomEnums::NT_packed_dabc) {
- // GL_BGRA is a special accepted value available since OpenGL 3.2. It
- // requires us to pass GL_TRUE for normalized.
- size = GL_BGRA;
- normalized = GL_TRUE;
- }
- for (int i = 0; i < column->get_num_elements(); ++i) {
- if (loc == 7) { // Temp hack
- _glVertexAttribIFormat(loc, size, type, offset);
- } else {
- _glVertexAttribFormat(loc, size, type, normalized, offset);
- }
- _glVertexAttribBinding(loc, binding);
- offset += column->get_element_stride();
- ++loc;
- }
- }
- size_t num_arrays = format->get_num_arrays();
- for (size_t ai = 0; ai < num_arrays; ++ai) {
- _glVertexBindingDivisor(ai, format->get_array(ai)->get_divisor());
- }
- _current_vertex_format = format;
- }
- #endif
- /**
- * Draws a series of disconnected triangles.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_TRIANGLES);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_tri_pcollector.add_level(num_vertices);
- _primitive_batches_tri_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLES, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_TRIANGLES,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLES,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_TRIANGLES,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of disconnected triangles with adjacency information.
- */
- #ifndef OPENGLES
- bool CLP(GraphicsStateGuardian)::
- draw_triangles_adj(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_triangles_adj: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_TRIANGLES_ADJACENCY);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_tri_pcollector.add_level(num_vertices);
- _primitive_batches_tri_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLES_ADJACENCY, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else {
- _glDrawRangeElements(GL_TRIANGLES_ADJACENCY,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLES_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else {
- glDrawArrays(GL_TRIANGLES_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- #endif // OPENGLES
- /**
- * Draws a series of triangle strips.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- report_my_gl_errors();
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_tristrips: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_composite_primitives(reader, GL_TRIANGLE_STRIP);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) {
- // One long triangle strip, connected by the degenerate vertices that
- // have already been set up within the primitive.
- int num_vertices = reader->get_num_vertices();
- _vertices_tristrip_pcollector.add_level(num_vertices);
- _primitive_batches_tristrip_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLE_STRIP, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_TRIANGLE_STRIP,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLE_STRIP,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_TRIANGLE_STRIP,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- } else {
- // Send the individual triangle strips, stepping over the degenerate
- // vertices.
- CPTA_int ends = reader->get_ends();
- _primitive_batches_tristrip_pcollector.add_level(ends.size());
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- int index_stride = reader->get_index_stride();
- GeomVertexReader mins(reader->get_mins(), 0);
- GeomVertexReader maxs(reader->get_maxs(), 0);
- nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
- reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
- unsigned int start = 0;
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_tristrip_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLE_STRIP, ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride,
- _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_TRIANGLE_STRIP,
- mins.get_data1i(), maxs.get_data1i(),
- ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride);
- }
- start = ends[i] + 2;
- }
- } else {
- unsigned int start = 0;
- int first_vertex = reader->get_first_vertex();
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_tristrip_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLE_STRIP, first_vertex + start,
- ends[i] - start, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_TRIANGLE_STRIP, first_vertex + start,
- ends[i] - start);
- }
- start = ends[i] + 2;
- }
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of triangle strips with adjacency information.
- */
- #ifndef OPENGLES
- bool CLP(GraphicsStateGuardian)::
- draw_tristrips_adj(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- report_my_gl_errors();
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_tristrips_adj: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_composite_primitives(reader, GL_TRIANGLE_STRIP_ADJACENCY);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- if (reader->is_indexed() &&
- (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
- // One long line strip, connected by strip cut indices.
- if (_explicit_primitive_restart) {
- glEnable(GL_PRIMITIVE_RESTART);
- _glPrimitiveRestartIndex(reader->get_strip_cut_index());
- }
- int num_vertices = reader->get_num_vertices();
- _vertices_tristrip_pcollector.add_level(num_vertices);
- _primitive_batches_tristrip_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLE_STRIP_ADJACENCY, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else {
- _glDrawRangeElements(GL_TRIANGLE_STRIP_ADJACENCY,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLE_STRIP_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else {
- glDrawArrays(GL_TRIANGLE_STRIP_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- if (_explicit_primitive_restart) {
- glDisable(GL_PRIMITIVE_RESTART);
- }
- } else {
- // Send the individual triangle strips, stepping over the degenerate
- // vertices.
- CPTA_int ends = reader->get_ends();
- _primitive_batches_tristrip_pcollector.add_level(ends.size());
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- int index_stride = reader->get_index_stride();
- GeomVertexReader mins(reader->get_mins(), 0);
- GeomVertexReader maxs(reader->get_maxs(), 0);
- nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
- reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
- unsigned int start = 0;
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_tristrip_pcollector.add_level(ends[i] - start);
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLE_STRIP_ADJACENCY, ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride,
- _instance_count);
- } else {
- _glDrawRangeElements(GL_TRIANGLE_STRIP_ADJACENCY,
- mins.get_data1i(), maxs.get_data1i(),
- ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride);
- }
- start = ends[i] + 1;
- }
- } else {
- unsigned int start = 0;
- int first_vertex = reader->get_first_vertex();
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_tristrip_pcollector.add_level(ends[i] - start);
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLE_STRIP_ADJACENCY, first_vertex + start,
- ends[i] - start, _instance_count);
- } else {
- glDrawArrays(GL_TRIANGLE_STRIP_ADJACENCY, first_vertex + start,
- ends[i] - start);
- }
- start = ends[i] + 1;
- }
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- #endif // OPENGLES
- /**
- * Draws a series of triangle fans.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_trifans: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_composite_primitives(reader, GL_TRIANGLE_FAN);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- // Send the individual triangle fans. There's no connecting fans with
- // degenerate vertices, so no worries about that.
- CPTA_int ends = reader->get_ends();
- _primitive_batches_trifan_pcollector.add_level(ends.size());
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- int index_stride = reader->get_index_stride();
- GeomVertexReader mins(reader->get_mins(), 0);
- GeomVertexReader maxs(reader->get_maxs(), 0);
- nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
- reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
- unsigned int start = 0;
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_trifan_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_TRIANGLE_FAN, ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride,
- _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_TRIANGLE_FAN,
- mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride);
- }
- start = ends[i];
- }
- } else {
- unsigned int start = 0;
- int first_vertex = reader->get_first_vertex();
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_trifan_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_TRIANGLE_FAN, first_vertex + start,
- ends[i] - start, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_TRIANGLE_FAN, first_vertex + start,
- ends[i] - start);
- }
- start = ends[i];
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of "patches", which can only be processed by a tessellation
- * shader.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_patches(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_patches: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- if (!get_supports_tessellation_shaders()) {
- return false;
- }
- #ifndef OPENGLES
- _glPatchParameteri(GL_PATCH_VERTICES, reader->get_object()->get_num_vertices_per_primitive());
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_PATCHES);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_patch_pcollector.add_level(num_vertices);
- _primitive_batches_patch_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_PATCHES, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_PATCHES,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_PATCHES,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_PATCHES,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- }
- #endif // OPENGLES
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of disconnected line segments.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_LINES);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_other_pcollector.add_level(num_vertices);
- _primitive_batches_other_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINES, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_LINES,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_LINES,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_LINES,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of disconnected line segments with adjacency information.
- */
- #ifndef OPENGLES
- bool CLP(GraphicsStateGuardian)::
- draw_lines_adj(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_lines_adj: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_LINES_ADJACENCY);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_other_pcollector.add_level(num_vertices);
- _primitive_batches_other_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINES_ADJACENCY, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else {
- _glDrawRangeElements(GL_LINES_ADJACENCY,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_LINES_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else {
- glDrawArrays(GL_LINES_ADJACENCY,
- reader->get_first_vertex(),
- num_vertices);
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- #endif // OPENGLES
- /**
- * Draws a series of line strips.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- report_my_gl_errors();
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_linestrips: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_composite_primitives(reader, GL_LINE_STRIP);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- if (reader->is_indexed() &&
- (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
- // One long triangle strip, connected by strip cut indices.
- #ifndef OPENGLES
- if (_explicit_primitive_restart) {
- glEnable(GL_PRIMITIVE_RESTART);
- _glPrimitiveRestartIndex(reader->get_strip_cut_index());
- }
- #endif // !OPENGLES
- int num_vertices = reader->get_num_vertices();
- _vertices_other_pcollector.add_level(num_vertices);
- _primitive_batches_other_pcollector.add_level(1);
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINE_STRIP, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif // !OPENGLES
- {
- _glDrawRangeElements(GL_LINE_STRIP,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- #ifndef OPENGLES
- if (_explicit_primitive_restart) {
- glDisable(GL_PRIMITIVE_RESTART);
- }
- #endif // !OPENGLES
- } else {
- // Send the individual line strips, stepping over the strip-cut indices.
- CPTA_int ends = reader->get_ends();
- _primitive_batches_other_pcollector.add_level(ends.size());
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- int index_stride = reader->get_index_stride();
- GeomVertexReader mins(reader->get_mins(), 0);
- GeomVertexReader maxs(reader->get_maxs(), 0);
- nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
- reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
- unsigned int start = 0;
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_other_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINE_STRIP, ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride,
- _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_LINE_STRIP,
- mins.get_data1i(), maxs.get_data1i(),
- ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride);
- }
- start = ends[i] + 1;
- }
- } else {
- unsigned int start = 0;
- int first_vertex = reader->get_first_vertex();
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_other_pcollector.add_level(ends[i] - start);
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_LINE_STRIP, first_vertex + start,
- ends[i] - start, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_LINE_STRIP, first_vertex + start, ends[i] - start);
- }
- start = ends[i] + 1;
- }
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Draws a series of line strips with adjacency information.
- */
- #ifndef OPENGLES
- bool CLP(GraphicsStateGuardian)::
- draw_linestrips_adj(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- report_my_gl_errors();
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_linestrips_adj: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_composite_primitives(reader, GL_LINE_STRIP_ADJACENCY);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- if (reader->is_indexed() &&
- (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
- // One long line strip, connected by strip cut indices.
- if (_explicit_primitive_restart) {
- glEnable(GL_PRIMITIVE_RESTART);
- _glPrimitiveRestartIndex(reader->get_strip_cut_index());
- }
- int num_vertices = reader->get_num_vertices();
- _vertices_other_pcollector.add_level(num_vertices);
- _primitive_batches_other_pcollector.add_level(1);
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINE_STRIP_ADJACENCY, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else {
- _glDrawRangeElements(GL_LINE_STRIP_ADJACENCY,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- if (_explicit_primitive_restart) {
- glDisable(GL_PRIMITIVE_RESTART);
- }
- } else {
- // Send the individual line strips, stepping over the strip-cut indices.
- CPTA_int ends = reader->get_ends();
- _primitive_batches_other_pcollector.add_level(ends.size());
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- int index_stride = reader->get_index_stride();
- GeomVertexReader mins(reader->get_mins(), 0);
- GeomVertexReader maxs(reader->get_maxs(), 0);
- nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
- reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
- unsigned int start = 0;
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_other_pcollector.add_level(ends[i] - start);
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_LINE_STRIP_ADJACENCY, ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride,
- _instance_count);
- } else {
- _glDrawRangeElements(GL_LINE_STRIP_ADJACENCY,
- mins.get_data1i(), maxs.get_data1i(),
- ends[i] - start,
- get_numeric_type(reader->get_index_type()),
- client_pointer + start * index_stride);
- }
- start = ends[i] + 1;
- }
- } else {
- unsigned int start = 0;
- int first_vertex = reader->get_first_vertex();
- for (size_t i = 0; i < ends.size(); i++) {
- _vertices_other_pcollector.add_level(ends[i] - start);
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_LINE_STRIP_ADJACENCY, first_vertex + start,
- ends[i] - start, _instance_count);
- } else {
- glDrawArrays(GL_LINE_STRIP_ADJACENCY, first_vertex + start, ends[i] - start);
- }
- start = ends[i] + 1;
- }
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- #endif // OPENGLES
- /**
- * Draws a series of disconnected points.
- */
- bool CLP(GraphicsStateGuardian)::
- draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
- // PStatGPUTimer timer(this, _draw_primitive_pcollector,
- // reader->get_current_thread());
- #ifndef NDEBUG
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "draw_points: " << *(reader->get_object()) << "\n";
- }
- #endif // NDEBUG
- #ifdef SUPPORT_IMMEDIATE_MODE
- if (_use_sender) {
- draw_immediate_simple_primitives(reader, GL_POINTS);
- } else
- #endif // SUPPORT_IMMEDIATE_MODE
- {
- int num_vertices = reader->get_num_vertices();
- _vertices_other_pcollector.add_level(num_vertices);
- _primitive_batches_other_pcollector.add_level(1);
- if (reader->is_indexed()) {
- const unsigned char *client_pointer;
- if (!setup_primitive(client_pointer, reader, force)) {
- return false;
- }
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawElementsInstanced(GL_POINTS, num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer, _instance_count);
- } else
- #endif
- {
- _glDrawRangeElements(GL_POINTS,
- reader->get_min_vertex(),
- reader->get_max_vertex(),
- num_vertices,
- get_numeric_type(reader->get_index_type()),
- client_pointer);
- }
- } else {
- #ifndef OPENGLES_1
- if (_supports_geometry_instancing && _instance_count > 0) {
- _glDrawArraysInstanced(GL_POINTS,
- reader->get_first_vertex(),
- num_vertices, _instance_count);
- } else
- #endif
- {
- glDrawArrays(GL_POINTS, reader->get_first_vertex(), num_vertices);
- }
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Called after a sequence of draw_primitive() functions are called, this
- * should do whatever cleanup is appropriate.
- */
- void CLP(GraphicsStateGuardian)::
- end_draw_primitives() {
- #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) // Display lists not supported by OpenGL ES.
- if (has_fixed_function_pipeline() && _geom_display_list != 0) {
- // If we were building a display list, close it now.
- glEndList();
- _load_display_list_pcollector.stop();
- if (!gl_compile_and_execute) {
- glCallList(_geom_display_list);
- }
- _primitive_batches_display_list_pcollector.add_level(1);
- }
- _geom_display_list = 0;
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline() && _transform_stale) {
- glMatrixMode(GL_MODELVIEW);
- call_glLoadMatrix(_internal_transform->get_mat());
- }
- if (has_fixed_function_pipeline() && _data_reader->is_vertex_transformed()) {
- // Restore the matrices that we pushed above.
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- }
- #endif
- GraphicsStateGuardian::end_draw_primitives();
- maybe_gl_finish();
- report_my_gl_errors();
- }
- #ifndef OPENGLES_1
- /**
- * Issues the given memory barriers, and clears the list of textures marked as
- * incoherent for the given bits.
- */
- void CLP(GraphicsStateGuardian)::
- issue_memory_barrier(GLbitfield barriers) {
- if (!gl_enable_memory_barriers || _glMemoryBarrier == nullptr) {
- return;
- }
- PStatGPUTimer timer(this, _memory_barrier_pcollector);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "Issuing memory barriers:";
- }
- _glMemoryBarrier(barriers);
- // Indicate that barriers no longer need to be issued for the relevant lists
- // of textures.
- if (barriers & GL_TEXTURE_FETCH_BARRIER_BIT) {
- _textures_needing_fetch_barrier.clear();
- GLCAT.spam(false) << " texture_fetch";
- }
- if (barriers & GL_SHADER_IMAGE_ACCESS_BARRIER_BIT) {
- _textures_needing_image_access_barrier.clear();
- GLCAT.spam(false) << " shader_image_access";
- }
- if (barriers & GL_TEXTURE_UPDATE_BARRIER_BIT) {
- _textures_needing_update_barrier.clear();
- GLCAT.spam(false) << " texture_update";
- }
- if (barriers & GL_FRAMEBUFFER_BARRIER_BIT) {
- _textures_needing_framebuffer_barrier.clear();
- GLCAT.spam(false) << " framebuffer";
- }
- GLCAT.spam(false) << "\n";
- report_my_gl_errors();
- }
- #endif // OPENGLES_1
- /**
- * Creates whatever structures the GSG requires to represent the texture
- * internally, and returns a newly-allocated TextureContext object with this
- * data. It is the responsibility of the calling function to later call
- * release_texture() with this same pointer (which will also delete the
- * pointer).
- *
- * This function should not be called directly to prepare a texture. Instead,
- * call Texture::prepare().
- */
- TextureContext *CLP(GraphicsStateGuardian)::
- prepare_texture(Texture *tex, int view) {
- PStatGPUTimer timer(this, _prepare_texture_pcollector);
- report_my_gl_errors();
- // Make sure we'll support this texture when it's rendered. Don't bother to
- // prepare it if we won't.
- Texture::TextureType texture_type = tex->get_texture_type();
- switch (texture_type) {
- case Texture::TT_3d_texture:
- if (!_supports_3d_texture) {
- GLCAT.warning()
- << "3-D textures are not supported by this OpenGL driver.\n";
- return nullptr;
- }
- break;
- case Texture::TT_2d_texture_array:
- if (!_supports_2d_texture_array) {
- GLCAT.warning()
- << "2-D texture arrays are not supported by this OpenGL driver.\n";
- return nullptr;
- }
- break;
- case Texture::TT_cube_map:
- if (!_supports_cube_map) {
- GLCAT.warning()
- << "Cube map textures are not supported by this OpenGL driver.\n";
- return nullptr;
- }
- break;
- case Texture::TT_buffer_texture:
- if (!_supports_buffer_texture) {
- GLCAT.warning()
- << "Buffer textures are not supported by this OpenGL driver.\n";
- return nullptr;
- }
- break;
- case Texture::TT_cube_map_array:
- if (!_supports_cube_map_array) {
- GLCAT.warning()
- << "Cube map arrays are not supported by this OpenGL driver.\n";
- return nullptr;
- }
- break;
- default:
- break;
- }
- CLP(TextureContext) *gtc = new CLP(TextureContext)(this, _prepared_objects, tex, view);
- gtc->_target = get_texture_target(texture_type);
- report_my_gl_errors();
- return gtc;
- }
- /**
- * Ensures that the current Texture data is refreshed onto the GSG. This
- * means updating the texture properties and/or re-uploading the texture
- * image, if necessary. This should only be called within the draw thread.
- *
- * If force is true, this function will not return until the texture has been
- * fully uploaded. If force is false, the function may choose to upload a
- * simple version of the texture instead, if the texture is not fully resident
- * (and if get_incomplete_render() is true).
- */
- bool CLP(GraphicsStateGuardian)::
- update_texture(TextureContext *tc, bool force) {
- CLP(TextureContext) *gtc;
- DCAST_INTO_R(gtc, tc, false);
- if (gtc->was_image_modified() || !gtc->_has_storage) {
- PStatGPUTimer timer(this, _texture_update_pcollector);
- // If the texture image was modified, reload the texture.
- apply_texture(gtc);
- Texture *tex = tc->get_texture();
- if (gtc->was_properties_modified()) {
- specify_texture(gtc, tex->get_default_sampler());
- }
- bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
- if (!okflag) {
- GLCAT.error()
- << "Could not load " << *tex << "\n";
- return false;
- }
- } else if (gtc->was_properties_modified()) {
- PStatGPUTimer timer(this, _texture_update_pcollector);
- // If only the properties have been modified, we don't necessarily need to
- // reload the texture.
- apply_texture(gtc);
- Texture *tex = tc->get_texture();
- if (specify_texture(gtc, tex->get_default_sampler())) {
- // Actually, looks like the texture *does* need to be reloaded.
- gtc->mark_needs_reload();
- bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
- if (!okflag) {
- GLCAT.error()
- << "Could not load " << *tex << "\n";
- return false;
- }
- } else {
- // The texture didn't need reloading, but mark it fully updated now.
- gtc->mark_loaded();
- }
- }
- gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- report_my_gl_errors();
- return true;
- }
- /**
- * Frees the GL resources previously allocated for the texture. This function
- * should never be called directly; instead, call Texture::release() (or
- * simply let the Texture destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_texture(TextureContext *tc) {
- CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
- #ifndef OPENGLES_1
- _textures_needing_fetch_barrier.erase(gtc);
- _textures_needing_image_access_barrier.erase(gtc);
- _textures_needing_update_barrier.erase(gtc);
- _textures_needing_framebuffer_barrier.erase(gtc);
- #endif
- glDeleteTextures(1, >c->_index);
- if (gtc->_buffer != 0) {
- _glDeleteBuffers(1, >c->_buffer);
- }
- delete gtc;
- }
- /**
- * Frees the GL resources previously allocated for the textures. This function
- * should never be called directly; instead, call Texture::release() (or
- * simply let the Texture destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_textures(const pvector<TextureContext *> &contexts) {
- if (contexts.empty()) {
- return;
- }
- GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * contexts.size() * 2);
- GLuint *buffers = indices + contexts.size();
- size_t num_indices = 0;
- size_t num_buffers = 0;
- for (TextureContext *tc : contexts) {
- CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
- #ifndef OPENGLES_1
- _textures_needing_fetch_barrier.erase(gtc);
- _textures_needing_image_access_barrier.erase(gtc);
- _textures_needing_update_barrier.erase(gtc);
- _textures_needing_framebuffer_barrier.erase(gtc);
- #endif
- indices[num_indices++] = gtc->_index;
- if (gtc->_buffer != 0) {
- buffers[num_buffers++] = gtc->_buffer;
- }
- delete gtc;
- }
- glDeleteTextures(num_indices, indices);
- if (num_buffers > 0) {
- _glDeleteBuffers(num_buffers, buffers);
- }
- }
- /**
- * This method should only be called by the GraphicsEngine. Do not call it
- * directly; call GraphicsEngine::extract_texture_data() instead.
- *
- * This method will be called in the draw thread to download the texture
- * memory's image into its ram_image value. It returns true on success, false
- * otherwise.
- */
- bool CLP(GraphicsStateGuardian)::
- extract_texture_data(Texture *tex) {
- bool success = true;
- // Make sure the error stack is cleared out before we begin.
- report_my_gl_errors();
- int num_views = tex->get_num_views();
- for (int view = 0; view < num_views; ++view) {
- TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
- nassertr(tc != nullptr, false);
- CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
- if (!do_extract_texture_data(gtc)) {
- success = false;
- }
- }
- return success;
- }
- #ifndef OPENGLES_1
- /**
- * Creates whatever structures the GSG requires to represent the sampler state
- * internally, and returns a newly-allocated SamplerContext object with this
- * data. It is the responsibility of the calling function to later call
- * release_sampler() with this same pointer (which will also delete the
- * pointer).
- *
- * This function should not be called directly to prepare a sampler object.
- * Instead, call SamplerState::prepare().
- */
- SamplerContext *CLP(GraphicsStateGuardian)::
- prepare_sampler(const SamplerState &sampler) {
- nassertr(_supports_sampler_objects, nullptr);
- PStatGPUTimer timer(this, _prepare_sampler_pcollector);
- CLP(SamplerContext) *gsc = new CLP(SamplerContext)(this, sampler);
- GLuint index = gsc->_index;
- // Sampler contexts are immutable in Panda, so might as well just initialize
- // all the settings here.
- _glSamplerParameteri(index, GL_TEXTURE_WRAP_S,
- get_texture_wrap_mode(sampler.get_wrap_u()));
- _glSamplerParameteri(index, GL_TEXTURE_WRAP_T,
- get_texture_wrap_mode(sampler.get_wrap_v()));
- _glSamplerParameteri(index, GL_TEXTURE_WRAP_R,
- get_texture_wrap_mode(sampler.get_wrap_w()));
- #ifndef OPENGLES
- #ifdef STDFLOAT_DOUBLE
- LVecBase4f fvalue = LCAST(float, sampler.get_border_color());
- _glSamplerParameterfv(index, GL_TEXTURE_BORDER_COLOR, fvalue.get_data());
- #else
- _glSamplerParameterfv(index, GL_TEXTURE_BORDER_COLOR,
- sampler.get_border_color().get_data());
- #endif
- #endif // OPENGLES
- SamplerState::FilterType minfilter = sampler.get_effective_minfilter();
- SamplerState::FilterType magfilter = sampler.get_effective_magfilter();
- bool uses_mipmaps = SamplerState::is_mipmap(minfilter) && !gl_ignore_mipmaps;
- #ifndef NDEBUG
- if (gl_force_mipmaps) {
- minfilter = SamplerState::FT_linear_mipmap_linear;
- magfilter = SamplerState::FT_linear;
- uses_mipmaps = true;
- }
- #endif
- _glSamplerParameteri(index, GL_TEXTURE_MIN_FILTER,
- get_texture_filter_type(minfilter, !uses_mipmaps));
- _glSamplerParameteri(index, GL_TEXTURE_MAG_FILTER,
- get_texture_filter_type(magfilter, true));
- // Set anisotropic filtering.
- if (_supports_anisotropy) {
- PN_stdfloat anisotropy = sampler.get_effective_anisotropic_degree();
- anisotropy = min(anisotropy, _max_anisotropy);
- anisotropy = max(anisotropy, (PN_stdfloat)1.0);
- _glSamplerParameterf(index, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
- }
- if (_supports_shadow_filter) {
- if ((sampler.get_magfilter() == SamplerState::FT_shadow) ||
- (sampler.get_minfilter() == SamplerState::FT_shadow)) {
- _glSamplerParameteri(index, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
- _glSamplerParameteri(index, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
- } else {
- _glSamplerParameteri(index, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
- _glSamplerParameteri(index, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
- }
- }
- if (_supports_texture_lod) {
- _glSamplerParameterf(index, GL_TEXTURE_MIN_LOD, sampler.get_min_lod());
- _glSamplerParameterf(index, GL_TEXTURE_MAX_LOD, sampler.get_max_lod());
- }
- #ifndef OPENGLES
- if (_supports_texture_lod_bias) {
- _glSamplerParameterf(index, GL_TEXTURE_LOD_BIAS, sampler.get_lod_bias());
- }
- #endif
- gsc->enqueue_lru(&_prepared_objects->_sampler_object_lru);
- report_my_gl_errors();
- return gsc;
- }
- #endif // !OPENGLES_1
- #ifndef OPENGLES_1
- /**
- * Frees the GL resources previously allocated for the sampler. This function
- * should never be called directly; instead, call SamplerState::release().
- */
- void CLP(GraphicsStateGuardian)::
- release_sampler(SamplerContext *sc) {
- CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
- if (gsc->_index != 0) {
- _glDeleteSamplers(1, &gsc->_index);
- }
- delete gsc;
- }
- #endif // !OPENGLES_1
- /**
- * Creates a new retained-mode representation of the given geom, and returns a
- * newly-allocated GeomContext pointer to reference it. It is the
- * responsibility of the calling function to later call release_geom() with
- * this same pointer (which will also delete the pointer).
- *
- * This function should not be called directly to prepare a geom. Instead,
- * call Geom::prepare().
- */
- GeomContext *CLP(GraphicsStateGuardian)::
- prepare_geom(Geom *geom) {
- PStatGPUTimer timer(this, _prepare_geom_pcollector);
- return new CLP(GeomContext)(geom);
- }
- /**
- * Frees the GL resources previously allocated for the geom. This function
- * should never be called directly; instead, call Geom::release() (or simply
- * let the Geom destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_geom(GeomContext *gc) {
- CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
- if (has_fixed_function_pipeline()) {
- ggc->release_display_lists();
- }
- report_my_gl_errors();
- delete ggc;
- }
- /**
- *
- */
- ShaderContext *CLP(GraphicsStateGuardian)::
- prepare_shader(Shader *se) {
- PStatGPUTimer timer(this, se->get_prepare_shader_pcollector());
- #ifndef OPENGLES_1
- ShaderContext *result = nullptr;
- switch (se->get_language()) {
- case Shader::SL_GLSL:
- if (_supports_glsl) {
- push_group_marker(std::string("Prepare Shader ") + se->get_debug_name());
- result = new CLP(ShaderContext)(this, se);
- pop_group_marker();
- break;
- } else {
- GLCAT.error()
- << "Tried to load GLSL shader, but GLSL shaders not supported.\n";
- return nullptr;
- }
- case Shader::SL_Cg:
- #if defined(HAVE_CG) && !defined(OPENGLES)
- if (_supports_basic_shaders) {
- push_group_marker(std::string("Prepare Shader ") + se->get_debug_name());
- result = new CLP(CgShaderContext)(this, se);
- pop_group_marker();
- break;
- } else {
- GLCAT.error()
- << "Tried to load Cg shader, but basic shaders not supported.\n";
- return nullptr;
- }
- #elif defined(OPENGLES)
- GLCAT.error()
- << "Tried to load Cg shader, but Cg support is not available for OpenGL ES.\n";
- return nullptr;
- #else
- GLCAT.error()
- << "Tried to load Cg shader, but Cg support not compiled in.\n";
- return nullptr;
- #endif
- default:
- GLCAT.error()
- << "Tried to load shader with unsupported shader language!\n";
- return nullptr;
- }
- if (result->valid()) {
- return result;
- }
- delete result;
- #endif // OPENGLES_1
- return nullptr;
- }
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- release_shader(ShaderContext *sc) {
- #ifndef OPENGLES_1
- if (sc->is_of_type(CLP(ShaderContext)::get_class_type())) {
- ((CLP(ShaderContext) *)sc)->release_resources();
- }
- #if defined(HAVE_CG) && !defined(OPENGLES_2)
- else if (sc->is_of_type(CLP(CgShaderContext)::get_class_type())) {
- ((CLP(CgShaderContext) *)sc)->release_resources();
- }
- #endif
- #endif
- delete sc;
- }
- /**
- * This is intended to be called only from the GLGeomContext destructor. It
- * saves the indicated display list index in the list to be deleted at the end
- * of the frame.
- */
- void CLP(GraphicsStateGuardian)::
- record_deleted_display_list(GLuint index) {
- LightMutexHolder holder(_lock);
- _deleted_display_lists.push_back(index);
- }
- /**
- * Creates a new retained-mode representation of the given data, and returns a
- * newly-allocated VertexBufferContext pointer to reference it. It is the
- * responsibility of the calling function to later call
- * release_vertex_buffer() with this same pointer (which will also delete the
- * pointer).
- *
- * This function should not be called directly to prepare a buffer. Instead,
- * call Geom::prepare().
- */
- VertexBufferContext *CLP(GraphicsStateGuardian)::
- prepare_vertex_buffer(GeomVertexArrayData *data) {
- if (_supports_buffers) {
- PStatGPUTimer timer(this, _prepare_vertex_buffer_pcollector);
- CLP(VertexBufferContext) *gvbc = new CLP(VertexBufferContext)(this, _prepared_objects, data);
- _glGenBuffers(1, &gvbc->_index);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "creating vertex buffer " << (int)gvbc->_index << ": "
- << data->get_num_rows() << " vertices "
- << *data->get_array_format() << "\n";
- }
- report_my_gl_errors();
- update_vertex_buffer(gvbc, data->get_handle(), false);
- return gvbc;
- }
- return nullptr;
- }
- /**
- * Makes sure that the data in the vertex buffer is up-to-date. This may bind
- * it to the GL_ARRAY_BUFFER binding point if necessary.
- */
- bool CLP(GraphicsStateGuardian)::
- update_vertex_buffer(CLP(VertexBufferContext) *gvbc,
- const GeomVertexArrayDataHandle *reader, bool force) {
- nassertr(_supports_buffers, false);
- if (reader->get_modified() == UpdateSeq::initial()) {
- // No need to re-apply.
- return true;
- }
- gvbc->set_active(true);
- if (gvbc->was_modified(reader)) {
- int num_bytes = reader->get_data_size_bytes();
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "copying " << num_bytes
- << " bytes into vertex buffer " << (int)gvbc->_index << "\n";
- }
- if (num_bytes != 0) {
- const unsigned char *client_pointer = reader->get_read_pointer(force);
- if (client_pointer == nullptr) {
- return false;
- }
- PStatGPUTimer timer(this, _load_vertex_buffer_pcollector, reader->get_current_thread());
- if (_current_vbuffer_index != gvbc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "binding vertex buffer " << (int)gvbc->_index << "\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, gvbc->_index);
- _current_vbuffer_index = gvbc->_index;
- }
- if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) {
- _glBufferData(GL_ARRAY_BUFFER, num_bytes, client_pointer,
- get_usage(reader->get_usage_hint()));
- } else {
- _glBufferSubData(GL_ARRAY_BUFFER, 0, num_bytes, client_pointer);
- }
- _data_transferred_pcollector.add_level(num_bytes);
- }
- gvbc->mark_loaded(reader);
- }
- gvbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- maybe_gl_finish();
- report_my_gl_errors();
- return true;
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_vertex_buffer(VertexBufferContext *vbc) {
- nassertv(_supports_buffers);
- CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext), vbc);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "deleting vertex buffer " << (int)gvbc->_index << "\n";
- }
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_vbuffer_index == gvbc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding vertex buffer\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, 0);
- _current_vbuffer_index = 0;
- }
- _glDeleteBuffers(1, &gvbc->_index);
- report_my_gl_errors();
- gvbc->_index = 0;
- delete gvbc;
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_vertex_buffers(const pvector<BufferContext *> &contexts) {
- if (contexts.empty()) {
- return;
- }
- nassertv(_supports_buffers);
- bool debug = GLCAT.is_debug() && gl_debug_buffers;
- GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * contexts.size());
- size_t num_indices = 0;
- for (BufferContext *bc : contexts) {
- CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext), bc);
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_vbuffer_index == gvbc->_index) {
- if (debug && GLCAT.is_spam()) {
- GLCAT.spam()
- << "unbinding vertex buffer " << gvbc->_index << "\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, 0);
- _current_vbuffer_index = 0;
- }
- if (debug) {
- GLCAT.debug()
- << "deleting vertex buffer " << gvbc->_index << "\n";
- }
- indices[num_indices++] = gvbc->_index;
- gvbc->_index = 0;
- delete gvbc;
- }
- _glDeleteBuffers(num_indices, indices);
- report_my_gl_errors();
- }
- /**
- * Internal function to bind a buffer object for the indicated data array, if
- * appropriate, or to unbind a buffer object if it should be rendered from
- * client memory.
- *
- * If the buffer object is bound, this function sets client_pointer to NULL
- * (representing the start of the buffer object in server memory); if the
- * buffer object is not bound, this function sets client_pointer the pointer
- * to the data array in client memory, that is, the data array passed in.
- *
- * If force is not true, the function may return false indicating the data is
- * not currently available.
- */
- bool CLP(GraphicsStateGuardian)::
- setup_array_data(const unsigned char *&client_pointer,
- const GeomVertexArrayDataHandle *array_reader,
- bool force) {
- if (!_supports_buffers) {
- // No support for buffer objects; always render from client.
- client_pointer = array_reader->get_read_pointer(force);
- return (client_pointer != nullptr);
- }
- if (!vertex_buffers || _geom_display_list != 0 ||
- array_reader->get_usage_hint() < gl_min_buffer_usage_hint) {
- // The array specifies client rendering only, or buffer objects are
- // configured off.
- if (_current_vbuffer_index != 0) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding vertex buffer\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, 0);
- _current_vbuffer_index = 0;
- }
- client_pointer = array_reader->get_read_pointer(force);
- return (client_pointer != nullptr);
- }
- // Prepare the buffer object and bind it.
- CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext),
- array_reader->prepare_now(get_prepared_objects(), this));
- nassertr(gvbc != (CLP(VertexBufferContext) *)nullptr, false);
- if (!update_vertex_buffer(gvbc, array_reader, force)) {
- return false;
- }
- if (_current_vbuffer_index != gvbc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "binding vertex buffer " << (int)gvbc->_index << "\n";
- }
- _glBindBuffer(GL_ARRAY_BUFFER, gvbc->_index);
- _current_vbuffer_index = gvbc->_index;
- }
- // NULL is the OpenGL convention for the first byte of the buffer object.
- client_pointer = nullptr;
- return true;
- }
- /**
- * Creates a new retained-mode representation of the given data, and returns a
- * newly-allocated IndexBufferContext pointer to reference it. It is the
- * responsibility of the calling function to later call release_index_buffer()
- * with this same pointer (which will also delete the pointer).
- *
- * This function should not be called directly to prepare a buffer. Instead,
- * call Geom::prepare().
- */
- IndexBufferContext *CLP(GraphicsStateGuardian)::
- prepare_index_buffer(GeomPrimitive *data) {
- if (_supports_buffers) {
- PStatGPUTimer timer(this, _prepare_index_buffer_pcollector);
- CLP(IndexBufferContext) *gibc = new CLP(IndexBufferContext)(this, _prepared_objects, data);
- _glGenBuffers(1, &gibc->_index);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "creating index buffer " << (int)gibc->_index << ": "
- << data->get_num_vertices() << " indices ("
- << data->get_vertices()->get_array_format()->get_column(0)->get_numeric_type()
- << ")\n";
- }
- report_my_gl_errors();
- GeomPrimitivePipelineReader reader(data, Thread::get_current_thread());
- apply_index_buffer(gibc, &reader, false);
- return gibc;
- }
- return nullptr;
- }
- /**
- * Makes the data the currently available data for rendering.
- */
- bool CLP(GraphicsStateGuardian)::
- apply_index_buffer(IndexBufferContext *ibc,
- const GeomPrimitivePipelineReader *reader,
- bool force) {
- nassertr(_supports_buffers, false);
- if (reader->get_modified() == UpdateSeq::initial()) {
- // No need to re-apply.
- return true;
- }
- CLP(IndexBufferContext) *gibc = DCAST(CLP(IndexBufferContext), ibc);
- if (_current_ibuffer_index != gibc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "binding index buffer " << (int)gibc->_index << "\n";
- }
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gibc->_index);
- _current_ibuffer_index = gibc->_index;
- gibc->set_active(true);
- }
- if (gibc->was_modified(reader)) {
- int num_bytes = reader->get_data_size_bytes();
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "copying " << num_bytes
- << " bytes into index buffer " << (int)gibc->_index << "\n";
- }
- if (num_bytes != 0) {
- const unsigned char *client_pointer = reader->get_read_pointer(force);
- if (client_pointer == nullptr) {
- return false;
- }
- PStatGPUTimer timer(this, _load_index_buffer_pcollector, reader->get_current_thread());
- if (gibc->changed_size(reader) || gibc->changed_usage_hint(reader)) {
- _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes, client_pointer,
- get_usage(reader->get_usage_hint()));
- } else {
- _glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, num_bytes,
- client_pointer);
- }
- _data_transferred_pcollector.add_level(num_bytes);
- }
- gibc->mark_loaded(reader);
- }
- gibc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- maybe_gl_finish();
- report_my_gl_errors();
- return true;
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_index_buffer(IndexBufferContext *ibc) {
- nassertv(_supports_buffers);
- CLP(IndexBufferContext) *gibc = DCAST(CLP(IndexBufferContext), ibc);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "deleting index buffer " << (int)gibc->_index << "\n";
- }
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_ibuffer_index == gibc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding index buffer\n";
- }
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- _current_ibuffer_index = 0;
- }
- _glDeleteBuffers(1, &gibc->_index);
- report_my_gl_errors();
- gibc->_index = 0;
- delete gibc;
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_index_buffers(const pvector<BufferContext *> &contexts) {
- if (contexts.empty()) {
- return;
- }
- nassertv(_supports_buffers);
- bool debug = GLCAT.is_debug() && gl_debug_buffers;
- GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * contexts.size());
- size_t num_indices = 0;
- for (BufferContext *bc : contexts) {
- CLP(IndexBufferContext) *gibc = DCAST(CLP(IndexBufferContext), bc);
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_ibuffer_index == gibc->_index) {
- if (debug && GLCAT.is_spam()) {
- GLCAT.spam()
- << "unbinding index buffer " << gibc->_index << "\n";
- }
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- _current_ibuffer_index = 0;
- }
- if (debug) {
- GLCAT.debug()
- << "deleting index buffer " << gibc->_index << "\n";
- }
- indices[num_indices++] = gibc->_index;
- gibc->_index = 0;
- delete gibc;
- }
- _glDeleteBuffers(num_indices, indices);
- report_my_gl_errors();
- }
- /**
- * Internal function to bind a buffer object for the indicated primitive's
- * index list, if appropriate, or to unbind a buffer object if it should be
- * rendered from client memory.
- *
- * If the buffer object is bound, this function sets client_pointer to NULL
- * (representing the start of the buffer object in server memory); if the
- * buffer object is not bound, this function sets client_pointer to to the
- * data array in client memory, that is, the data array passed in.
- *
- * If force is not true, the function may return false indicating the data is
- * not currently available.
- */
- bool CLP(GraphicsStateGuardian)::
- setup_primitive(const unsigned char *&client_pointer,
- const GeomPrimitivePipelineReader *reader,
- bool force) {
- if (!_supports_buffers) {
- // No support for buffer objects; always render from client.
- client_pointer = reader->get_read_pointer(force);
- return (client_pointer != nullptr);
- }
- if (!vertex_buffers || _geom_display_list != 0 ||
- reader->get_usage_hint() == Geom::UH_client) {
- // The array specifies client rendering only, or buffer objects are
- // configured off.
- if (_current_ibuffer_index != 0) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding index buffer\n";
- }
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- _current_ibuffer_index = 0;
- }
- client_pointer = reader->get_read_pointer(force);
- return (client_pointer != nullptr);
- }
- // Prepare the buffer object and bind it.
- IndexBufferContext *ibc = reader->prepare_now(get_prepared_objects(), this);
- nassertr(ibc != nullptr, false);
- if (!apply_index_buffer(ibc, reader, force)) {
- return false;
- }
- // NULL is the OpenGL convention for the first byte of the buffer object.
- client_pointer = nullptr;
- return true;
- }
- #ifndef OPENGLES
- /**
- * Creates a new retained-mode representation of the given data, and returns a
- * newly-allocated BufferContext pointer to reference it. It is the
- * responsibility of the calling function to later call release_shader_buffer()
- * with this same pointer (which will also delete the pointer).
- *
- * This function should not be called directly to prepare a buffer. Instead,
- * call ShaderBuffer::prepare().
- */
- BufferContext *CLP(GraphicsStateGuardian)::
- prepare_shader_buffer(ShaderBuffer *data) {
- if (_supports_shader_buffers) {
- PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector);
- CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects, data);
- _glGenBuffers(1, &gbc->_index);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "creating shader buffer " << (int)gbc->_index << ": "<< *data << "\n";
- }
- _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index);
- _current_sbuffer_index = gbc->_index;
- if (_use_object_labels) {
- string name = data->get_name();
- _glObjectLabel(GL_BUFFER, gbc->_index, name.size(), name.data());
- }
- // Some drivers require the buffer to be padded to 16 byte boundary.
- uint64_t num_bytes = (data->get_data_size_bytes() + 15u) & ~15u;
- if (_supports_buffer_storage) {
- _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
- } else {
- _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint()));
- }
- gbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- report_my_gl_errors();
- return gbc;
- }
- return nullptr;
- }
- /**
- * Binds the given shader buffer to the given binding slot.
- */
- void CLP(GraphicsStateGuardian)::
- apply_shader_buffer(GLuint base, ShaderBuffer *buffer) {
- GLuint index = 0;
- if (buffer != nullptr) {
- BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this);
- if (bc != nullptr) {
- CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
- index = gbc->_index;
- gbc->set_active(true);
- }
- }
- if (base >= _current_sbuffer_base.size()) {
- _current_sbuffer_base.resize(base + 1, 0);
- }
- if (_current_sbuffer_base[base] != index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "binding shader buffer " << (int)index
- << " to index " << base << "\n";
- }
- _glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base, index);
- _current_sbuffer_base[base] = index;
- _current_sbuffer_index = index;
- report_my_gl_errors();
- }
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_shader_buffer(BufferContext *bc) {
- nassertv(_supports_buffers);
- CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
- if (GLCAT.is_debug() && gl_debug_buffers) {
- GLCAT.debug()
- << "deleting shader buffer " << (int)gbc->_index << "\n";
- }
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_sbuffer_index == gbc->_index) {
- if (GLCAT.is_spam() && gl_debug_buffers) {
- GLCAT.spam()
- << "unbinding shader buffer\n";
- }
- _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
- _current_sbuffer_index = 0;
- }
- _glDeleteBuffers(1, &gbc->_index);
- report_my_gl_errors();
- gbc->_index = 0;
- delete gbc;
- }
- /**
- * Frees the GL resources previously allocated for the data. This function
- * should never be called directly; instead, call Data::release() (or simply
- * let the Data destruct).
- */
- void CLP(GraphicsStateGuardian)::
- release_shader_buffers(const pvector<BufferContext *> &contexts) {
- if (contexts.empty()) {
- return;
- }
- nassertv(_supports_buffers);
- bool debug = GLCAT.is_debug() && gl_debug_buffers;
- GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * contexts.size());
- size_t num_indices = 0;
- for (BufferContext *bc : contexts) {
- CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
- // Make sure the buffer is unbound before we delete it. Not strictly
- // necessary according to the OpenGL spec, but it might help out a flaky
- // driver, and we need to keep our internal state consistent anyway.
- if (_current_sbuffer_index == gbc->_index) {
- if (debug && GLCAT.is_spam()) {
- GLCAT.spam()
- << "unbinding shader buffer " << gbc->_index << "\n";
- }
- _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
- _current_sbuffer_index = 0;
- }
- if (debug) {
- GLCAT.debug()
- << "deleting shader buffer " << gbc->_index << "\n";
- }
- indices[num_indices++] = gbc->_index;
- gbc->_index = 0;
- delete gbc;
- }
- _glDeleteBuffers(num_indices, indices);
- report_my_gl_errors();
- }
- #endif
- #ifndef OPENGLES
- /**
- * Begins a new occlusion query. After this call, you may call
- * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.
- * Eventually, you should call end_occlusion_query() before the end of the
- * frame; that will return a new OcclusionQueryContext object that will tell
- * you how many pixels represented by the bracketed geometry passed the depth
- * test.
- *
- * It is not valid to call begin_occlusion_query() between another
- * begin_occlusion_query() .. end_occlusion_query() sequence.
- */
- void CLP(GraphicsStateGuardian)::
- begin_occlusion_query() {
- nassertv(_supports_occlusion_query);
- nassertv(_current_occlusion_query == nullptr);
- PT(CLP(OcclusionQueryContext)) query = new CLP(OcclusionQueryContext)(this);
- _glGenQueries(1, &query->_index);
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "beginning occlusion query index " << (int)query->_index << "\n";
- }
- _glBeginQuery(GL_SAMPLES_PASSED, query->_index);
- _current_occlusion_query = query;
- report_my_gl_errors();
- }
- #endif // !OPENGLES
- #ifndef OPENGLES
- /**
- * Ends a previous call to begin_occlusion_query(). This call returns the
- * OcclusionQueryContext object that will (eventually) report the number of
- * pixels that passed the depth test between the call to
- * begin_occlusion_query() and end_occlusion_query().
- */
- PT(OcclusionQueryContext) CLP(GraphicsStateGuardian)::
- end_occlusion_query() {
- nassertr(_current_occlusion_query != nullptr, nullptr);
- PT(OcclusionQueryContext) result = _current_occlusion_query;
- GLuint index = DCAST(CLP(OcclusionQueryContext), result)->_index;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "ending occlusion query index " << (int)index << "\n";
- }
- _current_occlusion_query = nullptr;
- _glEndQuery(GL_SAMPLES_PASSED);
- // Temporary hack to try working around an apparent driver bug on iMacs.
- // Occlusion queries sometimes incorrectly report 0 samples, unless we stall
- // the pipe to keep fewer than a certain maximum number of queries pending
- // at once.
- static ConfigVariableInt limit_occlusion_queries("limit-occlusion-queries", 0);
- if (limit_occlusion_queries > 0) {
- if (index > (unsigned int)limit_occlusion_queries) {
- PStatGPUTimer timer(this, _wait_occlusion_pcollector);
- GLuint result;
- _glGetQueryObjectuiv(index - (unsigned int)limit_occlusion_queries,
- GL_QUERY_RESULT, &result);
- }
- }
- report_my_gl_errors();
- return result;
- }
- #endif // !OPENGLES
- /**
- * Adds a timer query to the command stream, associated with the given PStats
- * collector index.
- */
- PT(TimerQueryContext) CLP(GraphicsStateGuardian)::
- issue_timer_query(int pstats_index) {
- #if defined(DO_PSTATS) && !defined(OPENGLES)
- nassertr(_supports_timer_query, nullptr);
- PT(CLP(TimerQueryContext)) query;
- // Hack
- if (pstats_index == _command_latency_pcollector.get_index()) {
- query = new CLP(LatencyQueryContext)(this, pstats_index);
- } else {
- query = new CLP(TimerQueryContext)(this, pstats_index);
- }
- if (_deleted_queries.size() >= 1) {
- query->_index = _deleted_queries.back();
- _deleted_queries.pop_back();
- } else {
- _glGenQueries(1, &query->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "Generating query for " << pstats_index
- << ": " << query->_index << "\n";
- }
- }
- // Issue the timestamp query.
- _glQueryCounter(query->_index, GL_TIMESTAMP);
- if (_use_object_labels) {
- // Assign a label to it based on the PStatCollector name.
- const PStatClient *client = PStatClient::get_global_pstats();
- string name = client->get_collector_fullname(pstats_index & 0x7fff);
- _glObjectLabel(GL_QUERY, query->_index, name.size(), name.data());
- }
- _pending_timer_queries.push_back((TimerQueryContext *)query);
- return (TimerQueryContext *)query;
- #else
- return nullptr;
- #endif
- }
- #ifndef OPENGLES_1
- /**
- * Dispatches a currently bound compute shader using the given work group
- * counts.
- */
- void CLP(GraphicsStateGuardian)::
- dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) {
- maybe_gl_finish();
- PStatGPUTimer timer(this, _compute_dispatch_pcollector);
- nassertv(_supports_compute_shaders);
- nassertv(_current_shader_context != nullptr);
- _glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
- maybe_gl_finish();
- }
- #endif // !OPENGLES_1
- /**
- * Creates a new GeomMunger object to munge vertices appropriate to this GSG
- * for the indicated state.
- */
- PT(GeomMunger) CLP(GraphicsStateGuardian)::
- make_geom_munger(const RenderState *state, Thread *current_thread) {
- PT(CLP(GeomMunger)) munger = new CLP(GeomMunger)(this, state);
- return GeomMunger::register_munger(munger, current_thread);
- }
- /**
- * Copy the pixels within the indicated display region from the framebuffer
- * into texture memory.
- *
- * If z > -1, it is the cube map index or layer index into which to copy.
- */
- bool CLP(GraphicsStateGuardian)::
- framebuffer_copy_to_texture(Texture *tex, int view, int z,
- const DisplayRegion *dr, const RenderBuffer &rb) {
- nassertr(tex != nullptr && dr != nullptr, false);
- set_read_buffer(rb._buffer_type);
- clear_color_write_mask();
- int xo, yo, w, h;
- dr->get_region_pixels(xo, yo, w, h);
- tex->set_size_padded(w, h, tex->get_z_size());
- if (tex->get_compression() == Texture::CM_default) {
- // Unless the user explicitly turned on texture compression, turn it off
- // for the copy-to-texture case.
- tex->set_compression(Texture::CM_off);
- }
- // Sanity check everything.
- if (z >= 0) {
- if (z >= tex->get_z_size()) {
- // This can happen, when textures with different layer counts are
- // attached to a buffer. We simply ignore this if it happens.
- return false;
- }
- if ((w != tex->get_x_size()) ||
- (h != tex->get_y_size())) {
- return false;
- }
- if (tex->get_texture_type() == Texture::TT_cube_map) {
- if (!_supports_cube_map) {
- return false;
- }
- nassertr(z < 6, false);
- if (w != h) {
- return false;
- }
- } else if (tex->get_texture_type() == Texture::TT_3d_texture) {
- if (!_supports_3d_texture) {
- return false;
- }
- } else if (tex->get_texture_type() == Texture::TT_2d_texture_array) {
- if (!_supports_2d_texture_array) {
- return false;
- }
- } else {
- GLCAT.error()
- << "Don't know how to copy framebuffer to texture " << *tex << "\n";
- }
- } else {
- nassertr(tex->get_texture_type() == Texture::TT_2d_texture, false);
- }
- // Match framebuffer format if necessary.
- if (tex->get_match_framebuffer_format()) {
- switch (tex->get_format()) {
- case Texture::F_depth_component:
- case Texture::F_depth_component16:
- case Texture::F_depth_component24:
- case Texture::F_depth_component32:
- case Texture::F_depth_stencil:
- // Don't remap if we're one of these special format.
- break;
- default:
- // If the texture is a color format, we want to match the presence of
- // sRGB and alpha according to the framebuffer.
- if (_current_properties->get_srgb_color()) {
- if (_current_properties->get_alpha_bits()) {
- tex->set_format(Texture::F_srgb_alpha);
- } else {
- tex->set_format(Texture::F_srgb);
- }
- } else {
- if (_current_properties->get_alpha_bits()) {
- tex->set_format(Texture::F_rgba);
- } else {
- tex->set_format(Texture::F_rgb);
- }
- }
- }
- }
- TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
- nassertr(tc != nullptr, false);
- CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
- apply_texture(gtc);
- bool needs_reload = specify_texture(gtc, tex->get_default_sampler());
- GLenum target = get_texture_target(tex->get_texture_type());
- GLint internal_format = get_internal_image_format(tex);
- int width = tex->get_x_size();
- int height = tex->get_y_size();
- int depth = tex->get_z_size();
- bool uses_mipmaps = tex->uses_mipmaps() && !gl_ignore_mipmaps;
- if (uses_mipmaps) {
- if (_supports_generate_mipmap) {
- #ifndef OPENGLES_2
- if (_glGenerateMipmap == nullptr) {
- glTexParameteri(target, GL_GENERATE_MIPMAP, true);
- }
- #endif
- } else {
- // If we can't auto-generate mipmaps, do without mipmaps.
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- uses_mipmaps = false;
- }
- }
- bool new_image = needs_reload || gtc->was_image_modified();
- if (z >= 0) {
- if (target == GL_TEXTURE_CUBE_MAP) {
- // Copy to a cube map face, which is treated as a 2D texture.
- target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
- depth = 1;
- z = -1;
- // Cube map faces seem to have trouble with CopyTexSubImage, so we
- // always reload the image.
- new_image = true;
- }
- }
- if (!gtc->_has_storage ||
- internal_format != gtc->_internal_format ||
- uses_mipmaps != gtc->_uses_mipmaps ||
- width != gtc->_width ||
- height != gtc->_height ||
- depth != gtc->_depth) {
- // If the texture properties have changed, we need to reload the image.
- new_image = true;
- }
- if (new_image && gtc->_immutable) {
- gtc->reset_data();
- glBindTexture(target, gtc->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
- }
- }
- #ifndef OPENGLES_1
- if (gtc->needs_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT)) {
- // Make sure that any incoherent writes to this texture have been synced.
- issue_memory_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
- }
- #endif
- if (z >= 0) {
- #ifndef OPENGLES_1
- if (new_image) {
- // These won't be used because we pass a NULL image, but we still have
- // to specify them. Might as well use the actual values.
- GLint external_format = get_external_image_format(tex);
- GLint component_type = get_component_type(tex->get_component_type());
- _glTexImage3D(target, 0, internal_format, width, height, depth, 0, external_format, component_type, nullptr);
- }
- _glCopyTexSubImage3D(target, 0, 0, 0, z, xo, yo, w, h);
- #endif
- } else {
- if (new_image) {
- // We have to create a new image. It seems that OpenGL accepts a size
- // higher than the framebuffer, but if we run into trouble we'll have to
- // replace this with something smarter.
- glCopyTexImage2D(target, 0, internal_format, xo, yo, width, height, 0);
- } else {
- // We can overlay the existing image.
- glCopyTexSubImage2D(target, 0, 0, 0, xo, yo, w, h);
- }
- }
- if (uses_mipmaps && _glGenerateMipmap != nullptr) {
- glEnable(target);
- _glGenerateMipmap(target);
- glDisable(target);
- }
- gtc->_has_storage = true;
- gtc->_uses_mipmaps = uses_mipmaps;
- gtc->_internal_format = internal_format;
- gtc->_width = width;
- gtc->_height = height;
- gtc->_depth = depth;
- gtc->mark_loaded();
- gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- report_my_gl_errors();
- // Force reload of texture state, since we've just monkeyed with it.
- _state_mask.clear_bit(TextureAttrib::get_class_slot());
- return true;
- }
- /**
- * Copy the pixels within the indicated display region from the framebuffer
- * into system memory, not texture memory. Returns true on success, false on
- * failure.
- *
- * This completely redefines the ram image of the indicated texture.
- */
- bool CLP(GraphicsStateGuardian)::
- framebuffer_copy_to_ram(Texture *tex, int view, int z,
- const DisplayRegion *dr, const RenderBuffer &rb) {
- nassertr(tex != nullptr && dr != nullptr, false);
- set_read_buffer(rb._buffer_type);
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- clear_color_write_mask();
- // Bug fix for RE, RE2, and VTX - need to disable texturing in order for
- // glReadPixels() to work NOTE: reading the depth buffer is *much* slower
- // than reading the color buffer
- set_state_and_transform(RenderState::make_empty(), _internal_transform);
- int xo, yo, w, h;
- dr->get_region_pixels(xo, yo, w, h);
- Texture::ComponentType component_type = tex->get_component_type();
- Texture::Format format = tex->get_format();
- switch (format) {
- case Texture::F_depth_stencil:
- if (_current_properties->get_float_depth()) {
- component_type = Texture::T_float;
- } else {
- component_type = Texture::T_unsigned_int_24_8;
- }
- break;
- case Texture::F_depth_component:
- if (_current_properties->get_float_depth()) {
- component_type = Texture::T_float;
- } else if (_current_properties->get_depth_bits() <= 8) {
- component_type = Texture::T_unsigned_byte;
- } else if (_current_properties->get_depth_bits() <= 16) {
- component_type = Texture::T_unsigned_short;
- } else {
- component_type = Texture::T_float;
- }
- break;
- case Texture::F_depth_component16:
- component_type = Texture::T_unsigned_short;
- break;
- case Texture::F_depth_component24:
- case Texture::F_depth_component32:
- component_type = Texture::T_float;
- break;
- default:
- if (_current_properties->get_srgb_color()) {
- if (_current_properties->get_alpha_bits()) {
- format = Texture::F_srgb_alpha;
- } else {
- format = Texture::F_srgb;
- }
- } else if (_current_properties->get_float_color()) {
- if (_current_properties->get_alpha_bits()) {
- format = Texture::F_rgba32;
- } else if (_current_properties->get_blue_bits()) {
- format = Texture::F_rgb32;
- } else if (_current_properties->get_green_bits()) {
- format = Texture::F_rg32;
- } else {
- format = Texture::F_r32;
- }
- } else {
- if (_current_properties->get_alpha_bits()) {
- format = Texture::F_rgba;
- } else {
- format = Texture::F_rgb;
- }
- }
- if (_current_properties->get_float_color()) {
- component_type = Texture::T_float;
- } else if (_current_properties->get_color_bits() <= 24
- && _current_properties->get_red_bits() <= 8
- && _current_properties->get_green_bits() <= 8
- && _current_properties->get_blue_bits() <= 8
- && _current_properties->get_alpha_bits() <= 8) {
- component_type = Texture::T_unsigned_byte;
- } else {
- component_type = Texture::T_unsigned_short;
- }
- }
- Texture::TextureType texture_type;
- int z_size;
- // TODO: should be extended to support 3D textures, 2D arrays and cube map
- // arrays.
- if (z >= 0) {
- texture_type = Texture::TT_cube_map;
- z_size = 6;
- } else {
- texture_type = Texture::TT_2d_texture;
- z_size = 1;
- }
- int num_views = tex->get_num_views();
- if (tex->get_x_size() != w || tex->get_y_size() != h ||
- tex->get_z_size() != z_size ||
- tex->get_component_type() != component_type ||
- tex->get_format() != format ||
- tex->get_texture_type() != texture_type ||
- view >= num_views) {
- tex->setup_texture(texture_type, w, h, z_size, component_type, format);
- // The above resets the number of views to 1, so set this back.
- num_views = std::max(view + 1, num_views);
- if (num_views > 1) {
- tex->set_num_views(num_views);
- }
- }
- nassertr(z < tex->get_z_size(), false);
- GLenum external_format = get_external_image_format(tex);
- // OpenGL ES implementations may support BGRA, but that doesn't imply they
- // also support it for glReadPixels specifically.
- if (!_supports_bgra_read && external_format == GL_BGRA) {
- external_format = GL_RGBA;
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glReadPixels(" << xo << ", " << yo << ", " << w << ", " << h << ", ";
- switch (external_format) {
- case GL_DEPTH_COMPONENT:
- GLCAT.spam(false) << "GL_DEPTH_COMPONENT, ";
- break;
- case GL_DEPTH_STENCIL:
- GLCAT.spam(false) << "GL_DEPTH_STENCIL, ";
- break;
- #ifndef OPENGLES_1
- case GL_RG:
- GLCAT.spam(false) << "GL_RG, ";
- break;
- #endif
- case GL_RGB:
- GLCAT.spam(false) << "GL_RGB, ";
- break;
- case GL_RGBA:
- GLCAT.spam(false) << "GL_RGBA, ";
- break;
- #ifndef OPENGLES
- case GL_BGR:
- GLCAT.spam(false) << "GL_BGR, ";
- break;
- #endif
- case GL_BGRA:
- GLCAT.spam(false) << "GL_BGRA, ";
- break;
- default:
- GLCAT.spam(false) << "unknown, ";
- break;
- }
- switch (get_component_type(component_type)) {
- case GL_UNSIGNED_BYTE:
- GLCAT.spam(false) << "GL_UNSIGNED_BYTE";
- break;
- case GL_UNSIGNED_SHORT:
- GLCAT.spam(false) << "GL_UNSIGNED_SHORT";
- break;
- case GL_FLOAT:
- GLCAT.spam(false) << "GL_FLOAT";
- break;
- #ifndef OPENGLES_1
- case GL_INT:
- GLCAT.spam(false) << "GL_INT";
- break;
- #endif
- default:
- GLCAT.spam(false) << "unknown";
- break;
- }
- GLCAT.spam(false)
- << ")" << endl;
- }
- unsigned char *image_ptr = tex->modify_ram_image();
- size_t image_size = tex->get_ram_image_size();
- if (z >= 0 || view > 0) {
- image_size = tex->get_expected_ram_page_size();
- if (z >= 0) {
- image_ptr += z * image_size;
- }
- if (view > 0) {
- image_ptr += (view * tex->get_z_size()) * image_size;
- nassertr(view < tex->get_num_views(), false);
- }
- }
- glReadPixels(xo, yo, w, h, external_format,
- get_component_type(component_type), image_ptr);
- // We may have to reverse the byte ordering of the image if GL didn't do it
- // for us.
- if (external_format == GL_RGBA || external_format == GL_RGB) {
- PTA_uchar new_image;
- const unsigned char *result =
- fix_component_ordering(new_image, image_ptr, image_size,
- external_format, tex);
- if (result != image_ptr) {
- memcpy(image_ptr, result, image_size);
- }
- }
- report_my_gl_errors();
- return true;
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- apply_fog(Fog *fog) {
- Fog::Mode fmode = fog->get_mode();
- glFogf(GL_FOG_MODE, get_fog_mode_type(fmode));
- if (fmode == Fog::M_linear) {
- PN_stdfloat onset, opaque;
- fog->get_linear_range(onset, opaque);
- glFogf(GL_FOG_START, onset);
- glFogf(GL_FOG_END, opaque);
- } else {
- // Exponential fog is always camera-relative.
- glFogf(GL_FOG_DENSITY, fog->get_exp_density());
- }
- call_glFogfv(GL_FOG_COLOR, fog->get_color());
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Sends the indicated transform matrix to the graphics API to be applied to
- * future vertices.
- *
- * This transform is the internal_transform, already converted into the GSG's
- * internal coordinate system.
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_transform() {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- // OpenGL ES 2 does not support glLoadMatrix.
- const TransformState *transform = _internal_transform;
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl;
- }
- DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
- glMatrixMode(GL_MODELVIEW);
- call_glLoadMatrix(transform->get_mat());
- }
- #endif
- _transform_stale = false;
- report_my_gl_errors();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_shade_model() {
- const ShadeModelAttrib *target_shade_model;
- _target_rs->get_attrib_def(target_shade_model);
- switch (target_shade_model->get_mode()) {
- case ShadeModelAttrib::M_smooth:
- glShadeModel(GL_SMOOTH);
- _flat_shade_model = false;
- break;
- case ShadeModelAttrib::M_flat:
- glShadeModel(GL_FLAT);
- _flat_shade_model = true;
- break;
- }
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifndef OPENGLES_1
- /**
- * Called when the current ShaderAttrib state has changed.
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_shader() {
- PStatTimer timer(_draw_set_state_shader_pcollector);
- ShaderContext *context = 0;
- Shader *shader = (Shader *)_target_shader->get_shader();
- // If we don't have a shader, apply the default shader.
- if (!has_fixed_function_pipeline() && !shader) {
- shader = _default_shader;
- nassertv(shader != nullptr);
- }
- if (shader) {
- if (_current_shader != shader) {
- context = shader->prepare_now(get_prepared_objects(), this);
- } else {
- context = _current_shader_context;
- }
- }
- // If it failed, try applying the default shader.
- if (_default_shader != nullptr && shader != _default_shader &&
- (context == 0 || !context->valid())) {
- shader = _default_shader;
- nassertv(shader != nullptr);
- if (_current_shader != shader) {
- context = shader->prepare_now(get_prepared_objects(), this);
- } else {
- context = _current_shader_context;
- }
- }
- if (context == 0 || !context->valid()) {
- if (_current_shader_context != 0) {
- _current_shader_context->unbind();
- _current_shader = 0;
- _current_shader_context = 0;
- }
- } else {
- if (context != _current_shader_context) {
- // Use a completely different shader than before. Unbind old shader,
- // bind the new one.
- if (_current_shader_context != nullptr &&
- _current_shader->get_language() != shader->get_language()) {
- // If it's a different type of shader, make sure to unbind the old.
- _current_shader_context->unbind();
- }
- context->bind();
- _current_shader = shader;
- }
- // Bind the shader storage buffers.
- context->update_shader_buffer_bindings(_current_shader_context);
- _current_shader_context = context;
- }
- #ifndef OPENGLES
- // Is the point size provided by the shader or by OpenGL?
- bool shader_point_size = _target_shader->get_flag(ShaderAttrib::F_shader_point_size);
- if (shader_point_size != _shader_point_size) {
- if (shader_point_size) {
- glEnable(GL_PROGRAM_POINT_SIZE);
- } else {
- glDisable(GL_PROGRAM_POINT_SIZE);
- }
- _shader_point_size = shader_point_size;
- }
- #endif
- report_my_gl_errors();
- }
- #endif // !OPENGLES_1
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_render_mode() {
- const RenderModeAttrib *target_render_mode;
- _target_rs->get_attrib_def(target_render_mode);
- _render_mode = target_render_mode->get_mode();
- PN_stdfloat thickness = target_render_mode->get_thickness();
- _point_perspective = target_render_mode->get_perspective();
- #ifndef OPENGLES // glPolygonMode not supported by OpenGL ES.
- switch (_render_mode) {
- case RenderModeAttrib::M_unchanged:
- case RenderModeAttrib::M_filled:
- case RenderModeAttrib::M_filled_flat:
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- break;
- case RenderModeAttrib::M_wireframe:
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- break;
- case RenderModeAttrib::M_point:
- glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
- break;
- default:
- GLCAT.error()
- << "Unknown render mode " << (int)_render_mode << endl;
- }
- #endif // OPENGLES
- // The thickness affects both the line width and the point size.
- if (thickness != _point_size) {
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "setting thickness to " << thickness << "\n";
- }
- glLineWidth(std::min((GLfloat)thickness, _max_line_width));
- #ifndef OPENGLES_2
- glPointSize(thickness);
- #endif
- _point_size = thickness;
- }
- report_my_gl_errors();
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- do_point_size();
- }
- #endif
- }
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_antialias() {
- const AntialiasAttrib *target_antialias;
- _target_rs->get_attrib_def(target_antialias);
- if (target_antialias->get_mode_type() == AntialiasAttrib::M_auto) {
- // In this special mode, we must enable antialiasing on a case-by-case
- // basis, because we enable it differently for polygons and for points and
- // lines.
- _auto_antialias_mode = true;
- } else {
- // Otherwise, explicitly enable or disable according to the bits that are
- // set. But if multisample is requested and supported, don't use the
- // other bits at all (they will be ignored by GL anyway).
- _auto_antialias_mode = false;
- unsigned short mode = target_antialias->get_mode();
- if (_supports_multisample &&
- (mode & AntialiasAttrib::M_multisample) != 0) {
- enable_multisample_antialias(true);
- } else {
- enable_multisample_antialias(false);
- enable_line_smooth((mode & AntialiasAttrib::M_line) != 0);
- enable_point_smooth((mode & AntialiasAttrib::M_point) != 0);
- enable_polygon_smooth((mode & AntialiasAttrib::M_polygon) != 0);
- }
- }
- #ifndef OPENGLES_2
- GLenum quality;
- switch (target_antialias->get_mode_quality()) {
- case AntialiasAttrib::M_faster:
- quality = GL_FASTEST;
- break;
- case AntialiasAttrib::M_better:
- quality = GL_NICEST;
- break;
- default:
- quality = GL_DONT_CARE;
- break;
- }
- if (_line_smooth_enabled) {
- glHint(GL_LINE_SMOOTH_HINT, quality);
- }
- if (_point_smooth_enabled) {
- glHint(GL_POINT_SMOOTH_HINT, quality);
- }
- #ifndef OPENGLES
- if (_polygon_smooth_enabled) {
- glHint(GL_POLYGON_SMOOTH_HINT, quality);
- }
- #endif
- #endif // !OPENGLES_2
- report_my_gl_errors();
- }
- #ifdef SUPPORT_FIXED_FUNCTION // OpenGL ES 2.0 doesn't support rescaling normals.
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_rescale_normal() {
- RescaleNormalAttrib::Mode mode = RescaleNormalAttrib::M_none;
- const RescaleNormalAttrib *target_rescale_normal;
- if (_target_rs->get_attrib(target_rescale_normal)) {
- mode = target_rescale_normal->get_mode();
- }
- switch (mode) {
- case RescaleNormalAttrib::M_none:
- glDisable(GL_NORMALIZE);
- if (_supports_rescale_normal && support_rescale_normal) {
- glDisable(GL_RESCALE_NORMAL);
- }
- break;
- case RescaleNormalAttrib::M_rescale:
- if (_supports_rescale_normal && support_rescale_normal) {
- glEnable(GL_RESCALE_NORMAL);
- glDisable(GL_NORMALIZE);
- } else {
- glEnable(GL_NORMALIZE);
- }
- break;
- case RescaleNormalAttrib::M_normalize:
- glEnable(GL_NORMALIZE);
- if (_supports_rescale_normal && support_rescale_normal) {
- glDisable(GL_RESCALE_NORMAL);
- }
- break;
- default:
- GLCAT.error()
- << "Unknown rescale_normal mode " << (int)mode << endl;
- }
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- // PandaCompareFunc - 1 + 0x200 === GL_NEVER, etc. order is sequential
- #define PANDA_TO_GL_COMPAREFUNC(PANDACMPFUNC) (PANDACMPFUNC-1 +0x200)
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_depth_test() {
- const DepthTestAttrib *target_depth_test;
- _target_rs->get_attrib_def(target_depth_test);
- DepthTestAttrib::PandaCompareFunc mode = target_depth_test->get_mode();
- if (mode == DepthTestAttrib::M_none) {
- enable_depth_test(false);
- } else {
- enable_depth_test(true);
- glDepthFunc(PANDA_TO_GL_COMPAREFUNC(mode));
- }
- report_my_gl_errors();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_alpha_test() {
- #ifndef OPENGLES_1
- if (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test)) {
- enable_alpha_test(false);
- } else
- #endif
- {
- const AlphaTestAttrib *target_alpha_test;
- _target_rs->get_attrib_def(target_alpha_test);
- AlphaTestAttrib::PandaCompareFunc mode = target_alpha_test->get_mode();
- if (mode == AlphaTestAttrib::M_none) {
- enable_alpha_test(false);
- } else {
- nassertv(GL_NEVER == (AlphaTestAttrib::M_never-1+0x200));
- glAlphaFunc(PANDA_TO_GL_COMPAREFUNC(mode), target_alpha_test->get_reference_alpha());
- enable_alpha_test(true);
- }
- }
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_depth_write() {
- const DepthWriteAttrib *target_depth_write;
- _target_rs->get_attrib_def(target_depth_write);
- DepthWriteAttrib::Mode mode = target_depth_write->get_mode();
- if (mode == DepthWriteAttrib::M_off) {
- #ifdef GSG_VERBOSE
- GLCAT.spam()
- << "glDepthMask(GL_FALSE)" << endl;
- #endif
- glDepthMask(GL_FALSE);
- } else {
- #ifdef GSG_VERBOSE
- GLCAT.spam()
- << "glDepthMask(GL_TRUE)" << endl;
- #endif
- glDepthMask(GL_TRUE);
- }
- report_my_gl_errors();
- }
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_cull_face() {
- const CullFaceAttrib *target_cull_face;
- _target_rs->get_attrib_def(target_cull_face);
- CullFaceAttrib::Mode mode = target_cull_face->get_effective_mode();
- switch (mode) {
- case CullFaceAttrib::M_cull_none:
- glDisable(GL_CULL_FACE);
- break;
- case CullFaceAttrib::M_cull_clockwise:
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- break;
- case CullFaceAttrib::M_cull_counter_clockwise:
- glEnable(GL_CULL_FACE);
- glCullFace(GL_FRONT);
- break;
- default:
- GLCAT.error()
- << "invalid cull face mode " << (int)mode << endl;
- break;
- }
- report_my_gl_errors();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_fog() {
- const FogAttrib *target_fog;
- _target_rs->get_attrib_def(target_fog);
- if (!target_fog->is_off()) {
- enable_fog(true);
- Fog *fog = target_fog->get_fog();
- nassertv(fog != nullptr);
- apply_fog(fog);
- } else {
- enable_fog(false);
- }
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_depth_offset() {
- const DepthOffsetAttrib *target_depth_offset = (const DepthOffsetAttrib *)
- _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot());
- int offset = target_depth_offset->get_offset();
- if (offset != 0) {
- // The relationship between these two parameters is a little unclear and
- // poorly explained in the GL man pages.
- glPolygonOffset((GLfloat) -offset, (GLfloat) -offset);
- enable_polygon_offset(true);
- } else {
- enable_polygon_offset(false);
- }
- PN_stdfloat min_value = target_depth_offset->get_min_value();
- PN_stdfloat max_value = target_depth_offset->get_max_value();
- #ifdef GSG_VERBOSE
- GLCAT.spam()
- << "glDepthRange(" << min_value << ", " << max_value << ")" << endl;
- #endif
- #ifdef OPENGLES
- // OpenGL ES uses a single-precision call.
- glDepthRangef((GLclampf)min_value, (GLclampf)max_value);
- #else
- // Mainline OpenGL uses a double-precision call.
- if (!_use_remapped_depth_range) {
- glDepthRange((GLclampd)min_value, (GLclampd)max_value);
- } else {
- // If we have a remapped depth range, we should adjust the values to range
- // from -1 to 1. We need to use an NV extension to pass unclamped values.
- _glDepthRangedNV(min_value * 2.0 - 1.0, max_value * 2.0 - 1.0);
- }
- #endif // OPENGLES
- report_my_gl_errors();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_material() {
- static Material empty;
- const Material *material;
- const MaterialAttrib *target_material;
- _target_rs->get_attrib_def(target_material);
- if (target_material == nullptr ||
- target_material->is_off()) {
- material = ∅
- } else {
- material = target_material->get_material();
- }
- bool has_material_force_color = _has_material_force_color;
- #ifndef NDEBUG
- if (_show_texture_usage) {
- // In show_texture_usage mode, all colors are white, so as not to
- // contaminate the texture color. This means we disable lighting
- // materials too.
- material = ∅
- has_material_force_color = false;
- }
- #endif // NDEBUG
- #ifdef OPENGLES
- const GLenum face = GL_FRONT_AND_BACK;
- #else
- GLenum face = material->get_twoside() ? GL_FRONT_AND_BACK : GL_FRONT;
- #endif
- call_glMaterialfv(face, GL_SPECULAR, material->get_specular());
- call_glMaterialfv(face, GL_EMISSION, material->get_emission());
- glMaterialf(face, GL_SHININESS, max(min(material->get_shininess(), (PN_stdfloat)128), (PN_stdfloat)0));
- if ((material->has_ambient() && material->has_diffuse()) || material->has_base_color()) {
- // The material has both an ambient and diffuse specified. This means we
- // do not need glMaterialColor().
- glDisable(GL_COLOR_MATERIAL);
- call_glMaterialfv(face, GL_AMBIENT, material->get_ambient());
- call_glMaterialfv(face, GL_DIFFUSE, material->get_diffuse());
- } else if (material->has_ambient()) {
- // The material specifies an ambient, but not a diffuse component. The
- // diffuse component comes from the object's color.
- if (has_material_force_color) {
- glDisable(GL_COLOR_MATERIAL);
- call_glMaterialfv(face, GL_DIFFUSE, _material_force_color);
- } else {
- #ifndef OPENGLES
- glColorMaterial(face, GL_DIFFUSE);
- #endif // OPENGLES
- glEnable(GL_COLOR_MATERIAL);
- }
- call_glMaterialfv(face, GL_AMBIENT, material->get_ambient());
- } else if (material->has_diffuse()) {
- // The material specifies a diffuse, but not an ambient component. The
- // ambient component comes from the object's color.
- if (has_material_force_color) {
- glDisable(GL_COLOR_MATERIAL);
- call_glMaterialfv(face, GL_AMBIENT, _material_force_color);
- } else {
- #ifndef OPENGLES
- glColorMaterial(face, GL_AMBIENT);
- #endif // OPENGLES
- glEnable(GL_COLOR_MATERIAL);
- }
- call_glMaterialfv(face, GL_DIFFUSE, material->get_diffuse());
- } else {
- // The material specifies neither a diffuse nor an ambient component.
- // Both components come from the object's color.
- if (has_material_force_color) {
- glDisable(GL_COLOR_MATERIAL);
- call_glMaterialfv(face, GL_AMBIENT, _material_force_color);
- call_glMaterialfv(face, GL_DIFFUSE, _material_force_color);
- } else {
- #ifndef OPENGLES
- glColorMaterial(face, GL_AMBIENT_AND_DIFFUSE);
- #endif // OPENGLES
- glEnable(GL_COLOR_MATERIAL);
- }
- }
- #ifndef OPENGLES
- glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, material->get_local());
- glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, material->get_twoside());
- if (_use_separate_specular_color) {
- glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
- } else {
- glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);
- }
- #endif
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Issues the logic operation attribute to the GL.
- */
- #if !defined(OPENGLES) || defined(OPENGLES_1)
- void CLP(GraphicsStateGuardian)::
- do_issue_logic_op() {
- const LogicOpAttrib *target_logic_op;
- _target_rs->get_attrib_def(target_logic_op);
- if (target_logic_op->get_operation() != LogicOpAttrib::O_none) {
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_CLEAR - 1 + (int)target_logic_op->get_operation());
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glEnable(GL_COLOR_LOGIC_OP)\n";
- GLCAT.spam() << "glLogicOp(" << target_logic_op->get_operation() << ")\n";
- }
- } else {
- glDisable(GL_COLOR_LOGIC_OP);
- glLogicOp(GL_COPY);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glDisable(GL_COLOR_LOGIC_OP)\n";
- }
- }
- }
- #endif
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_blending() {
- // Handle the color_write attrib. If color_write is off, then all the other
- // blending-related stuff doesn't matter. If the device doesn't support
- // color-write, we use blending tricks to effectively disable color write.
- const ColorWriteAttrib *target_color_write;
- _target_rs->get_attrib_def(target_color_write);
- unsigned int color_channels =
- target_color_write->get_channels() & _color_write_mask;
- #ifndef OPENGLES_1
- if (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write)) {
- color_channels &= ~(ColorWriteAttrib::C_alpha);
- }
- #endif
- if (color_channels == ColorWriteAttrib::C_off) {
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- if (gl_color_mask) {
- enable_blend(false);
- set_color_write_mask(ColorWriteAttrib::C_off);
- } else {
- enable_blend(true);
- _glBlendEquation(GL_FUNC_ADD);
- glBlendFunc(GL_ZERO, GL_ONE);
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n";
- GLCAT.spam() << "glBlendFunc(GL_ZERO, GL_ONE)\n";
- }
- return;
- } else {
- set_color_write_mask(color_channels);
- }
- const ColorBlendAttrib *target_color_blend;
- _target_rs->get_attrib_def(target_color_blend);
- CPT(ColorBlendAttrib) color_blend = target_color_blend;
- ColorBlendAttrib::Mode color_blend_mode = target_color_blend->get_mode();
- ColorBlendAttrib::Mode alpha_blend_mode = target_color_blend->get_alpha_mode();
- const TransparencyAttrib *target_transparency;
- _target_rs->get_attrib_def(target_transparency);
- TransparencyAttrib::Mode transparency_mode = target_transparency->get_mode();
- _color_blend_involves_color_scale = color_blend->involves_color_scale();
- // Is there a color blend set?
- if (color_blend_mode != ColorBlendAttrib::M_none) {
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- enable_blend(true);
- if (_supports_blend_equation_separate) {
- _glBlendEquationSeparate(get_blend_equation_type(color_blend_mode),
- get_blend_equation_type(alpha_blend_mode));
- } else {
- _glBlendEquation(get_blend_equation_type(color_blend_mode));
- }
- _glBlendFuncSeparate(get_blend_func(color_blend->get_operand_a()),
- get_blend_func(color_blend->get_operand_b()),
- get_blend_func(color_blend->get_alpha_operand_a()),
- get_blend_func(color_blend->get_alpha_operand_b()));
- #ifndef OPENGLES_1
- LColor c;
- if (_color_blend_involves_color_scale) {
- // Apply the current color scale to the blend mode.
- c = _current_color_scale;
- } else {
- c = color_blend->get_color();
- }
- _glBlendColor(c[0], c[1], c[2], c[3]);
- #endif
- if (GLCAT.is_spam()) {
- if (_supports_blend_equation_separate) {
- GLCAT.spam() << "glBlendEquationSeparate(" << color_blend_mode << ", "
- << alpha_blend_mode << ")\n";
- } else {
- GLCAT.spam() << "glBlendEquation(" << color_blend_mode << ")\n";
- }
- GLCAT.spam() << "glBlendFuncSeparate("
- << color_blend->get_operand_a() << ", "
- << color_blend->get_operand_b() << ", "
- << color_blend->get_alpha_operand_a() << ", "
- << color_blend->get_alpha_operand_b() << ")\n";
- #ifndef OPENGLES_1
- GLCAT.spam() << "glBlendColor(" << c << ")\n";
- #endif
- }
- return;
- }
- // No color blend; is there a transparency set?
- switch (transparency_mode) {
- case TransparencyAttrib::M_none:
- case TransparencyAttrib::M_binary:
- break;
- case TransparencyAttrib::M_alpha:
- case TransparencyAttrib::M_dual:
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- enable_blend(true);
- _glBlendEquation(GL_FUNC_ADD);
- if (old_alpha_blend) {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- _glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n";
- if (_supports_blend_equation_separate && !old_alpha_blend) {
- GLCAT.spam() << "glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)\n";
- } else {
- GLCAT.spam() << "glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)\n";
- }
- }
- return;
- case TransparencyAttrib::M_premultiplied_alpha:
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- enable_blend(true);
- _glBlendEquation(GL_FUNC_ADD);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n";
- GLCAT.spam() << "glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)\n";
- }
- return;
- case TransparencyAttrib::M_multisample:
- // We need to enable *both* of these in M_multisample case.
- enable_multisample_alpha_one(true);
- enable_multisample_alpha_mask(true);
- enable_blend(false);
- return;
- case TransparencyAttrib::M_multisample_mask:
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(true);
- enable_blend(false);
- return;
- default:
- GLCAT.error()
- << "invalid transparency mode " << (int)transparency_mode << endl;
- break;
- }
- if (_line_smooth_enabled || _point_smooth_enabled) {
- // If we have either of these turned on, we also need to have blend mode
- // enabled in order to see it.
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- enable_blend(true);
- _glBlendEquation(GL_FUNC_ADD);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n";
- GLCAT.spam() << "glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)\n";
- }
- return;
- }
- /*
- * For best polygon smoothing, we need: (1) a frame buffer that supports alpha
- * (2) sort polygons front-to-back (3) glBlendFunc(GL_SRC_ALPHA_SATURATE,
- * GL_ONE); Since these modes have other implications for the application, we
- * don't attempt to do this by default. If you really want good polygon
- * smoothing (and you don't have multisample support), do all this yourself.
- */
- // Nothing's set, so disable blending.
- enable_multisample_alpha_one(false);
- enable_multisample_alpha_mask(false);
- enable_blend(false);
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called the first time a particular light has been bound to a given id
- * within a frame, this should set up the associated hardware light with the
- * light's properties.
- */
- void CLP(GraphicsStateGuardian)::
- bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_bind_point_pcollector("Draw:Set
- // State:Light:Bind:Point"); PStatGPUTimer timer(this,
- // _draw_set_state_light_bind_point_pcollector);
- GLenum id = get_light_id(light_id);
- static const LColor black(0.0f, 0.0f, 0.0f, 1.0f);
- call_glLightfv(id, GL_AMBIENT, black);
- call_glLightfv(id, GL_DIFFUSE, get_light_color(light_obj));
- call_glLightfv(id, GL_SPECULAR, light_obj->get_specular_color());
- // Position needs to specify x, y, z, and w w == 1 implies non-infinite
- // position
- CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
- LPoint3 pos = light_obj->get_point() * transform->get_mat();
- LPoint4 fpos(pos[0], pos[1], pos[2], 1.0f);
- call_glLightfv(id, GL_POSITION, fpos);
- // GL_SPOT_DIRECTION is not significant when cutoff == 180
- // Exponent == 0 implies uniform light distribution
- glLightf(id, GL_SPOT_EXPONENT, 0.0f);
- // Cutoff == 180 means uniform point light source
- glLightf(id, GL_SPOT_CUTOFF, 180.0f);
- const LVecBase3 &att = light_obj->get_attenuation();
- glLightf(id, GL_CONSTANT_ATTENUATION, att[0]);
- glLightf(id, GL_LINEAR_ATTENUATION, att[1]);
- glLightf(id, GL_QUADRATIC_ATTENUATION, att[2]);
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called the first time a particular light has been bound to a given id
- * within a frame, this should set up the associated hardware light with the
- * light's properties.
- */
- void CLP(GraphicsStateGuardian)::
- bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_bind_directional_pcollector("Draw:Set
- // State:Light:Bind:Directional"); PStatGPUTimer timer(this,
- // _draw_set_state_light_bind_directional_pcollector);
- std::pair<DirectionalLights::iterator, bool> lookup = _dlights.insert(DirectionalLights::value_type(light, DirectionalLightFrameData()));
- DirectionalLightFrameData &fdata = (*lookup.first).second;
- if (lookup.second) {
- // The light was not computed yet this frame. Compute it now.
- CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
- LVector3 dir = light_obj->get_direction() * transform->get_mat();
- fdata._neg_dir.set(-dir[0], -dir[1], -dir[2], 0);
- }
- GLenum id = get_light_id( light_id );
- static const LColor black(0.0f, 0.0f, 0.0f, 1.0f);
- call_glLightfv(id, GL_AMBIENT, black);
- call_glLightfv(id, GL_DIFFUSE, get_light_color(light_obj));
- call_glLightfv(id, GL_SPECULAR, light_obj->get_specular_color());
- // Position needs to specify x, y, z, and w. w == 0 implies light is at
- // infinity
- call_glLightfv(id, GL_POSITION, fdata._neg_dir);
- // GL_SPOT_DIRECTION is not significant when cutoff == 180 In this case,
- // position x, y, z specifies direction
- // Exponent == 0 implies uniform light distribution
- glLightf(id, GL_SPOT_EXPONENT, 0.0f);
- // Cutoff == 180 means uniform point light source
- glLightf(id, GL_SPOT_CUTOFF, 180.0f);
- // Default attenuation values (only spotlight and point light can modify
- // these)
- glLightf(id, GL_CONSTANT_ATTENUATION, 1.0f);
- glLightf(id, GL_LINEAR_ATTENUATION, 0.0f);
- glLightf(id, GL_QUADRATIC_ATTENUATION, 0.0f);
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called the first time a particular light has been bound to a given id
- * within a frame, this should set up the associated hardware light with the
- * light's properties.
- */
- void CLP(GraphicsStateGuardian)::
- bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_bind_spotlight_pcollector("Draw:Set
- // State:Light:Bind:Spotlight"); PStatGPUTimer timer(this,
- // _draw_set_state_light_bind_spotlight_pcollector);
- Lens *lens = light_obj->get_lens();
- nassertv(lens != nullptr);
- GLenum id = get_light_id(light_id);
- static const LColor black(0.0f, 0.0f, 0.0f, 1.0f);
- call_glLightfv(id, GL_AMBIENT, black);
- call_glLightfv(id, GL_DIFFUSE, get_light_color(light_obj));
- call_glLightfv(id, GL_SPECULAR, light_obj->get_specular_color());
- // Position needs to specify x, y, z, and w w == 1 implies non-infinite
- // position
- CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
- const LMatrix4 &light_mat = transform->get_mat();
- LPoint3 pos = lens->get_nodal_point() * light_mat;
- LVector3 dir = lens->get_view_vector() * light_mat;
- LPoint4 fpos(pos[0], pos[1], pos[2], 1.0f);
- call_glLightfv(id, GL_POSITION, fpos);
- call_glLightfv(id, GL_SPOT_DIRECTION, dir);
- glLightf(id, GL_SPOT_EXPONENT, max(min(light_obj->get_exponent(), (PN_stdfloat)128), (PN_stdfloat)0));
- glLightf(id, GL_SPOT_CUTOFF, lens->get_hfov() * 0.5f);
- const LVecBase3 &att = light_obj->get_attenuation();
- glLightf(id, GL_CONSTANT_ATTENUATION, att[0]);
- glLightf(id, GL_LINEAR_ATTENUATION, att[1]);
- glLightf(id, GL_QUADRATIC_ATTENUATION, att[2]);
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Creates a depth buffer for shadow mapping. A derived GSG can override this
- * if it knows that a particular buffer type works best for shadow rendering.
- */
- GraphicsOutput *CLP(GraphicsStateGuardian)::
- make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) {
- // We override this to circumvent the fact that GraphicsEngine::make_output
- // can only be called from the app thread.
- if (!_supports_framebuffer_object) {
- return GraphicsStateGuardian::make_shadow_buffer(light, tex, host);
- }
- bool is_point = light->is_of_type(PointLight::get_class_type());
- // Determine the properties for creating the depth buffer.
- FrameBufferProperties fbp;
- fbp.set_depth_bits(shadow_depth_bits);
- WindowProperties props = WindowProperties::size(light->get_shadow_buffer_size());
- int flags = GraphicsPipe::BF_refuse_window;
- if (is_point) {
- flags |= GraphicsPipe::BF_size_square;
- }
- if (host != nullptr) {
- host = host->get_host();
- }
- CLP(GraphicsBuffer) *sbuffer = new CLP(GraphicsBuffer)(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host);
- sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
- get_engine()->add_window(sbuffer, light->get_shadow_buffer_sort());
- return sbuffer;
- }
- #ifdef SUPPORT_IMMEDIATE_MODE
- /**
- * Uses the ImmediateModeSender to draw a series of primitives of the
- * indicated type.
- */
- void CLP(GraphicsStateGuardian)::
- draw_immediate_simple_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode) {
- int num_vertices = reader->get_num_vertices();
- _vertices_immediate_pcollector.add_level(num_vertices);
- glBegin(mode);
- if (reader->is_indexed()) {
- for (int v = 0; v < num_vertices; ++v) {
- _sender.set_vertex(reader->get_vertex(v));
- _sender.issue_vertex();
- }
- } else {
- _sender.set_vertex(reader->get_first_vertex());
- for (int v = 0; v < num_vertices; ++v) {
- _sender.issue_vertex();
- }
- }
- glEnd();
- }
- #endif // SUPPORT_IMMEDIATE_MODE
- #ifdef SUPPORT_IMMEDIATE_MODE
- /**
- * Uses the ImmediateModeSender to draw a series of primitives of the
- * indicated type. This form is for primitive types like tristrips which must
- * involve several begin/end groups.
- */
- void CLP(GraphicsStateGuardian)::
- draw_immediate_composite_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode) {
- int num_vertices = reader->get_num_vertices();
- _vertices_immediate_pcollector.add_level(num_vertices);
- CPTA_int ends = reader->get_ends();
- int num_unused_vertices_per_primitive = reader->get_object()->get_num_unused_vertices_per_primitive();
- if (reader->is_indexed()) {
- int begin = 0;
- CPTA_int::const_iterator ei;
- for (ei = ends.begin(); ei != ends.end(); ++ei) {
- int end = (*ei);
- glBegin(mode);
- for (int v = begin; v < end; ++v) {
- _sender.set_vertex(reader->get_vertex(v));
- _sender.issue_vertex();
- }
- glEnd();
- begin = end + num_unused_vertices_per_primitive;
- }
- } else {
- _sender.set_vertex(reader->get_first_vertex());
- int begin = 0;
- CPTA_int::const_iterator ei;
- for (ei = ends.begin(); ei != ends.end(); ++ei) {
- int end = (*ei);
- glBegin(mode);
- for (int v = begin; v < end; ++v) {
- _sender.issue_vertex();
- }
- glEnd();
- begin = end + num_unused_vertices_per_primitive;
- }
- }
- }
- #endif // SUPPORT_IMMEDIATE_MODE
- /**
- * Calls glFlush().
- */
- void CLP(GraphicsStateGuardian)::
- gl_flush() const {
- PStatTimer timer(_flush_pcollector);
- glFlush();
- }
- /**
- * Returns the result of glGetError().
- */
- GLenum CLP(GraphicsStateGuardian)::
- gl_get_error() const {
- if (_check_errors) {
- PStatTimer timer(_check_error_pcollector);
- return glGetError();
- } else {
- return GL_NO_ERROR;
- }
- }
- /**
- * The internal implementation of report_errors(). Don't call this function;
- * use report_errors() instead. The return value is true if everything is ok,
- * or false if we should shut down.
- */
- bool CLP(GraphicsStateGuardian)::
- report_errors_loop(int line, const char *source_file, GLenum error_code,
- int &error_count) {
- while ((gl_max_errors < 0 || error_count < gl_max_errors) &&
- (error_code != GL_NO_ERROR)) {
- GLCAT.error()
- << "at " << line << " of " << source_file << " : "
- << get_error_string(error_code) << "\n";
- error_code = glGetError();
- error_count++;
- }
- return (error_code == GL_NO_ERROR);
- }
- /**
- * Returns an error string for an OpenGL error code.
- */
- string CLP(GraphicsStateGuardian)::
- get_error_string(GLenum error_code) {
- // We used to use gluErrorString here, but I (rdb) took it out because that
- // was really the only function we used from GLU. The idea with the error
- // table was taken from SGI's sample implementation.
- static const char *error_strings[] = {
- "invalid enumerant",
- "invalid value",
- "invalid operation",
- "stack overflow",
- "stack underflow",
- "out of memory",
- "invalid framebuffer operation",
- "context lost",
- };
- if (error_code == GL_NO_ERROR) {
- return "no error";
- #ifndef OPENGLES
- } else if (error_code == GL_TABLE_TOO_LARGE) {
- return "table too large";
- #endif
- } else if (error_code >= 0x0500 && error_code <= 0x0507) {
- return error_strings[error_code - 0x0500];
- }
- // Other error, somehow? Just display the error code then.
- std::ostringstream strm;
- strm << "GL error " << (int)error_code;
- return strm.str();
- }
- /**
- * Outputs the result of glGetString() on the indicated tag. The output
- * string is returned.
- */
- string CLP(GraphicsStateGuardian)::
- show_gl_string(const string &name, GLenum id) {
- string result;
- const GLubyte *text = glGetString(id);
- if (text == nullptr) {
- GLCAT.warning()
- << "Unable to query " << name << "\n";
- } else {
- result = (const char *)text;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << name << " = " << result << "\n";
- }
- }
- return result;
- }
- /**
- * Queries the runtime version of OpenGL in use.
- */
- void CLP(GraphicsStateGuardian)::
- query_gl_version() {
- _gl_vendor = show_gl_string("GL_VENDOR", GL_VENDOR);
- _gl_renderer = show_gl_string("GL_RENDERER", GL_RENDERER);
- _gl_version = show_gl_string("GL_VERSION", GL_VERSION);
- _gl_version_major = 0;
- _gl_version_minor = 0;
- // This is the most preposterous driver bug: NVIDIA drivers will claim
- // that the version is 1.2 as long as the process is named pview.exe!
- #ifndef OPENGLES
- if (_gl_version.substr(0, 10) == "1.2 NVIDIA") {
- Filename exec_name = ExecutionEnvironment::get_binary_name();
- if (cmp_nocase(exec_name.get_basename(), "pview.exe") == 0) {
- glGetIntegerv(GL_MAJOR_VERSION, &_gl_version_major);
- glGetIntegerv(GL_MINOR_VERSION, &_gl_version_minor);
- if (glGetError() == GL_INVALID_ENUM) {
- _gl_version_major = 1;
- _gl_version_minor = 2;
- GLCAT.warning()
- << "Driver possibly misreported GL_VERSION! Unable to detect "
- "correct OpenGL version.\n";
- } else if (_gl_version_major != 1 || _gl_version_minor != 2) {
- GLCAT.debug()
- << "Driver misreported GL_VERSION! Correct version detected as "
- << _gl_version_major << "." << _gl_version_minor << "\n";
- }
- return;
- }
- }
- // If we asked for a GL 3 context, let's first try and see if we can use the
- // OpenGL 3 way to query version.
- if (gl_version.get_num_words() > 0 && gl_version[0] >= 3) {
- glGetIntegerv(GL_MAJOR_VERSION, &_gl_version_major);
- glGetIntegerv(GL_MINOR_VERSION, &_gl_version_minor);
- if (_gl_version_major >= 1) {
- // Fair enough, seems to check out.
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Detected OpenGL version: "
- << _gl_version_major << "." << _gl_version_minor << "\n";
- }
- return;
- }
- }
- #endif // !OPENGLES
- // Otherwise, parse the GL_VERSION string.
- if (_gl_version.empty()) {
- GLCAT.error() << "Unable to detect OpenGL version\n";
- } else {
- string input = _gl_version;
- // Skip any initial words that don't begin with a digit.
- while (!input.empty() && !isdigit(input[0])) {
- size_t space = input.find(' ');
- if (space == string::npos) {
- break;
- }
- size_t next = space + 1;
- while (next < input.length() && isspace(input[next])) {
- ++next;
- }
- input = input.substr(next);
- }
- // Truncate after the first space.
- size_t space = input.find(' ');
- if (space != string::npos) {
- input = input.substr(0, space);
- }
- vector_string components;
- tokenize(input, components, ".");
- if (components.size() >= 1) {
- string_to_int(components[0], _gl_version_major);
- }
- if (components.size() >= 2) {
- string_to_int(components[1], _gl_version_minor);
- }
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "GL_VERSION decoded to: "
- << _gl_version_major << "." << _gl_version_minor
- << "\n";
- }
- }
- }
- /**
- * Queries the supported GLSL version.
- */
- void CLP(GraphicsStateGuardian)::
- query_glsl_version() {
- _gl_shadlang_ver_major = 0;
- _gl_shadlang_ver_minor = 0;
- #ifndef OPENGLES_1
- #ifndef OPENGLES
- // OpenGL 2.0 introduces GLSL in the core. In 1.x, it is an extension.
- if (_gl_version_major >= 2 || has_extension("GL_ARB_shading_language_100")) {
- string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
- _gl_shadlang_ver_major = 1;
- _gl_shadlang_ver_minor = (_gl_version_major >= 2) ? 1 : 0;
- if (ver.empty() ||
- sscanf(ver.c_str(), "%d.%d", &_gl_shadlang_ver_major,
- &_gl_shadlang_ver_minor) != 2) {
- GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
- }
- }
- #else
- // OpenGL ES 2.0 and above has shader support built-in.
- string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
- _gl_shadlang_ver_major = 1;
- _gl_shadlang_ver_minor = 0;
- if (ver.empty() ||
- sscanf(ver.c_str(), "OpenGL ES GLSL ES %d.%d", &_gl_shadlang_ver_major,
- &_gl_shadlang_ver_minor) != 2) {
- GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
- }
- #endif
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Detected GLSL "
- #ifdef OPENGLES
- "ES "
- #endif
- "version: "
- << _gl_shadlang_ver_major << "." << _gl_shadlang_ver_minor << "\n";
- }
- #endif // !OPENGLES_1
- }
- /**
- * Separates the string returned by GL_EXTENSIONS (or glx or wgl extensions)
- * into its individual tokens and saves them in the _extensions member.
- */
- void CLP(GraphicsStateGuardian)::
- save_extensions(const char *extensions) {
- if (extensions != nullptr) {
- vector_string tokens;
- extract_words(extensions, tokens);
- vector_string::iterator ti;
- for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
- _extensions.insert(*ti);
- }
- }
- }
- /**
- * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
- * further extensions strings may be appropriate to that interface, in
- * addition to the GL extension strings return by glGetString().
- */
- void CLP(GraphicsStateGuardian)::
- get_extra_extensions() {
- }
- /**
- * Outputs the list of GL extensions to notify, if debug mode is enabled.
- */
- void CLP(GraphicsStateGuardian)::
- report_extensions() const {
- if (GLCAT.is_debug()) {
- std::ostream &out = GLCAT.debug();
- out << "GL Extensions:\n";
- pset<string>::const_iterator ei;
- for (ei = _extensions.begin(); ei != _extensions.end(); ++ei) {
- size_t len = (*ei).size();
- out << " " << (*ei);
- // Display a second column.
- if (len <= 38) {
- if (++ei != _extensions.end()) {
- for (int i = len; i < 38; ++i) {
- out.put(' ');
- }
- out << ' ' << (*ei);
- } else {
- out.put('\n');
- break;
- }
- }
- out.put('\n');
- }
- }
- }
- /**
- * Returns the pointer to the GL extension function with the indicated name,
- * or NULL if the function is not available.
- */
- void *CLP(GraphicsStateGuardian)::
- get_extension_func(const char *name) {
- // First, look in the static-compiled namespace. If we were compiled to
- // expect at least a certain minimum runtime version of OpenGL, then we can
- // expect those extension functions to be available at compile time.
- // Somewhat more reliable than poking around in the runtime pointers.
- static struct {
- const char *name;
- void *fptr;
- } compiled_function_table[] = {
- #ifdef EXPECT_GL_VERSION_1_2
- { "glBlendColor", (void *)&glBlendColor },
- { "glBlendEquation", (void *)&glBlendEquation },
- { "glDrawRangeElements", (void *)&glDrawRangeElements },
- { "glTexImage3D", (void *)&glTexImage3D },
- { "glTexSubImage3D", (void *)&glTexSubImage3D },
- { "glCopyTexSubImage3D", (void *)&glCopyTexSubImage3D },
- #endif
- #ifdef EXPECT_GL_VERSION_1_3
- { "glActiveTexture", (void *)&glActiveTexture },
- { "glClientActiveTexture", (void *)&glClientActiveTexture },
- { "glCompressedTexImage1D", (void *)&glCompressedTexImage1D },
- { "glCompressedTexImage2D", (void *)&glCompressedTexImage2D },
- { "glCompressedTexImage3D", (void *)&glCompressedTexImage3D },
- { "glCompressedTexSubImage1D", (void *)&glCompressedTexSubImage1D },
- { "glCompressedTexSubImage2D", (void *)&glCompressedTexSubImage2D },
- { "glCompressedTexSubImage3D", (void *)&glCompressedTexSubImage3D },
- { "glGetCompressedTexImage", (void *)&glGetCompressedTexImage },
- { "glMultiTexCoord1f", (void *)&glMultiTexCoord1f },
- { "glMultiTexCoord2", (void *)&glMultiTexCoord2 },
- { "glMultiTexCoord3", (void *)&glMultiTexCoord3 },
- { "glMultiTexCoord4", (void *)&glMultiTexCoord4 },
- #endif
- #ifdef EXPECT_GL_VERSION_1_4
- { "glPointParameterfv", (void *)&glPointParameterfv },
- { "glSecondaryColorPointer", (void *)&glSecondaryColorPointer },
- #endif
- #ifdef EXPECT_GL_VERSION_1_5
- { "glBeginQuery", (void *)&glBeginQuery },
- { "glBindBuffer", (void *)&glBindBuffer },
- { "glBufferData", (void *)&glBufferData },
- { "glBufferSubData", (void *)&glBufferSubData },
- { "glDeleteBuffers", (void *)&glDeleteBuffers },
- { "glDeleteQueries", (void *)&glDeleteQueries },
- { "glEndQuery", (void *)&glEndQuery },
- { "glGenBuffers", (void *)&glGenBuffers },
- { "glGenQueries", (void *)&glGenQueries },
- { "glGetQueryObjectuiv", (void *)&glGetQueryObjectuiv },
- { "glGetQueryiv", (void *)&glGetQueryiv },
- #endif
- #ifdef OPENGLES
- { "glActiveTexture", (void *)&glActiveTexture },
- #ifndef OPENGLES_2
- { "glClientActiveTexture", (void *)&glClientActiveTexture },
- #endif
- { "glBindBuffer", (void *)&glBindBuffer },
- { "glBufferData", (void *)&glBufferData },
- { "glBufferSubData", (void *)&glBufferSubData },
- { "glDeleteBuffers", (void *)&glDeleteBuffers },
- { "glGenBuffers", (void *)&glGenBuffers },
- #endif
- { nullptr, nullptr }
- };
- int i = 0;
- while (compiled_function_table[i].name != nullptr) {
- if (strcmp(compiled_function_table[i].name, name) == 0) {
- return compiled_function_table[i].fptr;
- }
- ++i;
- }
- // If the extension function wasn't compiled in, then go get it from the
- // runtime. There's a different interface for each API.
- return do_get_extension_func(name);
- }
- /**
- * This is the virtual implementation of get_extension_func(). Each API-
- * specific GL implementation will map this method to the appropriate API call
- * to retrieve the extension function pointer. Returns NULL if the function
- * is not available.
- */
- void *CLP(GraphicsStateGuardian)::
- do_get_extension_func(const char *) {
- return nullptr;
- }
- /**
- * Sets up the glDrawBuffer to render into the buffer indicated by the
- * RenderBuffer object. This only sets up the color and aux bits; it does not
- * affect the depth, stencil, accum layers.
- */
- void CLP(GraphicsStateGuardian)::
- set_draw_buffer(int rbtype) {
- #ifndef OPENGLES_1 // Draw buffers not supported by OpenGL ES 1.
- if (_current_fbo) {
- GLuint buffers[16];
- int nbuffers = 0;
- int index = 0;
- if (_current_properties->get_color_bits() > 0) {
- if (rbtype & RenderBuffer::T_left) {
- buffers[nbuffers++] = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- if (_current_properties->is_stereo()) {
- if (rbtype & RenderBuffer::T_right) {
- buffers[nbuffers++] = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- }
- for (int i = 0; i < _current_properties->get_aux_rgba(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_rgba_0 << i)) {
- buffers[nbuffers++] = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_hrgba(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_hrgba_0 << i)) {
- buffers[nbuffers++] = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_float(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_float_0 << i)) {
- buffers[nbuffers++] = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- if (_glDrawBuffers != nullptr) {
- _glDrawBuffers(nbuffers, buffers);
- } else {
- nassertv(nbuffers == 1 && buffers[0] == GL_COLOR_ATTACHMENT0_EXT);
- }
- } else {
- #ifndef OPENGLES
- switch (rbtype & RenderBuffer::T_color) {
- case RenderBuffer::T_front:
- glDrawBuffer(GL_FRONT);
- break;
- case RenderBuffer::T_back:
- glDrawBuffer(GL_BACK);
- break;
- case RenderBuffer::T_right:
- glDrawBuffer(GL_RIGHT);
- break;
- case RenderBuffer::T_left:
- glDrawBuffer(GL_LEFT);
- break;
- case RenderBuffer::T_front_right:
- nassertv(_current_properties->is_stereo());
- glDrawBuffer(GL_FRONT_RIGHT);
- break;
- case RenderBuffer::T_front_left:
- nassertv(_current_properties->is_stereo());
- glDrawBuffer(GL_FRONT_LEFT);
- break;
- case RenderBuffer::T_back_right:
- nassertv(_current_properties->is_stereo());
- glDrawBuffer(GL_BACK_RIGHT);
- break;
- case RenderBuffer::T_back_left:
- nassertv(_current_properties->is_stereo());
- glDrawBuffer(GL_BACK_LEFT);
- break;
- default:
- break;
- }
- #endif // OPENGLES
- }
- #endif // OPENGLES_1
- // Also ensure that any global color channels are masked out.
- set_color_write_mask(_color_write_mask);
- report_my_gl_errors();
- }
- /**
- * Sets up the glReadBuffer to render into the buffer indicated by the
- * RenderBuffer object. This only sets up the color bits; it does not affect
- * the depth, stencil, accum layers.
- */
- void CLP(GraphicsStateGuardian)::
- set_read_buffer(int rbtype) {
- #ifndef OPENGLES_1 // Draw buffers not supported by OpenGL ES 1.
- if (rbtype & (RenderBuffer::T_depth | RenderBuffer::T_stencil)) {
- // Special case: don't have to call ReadBuffer for these.
- return;
- }
- if (_current_fbo) {
- GLuint buffer = GL_COLOR_ATTACHMENT0_EXT;
- int index = 1;
- if (_current_properties->is_stereo()) {
- if (rbtype & RenderBuffer::T_right) {
- buffer = GL_COLOR_ATTACHMENT1_EXT;
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_rgba(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_rgba_0 << i)) {
- buffer = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_hrgba(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_hrgba_0 << i)) {
- buffer = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- for (int i = 0; i < _current_properties->get_aux_float(); ++i) {
- if (rbtype & (RenderBuffer::T_aux_float_0 << i)) {
- buffer = GL_COLOR_ATTACHMENT0_EXT + index;
- }
- ++index;
- }
- #ifdef OPENGLES
- _glReadBuffer(buffer);
- #else
- glReadBuffer(buffer);
- #endif
- } else {
- #ifndef OPENGLES
- switch (rbtype & RenderBuffer::T_color) {
- case RenderBuffer::T_front:
- glReadBuffer(GL_FRONT);
- break;
- case RenderBuffer::T_back:
- glReadBuffer(GL_BACK);
- break;
- case RenderBuffer::T_right:
- glReadBuffer(GL_RIGHT);
- break;
- case RenderBuffer::T_left:
- glReadBuffer(GL_LEFT);
- break;
- case RenderBuffer::T_front_right:
- glReadBuffer(GL_FRONT_RIGHT);
- break;
- case RenderBuffer::T_front_left:
- glReadBuffer(GL_FRONT_LEFT);
- break;
- case RenderBuffer::T_back_right:
- glReadBuffer(GL_BACK_RIGHT);
- break;
- case RenderBuffer::T_back_left:
- glReadBuffer(GL_BACK_LEFT);
- break;
- default:
- break;
- }
- #endif // OPENGLES
- }
- report_my_gl_errors();
- #endif // OPENGLES_1
- }
- /**
- * Maps from the Geom's internal numeric type symbols to GL's.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_numeric_type(Geom::NumericType numeric_type) {
- switch (numeric_type) {
- case Geom::NT_uint16:
- return GL_UNSIGNED_SHORT;
- case Geom::NT_uint32:
- #ifndef OPENGLES_1
- return GL_UNSIGNED_INT;
- #else
- break;
- #endif
- case Geom::NT_uint8:
- case Geom::NT_packed_dcba:
- case Geom::NT_packed_dabc:
- return GL_UNSIGNED_BYTE;
- case Geom::NT_float32:
- return GL_FLOAT;
- case Geom::NT_float64:
- #ifndef OPENGLES
- return GL_DOUBLE;
- #else
- break;
- #endif
- case Geom::NT_stdfloat:
- // Shouldn't happen, display error.
- break;
- case Geom::NT_int8:
- return GL_BYTE;
- case Geom::NT_int16:
- return GL_SHORT;
- case Geom::NT_int32:
- #ifndef OPENGLES_1
- return GL_INT;
- #else
- break;
- #endif
- case Geom::NT_packed_ufloat:
- #ifndef OPENGLES_1
- return GL_UNSIGNED_INT_10F_11F_11F_REV;
- #else
- break;
- #endif
- }
- GLCAT.error()
- << "Invalid NumericType value (" << (int)numeric_type << ")\n";
- return GL_UNSIGNED_BYTE;
- }
- /**
- * Maps from the Texture's texture type symbols to GL's.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_texture_target(Texture::TextureType texture_type) const {
- switch (texture_type) {
- case Texture::TT_1d_texture:
- // There are no 1D textures in OpenGL ES. Fall back to 2D textures.
- #ifndef OPENGLES
- return GL_TEXTURE_1D;
- #endif
- case Texture::TT_1d_texture_array:
- // There are no 1D array textures in OpenGL ES. Fall back to 2D textures.
- #ifndef OPENGLES
- return GL_TEXTURE_1D_ARRAY;
- #endif
- case Texture::TT_2d_texture:
- return GL_TEXTURE_2D;
- case Texture::TT_3d_texture:
- #ifndef OPENGLES_1
- if (_supports_3d_texture) {
- return GL_TEXTURE_3D;
- }
- #endif
- return GL_NONE;
- case Texture::TT_2d_texture_array:
- #ifndef OPENGLES_1
- if (_supports_2d_texture_array) {
- return GL_TEXTURE_2D_ARRAY;
- }
- #endif
- return GL_NONE;
- case Texture::TT_cube_map:
- if (_supports_cube_map) {
- return GL_TEXTURE_CUBE_MAP;
- } else {
- return GL_NONE;
- }
- case Texture::TT_cube_map_array:
- #ifndef OPENGLES
- if (_supports_cube_map_array) {
- return GL_TEXTURE_CUBE_MAP_ARRAY;
- }
- #endif
- return GL_NONE;
- case Texture::TT_buffer_texture:
- #ifndef OPENGLES
- if (_supports_buffer_texture) {
- return GL_TEXTURE_BUFFER;
- }
- #endif
- return GL_NONE;
- }
- GLCAT.error() << "Invalid Texture::TextureType value!\n";
- return GL_TEXTURE_2D;
- }
- /**
- * Maps from the Texture's internal wrap mode symbols to GL's.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_texture_wrap_mode(SamplerState::WrapMode wm) const {
- if (gl_ignore_clamp) {
- return GL_REPEAT;
- }
- switch (wm) {
- case SamplerState::WM_clamp:
- return _edge_clamp;
- case SamplerState::WM_repeat:
- return GL_REPEAT;
- case SamplerState::WM_mirror:
- return _mirror_repeat;
- case SamplerState::WM_mirror_once:
- return _mirror_border_clamp;
- case SamplerState::WM_border_color:
- return _border_clamp;
- case SamplerState::WM_invalid:
- break;
- }
- GLCAT.error() << "Invalid SamplerState::WrapMode value!\n";
- return _edge_clamp;
- }
- /**
- * Maps from the GL's internal wrap mode symbols to Panda's.
- */
- SamplerState::WrapMode CLP(GraphicsStateGuardian)::
- get_panda_wrap_mode(GLenum wm) {
- switch (wm) {
- #ifndef OPENGLES
- case GL_CLAMP:
- #endif
- case GL_CLAMP_TO_EDGE:
- return SamplerState::WM_clamp;
- #ifndef OPENGLES
- case GL_CLAMP_TO_BORDER:
- return SamplerState::WM_border_color;
- #endif
- case GL_REPEAT:
- return SamplerState::WM_repeat;
- case GL_MIRRORED_REPEAT:
- return SamplerState::WM_mirror;
- #ifndef OPENGLES
- case GL_MIRROR_CLAMP_EXT:
- case GL_MIRROR_CLAMP_TO_EDGE_EXT:
- return SamplerState::WM_mirror;
- case GL_MIRROR_CLAMP_TO_BORDER_EXT:
- return SamplerState::WM_mirror_once;
- #endif
- }
- GLCAT.error() << "Unexpected GL wrap mode " << (int)wm << "\n";
- return SamplerState::WM_clamp;
- }
- /**
- * Maps from the Texture's internal filter type symbols to GL's.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_texture_filter_type(SamplerState::FilterType ft, bool ignore_mipmaps) {
- if (gl_ignore_filters) {
- return GL_NEAREST;
- } else if (ignore_mipmaps) {
- switch (ft) {
- case SamplerState::FT_nearest_mipmap_nearest:
- case SamplerState::FT_nearest:
- return GL_NEAREST;
- case SamplerState::FT_linear:
- case SamplerState::FT_linear_mipmap_nearest:
- case SamplerState::FT_nearest_mipmap_linear:
- case SamplerState::FT_linear_mipmap_linear:
- return GL_LINEAR;
- case SamplerState::FT_shadow:
- return GL_LINEAR;
- case SamplerState::FT_default:
- case SamplerState::FT_invalid:
- break;
- }
- } else {
- switch (ft) {
- case SamplerState::FT_nearest:
- return GL_NEAREST;
- case SamplerState::FT_linear:
- return GL_LINEAR;
- case SamplerState::FT_nearest_mipmap_nearest:
- return GL_NEAREST_MIPMAP_NEAREST;
- case SamplerState::FT_linear_mipmap_nearest:
- return GL_LINEAR_MIPMAP_NEAREST;
- case SamplerState::FT_nearest_mipmap_linear:
- return GL_NEAREST_MIPMAP_LINEAR;
- case SamplerState::FT_linear_mipmap_linear:
- return GL_LINEAR_MIPMAP_LINEAR;
- case SamplerState::FT_shadow:
- return GL_LINEAR;
- case SamplerState::FT_default:
- case SamplerState::FT_invalid:
- break;
- }
- }
- GLCAT.error() << "Invalid SamplerState::FilterType value!\n";
- return GL_NEAREST;
- }
- /**
- * Maps from the GL's internal filter type symbols to Panda's.
- */
- SamplerState::FilterType CLP(GraphicsStateGuardian)::
- get_panda_filter_type(GLenum ft) {
- switch (ft) {
- case GL_NEAREST:
- return SamplerState::FT_nearest;
- case GL_LINEAR:
- return SamplerState::FT_linear;
- case GL_NEAREST_MIPMAP_NEAREST:
- return SamplerState::FT_nearest_mipmap_nearest;
- case GL_LINEAR_MIPMAP_NEAREST:
- return SamplerState::FT_linear_mipmap_nearest;
- case GL_NEAREST_MIPMAP_LINEAR:
- return SamplerState::FT_nearest_mipmap_linear;
- case GL_LINEAR_MIPMAP_LINEAR:
- return SamplerState::FT_linear_mipmap_linear;
- }
- GLCAT.error() << "Unexpected GL filter type " << (int)ft << "\n";
- return SamplerState::FT_linear;
- }
- /**
- * Maps from the Texture's internal ComponentType symbols to GL's.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_component_type(Texture::ComponentType component_type) {
- switch (component_type) {
- case Texture::T_unsigned_byte:
- return GL_UNSIGNED_BYTE;
- case Texture::T_unsigned_short:
- return GL_UNSIGNED_SHORT;
- case Texture::T_float:
- return GL_FLOAT;
- case Texture::T_unsigned_int_24_8:
- if (_supports_depth_stencil) {
- return GL_UNSIGNED_INT_24_8_EXT;
- } else {
- return GL_UNSIGNED_BYTE;
- }
- case Texture::T_int:
- #ifndef OPENGLES_1
- return GL_INT;
- #endif
- case Texture::T_byte:
- return GL_BYTE;
- case Texture::T_short:
- return GL_SHORT;
- #ifndef OPENGLES_1
- case Texture::T_half_float:
- return GL_HALF_FLOAT;
- #endif
- #ifndef OPENGLES_1
- case Texture::T_unsigned_int:
- return GL_UNSIGNED_INT;
- #endif
- default:
- GLCAT.error() << "Invalid Texture::Type value!\n";
- return GL_UNSIGNED_BYTE;
- }
- }
- /**
- * Maps from the Texture's Format symbols to GL's.
- */
- GLint CLP(GraphicsStateGuardian)::
- get_external_image_format(Texture *tex) const {
- Texture::CompressionMode compression = tex->get_ram_image_compression();
- Texture::Format format = tex->get_format();
- if (compression != Texture::CM_off &&
- get_supports_compressed_texture_format(compression)) {
- switch (compression) {
- case Texture::CM_on:
- #ifndef OPENGLES
- switch (format) {
- case Texture::F_color_index:
- case Texture::F_depth_component:
- case Texture::F_depth_component16:
- case Texture::F_depth_component24:
- case Texture::F_depth_component32:
- case Texture::F_depth_stencil:
- case Texture::F_r11_g11_b10:
- case Texture::F_rgb9_e5:
- // This shouldn't be possible.
- nassertr(false, GL_RGB);
- break;
- case Texture::F_rgba:
- case Texture::F_rgbm:
- case Texture::F_rgba4:
- case Texture::F_rgba8:
- case Texture::F_rgba12:
- case Texture::F_rgba16:
- case Texture::F_rgba32:
- case Texture::F_rgba8i:
- case Texture::F_rgb10_a2:
- return GL_COMPRESSED_RGBA;
- case Texture::F_rgb:
- case Texture::F_rgb5:
- case Texture::F_rgba5:
- case Texture::F_rgb8:
- case Texture::F_rgb8i:
- case Texture::F_rgb12:
- case Texture::F_rgb332:
- case Texture::F_rgb16:
- case Texture::F_rgb32:
- return GL_COMPRESSED_RGB;
- case Texture::F_alpha:
- return GL_COMPRESSED_ALPHA;
- case Texture::F_red:
- case Texture::F_green:
- case Texture::F_blue:
- case Texture::F_r8i:
- case Texture::F_r16:
- case Texture::F_r16i:
- case Texture::F_r32:
- case Texture::F_r32i:
- return GL_COMPRESSED_RED;
- case Texture::F_rg:
- case Texture::F_rg8i:
- case Texture::F_rg16:
- case Texture::F_rg32:
- return GL_COMPRESSED_RG;
- case Texture::F_luminance:
- return GL_COMPRESSED_LUMINANCE;
- case Texture::F_luminance_alpha:
- case Texture::F_luminance_alphamask:
- return GL_COMPRESSED_LUMINANCE_ALPHA;
- case Texture::F_srgb:
- return GL_COMPRESSED_SRGB;
- case Texture::F_srgb_alpha:
- return GL_COMPRESSED_SRGB_ALPHA;
- case Texture::F_sluminance:
- return GL_COMPRESSED_SLUMINANCE;
- case Texture::F_sluminance_alpha:
- return GL_COMPRESSED_SLUMINANCE_ALPHA;
- }
- #endif
- break;
- case Texture::CM_dxt1:
- #ifndef OPENGLES
- if (format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
- } else if (format == Texture::F_srgb) {
- return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
- } else
- #endif
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- } else {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- case Texture::CM_dxt3:
- #ifndef OPENGLES
- if (format == Texture::F_srgb || format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
- }
- #endif
- #ifndef OPENGLES_1
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- #endif
- break;
- case Texture::CM_dxt5:
- #ifndef OPENGLES
- if (format == Texture::F_srgb || format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
- }
- #endif
- #ifndef OPENGLES_1
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- #endif
- break;
- case Texture::CM_fxt1:
- #ifndef OPENGLES
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- } else {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- #endif
- break;
- #ifdef OPENGLES
- case Texture::CM_pvr1_2bpp:
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
- } else {
- return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- }
- case Texture::CM_pvr1_4bpp:
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- } else {
- return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- }
- #else
- case Texture::CM_pvr1_2bpp:
- case Texture::CM_pvr1_4bpp:
- break;
- #endif // OPENGLES
- case Texture::CM_rgtc:
- #ifndef OPENGLES
- if (format == Texture::F_luminance) {
- return GL_COMPRESSED_LUMINANCE_LATC1_EXT;
- } else if (format == Texture::F_luminance_alpha) {
- return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT;
- } else if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_RED_RGTC1;
- } else {
- return GL_COMPRESSED_RG_RGTC2;
- }
- #endif
- break;
- case Texture::CM_etc1:
- #ifdef OPENGLES
- return GL_ETC1_RGB8_OES;
- #endif
- // Fall through - ETC2 is backward compatible
- case Texture::CM_etc2:
- if (format == Texture::F_rgbm) {
- return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
- } else if (format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
- } else if (format == Texture::F_srgb) {
- return GL_COMPRESSED_SRGB8_ETC2;
- } else if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA8_ETC2_EAC;
- } else {
- return GL_COMPRESSED_RGB8_ETC2;
- }
- break;
- case Texture::CM_eac:
- if (Texture::is_unsigned(tex->get_component_type())) {
- if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_R11_EAC;
- } else {
- return GL_COMPRESSED_RG11_EAC;
- }
- } else {
- if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_SIGNED_R11_EAC;
- } else {
- return GL_COMPRESSED_SIGNED_RG11_EAC;
- }
- }
- break;
- case Texture::CM_default:
- case Texture::CM_off:
- case Texture::CM_dxt2:
- case Texture::CM_dxt4:
- // This shouldn't happen.
- nassertr(false, GL_RGB);
- break;
- }
- }
- switch (format) {
- #ifndef OPENGLES
- case Texture::F_color_index:
- return GL_COLOR_INDEX;
- #endif
- case Texture::F_depth_component:
- case Texture::F_depth_component16:
- case Texture::F_depth_component24:
- case Texture::F_depth_component32:
- return GL_DEPTH_COMPONENT;
- case Texture::F_depth_stencil:
- return _supports_depth_stencil ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
- #ifndef OPENGLES
- case Texture::F_red:
- case Texture::F_r16:
- case Texture::F_r32:
- return GL_RED;
- case Texture::F_green:
- return GL_GREEN;
- case Texture::F_blue:
- return GL_BLUE;
- #endif
- case Texture::F_alpha:
- #ifdef OPENGLES
- return GL_ALPHA;
- #else
- return _supports_luminance_texture ? GL_ALPHA : GL_RED;
- #endif
- #ifndef OPENGLES_1
- case Texture::F_rg:
- case Texture::F_rg16:
- case Texture::F_rg32:
- return GL_RG;
- #endif
- case Texture::F_rgb:
- case Texture::F_rgb5:
- case Texture::F_rgb8:
- case Texture::F_rgb12:
- case Texture::F_rgb332:
- case Texture::F_rgb16:
- case Texture::F_rgb32:
- case Texture::F_srgb:
- case Texture::F_r11_g11_b10:
- case Texture::F_rgb9_e5:
- #ifdef OPENGLES
- // OpenGL ES never supports BGR, even if _supports_bgr is true.
- return GL_RGB;
- #else
- return _supports_bgr ? GL_BGR : GL_RGB;
- #endif
- case Texture::F_rgba:
- case Texture::F_rgbm:
- case Texture::F_rgba4:
- case Texture::F_rgba5:
- case Texture::F_rgba8:
- case Texture::F_rgba12:
- case Texture::F_rgba16:
- case Texture::F_rgba32:
- case Texture::F_srgb_alpha:
- case Texture::F_rgb10_a2:
- return _supports_bgr ? GL_BGRA : GL_RGBA;
- case Texture::F_luminance:
- #ifdef OPENGLES
- return GL_LUMINANCE;
- #else
- return _supports_luminance_texture ? GL_LUMINANCE : GL_RED;
- #endif
- case Texture::F_luminance_alphamask:
- case Texture::F_luminance_alpha:
- #ifdef OPENGLES
- return GL_LUMINANCE_ALPHA;
- #else
- return _supports_luminance_texture ? GL_LUMINANCE_ALPHA : GL_RG;
- #endif
- case Texture::F_sluminance:
- #ifdef OPENGLES
- return GL_LUMINANCE;
- #else
- return _supports_luminance_texture ? GL_LUMINANCE : GL_RGB;
- #endif
- case Texture::F_sluminance_alpha:
- #ifdef OPENGLES
- return GL_LUMINANCE_ALPHA;
- #else
- return _supports_luminance_texture ? GL_LUMINANCE_ALPHA : GL_RGBA;
- #endif
- #ifndef OPENGLES_1
- case Texture::F_r8i:
- case Texture::F_r16i:
- case Texture::F_r32i:
- return GL_RED_INTEGER;
- case Texture::F_rg8i:
- return GL_RG_INTEGER;
- case Texture::F_rgb8i:
- return GL_RGB_INTEGER;
- case Texture::F_rgba8i:
- return GL_RGBA_INTEGER;
- #endif
- default:
- break;
- }
- GLCAT.error()
- << "Invalid Texture::Format value in get_external_image_format(): "
- << format << "\n";
- return GL_RGB;
- }
- /**
- * Maps from the Texture's Format symbols to a suitable internal format for GL
- * textures.
- */
- GLint CLP(GraphicsStateGuardian)::
- get_internal_image_format(Texture *tex, bool force_sized) const {
- Texture::CompressionMode compression = tex->get_compression();
- if (compression == Texture::CM_default) {
- compression = (compressed_textures) ? Texture::CM_on : Texture::CM_off;
- }
- Texture::Format format = tex->get_format();
- if (tex->get_render_to_texture()) {
- // no compression for render targets
- compression = Texture::CM_off;
- }
- bool is_3d = (tex->get_texture_type() == Texture::TT_3d_texture ||
- tex->get_texture_type() == Texture::TT_2d_texture_array);
- if (get_supports_compressed_texture_format(compression)) {
- switch (compression) {
- case Texture::CM_on:
- // The user asked for just generic compression. OpenGL supports
- // requesting just generic compression, but we'd like to go ahead and
- // request a specific type (if we can figure out an appropriate choice),
- // since that makes saving the result as a pre-compressed texture more
- // dependable--this way, we will know which compression algorithm was
- // applied.
- switch (format) {
- case Texture::F_color_index:
- case Texture::F_depth_component:
- case Texture::F_depth_component16:
- case Texture::F_depth_component24:
- case Texture::F_depth_component32:
- case Texture::F_depth_stencil:
- case Texture::F_r8i:
- case Texture::F_rg8i:
- case Texture::F_rgb8i:
- case Texture::F_rgba8i:
- case Texture::F_r16i:
- case Texture::F_r32i:
- case Texture::F_r11_g11_b10:
- case Texture::F_rgb9_e5:
- // Unsupported; fall through to below.
- break;
- case Texture::F_rgbm:
- case Texture::F_rgba5:
- case Texture::F_rgb10_a2:
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- }
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- }
- return GL_COMPRESSED_RGBA;
- #endif
- break;
- case Texture::F_rgba4:
- #ifndef OPENGLES_1
- if (get_supports_compressed_texture_format(Texture::CM_dxt3) && !is_3d) {
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- }
- #endif
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_RGBA8_ETC2_EAC;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- }
- return GL_COMPRESSED_RGBA;
- #endif
- break;
- case Texture::F_rgba:
- case Texture::F_rgba8:
- case Texture::F_rgba12:
- case Texture::F_rgba16:
- case Texture::F_rgba32:
- #ifndef OPENGLES_1
- if (get_supports_compressed_texture_format(Texture::CM_dxt5) && !is_3d) {
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- }
- #endif
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_RGBA8_ETC2_EAC;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- }
- return GL_COMPRESSED_RGBA;
- #endif
- break;
- case Texture::F_rgb:
- case Texture::F_rgb5:
- case Texture::F_rgb8:
- case Texture::F_rgb12:
- case Texture::F_rgb332:
- case Texture::F_rgb16:
- case Texture::F_rgb32:
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_RGB8_ETC2;
- }
- #ifdef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_etc1) && !is_3d) {
- return GL_ETC1_RGB8_OES;
- }
- #else
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- return GL_COMPRESSED_RGB;
- #endif
- break;
- case Texture::F_alpha:
- #ifndef OPENGLES_1
- if (get_supports_compressed_texture_format(Texture::CM_dxt5) && !is_3d) {
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- }
- #endif
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- }
- return GL_COMPRESSED_ALPHA;
- #endif
- break;
- case Texture::F_red:
- case Texture::F_green:
- case Texture::F_blue:
- case Texture::F_r16:
- case Texture::F_r32:
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_rgtc) && !is_3d) {
- return GL_COMPRESSED_RED_RGTC1;
- }
- #endif
- if (get_supports_compressed_texture_format(Texture::CM_eac) && !is_3d) {
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_COMPRESSED_R11_EAC;
- } else {
- return GL_COMPRESSED_SIGNED_R11_EAC;
- }
- }
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- return GL_COMPRESSED_RED;
- #endif
- break;
- case Texture::F_rg:
- case Texture::F_rg16:
- case Texture::F_rg32:
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_rgtc) && !is_3d) {
- return GL_COMPRESSED_RG_RGTC2;
- }
- #endif
- if (get_supports_compressed_texture_format(Texture::CM_eac) && !is_3d) {
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_COMPRESSED_RG11_EAC;
- } else {
- return GL_COMPRESSED_SIGNED_RG11_EAC;
- }
- }
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- return GL_COMPRESSED_RG;
- #endif
- break;
- case Texture::F_luminance:
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- return GL_COMPRESSED_LUMINANCE;
- #endif
- break;
- case Texture::F_luminance_alpha:
- case Texture::F_luminance_alphamask:
- #ifndef OPENGLES_1
- if (get_supports_compressed_texture_format(Texture::CM_dxt5) && !is_3d) {
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- }
- #endif
- #ifndef OPENGLES
- if (get_supports_compressed_texture_format(Texture::CM_fxt1) && !is_3d) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- }
- return GL_COMPRESSED_LUMINANCE_ALPHA;
- #endif
- break;
- #ifndef OPENGLES
- case Texture::F_srgb:
- if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) {
- return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
- }
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_SRGB8_ETC2;
- }
- return GL_COMPRESSED_SRGB;
- case Texture::F_srgb_alpha:
- if (get_supports_compressed_texture_format(Texture::CM_dxt5) && !is_3d) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
- }
- if (get_supports_compressed_texture_format(Texture::CM_etc2) && !is_3d) {
- return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
- }
- return GL_COMPRESSED_SRGB_ALPHA;
- case Texture::F_sluminance:
- return GL_COMPRESSED_SLUMINANCE;
- case Texture::F_sluminance_alpha:
- return GL_COMPRESSED_SLUMINANCE_ALPHA;
- #else
- // For now, we don't support compressed sRGB textures in OpenGL ES.
- case Texture::F_srgb:
- case Texture::F_srgb_alpha:
- case Texture::F_sluminance:
- case Texture::F_sluminance_alpha:
- break;
- #endif
- }
- break;
- case Texture::CM_dxt1:
- #ifndef OPENGLES
- if (format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
- } else if (format == Texture::F_srgb) {
- return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
- } else
- #endif
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
- } else {
- return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
- }
- case Texture::CM_dxt3:
- #ifndef OPENGLES
- if (format == Texture::F_srgb || format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
- }
- #endif
- #ifndef OPENGLES_1
- return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
- #endif
- break;
- case Texture::CM_dxt5:
- #ifndef OPENGLES
- if (format == Texture::F_srgb || format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
- }
- #endif
- #ifndef OPENGLES_1
- return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
- #endif
- case Texture::CM_fxt1:
- #ifndef OPENGLES
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_FXT1_3DFX;
- } else {
- return GL_COMPRESSED_RGB_FXT1_3DFX;
- }
- #endif
- break;
- #ifdef OPENGLES
- case Texture::CM_pvr1_2bpp:
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
- } else {
- return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- }
- case Texture::CM_pvr1_4bpp:
- if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- } else {
- return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- }
- #else
- case Texture::CM_pvr1_2bpp:
- case Texture::CM_pvr1_4bpp:
- break;
- #endif
- case Texture::CM_rgtc:
- #ifndef OPENGLES
- if (format == Texture::F_luminance) {
- return GL_COMPRESSED_LUMINANCE_LATC1_EXT;
- } else if (format == Texture::F_luminance_alpha) {
- return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT;
- } else if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_RED_RGTC1;
- } else if (tex->get_num_components() == 2) {
- return GL_COMPRESSED_RG_RGTC2;
- }
- #endif
- break;
- case Texture::CM_etc1:
- #ifdef OPENGLES
- return GL_ETC1_RGB8_OES;
- #endif
- // Fall through - ETC2 is backward compatible
- case Texture::CM_etc2:
- if (format == Texture::F_rgbm) {
- return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
- } else if (format == Texture::F_srgb_alpha) {
- return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
- } else if (format == Texture::F_srgb) {
- return GL_COMPRESSED_SRGB8_ETC2;
- } else if (Texture::has_alpha(format)) {
- return GL_COMPRESSED_RGBA8_ETC2_EAC;
- } else {
- return GL_COMPRESSED_RGB8_ETC2;
- }
- break;
- case Texture::CM_eac:
- if (Texture::is_unsigned(tex->get_component_type())) {
- if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_R11_EAC;
- } else {
- return GL_COMPRESSED_RG11_EAC;
- }
- } else {
- if (tex->get_num_components() == 1) {
- return GL_COMPRESSED_SIGNED_R11_EAC;
- } else {
- return GL_COMPRESSED_SIGNED_RG11_EAC;
- }
- }
- break;
- case Texture::CM_default:
- case Texture::CM_off:
- case Texture::CM_dxt2:
- case Texture::CM_dxt4:
- // No compression: fall through to below.
- break;
- }
- }
- switch (format) {
- #ifndef OPENGLES
- case Texture::F_color_index:
- return GL_COLOR_INDEX;
- #endif
- case Texture::F_depth_stencil:
- if (_supports_depth_stencil) {
- #ifndef OPENGLES
- if (tex->get_component_type() == Texture::T_float) {
- return GL_DEPTH32F_STENCIL8;
- } else
- #endif
- {
- return force_sized ? GL_DEPTH24_STENCIL8 : GL_DEPTH_STENCIL;
- }
- }
- // Fall through.
- case Texture::F_depth_component:
- #ifndef OPENGLES
- if (tex->get_component_type() == Texture::T_float) {
- return GL_DEPTH_COMPONENT32F;
- } else
- #endif
- {
- return force_sized ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT;
- }
- case Texture::F_depth_component16:
- #ifdef OPENGLES
- return GL_DEPTH_COMPONENT16_OES;
- #else
- return GL_DEPTH_COMPONENT16;
- #endif
- case Texture::F_depth_component24:
- #ifdef OPENGLES
- if (_supports_depth24) {
- return GL_DEPTH_COMPONENT24_OES;
- } else {
- return GL_DEPTH_COMPONENT16_OES;
- }
- #else
- return GL_DEPTH_COMPONENT24;
- #endif
- case Texture::F_depth_component32:
- #ifdef OPENGLES
- if (_supports_depth32) {
- return GL_DEPTH_COMPONENT32_OES;
- } else if (_supports_depth24) {
- return GL_DEPTH_COMPONENT24_OES;
- } else {
- return GL_DEPTH_COMPONENT16_OES;
- }
- #else
- if (tex->get_component_type() == Texture::T_float) {
- return GL_DEPTH_COMPONENT32F;
- } else {
- return GL_DEPTH_COMPONENT32;
- }
- #endif
- case Texture::F_rgba:
- case Texture::F_rgbm:
- #ifndef OPENGLES_1
- if (tex->get_component_type() == Texture::T_float) {
- return GL_RGBA16F;
- } else
- #endif
- #ifdef OPENGLES
- {
- // In OpenGL ES, the internal format must match the external format.
- return _supports_bgr ? GL_BGRA : GL_RGBA;
- }
- #else
- if (tex->get_component_type() == Texture::T_unsigned_short) {
- return GL_RGBA16;
- } else if (tex->get_component_type() == Texture::T_short) {
- return GL_RGBA16_SNORM;
- } else if (tex->get_component_type() == Texture::T_byte) {
- return GL_RGBA8_SNORM;
- } else {
- return force_sized ? GL_RGBA8 : GL_RGBA;
- }
- #endif
- case Texture::F_rgba4:
- return GL_RGBA4;
- #ifdef OPENGLES
- case Texture::F_rgba8:
- return GL_RGBA8_OES;
- case Texture::F_rgba12:
- return force_sized ? GL_RGBA8 : GL_RGBA;
- #else
- case Texture::F_rgba8:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGBA8;
- } else {
- return GL_RGBA8_SNORM;
- }
- case Texture::F_r8i:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_R8UI;
- } else {
- return GL_R8I;
- }
- case Texture::F_rg8i:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RG8UI;
- } else {
- return GL_RG8I;
- }
- case Texture::F_rgb8i:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGB8UI;
- } else {
- return GL_RGB8I;
- }
- case Texture::F_rgba8i:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGBA8UI;
- } else {
- return GL_RGBA8I;
- }
- case Texture::F_rgba12:
- return GL_RGBA12;
- #endif // OPENGLES
- #ifndef OPENGLES
- case Texture::F_rgba16:
- if (tex->get_component_type() == Texture::T_float) {
- return GL_RGBA16F;
- } else if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGBA16;
- } else {
- return GL_RGBA16_SNORM;
- }
- case Texture::F_rgba32:
- return GL_RGBA32F;
- #endif // OPENGLES
- case Texture::F_rgb:
- switch (tex->get_component_type()) {
- case Texture::T_float: return GL_RGB16F;
- #ifndef OPENGLES
- case Texture::T_unsigned_short: return GL_RGB16;
- case Texture::T_short: return GL_RGB16_SNORM;
- case Texture::T_byte: return GL_RGB8_SNORM;
- #endif
- default:
- return force_sized ? GL_RGB8 : GL_RGB;
- }
- case Texture::F_rgb5:
- #ifdef OPENGLES
- // Close enough.
- return GL_RGB565_OES;
- #else
- return GL_RGB5;
- #endif
- case Texture::F_rgba5:
- return GL_RGB5_A1;
- #ifdef OPENGLES
- case Texture::F_rgb8:
- return GL_RGB8_OES;
- case Texture::F_rgb12:
- return force_sized ? GL_RGB8 : GL_RGB;
- case Texture::F_rgb16:
- return GL_RGB16F;
- #else
- case Texture::F_rgb8:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGB8;
- } else {
- return GL_RGB8_SNORM;
- }
- case Texture::F_rgb12:
- return GL_RGB12;
- case Texture::F_rgb16:
- if (tex->get_component_type() == Texture::T_float) {
- return GL_RGB16F;
- } else if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RGB16;
- } else {
- return GL_RGB16_SNORM;
- }
- #endif // OPENGLES
- case Texture::F_rgb32:
- return GL_RGB32F;
- #ifndef OPENGLES
- case Texture::F_rgb332:
- return GL_R3_G3_B2;
- #endif
- #if defined(OPENGLES_2)
- case Texture::F_r16:
- return GL_R16F_EXT;
- case Texture::F_rg16:
- return GL_RG16F_EXT;
- #elif !defined(OPENGLES_1)
- case Texture::F_r16:
- if (tex->get_component_type() == Texture::T_float) {
- return GL_R16F;
- } else if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_R16;
- } else {
- return GL_R16_SNORM;
- }
- case Texture::F_r16i:
- if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_R16UI;
- } else {
- return GL_R16I;
- }
- case Texture::F_rg16:
- if (tex->get_component_type() == Texture::T_float) {
- return GL_RG16F;
- } else if (Texture::is_unsigned(tex->get_component_type())) {
- return GL_RG16;
- } else {
- return GL_RG16_SNORM;
- }
- #endif
- #ifndef OPENGLES_1
- case Texture::F_r32:
- return GL_R32F;
- case Texture::F_rg32:
- return GL_RG32F;
- case Texture::F_red:
- case Texture::F_green:
- case Texture::F_blue:
- #ifndef OPENGLES
- if (!Texture::is_unsigned(tex->get_component_type())) {
- return GL_R8_SNORM;
- } else
- #endif
- {
- return force_sized ? GL_R8 : GL_RED;
- }
- #endif
- case Texture::F_alpha:
- #ifdef OPENGLES
- return force_sized ? GL_ALPHA8 : GL_ALPHA;
- #else
- if (_supports_luminance_texture) {
- return force_sized ? GL_ALPHA8 : GL_ALPHA;
- } else {
- return force_sized ? GL_R8 : GL_RED;
- }
- #endif
- case Texture::F_luminance:
- #ifdef OPENGLES
- return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE;
- #else
- if (_supports_luminance_texture) {
- switch (tex->get_component_type()) {
- case Texture::T_float:
- case Texture::T_half_float:
- return GL_LUMINANCE16F_ARB;
- case Texture::T_short:
- return GL_LUMINANCE16_SNORM;
- case Texture::T_unsigned_short:
- return GL_LUMINANCE16;
- default:
- return force_sized ? GL_LUMINANCE8 : GL_LUMINANCE;
- }
- } else {
- switch (tex->get_component_type()) {
- case Texture::T_float:
- case Texture::T_half_float:
- return GL_R16F;
- case Texture::T_short:
- return GL_R16_SNORM;
- case Texture::T_unsigned_short:
- return GL_R16;
- default:
- return force_sized ? GL_R8 : GL_RED;
- }
- }
- #endif
- case Texture::F_luminance_alpha:
- case Texture::F_luminance_alphamask:
- #ifdef OPENGLES
- return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA;
- #else
- if (_supports_luminance_texture) {
- switch (tex->get_component_type()) {
- case Texture::T_float:
- case Texture::T_half_float:
- return GL_LUMINANCE_ALPHA16F_ARB;
- case Texture::T_short:
- return GL_LUMINANCE16_SNORM;
- case Texture::T_unsigned_short:
- return GL_LUMINANCE16_ALPHA16;
- default:
- return force_sized ? GL_LUMINANCE8_ALPHA8 : GL_LUMINANCE_ALPHA;
- }
- } else {
- switch (tex->get_component_type()) {
- case Texture::T_float:
- case Texture::T_half_float:
- return GL_RG16F;
- case Texture::T_short:
- return GL_RG16_SNORM;
- case Texture::T_unsigned_short:
- return GL_RG16;
- default:
- return force_sized ? GL_RG8 : GL_RG;
- }
- }
- #endif
- #ifndef OPENGLES_1
- case Texture::F_rg:
- return force_sized ? GL_RG8 : GL_RG;
- #endif
- #ifndef OPENGLES_1
- case Texture::F_srgb:
- return _supports_texture_srgb ? GL_SRGB8 : GL_RGB8;
- case Texture::F_srgb_alpha:
- return _supports_texture_srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
- #endif
- #ifndef OPENGLES
- case Texture::F_sluminance:
- return _core_profile ? GL_SRGB8 : GL_SLUMINANCE8;
- case Texture::F_sluminance_alpha:
- return _core_profile ? GL_SRGB8_ALPHA8 : GL_SLUMINANCE8_ALPHA8;
- #endif
- #ifndef OPENGLES
- case Texture::F_r32i:
- return GL_R32I;
- #endif
- #ifndef OPENGLES_1
- case Texture::F_r11_g11_b10:
- return GL_R11F_G11F_B10F;
- case Texture::F_rgb9_e5:
- return GL_RGB9_E5;
- case Texture::F_rgb10_a2:
- return GL_RGB10_A2;
- #endif
- default:
- GLCAT.error()
- << "Invalid image format in get_internal_image_format(): "
- << (int)format << "\n";
- return force_sized ? GL_RGB8 : GL_RGB;
- }
- }
- /**
- * Returns true if the indicated GL minfilter type represents a mipmap format,
- * false otherwise.
- */
- bool CLP(GraphicsStateGuardian)::
- is_mipmap_filter(GLenum min_filter) {
- switch (min_filter) {
- case GL_NEAREST_MIPMAP_NEAREST:
- case GL_LINEAR_MIPMAP_NEAREST:
- case GL_NEAREST_MIPMAP_LINEAR:
- case GL_LINEAR_MIPMAP_LINEAR:
- return true;
- default:
- return false;
- }
- }
- /**
- * Returns true if the indicated GL internal format represents a compressed
- * texture format, false otherwise.
- */
- bool CLP(GraphicsStateGuardian)::
- is_compressed_format(GLenum format) {
- switch (format) {
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- #ifdef OPENGLES
- case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
- case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
- case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
- case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
- #else
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case GL_COMPRESSED_RGB_FXT1_3DFX:
- case GL_COMPRESSED_RGBA_FXT1_3DFX:
- case GL_COMPRESSED_RED_RGTC1:
- case GL_COMPRESSED_SIGNED_RED_RGTC1:
- case GL_COMPRESSED_RG_RGTC2:
- case GL_COMPRESSED_SIGNED_RG_RGTC2:
- case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
- case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
- case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
- case GL_COMPRESSED_RGB:
- case GL_COMPRESSED_SRGB_EXT:
- case GL_COMPRESSED_RGBA:
- case GL_COMPRESSED_SRGB_ALPHA_EXT:
- case GL_COMPRESSED_ALPHA:
- case GL_COMPRESSED_LUMINANCE:
- case GL_COMPRESSED_LUMINANCE_ALPHA:
- #endif
- return true;
- default:
- return false;
- }
- }
- /**
- * Maps from the texture stage's mode types to the corresponding OpenGL ids
- */
- GLint CLP(GraphicsStateGuardian)::
- get_texture_apply_mode_type(TextureStage::Mode am) {
- #ifdef SUPPORT_FIXED_FUNCTION
- switch (am) {
- case TextureStage::M_modulate: return GL_MODULATE;
- case TextureStage::M_decal: return GL_DECAL;
- case TextureStage::M_blend: return GL_BLEND;
- case TextureStage::M_replace: return GL_REPLACE;
- case TextureStage::M_add: return GL_ADD;
- case TextureStage::M_combine: return GL_COMBINE;
- case TextureStage::M_blend_color_scale: return GL_BLEND;
- case TextureStage::M_modulate_glow: return GL_MODULATE;
- case TextureStage::M_modulate_gloss: return GL_MODULATE;
- default:
- // Other modes shouldn't get here. Fall through and error.
- break;
- }
- GLCAT.error()
- << "Invalid TextureStage::Mode value" << endl;
- return GL_MODULATE;
- #else
- return 0;
- #endif
- }
- /**
- * Maps from the texture stage's CombineMode types to the corresponding OpenGL
- * ids
- */
- GLint CLP(GraphicsStateGuardian)::
- get_texture_combine_type(TextureStage::CombineMode cm) {
- #ifdef SUPPORT_FIXED_FUNCTION
- switch (cm) {
- case TextureStage::CM_undefined: // fall through
- case TextureStage::CM_replace: return GL_REPLACE;
- case TextureStage::CM_modulate: return GL_MODULATE;
- case TextureStage::CM_add: return GL_ADD;
- case TextureStage::CM_add_signed: return GL_ADD_SIGNED;
- case TextureStage::CM_interpolate: return GL_INTERPOLATE;
- case TextureStage::CM_subtract: return GL_SUBTRACT;
- case TextureStage::CM_dot3_rgb: return GL_DOT3_RGB;
- case TextureStage::CM_dot3_rgba: return GL_DOT3_RGBA;
- }
- GLCAT.error()
- << "Invalid TextureStage::CombineMode value" << endl;
- #endif
- return GL_REPLACE;
- }
- /**
- * Maps from the texture stage's CombineSource types to the corresponding
- * OpenGL ids
- */
- GLint CLP(GraphicsStateGuardian)::
- get_texture_src_type(TextureStage::CombineSource cs,
- int last_stage, int last_saved_result,
- int this_stage) const {
- #ifdef SUPPORT_FIXED_FUNCTION
- switch (cs) {
- case TextureStage::CS_undefined: // fall through
- case TextureStage::CS_texture: return GL_TEXTURE;
- case TextureStage::CS_constant: return GL_CONSTANT;
- case TextureStage::CS_primary_color: return GL_PRIMARY_COLOR;
- case TextureStage::CS_constant_color_scale: return GL_CONSTANT;
- case TextureStage::CS_previous:
- if (last_stage == this_stage - 1) {
- return GL_PREVIOUS;
- } else if (last_stage == -1) {
- return GL_PRIMARY_COLOR;
- } else if (_supports_texture_saved_result) {
- return GL_TEXTURE0 + last_stage;
- } else {
- GLCAT.warning()
- << "Current OpenGL driver does not support texture crossbar blending.\n";
- return GL_PRIMARY_COLOR;
- }
- case TextureStage::CS_last_saved_result:
- if (last_saved_result == this_stage - 1) {
- return GL_PREVIOUS;
- } else if (last_saved_result == -1) {
- return GL_PRIMARY_COLOR;
- } else if (_supports_texture_saved_result) {
- return GL_TEXTURE0 + last_saved_result;
- } else {
- GLCAT.warning()
- << "Current OpenGL driver does not support texture crossbar blending.\n";
- return GL_PRIMARY_COLOR;
- }
- }
- GLCAT.error()
- << "Invalid TextureStage::CombineSource value" << endl;
- #endif
- return GL_TEXTURE;
- }
- /**
- * Maps from the texture stage's CombineOperand types to the corresponding
- * OpenGL ids
- */
- GLint CLP(GraphicsStateGuardian)::
- get_texture_operand_type(TextureStage::CombineOperand co) {
- switch (co) {
- case TextureStage::CO_undefined: // fall through
- case TextureStage::CO_src_alpha: return GL_SRC_ALPHA;
- case TextureStage::CO_one_minus_src_alpha: return GL_ONE_MINUS_SRC_ALPHA;
- case TextureStage::CO_src_color: return GL_SRC_COLOR;
- case TextureStage::CO_one_minus_src_color: return GL_ONE_MINUS_SRC_COLOR;
- }
- GLCAT.error()
- << "Invalid TextureStage::CombineOperand value" << endl;
- return GL_SRC_COLOR;
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Maps from the fog types to gl version
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_fog_mode_type(Fog::Mode m) {
- switch(m) {
- case Fog::M_linear: return GL_LINEAR;
- case Fog::M_exponential: return GL_EXP;
- case Fog::M_exponential_squared: return GL_EXP2;
- /*
- case Fog::M_spline: return GL_FOG_FUNC_SGIS;
- */
- default:
- GLCAT.error() << "Invalid Fog::Mode value" << endl;
- return GL_EXP;
- }
- }
- #endif
- /**
- * Maps from ColorBlendAttrib::Mode to glBlendEquation value.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_blend_equation_type(ColorBlendAttrib::Mode mode) {
- switch (mode) {
- case ColorBlendAttrib::M_none:
- case ColorBlendAttrib::M_add:
- return GL_FUNC_ADD;
- case ColorBlendAttrib::M_subtract:
- return GL_FUNC_SUBTRACT;
- case ColorBlendAttrib::M_inv_subtract:
- return GL_FUNC_REVERSE_SUBTRACT;
- #ifdef OPENGLES
- case ColorBlendAttrib::M_min:
- return GL_MIN_EXT;
- case ColorBlendAttrib::M_max:
- return GL_MAX_EXT;
- #else
- case ColorBlendAttrib::M_min:
- return GL_MIN;
- case ColorBlendAttrib::M_max:
- return GL_MAX;
- #endif
- }
- GLCAT.error()
- << "Unknown color blend mode " << (int)mode << endl;
- return GL_FUNC_ADD;
- }
- /**
- * Maps from ColorBlendAttrib::Operand to glBlendFunc value.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_blend_func(ColorBlendAttrib::Operand operand) {
- switch (operand) {
- case ColorBlendAttrib::O_zero:
- return GL_ZERO;
- case ColorBlendAttrib::O_one:
- return GL_ONE;
- case ColorBlendAttrib::O_incoming_color:
- return GL_SRC_COLOR;
- case ColorBlendAttrib::O_one_minus_incoming_color:
- return GL_ONE_MINUS_SRC_COLOR;
- case ColorBlendAttrib::O_fbuffer_color:
- return GL_DST_COLOR;
- case ColorBlendAttrib::O_one_minus_fbuffer_color:
- return GL_ONE_MINUS_DST_COLOR;
- case ColorBlendAttrib::O_incoming_alpha:
- return GL_SRC_ALPHA;
- case ColorBlendAttrib::O_one_minus_incoming_alpha:
- return GL_ONE_MINUS_SRC_ALPHA;
- case ColorBlendAttrib::O_fbuffer_alpha:
- return GL_DST_ALPHA;
- case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
- return GL_ONE_MINUS_DST_ALPHA;
- #ifdef OPENGLES_1
- // OpenGL ES 1 has no constant blend factor.
- case ColorBlendAttrib::O_constant_color:
- case ColorBlendAttrib::O_color_scale:
- case ColorBlendAttrib::O_one_minus_constant_color:
- case ColorBlendAttrib::O_one_minus_color_scale:
- case ColorBlendAttrib::O_constant_alpha:
- case ColorBlendAttrib::O_alpha_scale:
- case ColorBlendAttrib::O_one_minus_constant_alpha:
- case ColorBlendAttrib::O_one_minus_alpha_scale:
- break;
- // No dual-source blending, either.
- case ColorBlendAttrib::O_incoming1_color:
- case ColorBlendAttrib::O_one_minus_incoming1_color:
- case ColorBlendAttrib::O_incoming1_alpha:
- case ColorBlendAttrib::O_one_minus_incoming1_alpha:
- break;
- #else
- case ColorBlendAttrib::O_constant_color:
- case ColorBlendAttrib::O_color_scale:
- return GL_CONSTANT_COLOR;
- case ColorBlendAttrib::O_one_minus_constant_color:
- case ColorBlendAttrib::O_one_minus_color_scale:
- return GL_ONE_MINUS_CONSTANT_COLOR;
- case ColorBlendAttrib::O_constant_alpha:
- case ColorBlendAttrib::O_alpha_scale:
- return GL_CONSTANT_ALPHA;
- case ColorBlendAttrib::O_one_minus_constant_alpha:
- case ColorBlendAttrib::O_one_minus_alpha_scale:
- return GL_ONE_MINUS_CONSTANT_ALPHA;
- case ColorBlendAttrib::O_incoming1_color:
- return GL_SRC1_COLOR;
- case ColorBlendAttrib::O_one_minus_incoming1_color:
- return GL_ONE_MINUS_SRC1_COLOR;
- case ColorBlendAttrib::O_incoming1_alpha:
- return GL_SRC1_ALPHA;
- case ColorBlendAttrib::O_one_minus_incoming1_alpha:
- return GL_ONE_MINUS_SRC1_ALPHA;
- #endif
- case ColorBlendAttrib::O_incoming_color_saturate:
- return GL_SRC_ALPHA_SATURATE;
- }
- GLCAT.error()
- << "Unknown color blend operand " << (int)operand << endl;
- return GL_ZERO;
- }
- /**
- * Maps from UsageHint to the GL symbol.
- */
- GLenum CLP(GraphicsStateGuardian)::
- get_usage(Geom::UsageHint usage_hint) {
- switch (usage_hint) {
- case Geom::UH_stream:
- #ifdef OPENGLES_1
- return GL_DYNAMIC_DRAW;
- #else
- return GL_STREAM_DRAW;
- #endif // OPENGLES
- case Geom::UH_static:
- case Geom::UH_unspecified:
- return GL_STATIC_DRAW;
- case Geom::UH_dynamic:
- return GL_DYNAMIC_DRAW;
- case Geom::UH_client:
- break;
- }
- GLCAT.error()
- << "Unexpected usage_hint " << (int)usage_hint << endl;
- return GL_STATIC_DRAW;
- }
- #ifndef NDEBUG
- /**
- * Returns a string describing an compression format.
- */
- const char *CLP(GraphicsStateGuardian)::
- get_compressed_format_string(GLenum format) {
- switch (format) {
- case 0x83F0: return "GL_COMPRESSED_RGB_S3TC_DXT1_EXT";
- case 0x83F1: return "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT";
- case 0x83F2: return "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT";
- case 0x83F3: return "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT";
- case 0x86B0: return "GL_COMPRESSED_RGB_FXT1_3DFX";
- case 0x86B1: return "GL_COMPRESSED_RGBA_FXT1_3DFX";
- case 0x88EE: return "GL_ETC1_SRGB8_NV";
- case 0x8A54: return "GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT";
- case 0x8A55: return "GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT";
- case 0x8A56: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT";
- case 0x8A57: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT";
- case 0x8B90: return "GL_PALETTE4_RGB8_OES";
- case 0x8B91: return "GL_PALETTE4_RGBA8_OES";
- case 0x8B92: return "GL_PALETTE4_R5_G6_B5_OES";
- case 0x8B93: return "GL_PALETTE4_RGBA4_OES";
- case 0x8B94: return "GL_PALETTE4_RGB5_A1_OES";
- case 0x8B95: return "GL_PALETTE8_RGB8_OES";
- case 0x8B96: return "GL_PALETTE8_RGBA8_OES";
- case 0x8B97: return "GL_PALETTE8_R5_G6_B5_OES";
- case 0x8B98: return "GL_PALETTE8_RGBA4_OES";
- case 0x8B99: return "GL_PALETTE8_RGB5_A1_OES";
- case 0x8C00: return "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG";
- case 0x8C01: return "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG";
- case 0x8C02: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG";
- case 0x8C03: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG";
- case 0x8C48: return "GL_COMPRESSED_SRGB_EXT";
- case 0x8C49: return "GL_COMPRESSED_SRGB_ALPHA_EXT";
- case 0x8C4A: return "GL_COMPRESSED_SLUMINANCE_EXT";
- case 0x8C4B: return "GL_COMPRESSED_SLUMINANCE_ALPHA_EXT";
- case 0x8C4C: return "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT";
- case 0x8C4D: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT";
- case 0x8C4E: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT";
- case 0x8C4F: return "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT";
- case 0x8C70: return "GL_COMPRESSED_LUMINANCE_LATC1_EXT";
- case 0x8C71: return "GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT";
- case 0x8C72: return "GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT";
- case 0x8C73: return "GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT";
- case 0x8D64: return "GL_ETC1_RGB8_OES";
- case 0x8DBB: return "GL_COMPRESSED_RED_RGTC1";
- case 0x8DBC: return "GL_COMPRESSED_SIGNED_RED_RGTC1";
- case 0x8DBD: return "GL_COMPRESSED_RG_RGTC2";
- case 0x8DBE: return "GL_COMPRESSED_SIGNED_RG_RGTC2";
- case 0x8E8C: return "GL_COMPRESSED_RGBA_BPTC_UNORM";
- case 0x8E8D: return "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM";
- case 0x8E8E: return "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT";
- case 0x8E8F: return "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT";
- case 0x9137: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG";
- case 0x9138: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG";
- case 0x9270: return "GL_COMPRESSED_R11_EAC";
- case 0x9271: return "GL_COMPRESSED_SIGNED_R11_EAC";
- case 0x9272: return "GL_COMPRESSED_RG11_EAC";
- case 0x9273: return "GL_COMPRESSED_SIGNED_RG11_EAC";
- case 0x9274: return "GL_COMPRESSED_RGB8_ETC2";
- case 0x9275: return "GL_COMPRESSED_SRGB8_ETC2";
- case 0x9276: return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2";
- case 0x9277: return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2";
- case 0x9278: return "GL_COMPRESSED_RGBA8_ETC2_EAC";
- case 0x9279: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC";
- case 0x93B0: return "GL_COMPRESSED_RGBA_ASTC_4x4_KHR";
- case 0x93B1: return "GL_COMPRESSED_RGBA_ASTC_5x4_KHR";
- case 0x93B2: return "GL_COMPRESSED_RGBA_ASTC_5x5_KHR";
- case 0x93B3: return "GL_COMPRESSED_RGBA_ASTC_6x5_KHR";
- case 0x93B4: return "GL_COMPRESSED_RGBA_ASTC_6x6_KHR";
- case 0x93B5: return "GL_COMPRESSED_RGBA_ASTC_8x5_KHR";
- case 0x93B6: return "GL_COMPRESSED_RGBA_ASTC_8x6_KHR";
- case 0x93B7: return "GL_COMPRESSED_RGBA_ASTC_8x8_KHR";
- case 0x93B8: return "GL_COMPRESSED_RGBA_ASTC_10x5_KHR";
- case 0x93B9: return "GL_COMPRESSED_RGBA_ASTC_10x6_KHR";
- case 0x93BA: return "GL_COMPRESSED_RGBA_ASTC_10x8_KHR";
- case 0x93BB: return "GL_COMPRESSED_RGBA_ASTC_10x10_KHR";
- case 0x93BC: return "GL_COMPRESSED_RGBA_ASTC_12x10_KHR";
- case 0x93BD: return "GL_COMPRESSED_RGBA_ASTC_12x12_KHR";
- case 0x93C0: return "GL_COMPRESSED_RGBA_ASTC_3x3x3_OES";
- case 0x93C1: return "GL_COMPRESSED_RGBA_ASTC_4x3x3_OES";
- case 0x93C2: return "GL_COMPRESSED_RGBA_ASTC_4x4x3_OES";
- case 0x93C3: return "GL_COMPRESSED_RGBA_ASTC_4x4x4_OES";
- case 0x93C4: return "GL_COMPRESSED_RGBA_ASTC_5x4x4_OES";
- case 0x93C5: return "GL_COMPRESSED_RGBA_ASTC_5x5x4_OES";
- case 0x93C6: return "GL_COMPRESSED_RGBA_ASTC_5x5x5_OES";
- case 0x93C7: return "GL_COMPRESSED_RGBA_ASTC_6x5x5_OES";
- case 0x93C8: return "GL_COMPRESSED_RGBA_ASTC_6x6x5_OES";
- case 0x93C9: return "GL_COMPRESSED_RGBA_ASTC_6x6x6_OES";
- case 0x93D0: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR";
- case 0x93D1: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR";
- case 0x93D2: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR";
- case 0x93D3: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR";
- case 0x93D4: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR";
- case 0x93D5: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR";
- case 0x93D6: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR";
- case 0x93D7: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR";
- case 0x93D8: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR";
- case 0x93D9: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR";
- case 0x93DA: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR";
- case 0x93DB: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR";
- case 0x93DC: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR";
- case 0x93DD: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR";
- case 0x93E0: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES";
- case 0x93E1: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES";
- case 0x93E2: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES";
- case 0x93E3: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES";
- case 0x93E4: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES";
- case 0x93E5: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES";
- case 0x93E6: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES";
- case 0x93E7: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES";
- case 0x93E8: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES";
- case 0x93E9: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES";
- case 0x93F0: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG";
- case 0x93F1: return "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG";
- default:
- return nullptr;
- }
- }
- #endif
- /**
- * Returns the value that that should be issued as the light's color, as
- * scaled by the current value of _light_color_scale, in the case of
- * color_scale_via_lighting.
- */
- LVecBase4 CLP(GraphicsStateGuardian)::
- get_light_color(Light *light) const {
- #ifndef NDEBUG
- if (_show_texture_usage) {
- // In show_texture_usage mode, all lights are white, so as not to
- // contaminate the texture color.
- return LVecBase4(1.0, 1.0, 1.0, 1.0);
- }
- #endif // NDEBUG
- const LColor &c = light->get_color();
- LVecBase4 light_color(c[0] * _light_color_scale[0],
- c[1] * _light_color_scale[1],
- c[2] * _light_color_scale[2],
- c[3] * _light_color_scale[3]);
- return light_color;
- }
- /**
- * Called by clear_state_and_transform() to ensure that the current modelview
- * and projection matrices are properly loaded in the graphics state, after a
- * callback might have mucked them up.
- */
- void CLP(GraphicsStateGuardian)::
- reissue_transforms() {
- prepare_lens();
- do_issue_transform();
- _active_texture_stage = -1;
- #ifndef OPENGLES_1
- // Might also want to reissue the vertex format, for good measure.
- _current_vertex_format.clear();
- memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
- #endif
- // Some libraries (Kivy) leave their buffers bound. How clumsy of them.
- if (_supports_buffers) {
- _glBindBuffer(GL_ARRAY_BUFFER, 0);
- _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- _current_vbuffer_index = 0;
- _current_ibuffer_index = 0;
- }
- #ifndef OPENGLES
- if (_supports_glsl) {
- _glDisableVertexAttribArray(0);
- _glDisableVertexAttribArray(1);
- }
- #endif
- // Since this is called by clear_state_and_transform(), we also should reset
- // the states that won't automatically be respecified when clearing the
- // state mask.
- _active_color_write_mask = ColorWriteAttrib::C_all;
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- if (_dithering_enabled) {
- glEnable(GL_DITHER);
- } else {
- glDisable(GL_DITHER);
- }
- if (_depth_test_enabled) {
- glEnable(GL_DEPTH_TEST);
- } else {
- glDisable(GL_DEPTH_TEST);
- }
- if (_stencil_test_enabled) {
- glEnable(GL_STENCIL_TEST);
- } else {
- glDisable(GL_STENCIL_TEST);
- }
- if (_blend_enabled) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
- #ifndef OPENGLES_2
- if (_multisample_mode != 0) {
- glEnable(GL_MULTISAMPLE);
- } else {
- glDisable(GL_MULTISAMPLE);
- glDisable(GL_SAMPLE_ALPHA_TO_ONE);
- glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
- }
- if (_line_smooth_enabled) {
- glEnable(GL_LINE_SMOOTH);
- } else {
- glDisable(GL_LINE_SMOOTH);
- }
- #endif
- #ifndef OPENGLES
- if (_polygon_smooth_enabled) {
- glEnable(GL_POLYGON_SMOOTH);
- } else {
- glDisable(GL_POLYGON_SMOOTH);
- }
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- if (_alpha_test_enabled) {
- glEnable(GL_ALPHA_TEST);
- } else {
- glDisable(GL_ALPHA_TEST);
- }
- if (_point_smooth_enabled) {
- glEnable(GL_POINT_SMOOTH);
- } else {
- glDisable(GL_POINT_SMOOTH);
- }
- }
- #endif
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Intended to be overridden by a derived class to enable or disable the use
- * of lighting overall. This is called by do_issue_light() according to
- * whether any lights are in use or not.
- */
- void CLP(GraphicsStateGuardian)::
- enable_lighting(bool enable) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_enable_lighting_pcollector("Draw:Set
- // State:Light:Enable lighting"); PStatGPUTimer timer(this,
- // _draw_set_state_light_enable_lighting_pcollector);
- if (enable) {
- glEnable(GL_LIGHTING);
- } else {
- glDisable(GL_LIGHTING);
- }
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Intended to be overridden by a derived class to indicate the color of the
- * ambient light that should be in effect. This is called by do_issue_light()
- * after all other lights have been enabled or disabled.
- */
- void CLP(GraphicsStateGuardian)::
- set_ambient_light(const LColor &color) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector _draw_set_state_light_ambient_pcollector("Draw:Set
- // State:Light:Ambient"); PStatGPUTimer timer(this,
- // _draw_set_state_light_ambient_pcollector);
- LColor c = color;
- c.set(c[0] * _light_color_scale[0],
- c[1] * _light_color_scale[1],
- c[2] * _light_color_scale[2],
- c[3] * _light_color_scale[3]);
- call_glLightModelfv(GL_LIGHT_MODEL_AMBIENT, c);
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Intended to be overridden by a derived class to enable the indicated light
- * id. A specific Light will already have been bound to this id via
- * bind_light().
- */
- void CLP(GraphicsStateGuardian)::
- enable_light(int light_id, bool enable) {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_enable_light_pcollector("Draw:Set
- // State:Light:Enable light"); PStatGPUTimer timer(this,
- // _draw_set_state_light_enable_light_pcollector);
- if (enable) {
- glEnable(get_light_id(light_id));
- } else {
- glDisable(get_light_id(light_id));
- }
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called immediately before bind_light() is called, this is intended to
- * provide the derived class a hook in which to set up some state (like
- * transform) that might apply to several lights.
- *
- * The sequence is: begin_bind_lights() will be called, then one or more
- * bind_light() calls, then end_bind_lights().
- */
- void CLP(GraphicsStateGuardian)::
- begin_bind_lights() {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector
- // _draw_set_state_light_begin_bind_pcollector("Draw:Set State:Light:Begin
- // bind"); PStatGPUTimer timer(this,
- // _draw_set_state_light_begin_bind_pcollector);
- // We need to temporarily load a new matrix so we can define the light in a
- // known coordinate system. We pick the transform of the root.
- // (Alternatively, we could leave the current transform where it is and
- // compute the light position relative to that transform instead of relative
- // to the root, by composing with the matrix computed by
- // _internal_transform->invert_compose(render_transform). But I think
- // loading a completely new matrix is simpler.)
- CPT(TransformState) render_transform =
- _cs_transform->compose(_scene_setup->get_world_transform());
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- call_glLoadMatrix(render_transform->get_mat());
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called after before bind_light() has been called one or more times (but
- * before any geometry is issued or additional state is changed), this is
- * intended to clean up any temporary changes to the state that may have been
- * made by begin_bind_lights().
- */
- void CLP(GraphicsStateGuardian)::
- end_bind_lights() {
- nassertv(has_fixed_function_pipeline());
- // static PStatCollector _draw_set_state_light_end_bind_pcollector("Draw:Set
- // State:Light:End bind"); PStatGPUTimer timer(this,
- // _draw_set_state_light_end_bind_pcollector);
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Intended to be overridden by a derived class to enable the indicated
- * clip_plane id. A specific PlaneNode will already have been bound to this
- * id via bind_clip_plane().
- */
- void CLP(GraphicsStateGuardian)::
- enable_clip_plane(int plane_id, bool enable) {
- nassertv(has_fixed_function_pipeline());
- if (enable) {
- glEnable(get_clip_plane_id(plane_id));
- } else {
- glDisable(get_clip_plane_id(plane_id));
- }
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called immediately before bind_clip_plane() is called, this is intended to
- * provide the derived class a hook in which to set up some state (like
- * transform) that might apply to several clip_planes.
- *
- * The sequence is: begin_bind_clip_planes() will be called, then one or more
- * bind_clip_plane() calls, then end_bind_clip_planes().
- */
- void CLP(GraphicsStateGuardian)::
- begin_bind_clip_planes() {
- nassertv(has_fixed_function_pipeline());
- // We need to temporarily load a new matrix so we can define the clip_plane
- // in a known coordinate system. We pick the transform of the root.
- // (Alternatively, we could leave the current transform where it is and
- // compute the clip_plane position relative to that transform instead of
- // relative to the root, by composing with the matrix computed by
- // _internal_transform->invert_compose(render_transform). But I think
- // loading a completely new matrix is simpler.)
- CPT(TransformState) render_transform =
- _cs_transform->compose(_scene_setup->get_world_transform());
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- call_glLoadMatrix(render_transform->get_mat());
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called the first time a particular clip_plane has been bound to a given id
- * within a frame, this should set up the associated hardware clip_plane with
- * the clip_plane's properties.
- */
- void CLP(GraphicsStateGuardian)::
- bind_clip_plane(const NodePath &plane, int plane_id) {
- nassertv(has_fixed_function_pipeline());
- GLenum id = get_clip_plane_id(plane_id);
- CPT(TransformState) transform = plane.get_transform(_scene_setup->get_scene_root().get_parent());
- const PlaneNode *plane_node;
- DCAST_INTO_V(plane_node, plane.node());
- LPlane xformed_plane = plane_node->get_plane() * transform->get_mat();
- #ifdef OPENGLES
- // OpenGL ES uses a single-precision call.
- LPlanef single_plane(LCAST(float, xformed_plane));
- glClipPlanef(id, single_plane.get_data());
- #else
- // Mainline OpenGL uses a double-precision call.
- LPlaned double_plane(LCAST(double, xformed_plane));
- glClipPlane(id, double_plane.get_data());
- #endif // OPENGLES
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Called after before bind_clip_plane() has been called one or more times
- * (but before any geometry is issued or additional state is changed), this is
- * intended to clean up any temporary changes to the state that may have been
- * made by begin_bind_clip_planes().
- */
- void CLP(GraphicsStateGuardian)::
- end_bind_clip_planes() {
- nassertv(has_fixed_function_pipeline());
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Simultaneously resets the render state and the transform state.
- *
- * This transform specified is the "internal" net transform, already converted
- * into the GSG's internal coordinate space by composing it to
- * get_cs_transform(). (Previously, this used to be the "external" net
- * transform, with the assumption that that GSG would convert it internally,
- * but that is no longer the case.)
- *
- * Special case: if (state==NULL), then the target state is already stored in
- * _target.
- */
- void CLP(GraphicsStateGuardian)::
- set_state_and_transform(const RenderState *target,
- const TransformState *transform) {
- report_my_gl_errors();
- #ifndef NDEBUG
- if (gsg_cat.is_spam()) {
- gsg_cat.spam() << "Setting GSG state to " << (void *)target << ":\n";
- target->write(gsg_cat.spam(false), 2);
- }
- #endif
- _state_pcollector.add_level(1);
- PStatGPUTimer timer1(this, _draw_set_state_pcollector);
- if (transform != _internal_transform) {
- // PStatGPUTimer timer(this, _draw_set_state_transform_pcollector);
- _transform_state_pcollector.add_level(1);
- _internal_transform = transform;
- do_issue_transform();
- }
- //XXX the _inv_state_mask system does not appear to be used at the moment.
- //if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) {
- // return;
- //}
- _target_rs = target;
- #ifndef OPENGLES_1
- determine_target_shader();
- _instance_count = _target_shader->get_instance_count();
- if (_target_shader != _state_shader) {
- do_issue_shader();
- _state_shader = _target_shader;
- _state_mask.clear_bit(TextureAttrib::get_class_slot());
- }
- else if (!has_fixed_function_pipeline() && _current_shader == nullptr) { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything.
- do_issue_shader();
- _state_mask.clear_bit(TextureAttrib::get_class_slot());
- }
- // Update all of the state that is bound to the shader program.
- if (_current_shader_context != nullptr) {
- _current_shader_context->set_state_and_transform(target, transform, _scene_setup->get_camera_transform(), _projection_mat);
- }
- #endif
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- int alpha_test_slot = AlphaTestAttrib::get_class_slot();
- if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) ||
- !_state_mask.get_bit(alpha_test_slot)
- #ifndef OPENGLES_1
- || (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test) !=
- _state_shader->get_flag(ShaderAttrib::F_subsume_alpha_test))
- #endif
- ) {
- // PStatGPUTimer timer(this, _draw_set_state_alpha_test_pcollector);
- do_issue_alpha_test();
- _state_mask.set_bit(alpha_test_slot);
- }
- }
- #endif
- int antialias_slot = AntialiasAttrib::get_class_slot();
- if (_target_rs->get_attrib(antialias_slot) != _state_rs->get_attrib(antialias_slot) ||
- !_state_mask.get_bit(antialias_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_antialias_pcollector);
- do_issue_antialias();
- _state_mask.set_bit(antialias_slot);
- }
- int clip_plane_slot = ClipPlaneAttrib::get_class_slot();
- if (_target_rs->get_attrib(clip_plane_slot) != _state_rs->get_attrib(clip_plane_slot) ||
- !_state_mask.get_bit(clip_plane_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_clip_plane_pcollector);
- do_issue_clip_plane();
- _state_mask.set_bit(clip_plane_slot);
- }
- int color_slot = ColorAttrib::get_class_slot();
- int color_scale_slot = ColorScaleAttrib::get_class_slot();
- if (_target_rs->get_attrib(color_slot) != _state_rs->get_attrib(color_slot) ||
- _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) ||
- !_state_mask.get_bit(color_slot) ||
- !_state_mask.get_bit(color_scale_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_color_pcollector);
- do_issue_color();
- do_issue_color_scale();
- _state_mask.set_bit(color_slot);
- _state_mask.set_bit(color_scale_slot);
- }
- int cull_face_slot = CullFaceAttrib::get_class_slot();
- if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) ||
- !_state_mask.get_bit(cull_face_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_cull_face_pcollector);
- do_issue_cull_face();
- _state_mask.set_bit(cull_face_slot);
- }
- int depth_offset_slot = DepthOffsetAttrib::get_class_slot();
- if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) ||
- !_state_mask.get_bit(depth_offset_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_depth_offset_pcollector);
- do_issue_depth_offset();
- _state_mask.set_bit(depth_offset_slot);
- }
- int depth_test_slot = DepthTestAttrib::get_class_slot();
- if (_target_rs->get_attrib(depth_test_slot) != _state_rs->get_attrib(depth_test_slot) ||
- !_state_mask.get_bit(depth_test_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_depth_test_pcollector);
- do_issue_depth_test();
- _state_mask.set_bit(depth_test_slot);
- }
- int depth_write_slot = DepthWriteAttrib::get_class_slot();
- if (_target_rs->get_attrib(depth_write_slot) != _state_rs->get_attrib(depth_write_slot) ||
- !_state_mask.get_bit(depth_write_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_depth_write_pcollector);
- do_issue_depth_write();
- _state_mask.set_bit(depth_write_slot);
- }
- int render_mode_slot = RenderModeAttrib::get_class_slot();
- if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) ||
- !_state_mask.get_bit(render_mode_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_render_mode_pcollector);
- do_issue_render_mode();
- _state_mask.set_bit(render_mode_slot);
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- int rescale_normal_slot = RescaleNormalAttrib::get_class_slot();
- if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) ||
- !_state_mask.get_bit(rescale_normal_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_rescale_normal_pcollector);
- do_issue_rescale_normal();
- _state_mask.set_bit(rescale_normal_slot);
- }
- int shade_model_slot = ShadeModelAttrib::get_class_slot();
- if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) ||
- !_state_mask.get_bit(shade_model_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_shade_model_pcollector);
- do_issue_shade_model();
- _state_mask.set_bit(shade_model_slot);
- }
- }
- #endif
- #if !defined(OPENGLES) || defined(OPENGLES_1)
- int logic_op_slot = LogicOpAttrib::get_class_slot();
- if (_target_rs->get_attrib(logic_op_slot) != _state_rs->get_attrib(logic_op_slot) ||
- !_state_mask.get_bit(logic_op_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_logic_op_pcollector);
- do_issue_logic_op();
- _state_mask.set_bit(logic_op_slot);
- }
- #endif
- int transparency_slot = TransparencyAttrib::get_class_slot();
- int color_write_slot = ColorWriteAttrib::get_class_slot();
- int color_blend_slot = ColorBlendAttrib::get_class_slot();
- if (_target_rs->get_attrib(transparency_slot) != _state_rs->get_attrib(transparency_slot) ||
- _target_rs->get_attrib(color_write_slot) != _state_rs->get_attrib(color_write_slot) ||
- _target_rs->get_attrib(color_blend_slot) != _state_rs->get_attrib(color_blend_slot) ||
- !_state_mask.get_bit(transparency_slot) ||
- !_state_mask.get_bit(color_write_slot) ||
- !_state_mask.get_bit(color_blend_slot)
- #ifndef OPENGLES_1
- || (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) !=
- _state_shader->get_flag(ShaderAttrib::F_disable_alpha_write))
- #endif
- ) {
- // PStatGPUTimer timer(this, _draw_set_state_blending_pcollector);
- do_issue_blending();
- _state_mask.set_bit(transparency_slot);
- _state_mask.set_bit(color_write_slot);
- _state_mask.set_bit(color_blend_slot);
- }
- int texture_slot = TextureAttrib::get_class_slot();
- if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
- !_state_mask.get_bit(texture_slot)) {
- PStatGPUTimer timer(this, _draw_set_state_texture_pcollector);
- determine_target_texture();
- do_issue_texture();
- // Since the TexGen and TexMatrix states depend partly on the particular
- // set of textures in use, we should force both of those to be reissued
- // every time we change the texture state.
- _state_mask.clear_bit(TexGenAttrib::get_class_slot());
- _state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
- _state_texture = _target_texture;
- _state_mask.set_bit(texture_slot);
- }
- // If one of the previously-loaded TexGen modes modified the texture matrix,
- // then if either state changed, we have to change both of them now.
- if (_tex_gen_modifies_mat) {
- int tex_gen_slot = TexGenAttrib::get_class_slot();
- int tex_matrix_slot = TexMatrixAttrib::get_class_slot();
- if (_target_rs->get_attrib(tex_gen_slot) != _state_rs->get_attrib(tex_gen_slot) ||
- _target_rs->get_attrib(tex_matrix_slot) != _state_rs->get_attrib(tex_matrix_slot) ||
- !_state_mask.get_bit(tex_gen_slot) ||
- !_state_mask.get_bit(tex_matrix_slot)) {
- _state_mask.clear_bit(tex_gen_slot);
- _state_mask.clear_bit(tex_matrix_slot);
- }
- }
- int tex_matrix_slot = TexMatrixAttrib::get_class_slot();
- if (_target_rs->get_attrib(tex_matrix_slot) != _state_rs->get_attrib(tex_matrix_slot) ||
- !_state_mask.get_bit(tex_matrix_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_tex_matrix_pcollector);
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- do_issue_tex_matrix();
- }
- #endif
- _state_mask.set_bit(tex_matrix_slot);
- #ifndef OPENGLES_1
- if (_current_shader_context) {
- _current_shader_context->issue_parameters(Shader::SSD_tex_matrix);
- }
- #endif
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- int tex_gen_slot = TexGenAttrib::get_class_slot();
- if (_target_tex_gen != _state_tex_gen ||
- !_state_mask.get_bit(tex_gen_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_tex_gen_pcollector);
- do_issue_tex_gen();
- _state_tex_gen = _target_tex_gen;
- _state_mask.set_bit(tex_gen_slot);
- }
- int material_slot = MaterialAttrib::get_class_slot();
- if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) ||
- !_state_mask.get_bit(material_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_material_pcollector);
- do_issue_material();
- _state_mask.set_bit(material_slot);
- }
- int light_slot = LightAttrib::get_class_slot();
- if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) ||
- !_state_mask.get_bit(light_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_light_pcollector);
- do_issue_light();
- _state_mask.set_bit(light_slot);
- }
- int fog_slot = FogAttrib::get_class_slot();
- if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) ||
- !_state_mask.get_bit(fog_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_fog_pcollector);
- do_issue_fog();
- _state_mask.set_bit(fog_slot);
- }
- }
- #endif
- int stencil_slot = StencilAttrib::get_class_slot();
- if (_target_rs->get_attrib(stencil_slot) != _state_rs->get_attrib(stencil_slot) ||
- !_state_mask.get_bit(stencil_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_stencil_pcollector);
- do_issue_stencil();
- _state_mask.set_bit(stencil_slot);
- }
- int scissor_slot = ScissorAttrib::get_class_slot();
- if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) ||
- !_state_mask.get_bit(scissor_slot)) {
- // PStatGPUTimer timer(this, _draw_set_state_scissor_pcollector);
- do_issue_scissor();
- _state_mask.set_bit(scissor_slot);
- }
- _state_rs = _target_rs;
- maybe_gl_finish();
- report_my_gl_errors();
- }
- /**
- * Frees some memory that was explicitly allocated within the glgsg.
- */
- void CLP(GraphicsStateGuardian)::
- free_pointers() {
- #if defined(HAVE_CG) && !defined(OPENGLES)
- if (_cg_context != 0) {
- cgDestroyContext(_cg_context);
- _cg_context = 0;
- }
- #endif
- }
- /**
- * This is called by set_state_and_transform() when the texture state has
- * changed.
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_texture() {
- DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
- #ifdef OPENGLES_1
- update_standard_texture_bindings();
- #else
- if (_current_shader_context == 0) {
- // No shader, or a non-Cg shader.
- if (_texture_binding_shader_context != 0) {
- _texture_binding_shader_context->disable_shader_texture_bindings();
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- update_standard_texture_bindings();
- }
- #endif
- } else {
- if (_texture_binding_shader_context == 0) {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- disable_standard_texture_bindings();
- }
- #endif
- _current_shader_context->update_shader_texture_bindings(nullptr);
- } else {
- _current_shader_context->
- update_shader_texture_bindings(_texture_binding_shader_context);
- }
- }
- _texture_binding_shader = _current_shader;
- _texture_binding_shader_context = _current_shader_context;
- #endif
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- * Applies the appropriate set of textures for the current state, using the
- * standard fixed-function pipeline.
- */
- void CLP(GraphicsStateGuardian)::
- update_standard_texture_bindings() {
- #ifndef NDEBUG
- if (_show_texture_usage) {
- update_show_usage_texture_bindings(-1);
- return;
- }
- #endif // NDEBUG
- int num_stages = _target_texture->get_num_on_ff_stages();
- #ifndef NDEBUG
- // Also check the _flash_texture. If it is non-NULL, we need to check to
- // see if our flash_texture is in the texture stack here. If so, then we
- // need to call the special show_texture method instead of the normal
- // texture stack.
- if (_flash_texture != nullptr) {
- double now = ClockObject::get_global_clock()->get_frame_time();
- int this_second = (int)floor(now);
- if (this_second & 1) {
- int show_stage_index = -1;
- for (int i = 0; i < num_stages && show_stage_index < 0; ++i) {
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- Texture *texture = _target_texture->get_on_texture(stage);
- if (texture == _flash_texture) {
- show_stage_index = i;
- }
- }
- if (show_stage_index >= 0) {
- update_show_usage_texture_bindings(show_stage_index);
- return;
- }
- }
- }
- #endif // NDEBUG
- nassertv(num_stages <= _max_texture_stages &&
- _num_active_texture_stages <= _max_texture_stages);
- _texture_involves_color_scale = false;
- int last_saved_result = -1;
- int last_stage = -1;
- int i;
- for (i = 0; i < num_stages; i++) {
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- Texture *texture = _target_texture->get_on_texture(stage);
- nassertv(texture != nullptr);
- // Issue the texture on stage i.
- set_active_texture_stage(i);
- // First, turn off the previous texture mode.
- glDisable(GL_TEXTURE_2D);
- if (_supports_cube_map) {
- glDisable(GL_TEXTURE_CUBE_MAP);
- }
- #ifndef OPENGLES
- glDisable(GL_TEXTURE_1D);
- if (_supports_3d_texture) {
- glDisable(GL_TEXTURE_3D);
- }
- #endif // OPENGLES
- int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
- TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
- if (tc == nullptr) {
- // Something wrong with this texture; skip it.
- continue;
- }
- // Then, turn on the current texture mode.
- GLenum target = get_texture_target(texture->get_texture_type());
- if (target == GL_NONE) {
- // Unsupported texture mode.
- continue;
- }
- #ifndef OPENGLES_1
- if (target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP_ARRAY) {
- // Cannot be applied via the FFP.
- continue;
- }
- #endif // OPENGLES
- glEnable(target);
- if (!update_texture(tc, false)) {
- glDisable(target);
- continue;
- }
- // Don't DCAST(); we already did the verification in update_texture.
- CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc;
- apply_texture(gtc);
- apply_sampler(i, _target_texture->get_on_sampler(stage), gtc);
- if (stage->involves_color_scale() && _color_scale_enabled) {
- LColor color = stage->get_color();
- color.set(color[0] * _current_color_scale[0],
- color[1] * _current_color_scale[1],
- color[2] * _current_color_scale[2],
- color[3] * _current_color_scale[3]);
- _texture_involves_color_scale = true;
- call_glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
- } else {
- call_glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, stage->get_color());
- }
- if (stage->get_mode() == TextureStage::M_decal) {
- if (texture->get_num_components() < 3 && _supports_texture_combine) {
- // Make a special case for 1- and 2-channel decal textures. OpenGL
- // does not define their use with GL_DECAL for some reason, so
- // implement them using the combiner instead.
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
- glTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE);
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
- } else {
- // Normal 3- and 4-channel decal textures.
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
- }
- } else if (stage->get_mode() == TextureStage::M_combine) {
- if (!_supports_texture_combine) {
- GLCAT.warning()
- << "TextureStage::M_combine mode is not supported.\n";
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- } else {
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
- glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, stage->get_rgb_scale());
- glTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, stage->get_alpha_scale());
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,
- get_texture_combine_type(stage->get_combine_rgb_mode()));
- switch (stage->get_num_combine_rgb_operands()) {
- case 3:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB,
- get_texture_src_type(stage->get_combine_rgb_source2(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB,
- get_texture_operand_type(stage->get_combine_rgb_operand2()));
- // fall through
- case 2:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,
- get_texture_src_type(stage->get_combine_rgb_source1(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB,
- get_texture_operand_type(stage->get_combine_rgb_operand1()));
- // fall through
- case 1:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,
- get_texture_src_type(stage->get_combine_rgb_source0(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB,
- get_texture_operand_type(stage->get_combine_rgb_operand0()));
- // fall through
- default:
- break;
- }
- glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA,
- get_texture_combine_type(stage->get_combine_alpha_mode()));
- switch (stage->get_num_combine_alpha_operands()) {
- case 3:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA,
- get_texture_src_type(stage->get_combine_alpha_source2(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA,
- get_texture_operand_type(stage->get_combine_alpha_operand2()));
- // fall through
- case 2:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA,
- get_texture_src_type(stage->get_combine_alpha_source1(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
- get_texture_operand_type(stage->get_combine_alpha_operand1()));
- // fall through
- case 1:
- glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA,
- get_texture_src_type(stage->get_combine_alpha_source0(),
- last_stage, last_saved_result, i));
- glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
- get_texture_operand_type(stage->get_combine_alpha_operand0()));
- // fall through
- default:
- break;
- }
- }
- } else {
- GLint glmode = get_texture_apply_mode_type(stage->get_mode());
- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
- }
- if (stage->get_saved_result()) {
- // This texture's result will be "saved" for a future stage's input.
- last_saved_result = i;
- } else {
- // This is a regular texture stage; it will be the "previous" input for
- // the next stage.
- last_stage = i;
- }
- }
- // Disable the texture stages that are no longer used.
- for (i = num_stages; i < _num_active_texture_stages; i++) {
- set_active_texture_stage(i);
- glDisable(GL_TEXTURE_2D);
- if (_supports_cube_map) {
- glDisable(GL_TEXTURE_CUBE_MAP);
- }
- #ifndef OPENGLES
- glDisable(GL_TEXTURE_1D);
- if (_supports_3d_texture) {
- glDisable(GL_TEXTURE_3D);
- }
- #endif // OPENGLES
- }
- // Save the count of texture stages for next time.
- _num_active_texture_stages = num_stages;
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Applies a white dummy texture. This is useful to bind to a texture slot
- * when a texture is missing. Also binds the default sampler to the unit.
- */
- void CLP(GraphicsStateGuardian)::
- apply_white_texture(GLuint unit) {
- set_active_texture_stage(unit);
- glBindTexture(GL_TEXTURE_2D, get_white_texture());
- // Also apply the default sampler, if there's a chance we'd applied anything
- // else.
- #ifndef OPENGLES_1
- if (_supports_sampler_objects) {
- _glBindSampler(unit, 0);
- }
- #endif
- }
- /**
- * Returns a white dummy texture. This is useful to bind to a texture slot
- * when a texture is missing.
- */
- GLuint CLP(GraphicsStateGuardian)::
- get_white_texture() {
- if (_white_texture == 0) {
- glGenTextures(1, &_white_texture);
- glBindTexture(GL_TEXTURE_2D, _white_texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- unsigned char data[] = {0xff, 0xff, 0xff, 0xff};
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data);
- }
- return _white_texture;
- }
- #ifndef NDEBUG
- /**
- * This is a special function that loads the usage textures in gl-show-
- * texture-usage mode, instead of loading the actual used textures.
- *
- * If the indicated stage_index is >= 0, then it is the particular texture
- * that is shown. Otherwise, the textures are rotated through based on
- * show_texture_usage_index.
- */
- void CLP(GraphicsStateGuardian)::
- update_show_usage_texture_bindings(int show_stage_index) {
- int num_stages = _target_texture->get_num_on_ff_stages();
- nassertv(num_stages <= _max_texture_stages &&
- _num_active_texture_stages <= _max_texture_stages);
- _texture_involves_color_scale = false;
- // First, we walk through the list of textures and pretend to render them
- // all, even though we don't actually render them, just so Panda will keep
- // track of the list of "active" textures correctly during the flash.
- int i;
- for (i = 0; i < num_stages; i++) {
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- Texture *texture = _target_texture->get_on_texture(stage);
- nassertv(texture != nullptr);
- int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
- TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
- if (tc == nullptr) {
- // Something wrong with this texture; skip it.
- break;
- }
- tc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
- }
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- // Disable all texture stages.
- for (i = 0; i < _num_active_texture_stages; i++) {
- set_active_texture_stage(i);
- #ifndef OPENGLES
- glDisable(GL_TEXTURE_1D);
- #endif // OPENGLES
- glDisable(GL_TEXTURE_2D);
- if (_supports_3d_texture) {
- #ifndef OPENGLES_1
- glDisable(GL_TEXTURE_3D);
- #endif // OPENGLES_1
- }
- if (_supports_cube_map) {
- glDisable(GL_TEXTURE_CUBE_MAP);
- }
- }
- }
- #endif
- // Save the count of texture stages for next time.
- _num_active_texture_stages = num_stages;
- if (num_stages > 0) {
- // Now, pick just one texture stage to apply.
- if (show_stage_index >= 0 && show_stage_index < num_stages) {
- i = show_stage_index;
- } else {
- i = _show_texture_usage_index % num_stages;
- }
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- Texture *texture = _target_texture->get_on_texture(stage);
- nassertv(texture != nullptr);
- // Choose the corresponding usage texture and apply it.
- set_active_texture_stage(i);
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- glEnable(GL_TEXTURE_2D);
- }
- #endif
- UsageTextureKey key(texture->get_x_size(), texture->get_y_size());
- UsageTextures::iterator ui = _usage_textures.find(key);
- GLuint index;
- if (ui == _usage_textures.end()) {
- // Need to create a new texture for this size.
- glGenTextures(1, &index);
- glBindTexture(GL_TEXTURE_2D, index);
- // TODO: this could be a lot simpler with glTexStorage2D followed by a
- // call to glClearTexImage.
- upload_usage_texture(texture->get_x_size(), texture->get_y_size());
- _usage_textures[key] = index;
- } else {
- // Just bind the previously-created texture.
- index = (*ui).second;
- glBindTexture(GL_TEXTURE_2D, index);
- }
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindTexture(GL_TEXTURE_2D, " << index << ")\n";
- }
- // TODO: glBindSampler(0) ?
- }
- report_my_gl_errors();
- }
- #endif // NDEBUG
- #ifndef NDEBUG
- /**
- * Uploads a special "usage" texture intended to be applied only in gl-show-
- * texture-usage mode, to reveal where texture memory is being spent.
- */
- void CLP(GraphicsStateGuardian)::
- upload_usage_texture(int width, int height) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "upload_usage_texture(" << width << ", " << height << ")\n";
- }
- static LColor colors[3] = {
- LColor(0.4, 0.5f, 0.8f, 1.0f), // mipmap 0: blue
- LColor(1.0f, 1.0f, 0.0f, 1.0f), // mipmap 1: yellow
- LColor(0.8f, 0.3, 0.3, 1.0f), // mipmap 2 and higher: red
- };
- // Allocate a temporary array large enough to contain the toplevel mipmap.
- uint32_t *buffer = (uint32_t *)PANDA_MALLOC_ARRAY(width * height * 4);
- int n = 0;
- while (true) {
- // Choose the color for the nth mipmap.
- LColor c = colors[min(n, 2)];
- // A simple union to store the colors values bytewise, and get the answer
- // wordwise, independently of machine byte-ordernig.
- union {
- struct {
- unsigned char r, g, b, a;
- } b;
- uint32_t w;
- } store;
- store.b.r = (unsigned char)(c[0] * 255.0f);
- store.b.g = (unsigned char)(c[1] * 255.0f);
- store.b.b = (unsigned char)(c[2] * 255.0f);
- store.b.a = 0xff;
- // Fill in the array.
- int num_pixels = width * height;
- for (int p = 0; p < num_pixels; ++p) {
- buffer[p] = store.w;
- }
- glTexImage2D(GL_TEXTURE_2D, n, GL_RGBA, width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, buffer);
- if (width == 1 && height == 1) {
- // That was the last mipmap level.
- break;
- }
- width = max(width >> 1, 1);
- height = max(height >> 1, 1);
- ++n;
- }
- PANDA_FREE_ARRAY(buffer);
- }
- #endif // NDEBUG
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- disable_standard_texture_bindings() {
- // Disable the texture stages that are no longer used.
- for (int i = 0; i < _num_active_texture_stages; i++) {
- set_active_texture_stage(i);
- #ifndef OPENGLES
- glDisable(GL_TEXTURE_1D);
- #endif // OPENGLES
- glDisable(GL_TEXTURE_2D);
- if (_supports_3d_texture) {
- #ifndef OPENGLES_1
- glDisable(GL_TEXTURE_3D);
- #endif // OPENGLES_1
- }
- if (_supports_cube_map) {
- glDisable(GL_TEXTURE_CUBE_MAP);
- }
- }
- _num_active_texture_stages = 0;
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_tex_matrix() {
- nassertv(_num_active_texture_stages <= _max_texture_stages);
- for (int i = 0; i < _num_active_texture_stages; i++) {
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- set_active_texture_stage(i);
- glMatrixMode(GL_TEXTURE);
- const TexMatrixAttrib *target_tex_matrix;
- _target_rs->get_attrib_def(target_tex_matrix);
- if (target_tex_matrix->has_stage(stage)) {
- call_glLoadMatrix(target_tex_matrix->get_mat(stage));
- } else {
- glLoadIdentity();
- // For some reason, the glLoadIdentity() call doesn't work on my Dell
- // laptop's IBM OpenGL driver, when used in conjunction with glTexGen(),
- // below. But explicitly loading an identity matrix does work. But
- // this buggy-driver workaround might have other performance
- // implications, so I leave it out.
- // call_glLoadMatrix(LMatrix4::ident_mat());
- }
- }
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- #ifdef SUPPORT_FIXED_FUNCTION
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_tex_gen() {
- nassertv(_num_active_texture_stages <= _max_texture_stages);
- // These are passed in for the four OBJECT_PLANE or EYE_PLANE values; they
- // effectively define an identity matrix that maps the spatial coordinates
- // one-for-one to UV's. If you want a mapping other than identity, use a
- // TexMatrixAttrib (or a TexProjectorEffect).
- #ifndef OPENGLES
- static const PN_stdfloat s_data[4] = { 1, 0, 0, 0 };
- static const PN_stdfloat t_data[4] = { 0, 1, 0, 0 };
- static const PN_stdfloat r_data[4] = { 0, 0, 1, 0 };
- static const PN_stdfloat q_data[4] = { 0, 0, 0, 1 };
- #endif
- _tex_gen_modifies_mat = false;
- for (int i = 0; i < _num_active_texture_stages; i++) {
- set_active_texture_stage(i);
- if (_supports_point_sprite) {
- #ifdef OPENGLES
- glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_FALSE);
- #else
- glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_FALSE);
- #endif // OPENGLES
- }
- #ifndef OPENGLES // TexGen not supported by OpenGL ES.
- glDisable(GL_TEXTURE_GEN_S);
- glDisable(GL_TEXTURE_GEN_T);
- glDisable(GL_TEXTURE_GEN_R);
- glDisable(GL_TEXTURE_GEN_Q);
- TextureStage *stage = _target_texture->get_on_ff_stage(i);
- TexGenAttrib::Mode mode = _target_tex_gen->get_mode(stage);
- switch (mode) {
- case TexGenAttrib::M_off:
- case TexGenAttrib::M_unused2:
- break;
- case TexGenAttrib::M_eye_sphere_map:
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- break;
- case TexGenAttrib::M_eye_cube_map:
- if (_supports_cube_map) {
- // We need to rotate the normals out of GL's coordinate system and
- // into the user's coordinate system. We do this by composing a
- // transform onto the texture matrix.
- LMatrix4 mat = _inv_cs_transform->get_mat();
- mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
- glMatrixMode(GL_TEXTURE);
- GLPf(MultMatrix)(mat.get_data());
- // Now we need to reset the texture matrix next time around to undo
- // this.
- _tex_gen_modifies_mat = true;
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- }
- break;
- case TexGenAttrib::M_world_cube_map:
- if (_supports_cube_map) {
- // We dynamically transform normals from eye space to world space by
- // applying the appropriate rotation transform to the current texture
- // matrix. Unlike M_world_position, we can't achieve this effect by
- // monkeying with the modelview transform, since the current modelview
- // doesn't affect GL_REFLECTION_MAP.
- CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
- LMatrix4 mat = camera_transform->get_mat();
- mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
- glMatrixMode(GL_TEXTURE);
- GLPf(MultMatrix)(mat.get_data());
- // Now we need to reset the texture matrix next time around to undo
- // this.
- _tex_gen_modifies_mat = true;
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- }
- break;
- case TexGenAttrib::M_eye_normal:
- if (_supports_cube_map) {
- // We need to rotate the normals out of GL's coordinate system and
- // into the user's coordinate system. We do this by composing a
- // transform onto the texture matrix.
- LMatrix4 mat = _inv_cs_transform->get_mat();
- mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
- glMatrixMode(GL_TEXTURE);
- GLPf(MultMatrix)(mat.get_data());
- // Now we need to reset the texture matrix next time around to undo
- // this.
- _tex_gen_modifies_mat = true;
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- }
- break;
- case TexGenAttrib::M_world_normal:
- if (_supports_cube_map) {
- // We dynamically transform normals from eye space to world space by
- // applying the appropriate rotation transform to the current texture
- // matrix. Unlike M_world_position, we can't achieve this effect by
- // monkeying with the modelview transform, since the current modelview
- // doesn't affect GL_NORMAL_MAP.
- CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
- LMatrix4 mat = camera_transform->get_mat();
- mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
- glMatrixMode(GL_TEXTURE);
- GLPf(MultMatrix)(mat.get_data());
- // Now we need to reset the texture matrix next time around to undo
- // this.
- _tex_gen_modifies_mat = true;
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- }
- break;
- case TexGenAttrib::M_eye_position:
- // To represent eye position correctly, we need to temporarily load the
- // coordinate-system transform.
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- call_glLoadMatrix(_cs_transform->get_mat());
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- GLPfv(TexGen)(GL_S, GL_EYE_PLANE, s_data);
- GLPfv(TexGen)(GL_T, GL_EYE_PLANE, t_data);
- GLPfv(TexGen)(GL_R, GL_EYE_PLANE, r_data);
- GLPfv(TexGen)(GL_Q, GL_EYE_PLANE, q_data);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- glEnable(GL_TEXTURE_GEN_Q);
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- break;
- case TexGenAttrib::M_world_position:
- // We achieve world position coordinates by using the eye position mode,
- // and loading the transform of the root node--thus putting the "eye" at
- // the root.
- {
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- CPT(TransformState) root_transform = _cs_transform->compose(_scene_setup->get_world_transform());
- call_glLoadMatrix(root_transform->get_mat());
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
- GLPfv(TexGen)(GL_S, GL_EYE_PLANE, s_data);
- GLPfv(TexGen)(GL_T, GL_EYE_PLANE, t_data);
- GLPfv(TexGen)(GL_R, GL_EYE_PLANE, r_data);
- GLPfv(TexGen)(GL_Q, GL_EYE_PLANE, q_data);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- glEnable(GL_TEXTURE_GEN_Q);
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- }
- break;
- case TexGenAttrib::M_point_sprite:
- if (_supports_point_sprite) {
- #ifdef OPENGLES
- glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
- #else
- glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
- #endif
- }
- break;
- case TexGenAttrib::M_constant:
- // To generate a constant UV(w) coordinate everywhere, we use EYE_LINEAR
- // mode, but we construct a special matrix that flattens the vertex
- // position to zero and then adds our desired value.
- {
- const LTexCoord3 &v = _target_tex_gen->get_constant_value(stage);
- glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
- LVecBase4 s(0.0f, 0.0f, 0.0f, v[0]);
- LVecBase4 t(0.0f, 0.0f, 0.0f, v[1]);
- LVecBase4 r(0.0f, 0.0f, 0.0f, v[2]);
- GLPfv(TexGen)(GL_S, GL_OBJECT_PLANE, s.get_data());
- GLPfv(TexGen)(GL_T, GL_OBJECT_PLANE, t.get_data());
- GLPfv(TexGen)(GL_R, GL_OBJECT_PLANE, r.get_data());
- GLPfv(TexGen)(GL_Q, GL_OBJECT_PLANE, q_data);
- glEnable(GL_TEXTURE_GEN_S);
- glEnable(GL_TEXTURE_GEN_T);
- glEnable(GL_TEXTURE_GEN_R);
- glEnable(GL_TEXTURE_GEN_Q);
- }
- break;
- case TexGenAttrib::M_unused:
- break;
- }
- #endif // OPENGLES
- }
- bool got_point_sprites = _supports_point_sprite &&
- (_target_tex_gen->get_geom_rendering(Geom::GR_point) & GeomEnums::GR_point_sprite) != 0;
- if (got_point_sprites != _tex_gen_point_sprite) {
- _tex_gen_point_sprite = got_point_sprites;
- #ifdef OPENGLES
- if (_tex_gen_point_sprite) {
- glEnable(GL_POINT_SPRITE_OES);
- } else {
- glDisable(GL_POINT_SPRITE_OES);
- }
- #else
- if (_tex_gen_point_sprite) {
- glEnable(GL_POINT_SPRITE_ARB);
- } else {
- glDisable(GL_POINT_SPRITE_ARB);
- }
- #endif // OPENGLES
- }
- report_my_gl_errors();
- }
- #endif // SUPPORT_FIXED_FUNCTION
- /**
- * Specifies the texture parameters. Returns true if the texture may need to
- * be reloaded. Pass non-NULL sampler argument to use different sampler
- * settings.
- */
- bool CLP(GraphicsStateGuardian)::
- specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) {
- #ifndef OPENGLES
- nassertr(gtc->_handle == 0 /* can't modify tex with active handle */, false);
- #endif
- Texture *tex = gtc->get_texture();
- GLenum target = get_texture_target(tex->get_texture_type());
- if (target == GL_NONE) {
- // Unsupported target (e.g. 3-d texturing on GL 1.1).
- return false;
- }
- #ifndef OPENGLES
- if (target == GL_TEXTURE_BUFFER) {
- // Buffer textures may not receive texture parameters.
- return false;
- }
- #endif // OPENGLES
- // Record the active sampler settings.
- gtc->_active_sampler = sampler;
- glTexParameteri(target, GL_TEXTURE_WRAP_S,
- get_texture_wrap_mode(sampler.get_wrap_u()));
- #ifndef OPENGLES
- if (target != GL_TEXTURE_1D)
- #endif
- {
- glTexParameteri(target, GL_TEXTURE_WRAP_T,
- get_texture_wrap_mode(sampler.get_wrap_v()));
- }
- #ifndef OPENGLES_1
- if (target == GL_TEXTURE_3D) {
- glTexParameteri(target, GL_TEXTURE_WRAP_R,
- get_texture_wrap_mode(sampler.get_wrap_w()));
- }
- #endif
- #ifndef OPENGLES
- LColor border_color = sampler.get_border_color();
- call_glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border_color);
- #endif // OPENGLES
- SamplerState::FilterType minfilter = sampler.get_effective_minfilter();
- SamplerState::FilterType magfilter = sampler.get_effective_magfilter();
- bool uses_mipmaps = SamplerState::is_mipmap(minfilter) && !gl_ignore_mipmaps;
- #ifndef NDEBUG
- if (gl_force_mipmaps) {
- minfilter = SamplerState::FT_linear_mipmap_linear;
- magfilter = SamplerState::FT_linear;
- uses_mipmaps = true;
- }
- #endif
- if (!tex->might_have_ram_image()) {
- // If it's a dynamically generated texture (that is, the RAM image isn't
- // available so it didn't pass through the CPU), we should enable GL-
- // generated mipmaps if we can.
- if (!_supports_generate_mipmap) {
- // However, if the GPU doesn't support mipmap generation, we have to
- // turn it off.
- uses_mipmaps = false;
- }
- }
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
- get_texture_filter_type(minfilter, !uses_mipmaps));
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER,
- get_texture_filter_type(magfilter, true));
- // Set anisotropic filtering.
- if (_supports_anisotropy) {
- PN_stdfloat anisotropy = sampler.get_effective_anisotropic_degree();
- anisotropy = min(anisotropy, _max_anisotropy);
- anisotropy = max(anisotropy, (PN_stdfloat)1.0);
- glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
- }
- #ifndef OPENGLES_1
- if (tex->get_format() == Texture::F_depth_stencil ||
- tex->get_format() == Texture::F_depth_component ||
- tex->get_format() == Texture::F_depth_component16 ||
- tex->get_format() == Texture::F_depth_component24 ||
- tex->get_format() == Texture::F_depth_component32) {
- #ifdef SUPPORT_FIXED_FUNCTION
- if (has_fixed_function_pipeline()) {
- glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
- }
- #endif
- if (_supports_shadow_filter) {
- if ((sampler.get_magfilter() == SamplerState::FT_shadow) ||
- (sampler.get_minfilter() == SamplerState::FT_shadow)) {
- glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
- glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
- } else {
- glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
- glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
- }
- }
- }
- #endif
- #ifndef OPENGLES_1
- if (_supports_texture_lod) {
- glTexParameterf(target, GL_TEXTURE_MIN_LOD, sampler.get_min_lod());
- glTexParameterf(target, GL_TEXTURE_MAX_LOD, sampler.get_max_lod());
- }
- #endif
- #ifndef OPENGLES
- if (_supports_texture_lod_bias) {
- glTexParameterf(target, GL_TEXTURE_LOD_BIAS, sampler.get_lod_bias());
- }
- #endif
- report_my_gl_errors();
- if (uses_mipmaps && !gtc->_uses_mipmaps) {
- // Suddenly we require mipmaps. This means the texture may need
- // reloading.
- return true;
- }
- return false;
- }
- /**
- * Updates OpenGL with the current information for this texture, and makes it
- * the current texture available for rendering.
- */
- bool CLP(GraphicsStateGuardian)::
- apply_texture(CLP(TextureContext) *gtc) {
- gtc->set_active(true);
- GLenum target = get_texture_target(gtc->get_texture()->get_texture_type());
- if (target == GL_NONE) {
- return false;
- }
- if (gtc->_target != target) {
- // The target has changed. That means we have to re-bind a new texture
- // object.
- gtc->reset_data();
- gtc->_target = target;
- }
- glBindTexture(target, gtc->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *gtc->get_texture() << "\n";
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Updates OpenGL with the current information for this sampler, and makes it
- * the current sampler available for rendering. Use NULL to unbind the
- * sampler.
- *
- * If the GSG doesn't support sampler objects, the sampler settings are
- * applied to the given texture context instead.
- */
- bool CLP(GraphicsStateGuardian)::
- apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc) {
- #ifndef OPENGLES_1
- if (_supports_sampler_objects) {
- // We support sampler objects. Prepare the sampler object and bind it to
- // the indicated texture unit.
- SamplerContext *sc = sampler.prepare_now(get_prepared_objects(), this);
- nassertr(sc != nullptr, false);
- CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
- gsc->enqueue_lru(&_prepared_objects->_sampler_object_lru);
- _glBindSampler(unit, gsc->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "glBindSampler(" << unit << ", "
- << gsc->_index << "): " << sampler << "\n";
- }
- } else
- #endif // !OPENGLES_1
- {
- // We don't support sampler objects. We'll have to bind the texture and
- // change the texture parameters if they don't match.
- if (gtc->_active_sampler != sampler) {
- set_active_texture_stage(unit);
- apply_texture(gtc);
- specify_texture(gtc, sampler);
- }
- }
- if (sampler.uses_mipmaps() && !gtc->_uses_mipmaps && !gl_ignore_mipmaps) {
- // The texture wasn't created with mipmaps, but we are trying to sample it
- // with mipmaps. We will need to reload it.
- GLCAT.info()
- << "reloading texture " << gtc->get_texture()->get_name()
- << " with mipmaps\n";
- apply_texture(gtc);
- gtc->mark_needs_reload();
- bool okflag = upload_texture(gtc, false, true);
- if (!okflag) {
- GLCAT.error()
- << "Could not load " << *gtc->get_texture() << "\n";
- return false;
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Uploads the entire texture image to OpenGL, including all pages.
- *
- * The return value is true if successful, or false if the texture has no
- * image.
- */
- bool CLP(GraphicsStateGuardian)::
- upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
- PStatGPUTimer timer(this, _load_texture_pcollector);
- Texture *tex = gtc->get_texture();
- if (_effective_incomplete_render && !force) {
- bool has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
- if (!has_image && tex->might_have_ram_image() &&
- tex->has_simple_ram_image() &&
- !_loader.is_null()) {
- // If we don't have the texture data right now, go get it, but in the
- // meantime load a temporary simple image in its place.
- async_reload_texture(gtc);
- has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
- if (!has_image) {
- if (gtc->was_simple_image_modified()) {
- return upload_simple_texture(gtc);
- }
- return true;
- }
- }
- }
- CPTA_uchar image;
- if (_supports_compressed_texture) {
- image = tex->get_ram_image();
- } else {
- image = tex->get_uncompressed_ram_image();
- }
- Texture::CompressionMode image_compression;
- if (image.is_null()) {
- image_compression = Texture::CM_off;
- } else {
- image_compression = tex->get_ram_image_compression();
- }
- if (!get_supports_compressed_texture_format(image_compression)) {
- image = tex->get_uncompressed_ram_image();
- image_compression = Texture::CM_off;
- // If this triggers, Panda cannot decompress the texture. Compile with
- // libsquish support or precompress the texture.
- nassertr(!image.is_null(), false);
- }
- int mipmap_bias = 0;
- int width = tex->get_x_size();
- int height = tex->get_y_size();
- int depth = tex->get_z_size();
- // If we'll use immutable texture storage, we have to pick a sized image
- // format.
- bool force_sized = (gl_immutable_texture_storage && _supports_tex_storage) ||
- (tex->get_texture_type() == Texture::TT_buffer_texture);
- GLint internal_format = get_internal_image_format(tex, force_sized);
- GLint external_format = get_external_image_format(tex);
- GLenum component_type = get_component_type(tex->get_component_type());
- if (GLCAT.is_debug()) {
- if (image.is_null()) {
- GLCAT.debug()
- << "loading texture with NULL image";
- } else if (image_compression != Texture::CM_off) {
- GLCAT.debug()
- << "loading pre-compressed texture";
- } else if (is_compressed_format(internal_format)) {
- GLCAT.debug()
- << "loading compressed texture";
- } else {
- GLCAT.debug()
- << "loading uncompressed texture";
- }
- GLCAT.debug(false) << " " << tex->get_name() << "\n";
- }
- // Ensure that the texture fits within the GL's specified limits. Need to
- // split dimensions because of texture arrays
- int max_dimension_x;
- int max_dimension_y;
- int max_dimension_z;
- switch (tex->get_texture_type()) {
- case Texture::TT_3d_texture:
- max_dimension_x = _max_3d_texture_dimension;
- max_dimension_y = _max_3d_texture_dimension;
- max_dimension_z = _max_3d_texture_dimension;
- break;
- case Texture::TT_cube_map:
- max_dimension_x = _max_cube_map_dimension;
- max_dimension_y = _max_cube_map_dimension;
- max_dimension_z = 6;
- break;
- case Texture::TT_2d_texture_array:
- max_dimension_x = _max_texture_dimension;
- max_dimension_y = _max_texture_dimension;
- max_dimension_z = _max_2d_texture_array_layers;
- break;
- case Texture::TT_cube_map_array:
- max_dimension_x = _max_texture_dimension;
- max_dimension_y = _max_texture_dimension;
- max_dimension_z = int(_max_2d_texture_array_layers / 6) * 6;
- break;
- case Texture::TT_buffer_texture:
- max_dimension_x = _max_buffer_texture_size;
- max_dimension_y = 1;
- max_dimension_z = 1;
- break;
- default:
- max_dimension_x = _max_texture_dimension;
- max_dimension_y = _max_texture_dimension;
- max_dimension_z = 1;
- }
- if (max_dimension_x == 0 || max_dimension_y == 0 || max_dimension_z == 0) {
- // Guess this GL doesn't support cube mapping3d textures2d texture arrays.
- report_my_gl_errors();
- return false;
- }
- // If it doesn't fit, we have to reduce it on-the-fly. We do this by
- // incrementing the mipmap_bias, so we're effectively loading a lower mipmap
- // level. This requires generating the mipmaps on the CPU if they haven't
- // already been generated. It would have been better if the user had
- // specified max-texture-dimension to reduce the texture at load time
- // instead; of course, the user doesn't always know ahead of time what the
- // hardware limits are.
- if ((max_dimension_x > 0 && max_dimension_y > 0 && max_dimension_z > 0) &&
- image_compression == Texture::CM_off) {
- while (tex->get_expected_mipmap_x_size(mipmap_bias) > max_dimension_x ||
- tex->get_expected_mipmap_y_size(mipmap_bias) > max_dimension_y ||
- tex->get_expected_mipmap_z_size(mipmap_bias) > max_dimension_z) {
- ++mipmap_bias;
- }
- if (mipmap_bias >= tex->get_num_ram_mipmap_images()) {
- // We need to generate some more mipmap images.
- if (tex->has_ram_image()) {
- tex->generate_ram_mipmap_images();
- if (mipmap_bias >= tex->get_num_ram_mipmap_images()) {
- // It didn't work. Send the smallest we've got, and hope for the
- // best.
- mipmap_bias = tex->get_num_ram_mipmap_images() - 1;
- }
- }
- }
- width = tex->get_expected_mipmap_x_size(mipmap_bias);
- height = tex->get_expected_mipmap_y_size(mipmap_bias);
- depth = tex->get_expected_mipmap_z_size(mipmap_bias);
- if (mipmap_bias != 0) {
- GLCAT.info()
- << "Reducing image " << tex->get_name()
- << " from " << tex->get_x_size() << " x " << tex->get_y_size()
- << " x " << tex->get_z_size() << " to "
- << width << " x " << height << " x " << depth << "\n";
- }
- }
- if (image_compression != Texture::CM_off) {
- #ifndef OPENGLES
- switch (tex->get_effective_quality_level()) {
- case Texture::QL_fastest:
- glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
- break;
- case Texture::QL_default:
- case Texture::QL_normal:
- glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
- break;
- case Texture::QL_best:
- glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
- break;
- }
- #endif
- }
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- GLenum target = get_texture_target(tex->get_texture_type());
- uses_mipmaps = (uses_mipmaps && !gl_ignore_mipmaps) || gl_force_mipmaps;
- #ifndef OPENGLES
- if (target == GL_TEXTURE_BUFFER) {
- // Buffer textures may not have mipmaps.
- uses_mipmaps = false;
- }
- #endif // OPENGLES
- bool needs_reload = false;
- if (!gtc->_has_storage ||
- gtc->_uses_mipmaps != uses_mipmaps ||
- gtc->_internal_format != internal_format ||
- gtc->_width != width ||
- gtc->_height != height ||
- gtc->_depth != depth) {
- // We need to reload a new GL Texture object.
- needs_reload = true;
- if (_use_object_labels) {
- // This seems like a good time to assign a label for the debug messages.
- const string &name = tex->get_name();
- _glObjectLabel(GL_TEXTURE, gtc->_index, name.size(), name.data());
- }
- }
- if (needs_reload && gtc->_immutable) {
- GLCAT.info() << "Attempt to modify texture with immutable storage, recreating texture.\n";
- gtc->reset_data();
- glBindTexture(target, gtc->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
- }
- }
- #ifndef OPENGLES
- if (target == GL_TEXTURE_BUFFER) {
- // Buffer textures don't support mipmappping.
- gtc->_generate_mipmaps = false;
- if (gtc->_buffer == 0) {
- // The buffer object wasn't created yet.
- _glGenBuffers(1, >c->_buffer);
- _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer);
- _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer);
- needs_reload = true;
- } else {
- _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer);
- if (gtc->_internal_format != internal_format) {
- _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer);
- }
- }
- } else
- #endif // !OPENGLES
- if (needs_reload) {
- // Figure out whether mipmaps will be generated by the GPU or by Panda (or
- // not at all), and how many mipmap levels should be created.
- gtc->_generate_mipmaps = false;
- int num_levels = 1;
- CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias);
- bool can_generate = _supports_generate_mipmap;
- #if defined(OPENGLES) && !defined(OPENGLES_1)
- // OpenGL ES doesn't support generating mipmaps for sRGB textures, so we
- // have to do this on the CPU, unless we have a special extension.
- if (internal_format == GL_SRGB8 || internal_format == GL_SRGB8_ALPHA8) {
- can_generate = has_extension("GL_NV_generate_mipmap_sRGB");
- }
- #endif
- if (image.is_null()) {
- // We don't even have a RAM image, so we have no choice but to let
- // mipmaps be generated on the GPU.
- if (uses_mipmaps) {
- if (can_generate) {
- num_levels = tex->get_expected_num_mipmap_levels() - mipmap_bias;
- gtc->_generate_mipmaps = true;
- } else {
- // If it can't, do without mipmaps.
- num_levels = 1;
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- }
- }
- } else {
- if (uses_mipmaps) {
- num_levels = tex->get_num_ram_mipmap_images() - mipmap_bias;
- if (num_levels <= 1) {
- // No RAM mipmap levels available. Should we generate some?
- if (!can_generate || !driver_generate_mipmaps ||
- image_compression != Texture::CM_off) {
- // Yes, the GL can't or won't generate them, so we need to. Note
- // that some drivers (nVidia) will *corrupt memory* if you ask
- // them to generate mipmaps for a pre-compressed texture.
- tex->generate_ram_mipmap_images();
- num_levels = tex->get_num_ram_mipmap_images() - mipmap_bias;
- }
- }
- if (num_levels <= 1) {
- // We don't have mipmap levels in RAM. Ask the GL to generate them
- // if it can.
- if (can_generate) {
- num_levels = tex->get_expected_num_mipmap_levels() - mipmap_bias;
- gtc->_generate_mipmaps = true;
- } else {
- // If it can't, do without mipmaps.
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- num_levels = 1;
- }
- }
- }
- }
- if (_supports_texture_max_level) {
- // By the time we get here, we have a pretty good prediction for the
- // number of mipmaps we're going to have, so tell the GL that's all it's
- // going to get.
- glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, num_levels - 1);
- }
- #ifndef OPENGLES_2
- if (gtc->_generate_mipmaps && _glGenerateMipmap == nullptr) {
- // The old, deprecated way to generate mipmaps.
- glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
- }
- #endif
- #ifndef OPENGLES
- if (!_supports_luminance_texture) {
- // Do we need to apply a swizzle mask to emulate these deprecated texture
- // formats?
- switch (tex->get_format()) {
- case Texture::F_alpha:
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_ZERO);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_ZERO);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_ZERO);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_RED);
- break;
- case Texture::F_luminance:
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_ONE);
- break;
- case Texture::F_luminance_alpha:
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED);
- glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, GL_GREEN);
- break;
- default:
- break;
- }
- }
- #endif
- // Allocate immutable storage for the texture, after which we can subload
- // it. Pre-allocating storage using glTexStorage is more efficient than
- // using glTexImage to load all of the individual images one by one later,
- // but we are not allowed to change the texture size or number of mipmap
- // levels after this point.
- if (gl_immutable_texture_storage && _supports_tex_storage && !gtc->_has_storage) {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "allocating storage for texture " << tex->get_name() << ", " << width
- << " x " << height << " x " << depth << ", mipmaps " << num_levels
- << ", uses_mipmaps = " << uses_mipmaps << "\n";
- }
- switch (tex->get_texture_type()) {
- case Texture::TT_buffer_texture:
- // Won't get here, but squelch compiler warning
- case Texture::TT_1d_texture:
- _glTexStorage1D(target, num_levels, internal_format, width);
- break;
- case Texture::TT_2d_texture:
- case Texture::TT_cube_map:
- case Texture::TT_1d_texture_array:
- _glTexStorage2D(target, num_levels, internal_format, width, height);
- break;
- case Texture::TT_3d_texture:
- case Texture::TT_2d_texture_array:
- case Texture::TT_cube_map_array:
- _glTexStorage3D(target, num_levels, internal_format, width, height, depth);
- break;
- }
- gtc->_has_storage = true;
- gtc->_immutable = true;
- gtc->_uses_mipmaps = uses_mipmaps;
- gtc->_internal_format = internal_format;
- gtc->_width = width;
- gtc->_height = height;
- gtc->_depth = depth;
- gtc->update_data_size_bytes(get_texture_memory_size(gtc));
- needs_reload = false;
- }
- } else {
- // Maybe we need to generate mipmaps on the CPU.
- if (!image.is_null() && uses_mipmaps) {
- if (tex->get_num_ram_mipmap_images() - mipmap_bias <= 1) {
- // No RAM mipmap levels available. Should we generate some?
- if (!_supports_generate_mipmap || !driver_generate_mipmaps ||
- image_compression != Texture::CM_off) {
- // Yes, the GL can't or won't generate them, so we need to. Note
- // that some drivers (nVidia) will *corrupt memory* if you ask them
- // to generate mipmaps for a pre-compressed texture.
- tex->generate_ram_mipmap_images();
- }
- }
- }
- }
- bool success = true;
- if (tex->get_texture_type() == Texture::TT_cube_map) {
- // A cube map must load six different 2-d images (which are stored as the
- // six pages of the system ram image).
- if (!_supports_cube_map) {
- report_my_gl_errors();
- return false;
- }
- nassertr(target == GL_TEXTURE_CUBE_MAP, false);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
- internal_format, external_format, component_type,
- true, 0, image_compression);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
- internal_format, external_format, component_type,
- true, 1, image_compression);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
- internal_format, external_format, component_type,
- true, 2, image_compression);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
- internal_format, external_format, component_type,
- true, 3, image_compression);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
- internal_format, external_format, component_type,
- true, 4, image_compression);
- success = success && upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias,
- GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
- internal_format, external_format, component_type,
- true, 5, image_compression);
- } else {
- // Any other kind of texture can be loaded all at once.
- success = upload_texture_image
- (gtc, needs_reload, uses_mipmaps, mipmap_bias, target,
- target, internal_format, external_format,
- component_type, false, 0, image_compression);
- }
- if (gtc->_generate_mipmaps && _glGenerateMipmap != nullptr &&
- !image.is_null()) {
- // We uploaded an image; we may need to generate mipmaps.
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "generating mipmaps for texture " << tex->get_name() << ", "
- << width << " x " << height << " x " << depth
- << ", uses_mipmaps = " << uses_mipmaps << "\n";
- }
- _glGenerateMipmap(target);
- }
- maybe_gl_finish();
- if (success) {
- if (needs_reload) {
- gtc->_has_storage = true;
- gtc->_uses_mipmaps = uses_mipmaps;
- gtc->_internal_format = internal_format;
- gtc->_width = width;
- gtc->_height = height;
- gtc->_depth = depth;
- gtc->update_data_size_bytes(get_texture_memory_size(gtc));
- }
- nassertr(gtc->_has_storage, false);
- if (tex->get_post_load_store_cache()) {
- tex->set_post_load_store_cache(false);
- // OK, get the RAM image, and save it in a BamCache record.
- if (do_extract_texture_data(gtc)) {
- if (tex->has_ram_image()) {
- BamCache *cache = BamCache::get_global_ptr();
- PT(BamCacheRecord) record = cache->lookup(tex->get_fullpath(), "txo");
- if (record != nullptr) {
- record->set_data(tex, tex);
- cache->store(record);
- }
- }
- }
- }
- GraphicsEngine *engine = get_engine();
- nassertr(engine != nullptr, false);
- engine->texture_uploaded(tex);
- gtc->mark_loaded();
- report_my_gl_errors();
- return true;
- }
- report_my_gl_errors();
- return false;
- }
- /**
- * Loads a texture image, or one page of a cube map image, from system RAM to
- * texture memory.
- *
- * texture_target is normally the same thing as page_target; both represent
- * the GL target onto which the texture image is loaded, e.g. GL_TEXTURE_1D,
- * GL_TEXTURE_2D, etc. The only time they may differ is in the case of cube
- * mapping, in which case texture_target will be target for the overall
- * texture, e.g. GL_TEXTURE_CUBE_MAP, and page_target will be the target for
- * this particular page, e.g. GL_TEXTURE_CUBE_MAP_POSITIVE_X.
- */
- bool CLP(GraphicsStateGuardian)::
- upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
- bool uses_mipmaps, int mipmap_bias,
- GLenum texture_target, GLenum page_target,
- GLint internal_format,
- GLint external_format, GLenum component_type,
- bool one_page_only, int z,
- Texture::CompressionMode image_compression) {
- // Make sure the error stack is cleared out before we begin.
- clear_my_gl_errors();
- if (texture_target == GL_NONE) {
- // Unsupported target (e.g. 3-d texturing on GL 1.1).
- return false;
- }
- if (image_compression != Texture::CM_off && !_supports_compressed_texture) {
- return false;
- }
- Texture *tex = gtc->get_texture();
- nassertr(tex != nullptr, false);
- CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias);
- int width = tex->get_expected_mipmap_x_size(mipmap_bias);
- int height = tex->get_expected_mipmap_y_size(mipmap_bias);
- int depth = tex->get_expected_mipmap_z_size(mipmap_bias);
- // Determine the number of images to upload.
- int num_levels = mipmap_bias + 1;
- if (uses_mipmaps) {
- num_levels = tex->get_expected_num_mipmap_levels();
- }
- int num_ram_mipmap_levels = 0;
- if (!image.is_null()) {
- if (uses_mipmaps) {
- num_ram_mipmap_levels = tex->get_num_ram_mipmap_images();
- } else {
- num_ram_mipmap_levels = 1;
- }
- }
- #ifndef OPENGLES_1
- if (needs_reload || num_ram_mipmap_levels > 0) {
- // Make sure that any incoherent writes to this texture have been synced.
- if (gtc->needs_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT)) {
- issue_memory_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
- }
- }
- #endif
- if (!needs_reload) {
- // Try to subload the image over the existing GL Texture object, possibly
- // saving on texture memory fragmentation.
- if (GLCAT.is_debug()) {
- if (num_ram_mipmap_levels == 0) {
- if (tex->has_clear_color()) {
- GLCAT.debug()
- << "clearing texture " << tex->get_name() << ", "
- << width << " x " << height << " x " << depth << ", z = " << z
- << ", uses_mipmaps = " << uses_mipmaps << ", clear_color = "
- << tex->get_clear_color() << "\n";
- } else {
- GLCAT.debug()
- << "not loading NULL image for texture " << tex->get_name()
- << ", " << width << " x " << height << " x " << depth
- << ", z = " << z << ", uses_mipmaps = " << uses_mipmaps << "\n";
- }
- } else {
- GLCAT.debug()
- << "updating image data of texture " << tex->get_name()
- << ", " << width << " x " << height << " x " << depth
- << ", z = " << z << ", mipmaps " << num_ram_mipmap_levels
- << ", uses_mipmaps = " << uses_mipmaps << "\n";
- }
- }
- for (int n = mipmap_bias; n < num_levels; ++n) {
- // we grab the mipmap pointer first, if it is NULL we grab the normal
- // mipmap image pointer which is a PTA_uchar
- const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n);
- CPTA_uchar ptimage;
- if (image_ptr == nullptr) {
- ptimage = tex->get_ram_mipmap_image(n);
- if (ptimage.is_null()) {
- if (n < num_ram_mipmap_levels) {
- // We were told we'd have this many RAM mipmap images, but we
- // don't. Raise a warning.
- GLCAT.warning()
- << "No mipmap level " << n << " defined for " << tex->get_name()
- << "\n";
- break;
- }
- if (tex->has_clear_color()) {
- // The texture has a clear color, so we should fill this mipmap
- // level to a solid color.
- #ifndef OPENGLES
- if (texture_target != GL_TEXTURE_BUFFER) {
- if (_supports_clear_texture) {
- // We can do that with the convenient glClearTexImage
- // function.
- vector_uchar clear_data = tex->get_clear_data();
- _glClearTexImage(gtc->_index, n - mipmap_bias, external_format,
- component_type, (void *)&clear_data[0]);
- continue;
- }
- } else {
- if (_supports_clear_buffer) {
- // For buffer textures we need to clear the underlying
- // storage.
- vector_uchar clear_data = tex->get_clear_data();
- _glClearBufferData(GL_TEXTURE_BUFFER, internal_format, external_format,
- component_type, (const void *)&clear_data[0]);
- continue;
- }
- }
- #endif // OPENGLES
- // Ask the Texture class to create the mipmap level in RAM. It'll
- // fill it in with the correct clear color, which we can then
- // upload.
- ptimage = tex->make_ram_mipmap_image(n);
- } else {
- // No clear color and no more images.
- break;
- }
- }
- image_ptr = ptimage;
- }
- PTA_uchar bgr_image;
- size_t view_size = tex->get_ram_mipmap_view_size(n);
- if (image_ptr != nullptr) {
- const unsigned char *orig_image_ptr = image_ptr;
- image_ptr += view_size * gtc->get_view();
- if (one_page_only) {
- view_size = tex->get_ram_mipmap_page_size(n);
- image_ptr += view_size * z;
- }
- nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false);
- if (image_compression == Texture::CM_off) {
- // If the GL doesn't claim to support BGR, we may have to reverse
- // the component ordering of the image.
- image_ptr = fix_component_ordering(bgr_image, image_ptr, view_size,
- external_format, tex);
- }
- }
- int width = tex->get_expected_mipmap_x_size(n);
- int height = tex->get_expected_mipmap_y_size(n);
- #ifndef OPENGLES_1
- int depth = tex->get_expected_mipmap_z_size(n);
- #endif
- #ifdef DO_PSTATS
- _data_transferred_pcollector.add_level(view_size);
- #endif
- switch (texture_target) {
- #ifndef OPENGLES_1
- case GL_TEXTURE_3D:
- if (_supports_3d_texture) {
- if (image_compression == Texture::CM_off) {
- _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
- external_format, view_size, image_ptr);
- }
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES_1
- #ifndef OPENGLES
- case GL_TEXTURE_1D:
- if (image_compression == Texture::CM_off) {
- glTexSubImage1D(page_target, n - mipmap_bias, 0, width,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexSubImage1D(page_target, n - mipmap_bias, 0, width,
- external_format, view_size, image_ptr);
- }
- break;
- #endif // OPENGLES
- #ifndef OPENGLES_1
- case GL_TEXTURE_2D_ARRAY:
- case GL_TEXTURE_CUBE_MAP_ARRAY:
- if (_supports_2d_texture_array) {
- if (image_compression == Texture::CM_off) {
- _glTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexSubImage3D(page_target, n - mipmap_bias, 0, 0, 0, width, height, depth,
- external_format, view_size, image_ptr);
- }
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES_1
- #ifndef OPENGLES
- case GL_TEXTURE_BUFFER:
- if (_supports_buffer_texture) {
- _glBufferSubData(GL_TEXTURE_BUFFER, 0, view_size, image_ptr);
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES
- default:
- if (image_compression == Texture::CM_off) {
- if (n==0) {
- // It's unfortunate that we can't adjust the width, too, but
- // TexSubImage2D doesn't accept a row-stride parameter.
- height = tex->get_y_size() - tex->get_pad_y_size();
- }
- glTexSubImage2D(page_target, n - mipmap_bias, 0, 0, width, height,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexSubImage2D(page_target, n - mipmap_bias, 0, 0, width, height,
- external_format, view_size, image_ptr);
- }
- break;
- }
- }
- // Did that fail? If it did, we'll immediately try again, this time
- // loading the texture from scratch.
- GLenum error_code = gl_get_error();
- if (error_code != GL_NO_ERROR) {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "GL texture subload failed for " << tex->get_name()
- << " : " << get_error_string(error_code) << "\n";
- }
- needs_reload = true;
- }
- }
- if (needs_reload) {
- // Load the image up from scratch, creating a new GL Texture object.
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "loading new texture object for " << tex->get_name() << ", " << width
- << " x " << height << " x " << depth << ", z = " << z << ", mipmaps "
- << num_ram_mipmap_levels << ", uses_mipmaps = " << uses_mipmaps << "\n";
- }
- // If there is immutable storage, this is impossible to do, and we should
- // not have gotten here at all.
- nassertr(!gtc->_immutable, false);
- if (num_ram_mipmap_levels == 0) {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << " (initializing NULL image)\n";
- }
- if ((external_format == GL_DEPTH_STENCIL) && get_supports_depth_stencil()) {
- #ifdef OPENGLES
- component_type = GL_UNSIGNED_INT_24_8_OES;
- #else
- component_type = GL_UNSIGNED_INT_24_8_EXT;
- #endif
- }
- }
- for (int n = mipmap_bias; n < num_levels; ++n) {
- const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n);
- CPTA_uchar ptimage;
- if (image_ptr == nullptr) {
- ptimage = tex->get_ram_mipmap_image(n);
- if (ptimage.is_null()) {
- if (n < num_ram_mipmap_levels) {
- // We were told we'd have this many RAM mipmap images, but we
- // don't. Raise a warning.
- GLCAT.warning()
- << "No mipmap level " << n << " defined for " << tex->get_name()
- << "\n";
- if (_supports_texture_max_level) {
- // Tell the GL we have no more mipmaps for it to use.
- glTexParameteri(texture_target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias);
- }
- break;
- }
- if (tex->has_clear_color()) {
- // Ask the Texture class to create the mipmap level in RAM. It'll
- // fill it in with the correct clear color, which we can then
- // upload.
- ptimage = tex->make_ram_mipmap_image(n);
- } else if (image_compression != Texture::CM_off) {
- // We can't upload a NULL compressed texture.
- if (_supports_texture_max_level) {
- // Tell the GL we have no more mipmaps for it to use.
- glTexParameteri(texture_target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias);
- }
- break;
- }
- }
- image_ptr = ptimage;
- }
- PTA_uchar bgr_image;
- size_t view_size = tex->get_ram_mipmap_view_size(n);
- if (image_ptr != nullptr) {
- const unsigned char *orig_image_ptr = image_ptr;
- image_ptr += view_size * gtc->get_view();
- if (one_page_only) {
- view_size = tex->get_ram_mipmap_page_size(n);
- image_ptr += view_size * z;
- }
- nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false);
- if (image_compression == Texture::CM_off) {
- // If the GL doesn't claim to support BGR, we may have to reverse
- // the component ordering of the image.
- image_ptr = fix_component_ordering(bgr_image, image_ptr, view_size,
- external_format, tex);
- }
- }
- int width = tex->get_expected_mipmap_x_size(n);
- int height = tex->get_expected_mipmap_y_size(n);
- #ifndef OPENGLES_1
- int depth = tex->get_expected_mipmap_z_size(n);
- #endif
- #ifdef DO_PSTATS
- _data_transferred_pcollector.add_level(view_size);
- #endif
- switch (texture_target) {
- #ifndef OPENGLES // 1-d textures not supported by OpenGL ES. Fall through.
- case GL_TEXTURE_1D:
- if (image_compression == Texture::CM_off) {
- glTexImage1D(page_target, n - mipmap_bias, internal_format,
- width, 0,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexImage1D(page_target, n - mipmap_bias, external_format, width,
- 0, view_size, image_ptr);
- }
- break;
- #endif // OPENGLES // OpenGL ES will fall through.
- #ifndef OPENGLES_1
- case GL_TEXTURE_3D:
- if (_supports_3d_texture) {
- if (image_compression == Texture::CM_off) {
- _glTexImage3D(page_target, n - mipmap_bias, internal_format,
- width, height, depth, 0,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexImage3D(page_target, n - mipmap_bias, external_format, width,
- height, depth,
- 0, view_size, image_ptr);
- }
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES_1
- #ifndef OPENGLES_1
- case GL_TEXTURE_2D_ARRAY:
- case GL_TEXTURE_CUBE_MAP_ARRAY:
- if (_supports_2d_texture_array) {
- if (image_compression == Texture::CM_off) {
- _glTexImage3D(page_target, n - mipmap_bias, internal_format,
- width, height, depth, 0,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexImage3D(page_target, n - mipmap_bias, external_format, width,
- height, depth,
- 0, view_size, image_ptr);
- }
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES_1
- #ifndef OPENGLES
- case GL_TEXTURE_BUFFER:
- if (_supports_buffer_texture) {
- _glBufferData(GL_TEXTURE_BUFFER, view_size, image_ptr,
- get_usage(tex->get_usage_hint()));
- } else {
- report_my_gl_errors();
- return false;
- }
- break;
- #endif // OPENGLES
- default:
- if (image_compression == Texture::CM_off) {
- glTexImage2D(page_target, n - mipmap_bias, internal_format,
- width, height, 0,
- external_format, component_type, image_ptr);
- } else {
- _glCompressedTexImage2D(page_target, n - mipmap_bias, external_format,
- width, height, 0, view_size, image_ptr);
- }
- }
- }
- // Report the error message explicitly if the GL texture creation failed.
- GLenum error_code = gl_get_error();
- if (error_code != GL_NO_ERROR) {
- GLCAT.error()
- << "GL texture creation failed for " << tex->get_name()
- << " : " << get_error_string(error_code) << "\n";
- gtc->_has_storage = false;
- return false;
- }
- }
- report_my_gl_errors();
- return true;
- }
- /**
- * Causes mipmaps to be generated for an uploaded texture.
- */
- void CLP(GraphicsStateGuardian)::
- generate_mipmaps(CLP(TextureContext) *gtc) {
- #ifndef OPENGLES
- if (_supports_dsa) {
- // OpenGL 4.5 offers an easy way to do this without binding.
- _glGenerateTextureMipmap(gtc->_index);
- return;
- }
- #endif
- if (_glGenerateMipmap != nullptr) {
- _state_texture = 0;
- update_texture(gtc, true);
- apply_texture(gtc);
- _glGenerateMipmap(gtc->_target);
- glBindTexture(gtc->_target, 0);
- }
- }
- /**
- * This is used as a standin for upload_texture when the texture in question
- * is unavailable (e.g. it hasn't yet been loaded from disk). Until the
- * texture image itself becomes available, we will render the texture's
- * "simple" image--a sharply reduced version of the same texture.
- */
- bool CLP(GraphicsStateGuardian)::
- upload_simple_texture(CLP(TextureContext) *gtc) {
- report_my_gl_errors();
- PStatGPUTimer timer(this, _load_texture_pcollector);
- Texture *tex = gtc->get_texture();
- nassertr(tex != nullptr, false);
- GLenum internal_format = GL_RGBA;
- GLenum external_format = GL_BGRA;
- const unsigned char *image_ptr = tex->get_simple_ram_image();
- if (image_ptr == nullptr) {
- return false;
- }
- size_t image_size = tex->get_simple_ram_image_size();
- PTA_uchar bgr_image;
- if (!_supports_bgr) {
- // If the GL doesn't claim to support BGR, we may have to reverse the
- // component ordering of the image.
- external_format = GL_RGBA;
- image_ptr = fix_component_ordering(bgr_image, image_ptr, image_size,
- external_format, tex);
- }
- int width = tex->get_simple_x_size();
- int height = tex->get_simple_y_size();
- GLenum component_type = GL_UNSIGNED_BYTE;
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "loading simple image for " << tex->get_name() << "\n";
- }
- // Turn off mipmaps for the simple texture.
- if (tex->uses_mipmaps() && _supports_texture_max_level) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- }
- #ifdef DO_PSTATS
- _data_transferred_pcollector.add_level(image_size);
- #endif
- #ifdef OPENGLES
- internal_format = external_format;
- #endif
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format,
- width, height, 0,
- external_format, component_type, image_ptr);
- gtc->mark_simple_loaded();
- report_my_gl_errors();
- return true;
- }
- /**
- * Asks OpenGL how much texture memory is consumed by the indicated texture
- * (which is also the currently-selected texture).
- */
- size_t CLP(GraphicsStateGuardian)::
- get_texture_memory_size(CLP(TextureContext) *gtc) {
- Texture *tex = gtc->get_texture();
- #ifdef OPENGLES // Texture querying not supported on OpenGL ES.
- int width = tex->get_x_size();
- int height = tex->get_y_size();
- int depth = 1;
- int scale = 1;
- size_t num_bytes = 2; // Temporary assumption?
- #else
- GLenum target = get_texture_target(tex->get_texture_type());
- GLenum page_target = target;
- GLint scale = 1;
- if (target == GL_TEXTURE_CUBE_MAP) {
- // We need a particular page to get the level parameter from.
- page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
- scale = 6;
- } else if (target == GL_TEXTURE_BUFFER) {
- // In the case of buffer textures, we provided the size to begin with, so
- // no point in querying anything. Plus, glGetTexParameter is not even
- // supported for buffer textures.
- return tex->get_expected_ram_image_size();
- }
- clear_my_gl_errors();
- GLint internal_format;
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
- if (is_compressed_format(internal_format)) {
- // Try to get the compressed size.
- GLint image_size;
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
- GLenum error_code = gl_get_error();
- if (error_code != GL_NO_ERROR) {
- if (GLCAT.is_debug()) {
- GLCAT.debug()
- << "Couldn't get compressed size for " << tex->get_name()
- << " : " << get_error_string(error_code) << "\n";
- }
- // Fall through to the noncompressed case.
- } else {
- return image_size * scale;
- }
- }
- // OK, get the noncompressed size.
- GLint red_size, green_size, blue_size, alpha_size;
- GLint depth_size = 0, luminance_size = 0, intensity_size = 0;
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_RED_SIZE, &red_size);
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_GREEN_SIZE, &green_size);
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_BLUE_SIZE, &blue_size);
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_ALPHA_SIZE, &alpha_size);
- if (get_supports_luminance_texture()) {
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_LUMINANCE_SIZE, &luminance_size);
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_INTENSITY_SIZE, &intensity_size);
- }
- if (get_supports_depth_texture()) {
- glGetTexLevelParameteriv(page_target, 0,
- GL_TEXTURE_DEPTH_SIZE, &depth_size);
- }
- GLint width = 1, height = 1, depth = 1;
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_WIDTH, &width);
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_HEIGHT, &height);
- if (_supports_3d_texture || _supports_2d_texture_array) {
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_DEPTH, &depth);
- }
- report_my_gl_errors();
- size_t num_bits = (red_size + green_size + blue_size + alpha_size + luminance_size + intensity_size + depth_size);
- size_t num_bytes = (num_bits + 7) / 8;
- #endif // OPENGLES
- size_t result = num_bytes * width * height * depth * scale;
- if (gtc->_uses_mipmaps) {
- result = (result * 4) / 3;
- }
- return result;
- }
- /**
- * Checks the list of resident texture objects to see if any have recently
- * been evicted.
- */
- void CLP(GraphicsStateGuardian)::
- check_nonresident_texture(BufferContextChain &chain) {
- #if defined(SUPPORT_FIXED_FUNCTION) && !defined(OPENGLES) // Residency queries not supported by OpenGL ES.
- size_t num_textures = chain.get_count();
- if (num_textures == 0) {
- return;
- }
- CLP(TextureContext) **gtc_list = (CLP(TextureContext) **)alloca(num_textures * sizeof(CLP(TextureContext) *));
- GLuint *texture_list = (GLuint *)alloca(num_textures * sizeof(GLuint));
- size_t ti = 0;
- BufferContext *node = chain.get_first();
- while (node != nullptr) {
- CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), node);
- gtc_list[ti] = gtc;
- texture_list[ti] = gtc->_index;
- node = node->get_next();
- ++ti;
- }
- nassertv(ti == num_textures);
- GLboolean *results = (GLboolean *)alloca(num_textures * sizeof(GLboolean));
- bool all_resident = (glAreTexturesResident(num_textures, texture_list, results) != 0);
- report_my_gl_errors();
- if (!all_resident) {
- // Some are now nonresident.
- for (ti = 0; ti < num_textures; ++ti) {
- if (!results[ti]) {
- gtc_list[ti]->set_resident(false);
- }
- }
- }
- #endif // OPENGLES
- }
- /**
- * The internal implementation of extract_texture_data(), given an already-
- * created TextureContext.
- */
- bool CLP(GraphicsStateGuardian)::
- do_extract_texture_data(CLP(TextureContext) *gtc) {
- report_my_gl_errors();
- GLenum target = gtc->_target;
- if (target == GL_NONE) {
- return false;
- }
- #ifndef OPENGLES_1
- // Make sure any incoherent writes to the texture have been synced.
- if (gtc->needs_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT)) {
- issue_memory_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
- }
- #endif
- Texture *tex = gtc->get_texture();
- glBindTexture(target, gtc->_index);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
- }
- #ifndef OPENGLES
- if (target == GL_TEXTURE_BUFFER) {
- _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer);
- }
- #endif
- GLint wrap_u, wrap_v, wrap_w;
- GLint minfilter, magfilter;
- #ifndef OPENGLES
- GLfloat border_color[4];
- #endif
- #ifdef OPENGLES
- if (true) {
- #else
- if (target != GL_TEXTURE_BUFFER) {
- #endif
- glGetTexParameteriv(target, GL_TEXTURE_WRAP_S, &wrap_u);
- glGetTexParameteriv(target, GL_TEXTURE_WRAP_T, &wrap_v);
- wrap_w = GL_REPEAT;
- #ifndef OPENGLES_1
- if (_supports_3d_texture) {
- glGetTexParameteriv(target, GL_TEXTURE_WRAP_R, &wrap_w);
- }
- #endif
- glGetTexParameteriv(target, GL_TEXTURE_MIN_FILTER, &minfilter);
- glGetTexParameteriv(target, GL_TEXTURE_MAG_FILTER, &magfilter);
- #ifndef OPENGLES
- glGetTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border_color);
- #endif
- }
- GLenum page_target = target;
- if (target == GL_TEXTURE_CUBE_MAP) {
- // We need a particular page to get the level parameter from.
- page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
- }
- GLint width = gtc->_width, height = gtc->_height, depth = gtc->_depth;
- #ifndef OPENGLES
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_WIDTH, &width);
- if (target != GL_TEXTURE_1D) {
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_HEIGHT, &height);
- }
- if (_supports_3d_texture && target == GL_TEXTURE_3D) {
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_DEPTH, &depth);
- } else if (target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP_ARRAY) {
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_DEPTH, &depth);
- } else if (target == GL_TEXTURE_CUBE_MAP) {
- depth = 6;
- }
- #endif
- clear_my_gl_errors();
- if (width <= 0 || height <= 0 || depth <= 0) {
- GLCAT.error()
- << "No texture data for " << tex->get_name() << "\n";
- return false;
- }
- GLint internal_format = GL_RGBA;
- #ifndef OPENGLES
- if (target != GL_TEXTURE_BUFFER) {
- glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
- } else {
- // Some drivers give the wrong result for the above call. No problem; we
- // already know the internal format of a buffer texture since glTexBuffer
- // required passing the exact sized format.
- internal_format = gtc->_internal_format;
- }
- #endif // OPENGLES
- // Make sure we were able to query those parameters properly.
- GLenum error_code = gl_get_error();
- if (error_code != GL_NO_ERROR) {
- GLCAT.error()
- << "Unable to query texture parameters for " << tex->get_name()
- << " : " << get_error_string(error_code) << "\n";
- return false;
- }
- Texture::ComponentType type = Texture::T_unsigned_byte;
- Texture::Format format = Texture::F_rgb;
- Texture::CompressionMode compression = Texture::CM_off;
- switch (internal_format) {
- #ifndef OPENGLES
- case GL_COLOR_INDEX:
- format = Texture::F_color_index;
- break;
- #endif
- #if GL_DEPTH_COMPONENT != GL_DEPTH_COMPONENT24
- case GL_DEPTH_COMPONENT:
- #endif
- case GL_DEPTH_COMPONENT16:
- case GL_DEPTH_COMPONENT24:
- case GL_DEPTH_COMPONENT32:
- type = Texture::T_unsigned_short;
- format = Texture::F_depth_component;
- break;
- #ifndef OPENGLES
- case GL_DEPTH_COMPONENT32F:
- type = Texture::T_float;
- format = Texture::F_depth_component;
- break;
- #endif
- case GL_DEPTH_STENCIL:
- case GL_DEPTH24_STENCIL8:
- type = Texture::T_unsigned_int_24_8;
- format = Texture::F_depth_stencil;
- break;
- #ifndef OPENGLES
- case GL_DEPTH32F_STENCIL8:
- type = Texture::T_float;
- format = Texture::F_depth_stencil;
- break;
- #endif
- case GL_RGBA:
- case 4:
- format = Texture::F_rgba;
- break;
- case GL_RGBA4:
- format = Texture::F_rgba4;
- break;
- #ifdef OPENGLES
- case GL_RGBA8_OES:
- format = Texture::F_rgba8;
- break;
- #else
- case GL_RGBA8:
- format = Texture::F_rgba8;
- break;
- #endif
- #ifndef OPENGLES
- case GL_RGBA12:
- type = Texture::T_unsigned_short;
- format = Texture::F_rgba12;
- break;
- #endif
- case GL_RGB:
- case 3:
- format = Texture::F_rgb;
- break;
- #ifndef OPENGLES
- case GL_RGB5:
- format = Texture::F_rgb5;
- break;
- #endif
- case GL_RGB5_A1:
- format = Texture::F_rgba5;
- break;
- #ifndef OPENGLES
- case GL_RGB8:
- format = Texture::F_rgb8;
- break;
- case GL_RGB12:
- format = Texture::F_rgb12;
- break;
- case GL_RGBA16:
- format = Texture::F_rgba16;
- break;
- case GL_R3_G3_B2:
- format = Texture::F_rgb332;
- break;
- case GL_R8I:
- type = Texture::T_byte;
- format = Texture::F_r8i;
- break;
- case GL_RG8I:
- type = Texture::T_byte;
- format = Texture::F_rg8i;
- break;
- case GL_RGB8I:
- type = Texture::T_byte;
- format = Texture::F_rgb8i;
- break;
- case GL_RGBA8I:
- type = Texture::T_byte;
- format = Texture::F_rgba8i;
- break;
- case GL_R8UI:
- type = Texture::T_unsigned_byte;
- format = Texture::F_r8i;
- break;
- case GL_RG8UI:
- type = Texture::T_unsigned_byte;
- format = Texture::F_rg8i;
- break;
- case GL_RGB8UI:
- type = Texture::T_unsigned_byte;
- format = Texture::F_rgb8i;
- break;
- case GL_RGBA8UI:
- type = Texture::T_unsigned_byte;
- format = Texture::F_rgba8i;
- break;
- case GL_R16I:
- type = Texture::T_short;
- format = Texture::F_r16i;
- break;
- case GL_R16UI:
- type = Texture::T_unsigned_short;
- format = Texture::F_r16i;
- break;
- #endif
- #ifndef OPENGLES_1
- case GL_RGBA16F:
- type = Texture::T_float;
- format = Texture::F_rgba16;
- break;
- case GL_RGB16F:
- type = Texture::T_float;
- format = Texture::F_rgb16;
- break;
- case GL_RG16F:
- type = Texture::T_float;
- format = Texture::F_rg16;
- break;
- case GL_R16F:
- type = Texture::T_float;
- format = Texture::F_r16;
- break;
- case GL_RGBA32F:
- type = Texture::T_float;
- format = Texture::F_rgba32;
- break;
- case GL_RGB32F:
- type = Texture::T_float;
- format = Texture::F_rgb32;
- break;
- case GL_RG32F:
- type = Texture::T_float;
- format = Texture::F_rg32;
- break;
- case GL_R32F:
- type = Texture::T_float;
- format = Texture::F_r32;
- break;
- #endif
- #ifndef OPENGLES
- case GL_RGB16:
- type = Texture::T_unsigned_short;
- format = Texture::F_rgb16;
- break;
- case GL_RG16:
- type = Texture::T_unsigned_short;
- format = Texture::F_rg16;
- break;
- case GL_R16:
- type = Texture::T_unsigned_short;
- format = Texture::F_r16;
- break;
- case GL_RGB16_SNORM:
- type = Texture::T_short;
- format = Texture::F_rgb16;
- break;
- case GL_RG16_SNORM:
- type = Texture::T_short;
- format = Texture::F_rg16;
- break;
- case GL_R16_SNORM:
- type = Texture::T_short;
- format = Texture::F_r16;
- break;
- #endif
- #ifndef OPENGLES_1
- case GL_R11F_G11F_B10F:
- type = Texture::T_float;
- format = Texture::F_r11_g11_b10;
- break;
- case GL_RGB9_E5:
- type = Texture::T_float;
- format = Texture::F_rgb9_e5;
- break;
- case GL_RGB10_A2:
- type = Texture::T_unsigned_short;
- format = Texture::F_rgb10_a2;
- break;
- #endif
- #ifdef OPENGLES_2
- case GL_RED_EXT:
- case GL_R8_EXT:
- format = Texture::F_red;
- break;
- #endif
- #ifndef OPENGLES
- case GL_R32I:
- type = Texture::T_int;
- format = Texture::F_r32i;
- break;
- #endif
- #ifndef OPENGLES
- case GL_RED:
- format = Texture::F_red;
- break;
- case GL_GREEN:
- format = Texture::F_green;
- break;
- case GL_BLUE:
- format = Texture::F_blue;
- break;
- #endif // OPENGLES
- case GL_ALPHA:
- format = Texture::F_alpha;
- break;
- case GL_LUMINANCE:
- #ifndef OPENGLES
- case GL_LUMINANCE16:
- case GL_LUMINANCE16F_ARB:
- #endif
- case 1:
- format = Texture::F_luminance;
- break;
- case GL_LUMINANCE_ALPHA:
- #ifndef OPENGLES
- case GL_LUMINANCE_ALPHA16F_ARB:
- #endif
- case 2:
- format = Texture::F_luminance_alpha;
- break;
- #ifndef OPENGLES_1
- case GL_SRGB:
- case GL_SRGB8:
- format = Texture::F_srgb;
- break;
- case GL_SRGB_ALPHA:
- case GL_SRGB8_ALPHA8:
- format = Texture::F_srgb_alpha;
- break;
- #endif // OPENGLES_1
- #ifndef OPENGLES
- case GL_SLUMINANCE:
- case GL_SLUMINANCE8:
- format = Texture::F_sluminance;
- break;
- case GL_SLUMINANCE_ALPHA:
- case GL_SLUMINANCE8_ALPHA8:
- format = Texture::F_sluminance_alpha;
- break;
- #endif // OPENGLES
- #ifndef OPENGLES
- case GL_COMPRESSED_RGB:
- format = Texture::F_rgb;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_RGBA:
- format = Texture::F_rgba;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_ALPHA:
- format = Texture::F_alpha;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_LUMINANCE:
- format = Texture::F_luminance;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_LUMINANCE_ALPHA:
- format = Texture::F_luminance_alpha;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_SRGB:
- format = Texture::F_srgb;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_SRGB_ALPHA:
- format = Texture::F_srgb_alpha;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_SLUMINANCE:
- format = Texture::F_sluminance;
- compression = Texture::CM_on;
- break;
- case GL_COMPRESSED_SLUMINANCE_ALPHA:
- format = Texture::F_sluminance_alpha;
- compression = Texture::CM_on;
- break;
- #endif
- case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- format = Texture::F_rgb;
- compression = Texture::CM_dxt1;
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- format = Texture::F_rgbm;
- compression = Texture::CM_dxt1;
- break;
- #ifndef OPENGLES
- case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
- format = Texture::F_srgb;
- compression = Texture::CM_dxt1;
- break;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- format = Texture::F_srgb_alpha;
- compression = Texture::CM_dxt1;
- break;
- #endif
- #ifdef OPENGLES
- case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
- format = Texture::F_rgb;
- compression = Texture::CM_pvr1_2bpp;
- break;
- case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
- format = Texture::F_rgba;
- compression = Texture::CM_pvr1_2bpp;
- break;
- case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
- format = Texture::F_rgb;
- compression = Texture::CM_pvr1_4bpp;
- break;
- case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
- format = Texture::F_rgba;
- compression = Texture::CM_pvr1_4bpp;
- break;
- #else
- case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- format = Texture::F_rgba;
- compression = Texture::CM_dxt3;
- break;
- case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- format = Texture::F_rgba;
- compression = Texture::CM_dxt5;
- break;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- format = Texture::F_srgb_alpha;
- compression = Texture::CM_dxt3;
- break;
- case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- format = Texture::F_srgb_alpha;
- compression = Texture::CM_dxt5;
- break;
- case GL_COMPRESSED_RGB_FXT1_3DFX:
- format = Texture::F_rgb;
- compression = Texture::CM_fxt1;
- break;
- case GL_COMPRESSED_RGBA_FXT1_3DFX:
- format = Texture::F_rgba;
- compression = Texture::CM_fxt1;
- break;
- case GL_COMPRESSED_LUMINANCE_LATC1_EXT:
- format = Texture::F_luminance;
- compression = Texture::CM_rgtc;
- break;
- case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- format = Texture::F_luminance_alpha;
- compression = Texture::CM_rgtc;
- break;
- case GL_COMPRESSED_RED_RGTC1:
- format = Texture::F_red;
- compression = Texture::CM_rgtc;
- break;
- case GL_COMPRESSED_SIGNED_RED_RGTC1:
- type = Texture::T_byte;
- format = Texture::F_red;
- compression = Texture::CM_rgtc;
- break;
- case GL_COMPRESSED_RG_RGTC2:
- format = Texture::F_rg;
- compression = Texture::CM_rgtc;
- break;
- case GL_COMPRESSED_SIGNED_RG_RGTC2:
- type = Texture::T_byte;
- format = Texture::F_rg;
- compression = Texture::CM_rgtc;
- break;
- #endif
- default:
- GLCAT.warning()
- << "Unhandled internal format for " << tex->get_name()
- << " : " << hex << "0x" << internal_format << dec << "\n";
- return false;
- }
- // We don't want to call setup_texture() again; that resets too much.
- // Instead, we'll just set the individual components.
- tex->set_x_size(width);
- tex->set_y_size(height);
- tex->set_z_size(depth);
- tex->set_component_type(type);
- tex->set_format(format);
- #ifdef OPENGLES
- if (true) {
- #else
- if (target != GL_TEXTURE_BUFFER) {
- #endif
- tex->set_wrap_u(get_panda_wrap_mode(wrap_u));
- tex->set_wrap_v(get_panda_wrap_mode(wrap_v));
- tex->set_wrap_w(get_panda_wrap_mode(wrap_w));
- tex->set_minfilter(get_panda_filter_type(minfilter));
- //tex->set_magfilter(get_panda_filter_type(magfilter));
- #ifndef OPENGLES
- tex->set_border_color(LColor(border_color[0], border_color[1],
- border_color[2], border_color[3]));
- #endif
- }
- PTA_uchar image;
- size_t page_size = 0;
- if (!extract_texture_image(image, page_size, tex, target, page_target,
- type, compression, 0)) {
- return false;
- }
- int num_views = tex->get_num_views();
- if (num_views == 1) {
- // Replace the entire image, since we are modifying the only view.
- tex->set_ram_image(image, compression, page_size);
- } else {
- // We're only modifying a single view, so we can't stomp all over the
- // existing content.
- PTA_uchar ram_image = tex->modify_ram_image();
- nassertr(ram_image.size() == image.size() * num_views, false);
- memcpy(ram_image.p() + image.size() * gtc->get_view(), image.p(), image.size());
- }
- if (gtc->_uses_mipmaps) {
- // Also get the mipmap levels.
- GLint num_expected_levels = tex->get_expected_num_mipmap_levels();
- GLint highest_level = num_expected_levels;
- if (_supports_texture_max_level) {
- glGetTexParameteriv(target, GL_TEXTURE_MAX_LEVEL, &highest_level);
- highest_level = min(highest_level, num_expected_levels);
- }
- for (int n = 1; n <= highest_level; ++n) {
- if (!extract_texture_image(image, page_size, tex, target, page_target,
- type, compression, n)) {
- return false;
- }
- if (num_views == 1) {
- tex->set_ram_mipmap_image(n, image, page_size);
- } else {
- PTA_uchar ram_mipmap_image = tex->modify_ram_mipmap_image(n);
- memcpy(ram_mipmap_image.p() + image.size() * gtc->get_view(), image.p(), image.size());
- }
- }
- }
- return true;
- }
- /**
- * Called from extract_texture_data(), this gets just the image array for a
- * particular mipmap level (or for the base image).
- */
- bool CLP(GraphicsStateGuardian)::
- extract_texture_image(PTA_uchar &image, size_t &page_size,
- Texture *tex, GLenum target, GLenum page_target,
- Texture::ComponentType type,
- Texture::CompressionMode compression, int n) {
- #ifdef OPENGLES // Extracting texture data unsupported in OpenGL ES.
- nassert_raise("OpenGL ES does not support extracting texture data");
- return false;
- #else
- // Make sure the GL driver does not align textures, otherwise we get corrupt
- // memory, since we don't take alignment into account.
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- if (target == GL_TEXTURE_CUBE_MAP) {
- // A cube map, compressed or uncompressed. This we must extract one page
- // at a time.
- // If the cube map is compressed, we assume that all the compressed pages
- // are exactly the same size. OpenGL doesn't make this assumption, but it
- // happens to be true for all currently extant compression schemes, and it
- // makes things simpler for us. (It also makes things much simpler for
- // the graphics hardware, so it's likely to continue to be true for a
- // while at least.)
- GLenum external_format = get_external_image_format(tex);
- GLenum pixel_type = get_component_type(type);
- page_size = tex->get_expected_ram_mipmap_page_size(n);
- if (compression != Texture::CM_off) {
- GLint image_size;
- glGetTexLevelParameteriv(page_target, n,
- GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
- nassertr(image_size <= (int)page_size, false);
- page_size = image_size;
- }
- image = PTA_uchar::empty_array(page_size * 6);
- for (int z = 0; z < 6; ++z) {
- page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
- if (compression == Texture::CM_off) {
- glGetTexImage(page_target, n, external_format, pixel_type,
- image.p() + z * page_size);
- } else {
- _glGetCompressedTexImage(page_target, 0, image.p() + z * page_size);
- }
- }
- #ifndef OPENGLES
- } else if (target == GL_TEXTURE_BUFFER) {
- // In the case of a buffer texture, we need to get it from the buffer.
- image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_view_size(n));
- _glGetBufferSubData(target, 0, image.size(), image.p());
- #endif
- } else if (compression == Texture::CM_off) {
- // An uncompressed 1-d, 2-d, or 3-d texture.
- image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_view_size(n));
- GLenum external_format = get_external_image_format(tex);
- GLenum pixel_type = get_component_type(type);
- glGetTexImage(target, n, external_format, pixel_type, image.p());
- } else {
- // A compressed 1-d, 2-d, or 3-d texture.
- GLint image_size;
- glGetTexLevelParameteriv(target, n, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
- page_size = image_size / tex->get_z_size();
- image = PTA_uchar::empty_array(image_size);
- // Some drivers (ATI!) seem to try to overstuff more bytes in the array
- // than they asked us to allocate (that is, more bytes than
- // GL_TEXTURE_COMPRESSED_IMAGE_SIZE), requiring us to overallocate and
- // then copy the result into our final buffer. Sheesh.
- // We'll only do this for small textures (the ATI bug doesn't *seem* to
- // affect large textures), to save on the overhead of the double-copy, and
- // reduce risk from an overly-large alloca().
- #ifndef NDEBUG
- static const int max_trouble_buffer = 102400;
- #else
- static const int max_trouble_buffer = 1024;
- #endif
- if (image_size < max_trouble_buffer) {
- static const int extra_space = 32;
- unsigned char *buffer = (unsigned char *)alloca(image_size + extra_space);
- #ifndef NDEBUG
- // Tag the buffer with a specific byte so we can report on whether that
- // driver bug is still active.
- static unsigned char keep_token = 0x00;
- unsigned char token = ++keep_token;
- memset(buffer + image_size, token, extra_space);
- #endif
- _glGetCompressedTexImage(target, n, buffer);
- memcpy(image.p(), buffer, image_size);
- #ifndef NDEBUG
- int count = extra_space;
- while (count > 0 && buffer[image_size + count - 1] == token) {
- --count;
- }
- if (count != 0) {
- GLCAT.warning()
- << "GL graphics driver overfilled " << count
- << " bytes into a " << image_size
- << "-byte buffer provided to glGetCompressedTexImage()\n";
- }
- // This had better not equal the amount of buffer space we set aside.
- // If it does, we assume the driver might have overfilled even our
- // provided extra buffer.
- nassertr(count != extra_space, true)
- #endif // NDEBUG
- } else {
- _glGetCompressedTexImage(target, n, image.p());
- }
- }
- // Now see if we were successful.
- GLenum error_code = gl_get_error();
- if (error_code != GL_NO_ERROR) {
- GLCAT.error()
- << "Unable to extract texture for " << *tex
- << ", mipmap level " << n
- << " : " << get_error_string(error_code) << "\n";
- nassertr(false, false);
- return false;
- }
- return true;
- #endif // OPENGLES
- }
- /**
- * Internally sets the point size parameters after any of the properties have
- * changed that might affect this.
- */
- #ifdef SUPPORT_FIXED_FUNCTION
- void CLP(GraphicsStateGuardian)::
- do_point_size() {
- if (!_point_perspective) {
- // Normal, constant-sized points. Here _point_size is a width in pixels.
- static LVecBase3f constant(1.0f, 0.0f, 0.0f);
- _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, constant.get_data());
- } else {
- // Perspective-sized points. Here _point_size is a width in 3-d units.
- // To arrange that, we need to figure out the appropriate scaling factor
- // based on the current viewport and projection matrix.
- LVector3 height(0.0f, _point_size, 1.0f);
- height = height * _projection_mat->get_mat();
- height = height * _internal_transform->get_scale()[1];
- PN_stdfloat s = height[1] * _viewport_height / _point_size;
- if (_current_lens->is_orthographic()) {
- // If we have an orthographic lens in effect, we don't actually apply a
- // perspective transform: we just scale the points once, regardless of
- // the distance from the camera.
- LVecBase3f constant(1.0f / (s * s), 0.0f, 0.0f);
- _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, constant.get_data());
- } else {
- // Otherwise, we give it a true perspective adjustment.
- LVecBase3f square(0.0f, 0.0f, 1.0f / (s * s));
- _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, square.get_data());
- }
- }
- report_my_gl_errors();
- }
- #endif
- /**
- * Returns true if this particular GSG supports the specified Cg Shader
- * Profile.
- */
- bool CLP(GraphicsStateGuardian)::
- get_supports_cg_profile(const string &name) const {
- #if !defined(HAVE_CG) || defined(OPENGLES)
- return false;
- #else
- CGprofile profile = cgGetProfile(name.c_str());
- if (profile == CG_PROFILE_UNKNOWN) {
- GLCAT.error() << name << ", unknown Cg-profile\n";
- return false;
- }
- return (cgGLIsProfileSupported(profile) != 0);
- #endif
- }
- /**
- * Binds a framebuffer object.
- */
- void CLP(GraphicsStateGuardian)::
- bind_fbo(GLuint fbo) {
- if (_current_fbo == fbo) {
- return;
- }
- PStatGPUTimer timer(this, _fbo_bind_pcollector);
- nassertv(_glBindFramebuffer != 0);
- #if defined(OPENGLES_2)
- _glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- #elif defined(OPENGLES_1)
- _glBindFramebuffer(GL_FRAMEBUFFER_OES, fbo);
- #else
- _glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo);
- #endif
- _current_fbo = fbo;
- }
- // GL stencil code section
- static int gl_stencil_operations_array[] = {
- GL_KEEP,
- GL_ZERO,
- GL_REPLACE,
- #ifdef OPENGLES_1
- GL_INCR_WRAP_OES,
- GL_DECR_WRAP_OES,
- #else
- GL_INCR_WRAP,
- GL_DECR_WRAP,
- #endif
- GL_INVERT,
- GL_INCR,
- GL_DECR,
- };
- /**
- * Set stencil render states.
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_stencil() {
- if (!_supports_stencil) {
- return;
- }
- const StencilAttrib *stencil;
- if (_target_rs->get_attrib(stencil)) {
- // DEBUG
- if (false) {
- GLCAT.debug() << "STENCIL STATE CHANGE\n";
- GLCAT.debug() << "\n"
- << "SRS_front_comparison_function " << (int)stencil->get_render_state(StencilAttrib::SRS_front_comparison_function) << "\n"
- << "SRS_front_stencil_fail_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation) << "\n"
- << "SRS_front_stencil_pass_z_fail_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation) << "\n"
- << "SRS_front_stencil_pass_z_pass_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation) << "\n"
- << "SRS_reference " << (int)stencil->get_render_state(StencilAttrib::SRS_reference) << "\n"
- << "SRS_read_mask " << (int)stencil->get_render_state(StencilAttrib::SRS_read_mask) << "\n"
- << "SRS_write_mask " << (int)stencil->get_render_state(StencilAttrib::SRS_write_mask) << "\n"
- << "SRS_back_comparison_function " << (int)stencil->get_render_state(StencilAttrib::SRS_back_comparison_function) << "\n"
- << "SRS_back_stencil_fail_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation) << "\n"
- << "SRS_back_stencil_pass_z_fail_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation) << "\n"
- << "SRS_back_stencil_pass_z_pass_operation " << (int)stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation) << "\n";
- }
- #ifndef OPENGLES
- if (_supports_two_sided_stencil) {
- // TODO: add support for OpenGL 2.0-style glStencilFuncSeparate.
- unsigned int back_compare;
- back_compare = stencil->get_render_state(StencilAttrib::SRS_back_comparison_function);
- if (back_compare != RenderAttrib::M_none) {
- glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
- _glActiveStencilFaceEXT(GL_BACK);
- glStencilFunc(
- PANDA_TO_GL_COMPAREFUNC(back_compare),
- stencil->get_render_state(StencilAttrib::SRS_reference),
- stencil->get_render_state(StencilAttrib::SRS_read_mask));
- glStencilOp(
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation)],
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation)],
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation)]
- );
- glStencilMask(stencil->get_render_state(StencilAttrib::SRS_write_mask));
- } else {
- glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
- }
- _glActiveStencilFaceEXT(GL_FRONT);
- }
- #endif // OPENGLES
- unsigned int front_compare;
- front_compare = stencil->get_render_state(StencilAttrib::SRS_front_comparison_function);
- if (front_compare != RenderAttrib::M_none) {
- glEnable(GL_STENCIL_TEST);
- glStencilFunc(
- PANDA_TO_GL_COMPAREFUNC(front_compare),
- stencil->get_render_state(StencilAttrib::SRS_reference),
- stencil->get_render_state(StencilAttrib::SRS_read_mask));
- glStencilOp(
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation)],
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation)],
- gl_stencil_operations_array[stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation)]
- );
- glStencilMask(stencil->get_render_state(StencilAttrib::SRS_write_mask));
- } else {
- glDisable(GL_STENCIL_TEST);
- }
- if (stencil->get_render_state(StencilAttrib::SRS_clear)) {
- // clear stencil buffer
- glClearStencil(stencil->get_render_state(StencilAttrib::SRS_clear_value));
- glClear(GL_STENCIL_BUFFER_BIT);
- }
- } else {
- glDisable(GL_STENCIL_TEST);
- #ifndef OPENGLES
- if (_supports_two_sided_stencil) {
- glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
- }
- #endif // OPENGLES
- }
- }
- /**
- *
- */
- void CLP(GraphicsStateGuardian)::
- do_issue_scissor() {
- const ScissorAttrib *target_scissor;
- _target_rs->get_attrib_def(target_scissor);
- if (!target_scissor->is_off()) {
- // A non-off ScissorAttrib means to override the scissor setting that was
- // specified by the DisplayRegion.
- if (!_scissor_enabled) {
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glEnable(GL_SCISSOR_TEST)\n";
- }
- glEnable(GL_SCISSOR_TEST);
- _scissor_enabled = true;
- }
- const LVecBase4 &frame = target_scissor->get_frame();
- int x = (int)(_viewport_x + _viewport_width * frame[0] + 0.5f);
- int y = (int)(_viewport_y + _viewport_height * frame[2] + 0.5f);
- int width = (int)(_viewport_width * (frame[1] - frame[0]) + 0.5f);
- int height = (int)(_viewport_height * (frame[3] - frame[2]) + 0.5f);
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glScissor(" << x << ", " << y << ", " << width << ", " << height << ")\n";
- }
- glScissor(x, y, width, height);
- _scissor_attrib_active = true;
- } else if (_scissor_attrib_active) {
- _scissor_attrib_active = false;
- if (_scissor_array.size() > 0) {
- // Scissoring is enabled on the display region. Revert to the scissor
- // state specified in the DisplayRegion.
- #ifndef OPENGLES
- if (_supports_viewport_arrays) {
- _glScissorArrayv(0, _scissor_array.size(), _scissor_array[0].get_data());
- } else
- #endif // OPENGLES
- {
- const LVecBase4i sr = _scissor_array[0];
- glScissor(sr[0], sr[1], sr[2], sr[3]);
- }
- } else if (_scissor_enabled) {
- // The display region had no scissor enabled. Disable scissoring.
- if (GLCAT.is_spam()) {
- GLCAT.spam()
- << "glDisable(GL_SCISSOR_TEST)\n";
- }
- glDisable(GL_SCISSOR_TEST);
- _scissor_enabled = false;
- }
- }
- }
|