Ada je tu: Nvidia uvádí GeForce RTX 4090, 4080 a 4080. Až 2–4× rychlejší, ale i o 80 % dražší

20. 9. 2022

Sdílet

 Autor: Nvidia
Nová architektura GPU je konečně odhalená: má novou architekturu shaderů, RT jader i Tensor jader, na druhou stranu některé novinky jí chybí. Šok jsou však ceny, které jsou hlavně u „levnějších“ modelů někde úplně jinde…

Nvidia měla na dnešek naplánovanou keynote na konferenci GTC 2022. Jak se všeobecně čekalo, šéf firmy Jen-Hsun Huang na této prezentaci vedle různých enterprise, AI a automobilních věcí konečně představil a odhalil nové grafiky: GeForce RTX 4090 a GeForce RTX 4080 s novou architekturou Ada Lovelace. Přijdou na trh v příštích dvou měsících, nicméně nemáme úplně dobré zprávy o cenách, které za ně bude Nvidia účtovat.

GeForce RTX 4000 je nová generace GPU i nová architektura. Ta se jmenuje po rané průkopnici programování a původně jsme o ní psali jako o Lovelace. Nicméně se zdá, že zatímco celé označení je „Ada Lovelace“, Nvidia teď většinou používá zkrácené označení Ada. Jde každopádně o jednu a tu samou architekturu, v tomto se neoficiální průsaky nemýlily. Nvidia pro výrobu čipů architektury Ada Lovelace používá proces 4N, který je upravenou verzí 4nm procesu TSMC (nebo podle některých neoficiálních zdrojů 5nm, nicméně úpravy mohou být tak dalekosáhlé, že si označení za 4nm zaslouží).

Ada (Lovelace): Nové shadery, RT jádra a Tensor jádra

Architektura přináší novou generaci bloků SM se shadery (FP32 jednotkami), čtvrtou generaci Tensor jader s podporou FP8 výpočtů (jde zřejmě o stejnou architekturu, jakou mají GPU Hopper) a třetí generaci RT jader pro ray tracing.

Ta mají údajně až 2× kapacitu pro hledání průsečíků paprsků s trojúhelníky. RT jádra třetí generace mají také nové enginy OMM (Opacity Micromap) a DMM (Displaced Micro-Mesh). První zrychluje výpočty ray tracingu s průhlednými texturami a objekty, druhý zrychluje budování BVH struktury (pomocné struktury boxů obalujících 3D struktury používané při analýze paprsků) u scén s komplexní geometrií.

Ada asi obnáší řadu různých změn pod povrchem (GPU mimochodem podporují CUDA 8.9, zatímco Ampere CUDA 8.6), ale Nvidia se zatím v prezentaci omezila na odhalení jedné důležité architektonické změny: Shader Execution Reordering (SER).

Zřejmě by mělo jít o nějakou formu schopnosti dynamicky měnit scheduling shaderů jednotlivým jednotkám SM a řadit jejich vykonání tak, aby optimálně využívaly zdroje GPU. Mohlo by tedy jít o nějakou obdobu Out-of-Order vykonávání instrukcí na procesorech. Tato technika má výrazně zlepšovat výkon shaderů (FP32 jednotek).

GPU Nvidia AD102 s architekturou Ada Lovelace ilustrace GPU Nvidia AD102 s architekturou Ada Lovelace, ilustrace (zdroj: Nvidia)

V této první vlně Nvidia oznámila dvě, respektive spíš tři modely grafik nové generace:

GeForce RTX 4090

Špičkou nabídky je GeForce RTX 4090, která by měla být založená na největším GPU generace Ada Lovelace, AD102, nicméně nemá jeho plnou konfiguraci. Podle Nvidie čip AD102 obsahuje 76,3 miliard tranzistorů a přes 18 000 jednotek FP32 (mělo by jít o 144 bloků SM, takže přesně 18 432 shaderů, 576 Tensor jader a 144 RT jader). Dokáže s nimi vyrobit výkon v FP32 výpočtech až 90 TFLOPS. Plocha křemíku je 608,4 mm².

V GeForce RTX 4090 je tento čip ale osekaný na 16 384 shaderů (128 bloků SM) a uvádí se pro ni teoretický výkon „jen“ 83 TFLOPS. Jejich takt je 2,23 GHz v základu a boost je uváděn 2,52 GHz, přičemž reálně asi GPU poběží výš. Podle Nvidie se „v laboratořích“ podařilo GPU Ada přetaktovat nad 3,0 GHz, ale nevíme, zda je řeč o čipu AD102 a zda to bylo prakticky použitelné.

Nvidia GeForce RTX 4090 Founders Edition 03 Nvidia GeForce RTX 4090 Founders Edition (zdroj: Nvidia)

Karta používá plnou 384bitovou sběrnici čipu AD102 a na ní nese 24 GB paměti GDDR6X, která by měla mít frekvenci 21,0 GHz efektivně, a tím pádem propustnost 1008 GB/s. GPU má 176 ROP a 72 MB L2 cache.

