Luku 10: Testaus ja optimointi Pelien testaus Testityyppejä Suorituskyvyn analysointi Optimointikikkoja Grafiikkaliukuhihnan optimointi
Testaus Monet normaalin ohjelmistotuotteen testauksen opeista ovat sellaisenaan hyödyllisiä myös peleissä Bugitietokanta, riskianalyysi, testikattavuus... Harvoin kukaan kuolee tai menettää valtavia summia rahaa, jos peli kaatuu Niinpä useimmat virheet eivät ole yhtä kriittisiä kuin vakavissa sovelluksissa Peleissä on enemmän liikkuvia osia kuin monissa muissa sovelluksissa, joten kaikkea on hankalampi kattaa jäykillä testitapauksilla
Testaus Pelit ovat usein läheisesti tekemisissä raudan kanssa (nykyään erityisesti GPU:t) Varsinkin PC:llä erilaisia rautakonfiguraatioita julmetun paljon kaikkien yhdistelmien testaaminen kaikissa eri pelitilanteissa äärimmäisen työlästä Kaiken tehon irti ottaminen vaatii monimutkaisia ja kummallisia kikkoja, joten bugit ovat usein mystisiä Ei siis ihme, että pelit ovat usein bugisia
Testaus Pelien testauksessa painopistealueet ovat hieman erilaiset kuin perinteisessä ohjelmistotuotannossa Peleissä tyypillisiä testityyppejä (McShaffry): toiminnallisuustestit rasitustestit suorituskykytestit pelattavuustestit käytettävyystestit konfiguraatiotestit
Suorituskyky ja konfiguraatiot Pelit vievät tyypillisesti laitteiston sietokyvyn rajoille, joten kuormitustestit ovat tarpeen Tutkitaan pelin toimintaa kun resurssit ovat vähissä, sekä muissa erikoistilanteissa Tippuuko fps jossain tilanteissa? Millainen CPU/GPU vaaditaan? Paljonko muistia? Entä jos levytila loppuu? Näyttötila vaihtuu kesken pelin? Verkkoyhteys katkeaa? Eri laitteilla (PC, Android) ja softaversioilla yllättävät interaktiot tulostinajureiden jne kanssa
Pelattavuustestit Pelattavuustesteillä on tarkoitus varmistaa, että pelin pelaaminen on (kohdeyleisön mielestä) hauskaa, viihdyttävää, mielenkiintoista tyypillisesti käytetään ulkopuolisia pelaajia, joille peli annetaan ja lopuksi haastatellaan kilpailijoidenkin pelejä voi pelauttaa ihmisillä, jolloin saa ideoita: miten voisi tehdä paremman huijauskoodeilla ja neuvoilla kannattaa yrittää antaa testaajalle kuva pelistä laajemmin, ei vain alusta
Metriikat, A/B-testit Verkossa toimivissa peleissä pelaajien toimista voi kerätä paljon hyödyllistä informaatiota (metriikat) esim. mitä toimintoja ei käytetä, mitä pelaajat tekivät vähän ennen lopettamista A/B-testaus: annetaan osalle käyttäjistä erilainen tuote ja tutkitaan onko tulos parempi vai huonompi esim. käyttöliittymän yksityiskohtien hiominen pitkään käytetty markkinoinnissa Tarvitaanko enää pelisuunnittelua, kun kaiken voi ratkaista metriikoilla?
Käytettävyystestit Käytettävyystestissä testataan, osaako pelaaja toimia pelissä esim. annetaan ulkopuoliselle tehtävä ilman ohjeita ja katsotaan, miten käy toisin kuin pelattavuustesteissä, tekijöiltä ei saa kysyä apua ensimmäistä kertaa pelin näkevän katselu on usein tekijälle silmät avaava kokemus videointi helpottaa analyysiä
Automaattinen testaus Joitakin osia pelien testaamisesta voidaan automatisoida Ei korvaa ihmistestausta Syöte voi tulla pelaajan sijaan testiskriptiltä Koodissa on testejä, jotka varmistavat, että peli on ehjässä tilassa Automaattiset testit voidaan ajaa jokaisen käännöksen jälkeen ja/tai joka yö Hankalaa jos pelin säännöt muuttuvat jatkuvasti, vanha skripti ei ole enää validi
Betatestit Varsinkin verkkopeleissä on yleistä käyttää julkista betatestiä: peli julkistetaan osalle yleisöstä ennen varsinaista julkaisua MMO-peleissä palvelinten kuormitusta on hyvin vaikeaa simuloida realistisesti muuten Yleisö kannattaa valita: hardcore-pelaajia, jotka oikeasti pelaavat riittävästi Testaajat kannattaa palkita, jotta heiltä saisi hyvää palautetta
Optimointi Optimoinnilla tarkoitetaan tässä suorituskyvyn parantamista Esimerkiksi prosessorin, muistin ja väylän käyttö Peruskäyttäjällä ei yleensä ole uusinta grafiikkakorttia; ihmiset yrittävät pelata uusia pelejä hyvinkin vanhoilla koneilla Mitä pienemmät laitevaatimukset, sitä suurempi potentiaalinen asiakaskunta
Optimoinnin tasot Optimoinnin voi jakaa kolmeen tasoon Järjestelmätaso: resurssienkäytön tasapainotus Sovellustaso: kertaluokkiin vaikuttavia muutoksia tietorakenteissa ja algoritmeissa octree, LoD, karsinta,... potentiaalia vaikuttaa ratkaisevasti epätarkkakin ajoajan analyysi voi olla avuksi Mikrotaso: yhden tai muutaman koodirivin tasolla tapahtuvaa suurimmassa osassa koodia turhaa
Analyysi Optimoinnin ensimmäinen vaihe on analyysi: jos ei tiedä, miksi peli on hidas, asialle on vaikea tehdä mitään Kun jossakin prosessissa on monta vaihetta, muodostuu yleensä yksi pullonkaula: minkään muun vaiheen nopeuttaminen ei auta yhtään grafiikassa keskeistä: CPU lähettää datan, kulkee väylässä, etenee kortin liukuhihnaa pitkin Analyysin tavoitteena selvittää, paljonko aikaa ja muistia kuluu mihinkin koodin osaan
Analyysi Täytyy mitata jotain, jotta sitä voisi parantaa hyvä mittari on toistettava, edustava ja käytettävä yleisin mittari peleissä fps epälineaarisuuden vuoksi ms/frame parempi Ajastimilla on helppo saada karkeita arvioita paljonko aikaa kuluu jossakin koodin osassa gettime() tai vastaava, lasketaan erotuksia Vastaavasti tutkimalla vapaan muistin määrää (tai pitämällä kirjaa) voi tutkia muistinkulutusta
Pullonkaulan etsiminen Pullonkaulaa voi etsiä kokeilemalla korvataan prosessin vaiheita yksi kerrallaan yksinkertaistetulla versiolla, esim. prosessori: törmäystarkistuksen ja AI:n poisto väylä: tekstuurien korvaus pienemmillä versioilla pikselien määrä: piirtoikkunan koon pienennys toinen vaihtoehto monimutkaisempi versio (varo kääntäjän optimointeja) Jos peli ei nopeudu, kyseinen vaihe ei luultavasti ole pullonkaula Ongelma: muutokset vaikuttavat muihinkin osiin
Työkalut Profilointityökaluilla voi tutkia tarkemmin, paljonko aikaa ja muistia kuluu koodin osissa voi aiheuttaa overheadia ja vääristymiä 3d-korttien valmistajilla on työkaluja, joilla voi tutkia eri liukuhihnan vaiheiden kuormitusta ym. Laitteiston nopeuden muuttaminen Välimuistihutien laskeminen Työkaluja: VTune, MS PIX, Intel GPA, NSight, gdebugger
Yleisiä optimointikikkoja Ajankäytön jakauma on hyvin vino: suurin osa ajasta kuluu pienessä osassa koodia, joten mikrotason optimointikin kannattaa usein kunhan sen tekee oikeaan paikkaan Tärkeämpää kuin tuntea joukko niksejä on tuntea kohdelaitteisto ja ohjelmistoympäristö prosessorin ja 3d-kortin toiminta (väli)muistiarkkitehtuuri (datan sijoittelu) kääntäjä, käytetyt kirjastot/ohjelmistokehykset
Yleisiä optimointikikkoja Katsotaan kuitenkin joukko niksejä :) usein ristiriidassa selkeyden ja luotettavuuden kanssa, joten ajattele ennen käyttöä! Prosessorinkäyttöä voi usein muuttaa muistinkäytöksi laskemalla arvot valmiiksi klassinen esimerkki trigonometriset funktiot tosin välimuistin tehokkuus huononee Sievennä matemaattiset lausekkeet x/z+y/z (x+y)/z
Yleisiä optimointikikkoja Ota operaatioiden kestot huomioon jakolasku on hidas: x/c (1/c)*x liukulukujen matkiminen kokonaisluvuilla Merkkijonojen vertailu on hidasta Käytä staattisia rakenteita dynaamisten sijaan jos mahdollista, dynaamisissa on overheadia dynaamiset tietotyypit, dynaaminen sitominen jatkuva dynaaminen muistinvaraus/-vapautus taulukko vs. dynaaminen vektori
Yleisiä optimointikikkoja Älä käytä ylimääräisiä bittejä tiedon tallentamiseen jos tiedetään, että arvo on aina 1..100, menee 32- bittinen luku paljolti hukkaan; 8 (tai 7) bittiä riittää mutta joskus 32-bittisiä on nopeampi käsitellä... Muistinkulutusta vähentää resurssien tallennusmuodon oikea valinta äänitiedostoksi voi riittää 11 khz mono 44 khz stereon sijaan, tilankäyttö 1/8 bittikarttojen/tekstuurien bittimäärät per väri pakkaaminen: sekä häviöllinen että häviötön
Yleisiä optimointikikkoja STL:n tietorakenteet muistirohmuja Symmetrian hyödyntäminen: joskus riittää puolet tai vähemmänkin muistista/ajoajasta, jos esim. toinen puoli datasta on sama peilattuna SIMD-arkkitehtuuri: koodin vektorointi Tiedon sijoittelu muistiin (alignment) Pelkkä poikkeusten olemassaolo hidastaa funktiokutsuja
Yleisiä optimointikikkoja Hyödynnä välimuisti: sekä data että käskyt peräkkäin käytetty data peräkkäin Kääntäjä pitää yksittäiset funktiot yleensä muistissa jatkuvana palana ja käännösyksikön funktiot peräkkäisinä, joten: pidä koodi lyhyenä (vähän konekielikäskyjä) sijoita käytetyt funktiot peräkkäin vältä varsinkin ulkopuolisten funktioiden kutsua ristiriita: funktiot lyhentävät koodia - inlining
Resurssivälimuisti Kovalevyltä lataaminen on erityisen hidasta, joten olisi tärkeää, että data mahtuu muistiin Koska tähän ei kuitenkaan päästä, peleihin toteutetaan usein erillinen resurssivälimuisti resursseja ovat esimerkiksi hahmoanimaatiot, kolmioverkot, tekstuurit, kentät, äänet, videot,... pitää kirjaa, mitkä resurssit on ladattuna ja paljonko niitä on käytetty lataa pyydettäessä resursseja levyltä ja heittää tarpeettomimmat pois muistista hallinnoi muistivarastoja, ei jatkuvaa new/deleteä
Pelimaailma ja resurssit Resurssit voi ottaa huomioon jo pelimaailmaa suunniteltaessa yksi kenttä tai sen osa käyttää juuri sen verran muistia kuin laitteessa on ladataanko jatkuvasti vai latauspisteissä segmentit ja puskurivyöhykkeet Halo 1 esimerkillinen Etenkin konsoleilla kätevää, koska voidaan usein optimoida tietylle tunnetulle laitteelle
Datan siirto Eniten väylää kuluttaa yleensä tekstuurien siirto pidetään tekstuurit mahdollisimman pitkään grafiikkakortilla ja vältetään niiden vaihtelua Tekstuuribudjetti: käytetään resurssit siellä missä se näkyy pelaajalle eniten kokeillaan peliä eri kokoisilla versioilla tekstuureista ja katsotaan, onko lisäresoluutiosta näkyvää hyötyä läpinäkyvyys ym. kikat ei kopioita samasta tekstuurista
Datan siirto Verteksien siirto harvoin nykyään ongelma kolmioketjut ja -viuhkat voivat olla jopa hitaampia kuin pelkät kolmiot; riippuu laitteesta verteksien optimointi GPU:n välimuisteja ajatellen tähänkin automaattisia työkaluja API-kutsujen määrän minimointi batching: useita pieniä tehtäviä samaan kutsuun kolmioketjut voi yhdistää degeneroituneilla 3d-mallien instanssit Tilavaihdosten minimointi materiaalin perusteella järjestäminen
Geometria Verteksisävytin nykyään harvoin pullonkaula tosin kuluttaa monilla laitteilla samoja resursseja kuin pikselisävyttimet Jos tehdään useita transformaatioita, kerrotaan matriisit yhteen ennen verteksien kertomista ei siis tehdä verteksisävyttimessä asioita, jotka voidaan tehdä kerran framessa tai kerran per objekti CPU:lla esim. jotkin koordinaatistomuunnokset Valitaan valonlähteet, jotka vaikuttavat objektiin
Rasterointi/pikselisävytin Rasteroinnissa minimoidaan sitä, montako kertaa kukin pikseli piirretään Varmistetaan, että pikselit karsitaan mahdollisimman aikaisessa vaiheessa Hi-z, early z, front-to-back, occlusion query Z-puskuri pois päältä skyboxissa, menuissa ym. Hyödynnä ilmainen interpolointi rasterointi, bilineaarinen suodatus Kaikkia vektoreita ei tarvitse normalisoida Tunne GPU-rauta (esim. vektorointi, pistetulot) Usein molemmat if-haarat suoritetaan jos jokainen pikseli ei ole samaa mieltä
Tekstuurikaista Pullonkaula voi myös GPU:n sisäinen muistikaistanleveys johtuen yleensä lukuisista tekstuurien arvojen hakemisista erityisesti monet efektit ottavat jopa kymmeniä näytteitä pikseliä kohti pienemmät tekstuurit, vähemmän bittejä/pikseli tekstuurien pakkaaminen mipmap vähentää myös kaistan käyttöä prefetching: rauta hakee arvon etukäteen, jos sävyttimen toiminta on ennustettavaa
Mobiiliympäristöt Mobiililaitteilla tehoa suhteellisen vähän GPU:lla yleinen pullonkaula pikseliprosessointi ei screen space -tekniikoita voi kannattaa siirtää kuormaa CPU:lle kaistanleveysongelmat (pakkaa tekstuurit) Myös akun kulutus pitää ottaa huomioon Mobiili-GPU:iden arkkitehtuuri voi olla erilaista esim. ipad2: tile-based deferred Visuaalisuus huijataan: artistit töihin, sisäänleivotut varjot ym., trilight-malli tekstuuriin