Tato grafika bude mít výchozí TDP 450 W jako GeForce RTX 3090 Ti (ale OC karty ho asi mohou mít zvýšené). Proti té je podle Nvidie tato karta rychlejší až 2–4×, nicméně ten čtyřnásobek asi platí pro nějaký vybraný netypický případ, bližší typickému výkonu asi bude ten údaj 2×. Ovšem pozor, Nvidia zřejmě také tyto údaje zakládá na testech ve hrách s aktivním upscalingem DLSS, takže pozor na to, že v nativním rozlišení by to mohlo být jiné. Jako obvykle pamatujte, že jde o oficiální výsledky, které mohou být selektivní nebo jinak ovlivněné. S „děláním obrázku“ proto raději počkejte na nezávislé recenze.

Nvidia GeForce RTX 4090 Founders Edition 02 Nvidia GeForce RTX 4090 Founders Edition (zdroj: Nvidia)

Ty nejsou tak daleko, protože podle Nvidie bude GeForce RTX 4090 vydaná 12. 10., tedy za tři týdny. Karta bude nabízená v nereferenčních verzích různých výrobců, ale i v provedení Founders Edition přímo od Nvidie, které můžete vidět na obrázcích. Jde o tříslotovou kartu se stejným principem chlazení, jaké je u RTX 3090 a 3090 Ti, ale ventilátory jsou větší.

RTX 4090 používá napájení 16pinovým konektorem ATX 3.0 / 12VHPWR, který musí dodávat alespoň 450 W. Volitelně je ale možné použít adaptér ze tří klasických osmipinových konektorů. Doporučen je 850W zdroj a lepší.

Tip: 660W spotřeba realita? Nereferenční karta GeForce RTX 4090 má čtyřslotový chladič, 13 heatpipe

Nepotěšující je cena. Jen-Hsun Huang oznámil, že za tento model zaplatíte v referenční verzi 1599 dolarů, tedy ještě víc, než stála při uvedení top karta minulé generace Ampere (RTX 3090). U nás to nyní s DPH vychází na 47 800 Kč / 1941 €.

Představení Nvidia GeForce RTX 4090 Představení Nvidia GeForce RTX 4090 (zdroj: Nvidia)

GeForce RTX 4080 16GB

Nvidia rovnou oznámila druhý model v řadě, ale jak už prosáklo dříve, ve skutečnosti jsou to modely dva. Lepší z těchto karet je „GeForce RTX 4080 16GB“. Nebylo to řečeno oficiálně, ale je zřejmě založená na dalším čipu, AD103. I zde Nvidia uvádí podobné zrychlení 2–4×, ale proti GeForce RTX 3080 Ti opět asi počítejte s tím, že typicky bude výsledek na spodní straně tohoto intervalu.

Tato 16GB verze GeForce RTX 4080 má 9728 shaderů (76 SM), které běží na podobném taktu jako u vyššího modelu – oficiálně je to 2,21 GHz v základu a 2,505 GHz v boostu.

Tato karta má 256bitovou paměťovou sběrnici s 16 GB paměti GDDR6X. Dle neoficiálních informací ale již na taktu 22,4 GHz efektivně, což jí dává propustnost 716 GB/s (to je přes 90 % propustnosti GeForce RTX 3080 10GB). Použité GPU má 64MB L2 cache a 112 ROP.

DP této karty má být údajně 320 W a také vyžaduje napájení buď ATX 3.0 16pinem s podporou 450 W, nebo adaptérem na tři osmipiny.

I tato karta bude dostupná jak v nereferenčních provedeních, tak v referenční verzi Founders Edition s podobným chladičem o tloušťce tři sloty. Tato karta vyjde až později, má být na prodej v listopadu (novembri). Cenu Nvidia stanovila na 1199 $, to u nás momentálně znamená 35 900 Kč / 1456 €. Připomeňme, že před dvěma lety obdoba této karty (RTX 3080) měla doporučenou cenu 699 $. Zdražení je to o 71,5 %…

Představení Nvidia GeForce RTX 4080 Představení Nvidia GeForce RTX 4080 (zdroj: Nvidia)

GeForce RTX 4070 RTX 4080 12GB

Nvidia ale bude prodávat ještě GeForce RTX 4080 s 12GB pamětí GDDR6X. Ta je nicméně asi natolik odlišná, že by asi spíše měla být nazývána RTX 4070 a fakticky jde asi spíš o třetí model v nabídce (jakým před dvěma lety byla RTX 3070 a před čtyřmi RTX 2070). Dokonce je údajně i založen na jiném čipu AD104.

GeForce RTX 4080 12GB má jen 7680 shaderů (60 SM). Jejich takty jsou asi o 100 MHz vyšší (základ 2,31 GHz, boost 2,61 GHz), to ale nedožene velkou redukci výpočetních jednotek. Také je zde problém, že se snížila nejen kapacita pamětí, ale i propustnost. GPU má jen 192bitovou sběrnici a na ní paměti GDDR6X s efektivní frekvencí 21,0 GHz, což dává propustnost 504 GB/s. Použité GPU má 48MB L2 cache a 80 ROP.

Specifikace prvích tří modelů GeForce RTX 4000 Specifikace prvních tří modelů GeForce RTX 4000 (zdroj: Nvidia)

TDP této karty je 285 W a její napájení je buď 16pinem (ovšem už stačí, aby zdroj dodával 300 W), nebo adaptérem na dva klasické osmipiny. Tato karta možná nebude mít Founders Edition verzi, ale jen nereferenční karty. Její vydání také zřejmě bude až v listopadu.

Výkon bude kvůli specifikacím výrazně nižší než u RTX 4080 16GB, nejspíš bude zhruba odpovídat postavení, které by normálně měl model RTX 4070. Proč Nvidia toto jméno tedy nepoužila? Možná proto, že i tato karta bude mít hodně vysokou cenu – firma si řekne o 899 $. Tato cena je o 80 % vyšší, než kolik stála oficiálně GeForce RTX 3070, takže o číslo vyšší označení ji asi má učinit stravitelnější. U nás doporučená částka po přidání DPH za nynějších měnových kurzů odpovídá 26 900 Kč / 1092 €.

Levnější grafiky pořád jen RTX 3000

Zbytek nabídky zatím oznámen nebyl a přijde asi až v příštím roce. Nvidia dokonce v prezentaci explicitně ukázala, že pod úrovní tří nových grafik Ada dál bude prodávat část GPU generace Ampere – modely GeForce RTX 3080, RTX 3070 a RTX 3060.

Vtírá se domněnka, zda Nvidia třeba tak brutálně zdražila oba modely GeForce RTX 4080 proto, aby ponechala prostor pro pokračující doprodávání těchto GPU generace Ampere bez toho, aby u nich musely být výrazně snížené ceny. Uvidíme, zda ji třeba později nepřinutí zlevnit konkurenční tlak od AMD, které během zhruba dvou měsíců uvede vlastní novou generaci GPU taktéž. AMD ale může prostě nasadit vyšší ceny také.

Mimochodem: leaker Kopite7kimi, který má velmi dobré interní zdroje, uvádí, že i cena RTX 4090 původně mohla být vyšší, na 1599 $ prý podle něj byla zrevidovaná až poměrně krátce před samotným zveřejněním prezentace. Tedy alespoň dle jeho zdrojů, jde ale o těžko ověřitelné tvrzení (pokud neuniknou nějaké pracovní slajdy).

Nereferenční grafiky GeForce generace RTX 4000 Nereferenční grafiky GeForce generace RTX 4000 (zdroj: Nvidia)

Konektivita a multimediální výbava: bez DisplayPortu 2.0 a jen s PCIe 4.0, ale zase s enkodérem do AV1

GPU architektury Ada Lovelace na druhou stranu nepřinášejí některé nové věci, které byste možná čekali. Například je zachováno rozhraní PCI Express 4.0 ×16, tato GPU tedy nepoužívají pro připojení do systému PCI Express 5.0. Nemusí to nutně být na škodu, je možné, že by dvojnásobná propustnost nové generace rozhraní nepřinesla nějaké reálné nárůsty výkonu. Nvidia také úplně odebrala podporu NVLinku a SLI, už ji nemá ani nejvyšší model RTX 4090.

Co je také bez změny, je zřejmě výstup DisplayPort. Podle specifikací Ada Lovelace zatím umí jen DisplayPort 1.4a, a ne DisplayPort 2.0, nicméně i tak je podporováno rozlišení až 7680 × 4320 bodů, včetně 12-bitového HDR při 60 Hz. Rozlišení 4K je možné až při 240 Hz, ale tyto konfigurace vyžadují kompresi DSC. Výstupy HDMI 2.1a podporují 4K při 120 Hz a 8K při 60 Hz, včetně HDR.

Také hardwarové dekodéry videa jsou, zdá se, beze změny proti Ampere, Nvidia použila stejnou 5. generaci dekódovacího bloku, která však podporuje víceméně všechny relevantní formáty až po AV1 (není však podporován formát VVC).

Cyber25

Ovšem důležitější je, že Ada Lovelace má už nový enkódovací (komprimační) blok 8. generace (Ampere a Turing mají 7. generaci). Přesněji, tyto enkodéry jsou na GeForce RTX 4080 i RTX 4090 dokonce dva paralelní. Nová generace by mohla dosahovat lepší kvality kódovaného videa, zejména ale přináší poprvé u Nvidie podporu enkódování do formátu AV1. Není to ale prvenství, to získal Intel se svými GPU Arc.

Zdroje: Nvidia (1, 2, 3), VideoCardz (1, 2)

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »