Files
godot-website/article/betsy-gpu-texture-compressor/index.html
2025-03-24 22:05:22 +00:00

15 lines
38 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html><html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=author content="Godot Engine"><meta name=description content="My name is Matias N. Goldberg, I normally maintain the 2.x branch of Ogre, and I wrote Betsy, a GPU texture compressor that runs on GPUs."><script defer data-domain=godotengine.org src=https://plausible.godot.foundation/js/script.file-downloads.outbound-links.js></script><meta property="og:site_name" content="Godot Engine"><meta property="og:url" content="https://godotengine.org/article/betsy-gpu-texture-compressor/"><meta property="og:type" content="website"><meta property="og:description" content="My name is Matias N. Goldberg, I normally maintain the 2.x branch of Ogre, and I wrote Betsy, a GPU texture compressor that runs on GPUs."><meta property="og:image" content="https://godotengine.org/storage/app/uploads/public/5fb/e92/016/5fbe920160a53834592537.jpg"><meta name=twitter:card content="summary_large_image"><meta property="twitter:domain" content="godotengine.org"><meta property="twitter:url" content="https://godotengine.org/article/betsy-gpu-texture-compressor/"><meta property="og:title" content="Introducing the Betsy GPU texture compressor Godot Engine"><title>Introducing the Betsy GPU texture compressor Godot Engine</title>
<link rel=alternate type=application/rss+xml title="Godot News" href=/rss.xml><link rel=alternate type=application/json title="Godot News" href=/rss.json><link rel=alternate type=application/atom+xml title="Godot News" href=/atom.xml><link rel=icon href=/assets/favicon.png sizes=any><link rel=icon href=/assets/favicon.svg type=image/svg+xml><link rel=stylesheet href=/assets/css/main.css?121><link rel=stylesheet href=/assets/css/header.css?1><link rel=stylesheet href=/assets/css/tobii.min.css><link rel=preload as=font href=/assets/fonts/Montserrat-Italic-VariableFont_wght.woff2 crossorigin><link rel=preload as=font href=/assets/fonts/Montserrat-VariableFont_wght.woff2 crossorigin><link rel=me href=https://mastodon.gamedev.place/@godotengine><input type=checkbox id=nav_toggle_cb><header class="flex column"><div class="container flex align-center"><div id=nav_head><a href=/ id=logo-link><img class=nav-logo src=/assets/logo.svg width=136 height=48 alt="Godot Engine">
<img class="nav-logo dark-logo" src=/assets/logo_dark.svg width=136 height=48 alt="Godot Engine"></a><div class=mobile-links><span class="fund mobile"><a href=https://fund.godotengine.org><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width:13px;fill:#fff;margin-right:4px"><path d="M47.6 300.4 228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6.0 115.2.0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg> Donate</a></span>
<label for=nav_toggle_cb id=nav_toggle_btn><img src=/assets/icons/hamburger.svg width=24 height=24 alt="Main menu"></label></div></div><nav id=nav><ul class=left><li><a href=/features/>Features</a><li><a href=/showcase/>Showcase</a><li><a href=/blog/>Blog</a><li><a href=/community/>Community</a><li><a href=https://godotengine.org/asset-library/asset>Assets</a></ul><ul class=right><li><a href=/download/windows/ class=set-os-download-url>Download</a><li><a href=https://docs.godotengine.org>Docs</a><li><a href=https://docs.godotengine.org/en/stable/contributing/how_to_contribute.html>Contribute</a><li class="fund desktop"><a href=https://fund.godotengine.org><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width:13px;fill:#fff;margin-right:4px;top:1px;position:relative"><path d="M47.6 300.4 228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6.0 115.2.0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg> Donate</a></ul></nav></div></header><script>document.addEventListener("click",function(e){const t=document.querySelector(".language-selector");if(!t)return;t.contains(e.target)||t.classList.remove("open")});function setLanguagePreference(e,t){e.preventDefault();const s=t.getAttribute("data-lang-path"),o=t.getAttribute("data-lang"),n=new Date;n.setDate(n.getDate()+365),document.cookie=`preferred_language=${o}; expires=${n.toUTCString()}; path=/; SameSite=Lax`,window.location.href=s}</script><main><style>body{background-color:var(--background-color)}h1{margin-bottom:8px;margin-top:32px}:not(pre)>code{background:var(--code-background-color);padding:1px 4px;font-size:.95em;border-radius:3px}pre{background:var(--codeblock-background-color);color:var(--codeblock-color)}pre code{display:block;overflow-x:auto;padding:.5em}.date-big{line-height:2;margin-left:32px}article{background-color:var(--base-color);box-shadow:0 3px 2px rgba(0,0,0,.15)}figure{margin:0}figure img{margin:0}article img,article video{max-width:100%;height:auto;display:block;margin:auto;margin-top:16px;margin-bottom:16px}article h1{margin-top:64px}article h2,article h3,article h4{margin-top:42px}.article-info{display:flex;flex-direction:column;gap:8px}.article-metadata{display:flex;gap:24px;align-items:center;font-family:var(--header-font-family);margin-bottom:12px}@media(max-width:900px){.article-metadata{flex-direction:column;align-items:flex-start;gap:16px}}.article-author{color:var(--base-color-text-subtitle-date);font-weight:700;font-size:18px;flex-grow:1;display:flex;gap:12px;align-items:center}.article-author .avatar{border-radius:100%;margin:0;background:0 0}.article-author .by{color:var(--base-color-text-subtitle)}.article-metadata .date{color:var(--base-color-text-subtitle-date)}.article-metadata .date.post-recent-highlight{color:var(--post-recent-highlight-color);opacity:.8}.article-metadata .date.post-recent-highlight::after{font-size:80%;content:"NEW";border:2px solid var(--post-recent-highlight-color);padding:2px 3px;margin-left:8px}.tag.active{filter:saturate(.75)}@media screen and (min-width:900px){article .content{width:70%;margin:auto}}@media(max-width:900px){body{background-color:var(--base-color)}article{background-color:initial;box-shadow:none}article img:first-child,article video:first-child{max-width:100%}}.blog-navigation{display:grid;grid-template-columns:1fr 1fr;padding-top:30px;padding-bottom:60px}.blog-navigation .next{text-align:right}@media(max-width:900px){.blog-navigation{grid-template-columns:1fr;gap:20px;border-top:1px solid var(--code-background-color)}.blog-navigation .next{text-align:left}}.blog-navigation span{opacity:.6;font-weight:700;margin-bottom:5px;display:block}.blog-navigation a{display:inline-block;text-decoration:none;color:inherit;opacity:.6;transition:opacity .2s}.blog-navigation a:hover{opacity:1}</style><link rel=stylesheet href=/assets/css/highlight.obsidian.min.css><div class=container><article class=padded><div class="content article-container"><figure class=article-cover><img src=/storage/app/uploads/public/5fb/e92/016/5fbe920160a53834592537.jpg title alt=" " class=rounded-lg style=width:100%;height:auto;background-color:initial></figure><div class=article-info><h1>Introducing the Betsy GPU texture compressor</h1><div class=article-metadata><div class=article-author><span>By: </span><img class=avatar width=25 height=25 src=/assets/images/authors/default_avatar.svg alt="Matias Goldberg" loading=lazy>
<span class=by>Matias Goldberg</span></div><span class=date data-post-date="2020-11-25 00:00:00 +0000">25 November 2020</span></div><div class=tags><a href=/blog/progress-report><div class="tag active">Progress Report</div></a></div></div><div class="card card-warning"><p>This article is from <strong>November 2020</strong>, some of its contents might be outdated and no longer accurate.<br>You can find up-to-date information about the engine in the <a href=https://docs.godotengine.org/en/stable/>official documentation</a>.</div><div class=article-body><p>My name is <a href=https://twitter.com/matiasgoldberg>Matias N. Goldberg</a>, I normally maintain the <a href=https://www.ogre3d.org/download/sdk/sdk-ogre-next>2.x branch of Ogre</a> aka <a href=https://github.com/OGRECave/ogre-next>ogre-next</a> and I wrote Betsy, a GPU texture compressor that runs on GPUs.<p>This work was commissioned by Godot Engine through the Software Freedom Conservancy to solve a major complaint: importing textures is excruciantly slow and takes many minutes.<p>Certain compression algorithms such as BC1-5 are quite simple and there are already fast high quality compression algorithms.<p>However algorithms such as BC6, ETC1, ETC2 and EAC are currently taking the majority of time and thus considerably attention were given to these.<p>Nonetheless Betsy implemented compute-shader versions for BC1,3,4,5,6, ETC1,2 and EAC algorithms.<p>Betsy works as a standard Command Line tool which means it can be used like any other exe tool outside of Godot.<p>More importantly, Betsy was developed with integration to Godot in mind, which is why its code has been isolated and abstracted from API details, written in GLSL shaders since Godot uses Vulkan.<p>Godot can make further performance improvements a standard CLI tool cant, because Godot doesnt have to initialize the graphics API every time it is invoked to compress a texture; and Godot can upload a texture just once and then encode into multiple formats (e.g. compress into BC1 and ETC1, or EAC and BC4 at the same time); rather than re-upload the texture again for every format we need to compress into.<p>Theoretically the CLI tool could accept to encode many textures in a single invocation (e.g. encode A.png into A.etc1.ktx A.etc2.ktx A.bc1.ktx and B.etc1.ktx B.etc2.ktx B.bc1.ktx) but we were worried that could complicate the code and make its integration with Godot harder. Additionally its hard to write a generic tool interface that would take advantage of such command line syntax.<h1 id=what-is-texture-compression-and-why-you-care>What is texture compression and why you care</h1><p>GPUs have precious limited available RAM. Too many big textures and you can see yourself running out of RAM.<p>When that happens, the GPU driver may fallback to streaming from regular system RAM over the PCI-E bus.<p>When this works, a significant performance degradation can happen.
When this doesnt, the game will just crash.<p>On phones, lower RAM consumption isnt the only reason. Smaller textures mean less bandwidth. And less bandwidth means better battery life (and sometimes less bandwidth can also mean higher performance on both desktop and mobile).<p>There are three major types of texture compression categories, but only one is useful for GPUs:<h2 id=lossless-eg-png>Lossless (e.g. PNG)</h2><p>This compression family lower the size of the texture using algorithms such as <a href=https://en.wikipedia.org/wiki/Gzip>gzip</a>, <a href=https://en.wikipedia.org/wiki/Bzip2>bzip</a> or <a href=https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm>LZMA</a>, among others. The main advantage is that the bits of the original texture are preserved. Thats why its <strong>loss</strong>-<strong>less</strong> i.e. no quality is lost.<p>The disadvantage is that they take a variable time to decompress i.e. it can take anywhere as 1 nanosecond to 30 seconds, or who knows, maybe more.<p>Unfortunately, due to this variable-time in decompression, these family of texture compression algorithms are of no use for a GPU.<p>Another disadvantage is that the compression ratio is unknown. A 1024x1024 may compress to 10kb, while another 1024x1024 texture may compress to 0.7MB.<p>This large unknown disparity in size also makes them inviable for GPUs.<p>This means that a 1024x1024 RGBA PNG texture that is only 10kb on disk <strong>must</strong> be decompressed before uploading to the GPU, thus always occupying 4MB of video RAM.<h2 id=lossy-eg-jpeg>Lossy (e.g. JPEG)</h2><p>This compression family uses algorithms like <a href=https://en.wikipedia.org/wiki/Discrete_cosine_transform>Discrete Cosine Transform</a> (aka DCT) to discard some details of the original image in order to hugely reduce their size.<p>That is: they do lose quality, but it is often hard to notice and achieve huge size reductions.<p>Unfortunately, theyre still a no-go for GPUs due to how expensive it is to decode DCT, and because decompressing often requires postprocessing passes that looks on the whole texture to hide artifacts and improve quality.<p>Therefore, just like in the PNG case, a 1024x1024 RGB JPEG texture that is only 120kb on disk <strong>must</strong> be decompressed before uploading to the GPU, thus always occupying 4MB of video RAM.<h2 id=gpu-lossy-eg-bc1-7-etc1-2-eac-pvrtc-astc>GPU lossy (e.g. BC1-7, ETC1-2, EAC, PVRTC, ASTC)</h2><p>We finally arrive at the family this article is about!<p>This family of compression algorithms meet two criterias that are <em>key</em> for GPUs:<ol><li>Decompression is fast and easy, and can be done in constant O(1) time. i.e. it always takes the same amount of time. e.g. if it takes 20 nanoseconds on a given GPU, it <em>always</em> takes 20 nanoseconds, no matter which pixel you need to fetch.<li>Compression ratio is constant. For example BC1 has a 1:8 compression ratio. That means that a 1024x1024 RGB texture which on RAM would occupy 4MB uncompressed, using BC1 it will <em>always</em> occupy 0.5MB</ol><p><strong>To be clear:</strong> except rare occassions, these formats cannot compete in quality against the other family of lossy formats (like JPEG), and they do not intend to.<p>Fast decompression and reasonable quality are the goals here. If you dont like it and have infinite money, you can always buy a 24GB RAM GPU.<p>But for the rest of us, this compression family is a good deal.<p><strong>Why are there so many formats (BC1, BC4, ETC1, etc, etc) you ask?</strong><p>Well, one reason is patents. For example ETC1, ETC2 and EAC appeared because phone vendors didnt want to pay the patent for the BC1-5 family of compression algorithms. This patent <a href=https://en.wikipedia.org/wiki/S3_Texture_Compression#Patent>expired on March 16, 2018</a>. However plenty of phones came before 2018.<p>Another reason is that some algorithms are better suited for different content. For example BC5 & EAC are very good at compressing <em>normal maps</em>, while BC1 & ETC1 are reasonably good for compressing RGB <em>photo-like</em> textures, and BC3 and ETC2 are reasonably good for RGBA photo-like textures.<p>Then some formats simply are improvements over time: BC7 came <em>much</em> later and is BC1 on steroids. Although it is double the size of BC1, it preserves quality much better than BC1 ever did. If your card supports DirectX10, then it supports BC7. But if its older than that, it wont.<p>Then there is the relatively-new ASTC which is a very good and flexible patent-free compression format, but desktop vendors resist it because it is very expensive in terms of HW manufacturing costs and die area size. Also not all phones support it, either because theyre too old, or due to costs.<h1 id=gpu-compression-can-take-a-lot-of-time>GPU compression can take (a lot of) time</h1><p>OK, so we just have to use those formats I mentioned above right? We use BC1, BC3, ETC1, etc.<p>Hold on! Not so fast:<p>Some GPUs support some textures but not others. While there are assumptions that can be made (e.g. all desktop DX10-class GPUs and newer support BC1-7), if you aim at different platforms (Windows, iOS, Android) the answer to the question <em>which format should we use?</em> then the answer will be: <strong>All of them</strong>.<p>Games usually have to bundle the same textures in all suitable formats and pick the best supported variation based on the HW it is running.<p>This isnt good for package sizes on disk, but we ensure compatibility. Some mobile games decide to download the texture pack after installation once it knows which device it is running on.<p>Other solutions like <a href=https://github.com/BinomialLLC/basis_universal>Basis Universal</a> use the term transcoding to generate an ETC1 or BC1 texture based on a single binary package. By trading off some quality and saving shared information, an ETC1 or BC1 texture can be generated on the fly.<p>The main problem is that some formats like BC1 are easy to encode. <strong>But other formats like ETC2 or BC7 can take a lot of time</strong>. Encoding a bunch of small textures can take literal minutes!<p>If an artist needs to wait 30 seconds to deploy to his phone every time he changes a few textures to see how it looks like, then weve got a big iteration problem!<p>Thats where betsy comes in.<h1 id=not-all-encoders-are-born-equal>Not all encoders are born equal</h1><p>Performance isnt the only metric! Another issue is that not all compressors produce the same quality!<p>There are many ways to encode a texture, and arriving to each solution will achieve slightly different results. Some compressors are better at preserving quality than others.<p>Betsy aims at encoding in high quality. Even if we may not end up being <em>the</em> best encoder, we cant sacrifice quality for speed (unless its configurable and the user wants to)<h1 id=betsy-is-a-gpu-encoder>Betsy is a GPU encoder</h1><p>Regular code is written to be run on a CPU. Betsy runs on a GPU.
GPUs are massively parallel machines. And GPU compression happens to parallelize quite well (some formats better than others)<p>What is faster depends on what CPU do you own vs what GPU do you own, and which compression format is selected.
A 64-core Ryzen Threadripper paired with a GeForce 1030 or Radeon HD 7350, the CPU will obviously beat the GPU.<p>However if you own the latest GeForce 3080 or Radeon 6800 XT, chances are a GPU encoder will win in encoding time.<p>It is important to mention that my work was mostly porting existing codecs written in C or C++ for the CPU, and convert them into GPU-friendly compute shaders. A notable exception is ETC2s P-mode which has been extended with our own efforts to significantly improve encoding quality of this mode.<p>You can find more information in the <a href=https://github.com/darksylinc/betsy/blob/master/LICENSE.md>LICENSE</a> and <a href=https://github.com/darksylinc/betsy/blob/master/README.md>README</a> pages.<h1 id=benchmark-performance>Benchmark Performance</h1><p>OK enough boring explanation! Lets see how Betsy fares against other encoders:<p>Notes:<ol><li>CPU is Intel i7 7700 3.6Ghz (4.2Ghz turbo). 2x16GB RAM 3000Mhz. 4 core, 8 threads.<li>GPU is AMD Radeon HD 560 2GB VRAM<li>Benchmark was done on Linux Ubuntu 18.04 LTS (HWE), Mesa 20.1.3<li><a href=https://github.com/google/etc2comp>etc2comp</a> is what Godot currently uses<li><a href=https://github.com/elasota/ConvectionKernels>ConvectionKernels</a> aka CVTT, which uses SIMD/SSE to run compress multiple blocks at once, similarly to what Betsy does.<li>Multithreaded tests were done spawning one process for each texture. This is suboptimal as it results in oversubscription for CVTT, and excessive contention of access to GPU for Betsy. The exception is etc2comp which supports native per-texture multithreading out of the box.<li>Single threaded numbers for CVTT are included because multithreading is not supported out of the box by CVTT<li>Single threaded numbers for etc2comp are not included because it supports multithreading out of the box, and single threaded version is simply way too slow.<li>The original Kodim sets resolution was too small to fully saturate the GPU in Betsy (according to radentop, around 60% GPU usage), which is why there is a strong difference between the multithreaded and singlethreaded versions. This difference gets much smaller for the 2048 upscaled set (keeping aspect ratio).<li>Betsy was run for every texture independently, which includes a small overhead for initializing OpenGL every time. This could be further improved to support encoding multiple textures with one single invocation.<li>Betsys first run in a system may take considerably larger time as compute shaders must be compiled and will later be cached by the driver in subsequent runs. This will happen again if the driver is updated.<li>All values are time in seconds.<li>Betsys Quality = 1 (medium quality) was included because it only affects ETC1 modes (not the extended T, H and P ETC2 modes), the quality difference is very low, but the performance gains are gigantic.<li>The scripts used to run this benchmark can be found on <a href=https://github.com/darksylinc/betsy_benchmark>Github/betsy_benchmark</a></ol><h2 id=etc1>ETC1</h2><table><thead><tr><th>Set<th><strong>Betsy MT (q = 1)</strong><th><strong>Betsy ST (q = 2)</strong><th><strong>Betsy MT (q = 2)</strong><th><strong>CVTT ST</strong><th><strong>CVTT MT</strong><th><strong>etc2comp MT</strong><tbody><tr><td>Kodim (25 textures)<td>1,36<td>11,45<td>9,01<td>34,88<td>6,85<td>18,35<tr><td>Upscaled to 2048xN Kodim<td>3,6<td>56,95<td>52,72<td>244,14<td>48,29<td>138,5</table><h2 id=etc2-rgb>ETC2 RGB</h2><table><thead><tr><th>Set<th><strong>Betsy MT (q = 1)</strong><th><strong>Betsy ST (q = 2)</strong><th><strong>Betsy MT (q = 2)</strong><th><strong>CVTT ST</strong><th><strong>CVTT MT</strong><th><strong>etc2comp MT</strong><tbody><tr><td>Kodim (25 textures)<td>3,16<td>13,21<td>10,85<td>45,44<td>8,16<td>39,29<tr><td>Upscaled to 2048xN Kodim<td>14,68<td>69,55<td>65,41<td>322,26<td>57,81<td>253,48</table><h2 id=bc1>BC1</h2><table><thead><tr><th>Set<th><strong>Betsy ST</strong><th><strong>Betsy MT</strong><th><strong>nvcompress ST (CPU)</strong><th><strong>nvcompress MT (CPU)</strong><tbody><tr><td>Kodim (25 textures)<td>3,12<td>0,6<td>12,6<td>2,4<tr><td>Upscaled to 2048xN Kodim<td>4,87<td>1,34<td>85,07<td>16,17</table><p><em>Notes:</em><ol><li><em>This needs more research, but it appears BC1 is bottleneck by GPU initialization, GPU uploads and data download, rather than encoding time</em><li><em>CVTT supports BC1, however its CLI tool doesnt come with BC1 support out of the box and I had no time to add support for it to do the benchmark</em></ol><h2 id=bc5>BC5</h2><table><thead><tr><th> <th><strong>Betsy ST</strong><th><strong>Betsy MT</strong><th><strong>nvcompress ST (CPU)</strong><th><strong>nvcompress MT (CPU)</strong><tbody><tr><td>Kodim set (25 textures)<td>2,94<td>0,73<td>1,97<td>0,42<tr><td>Upscaled to 2048xN Kodim set<td>5,84<td>1,70<td>13,18<td>0,86</table><p><em>Notes:</em><ol><li><em>This needs more research, but it appears BC5 is bottleneck by GPU initialization, GPU uploads and data download, rather than encoding time</em><li><em>CVTT supports BC5, however its CLI tool doesnt come with BC5 support out of the box and I had no time to add support for it to do the benchmark</em></ol><h1 id=benchmark-quality>Benchmark quality</h1><p>Notes:<ol><li>RSMLE: <strong>Lower is better</strong>. It means Root Mean Squared Logarithmic Error.<li>Betsy q = 1 is medium quality<li>Betsy q = 2 is maximum quality<li>It is unknown why ConvectionKernels some RSMLE increased in ETC2 over ETC1. It could be a CVTT bug, or a difference in how CVTT measures error vs how I measured it.</ol><h2 id=etc1-rgb>ETC1 RGB</h2><table><thead><tr><th> <th>Betsy (q = 1)<th>Betsy (q = 2)<th>ConvectionKernels<th>etc2comp<tbody><tr><td><strong>Average</strong><td><strong>0.0244532</strong><td><strong>0.02389376</strong><td><strong>0.02383864</strong><td><strong>0.02502284</strong><tr><td>kodim01.png<td>0.027957<td>0.027166<td>0.026895<td>0.028578<tr><td>kodim02.png<td>0.021755<td>0.021433<td>0.021336<td>0.021954<tr><td>kodim03.png<td>0.019651<td>0.019205<td>0.01924<td>0.019886<tr><td>kodim04.png<td>0.021453<td>0.021123<td>0.020989<td>0.021704<tr><td>kodim05.png<td>0.033579<td>0.032763<td>0.032588<td>0.034686<tr><td>kodim06.png<td>0.024893<td>0.024265<td>0.024038<td>0.02545<tr><td>kodim07.png<td>0.021911<td>0.021344<td>0.021057<td>0.022076<tr><td>kodim08.png<td>0.032968<td>0.032022<td>0.031604<td>0.034042<tr><td>kodim09.png<td>0.02002<td>0.019602<td>0.01945<td>0.020201<tr><td>kodim10.png<td>0.01986<td>0.019409<td>0.019306<td>0.020063<tr><td>kodim11.png<td>0.023587<td>0.023122<td>0.022928<td>0.024056<tr><td>kodim12.png<td>0.018662<td>0.018255<td>0.018087<td>0.018833<tr><td>kodim13.png<td>0.035328<td>0.034559<td>0.034416<td>0.036992<tr><td>kodim14.png<td>0.030336<td>0.029845<td>0.029985<td>0.030972<tr><td>kodim15.png<td>0.02186<td>0.021373<td>0.021296<td>0.02207<tr><td>kodim16.png<td>0.019238<td>0.018806<td>0.018663<td>0.019542<tr><td>kodim17.png<td>0.020262<td>0.019792<td>0.019767<td>0.020738<tr><td>kodim18.png<td>0.027783<td>0.02724<td>0.02728<td>0.028719<tr><td>kodim19.png<td>0.023358<td>0.022718<td>0.022477<td>0.023831<tr><td>kodim20.png<td>0.019751<td>0.019281<td>0.019395<td>0.020377<tr><td>kodim21.png<td>0.025063<td>0.024488<td>0.024325<td>0.025592<tr><td>kodim22.png<td>0.02337<td>0.022873<td>0.023036<td>0.023909<tr><td>kodim23.png<td>0.020536<td>0.020071<td>0.020435<td>0.021016<tr><td>kodim24.png<td>0.028179<td>0.027181<td>0.028165<td>0.029515<tr><td>kodim25.png<td>0.02997<td>0.029408<td>0.029208<td>0.030769</table><h2 id=etc2-rgb-1>ETC2 RGB</h2><table><thead><tr><th> <th>Betsy (q = 1)<th>Betsy (q = 2)<th>ConvectionKernels<th>etc2comp<tbody><tr><td><strong>Average</strong><td><strong>0.02432348</strong><td><strong>0.02378536</strong><td><strong>0.02400548</strong><td><strong>0.02412212</strong><tr><td>kodim01.png<td>0.027986<td>0.027183<td>0.027318<td>0.027943<tr><td>kodim02.png<td>0.021448<td>0.021169<td>0.020991<td>0.020887<tr><td>kodim03.png<td>0.019842<td>0.019471<td>0.018829<td>0.018195<tr><td>kodim04.png<td>0.021408<td>0.021105<td>0.020959<td>0.020991<tr><td>kodim05.png<td>0.03355<td>0.032722<td>0.033068<td>0.033774<tr><td>kodim06.png<td>0.02492<td>0.024283<td>0.024317<td>0.024731<tr><td>kodim07.png<td>0.02115<td>0.020651<td>0.020834<td>0.020812<tr><td>kodim08.png<td>0.033198<td>0.032233<td>0.03278<td>0.033356<tr><td>kodim09.png<td>0.019712<td>0.019335<td>0.019633<td>0.01955<tr><td>kodim10.png<td>0.019716<td>0.019285<td>0.019424<td>0.019459<tr><td>kodim11.png<td>0.023473<td>0.022995<td>0.023223<td>0.023395<tr><td>kodim12.png<td>0.018597<td>0.018215<td>0.018109<td>0.017998<tr><td>kodim13.png<td>0.03541<td>0.034635<td>0.035375<td>0.036008<tr><td>kodim14.png<td>0.030282<td>0.029791<td>0.029887<td>0.029976<tr><td>kodim15.png<td>0.021945<td>0.021515<td>0.021449<td>0.021243<tr><td>kodim16.png<td>0.01877<td>0.018382<td>0.01866<td>0.018717<tr><td>kodim17.png<td>0.020175<td>0.019729<td>0.019988<td>0.019844<tr><td>kodim18.png<td>0.028034<td>0.027471<td>0.027707<td>0.02808<tr><td>kodim19.png<td>0.023166<td>0.022545<td>0.022932<td>0.023125<tr><td>kodim20.png<td>0.019773<td>0.019299<td>0.019545<td>0.019642<tr><td>kodim21.png<td>0.02487<td>0.024313<td>0.024721<td>0.024833<tr><td>kodim22.png<td>0.02336<td>0.022859<td>0.023109<td>0.02323<tr><td>kodim23.png<td>0.019306<td>0.018971<td>0.019284<td>0.018592<tr><td>kodim24.png<td>0.028032<td>0.027072<td>0.02846<td>0.028448<tr><td>kodim25.png<td>0.029964<td>0.029405<td>0.029535<td>0.030224</table><h2 id=bc1-1>BC1</h2><table><thead><tr><th> <th><strong>Betsy</strong><th><strong>nvcompress</strong><tbody><tr><td><strong>Average</strong><td><strong>0.028589</strong><td><strong>0.026943</strong><tr><td>kodim01.png<td>0.034792<td>0.032564<tr><td>kodim02.png<td>0.02562<td>0.024146<tr><td>kodim03.png<td>0.020277<td>0.018898<tr><td>kodim04.png<td>0.023814<td>0.022424<tr><td>kodim05.png<td>0.040001<td>0.037991<tr><td>kodim06.png<td>0.0298<td>0.028163<tr><td>kodim07.png<td>0.024172<td>0.022762<tr><td>kodim08.png<td>0.042638<td>0.040288<tr><td>kodim09.png<td>0.022284<td>0.020803<tr><td>kodim10.png<td>0.022031<td>0.020619<tr><td>kodim11.png<td>0.028216<td>0.026493<tr><td>kodim12.png<td>0.020243<td>0.01892<tr><td>kodim13.png<td>0.045498<td>0.042873<tr><td>kodim14.png<td>0.034357<td>0.032726<tr><td>kodim15.png<td>0.024274<td>0.023026<tr><td>kodim16.png<td>0.021505<td>0.019977<tr><td>kodim17.png<td>0.023141<td>0.021715<tr><td>kodim18.png<td>0.033236<td>0.031521<tr><td>kodim19.png<td>0.027642<td>0.025856<tr><td>kodim20.png<td>0.02268<td>0.021438<tr><td>kodim21.png<td>0.029862<td>0.028107<tr><td>kodim22.png<td>0.026809<td>0.02531<tr><td>kodim23.png<td>0.020595<td>0.019312<tr><td>kodim24.png<td>0.034883<td>0.033199<tr><td>kodim25.png<td>0.036355<td>0.034444</table><h2 id=bc5-1>BC5</h2><table><thead><tr><th> <th><strong>Betsy</strong><th><strong>nvcompress</strong><tbody><tr><td><strong>Average</strong><td><strong>0,40713988</strong><td><strong>0,4071152</strong><tr><td>kodim01.png<td>0,380992<td>0,380943<tr><td>kodim02.png<td>0,147688<td>0,147673<tr><td>kodim03.png<td>0,341727<td>0,341719<tr><td>kodim04.png<td>0,35518<td>0,355176<tr><td>kodim05.png<td>0,317971<td>0,317893<tr><td>kodim06.png<td>0,467379<td>0,467362<tr><td>kodim07.png<td>0,368736<td>0,368721<tr><td>kodim08.png<td>0,507863<td>0,507801<tr><td>kodim09.png<td>0,511146<td>0,511134<tr><td>kodim10.png<td>0,482672<td>0,482662<tr><td>kodim11.png<td>0,364467<td>0,364442<tr><td>kodim12.png<td>0,570088<td>0,570084<tr><td>kodim13.png<td>0,388023<td>0,387951<tr><td>kodim14.png<td>0,313848<td>0,313811<tr><td>kodim15.png<td>0,495639<td>0,49563<tr><td>kodim16.png<td>0,404634<td>0,404633<tr><td>kodim17.png<td>0,329696<td>0,329685<tr><td>kodim18.png<td>0,229002<td>0,228954<tr><td>kodim19.png<td>0,424489<td>0,424464<tr><td>kodim20.png<td>0,70348<td>0,703468<tr><td>kodim21.png<td>0,487161<td>0,487138<tr><td>kodim22.png<td>0,393033<td>0,393022<tr><td>kodim23.png<td>0,364163<td>0,364154<tr><td>kodim24.png<td>0,424539<td>0,424513<tr><td>kodim25.png<td>0,404881<td>0,404847</table><h1 id=get-technical>Get Technical</h1><p>If youre a GPU nerd, we have an <a href=https://github.com/darksylinc/betsy/blob/master/Docs/technical_doc_advanced.md>in-depth analysis</a> of how betsys compute shaders are implemented.<p>Keep in mind it is quite advanced and assumes youre somewhat familiar with GPU shader coding.</div></div></article><div class=blog-navigation><div class=previous><span>Previous</span>
<a rel=prev href=/article/dev-snapshot-godot-3-2-4-beta-2/>Dev snapshot: Godot 3.2.4 beta 2</a></div><div class=next><span>Next</span>
<a rel=next href=/article/dev-snapshot-godot-3-2-4-beta-3/>Dev snapshot: Godot 3.2.4 beta 3</a></div></div></div><link rel=stylesheet href=/assets/css/anchor-link.css?1><link rel=stylesheet href=/assets/css/article-cards.css?3><script src=/assets/js/anchor-link.js></script><script>document.addEventListener("DOMContentLoaded",()=>{window.applyAnchorLinks(".article-body"),document.querySelectorAll(".article-cover img, .article-body img").forEach(e=>{if(e.classList.contains("lightbox-ignore"))return;const t=document.createElement("a");t.href=e.src,t.classList.add("lightbox"),t.dataset.group="article",e.parentNode.appendChild(t),t.appendChild(e)})})</script></main><footer class=footer-global><div class=wrapper><div class=columns><div class=col><h2>Godot Engine</h2><ul><li><a class=set-os-download-url href=/download>Download</a><li><a href=https://docs.godotengine.org>Documentation</a><li><a href=/features/>Features</a><li><a href=https://editor.godotengine.org/releases/latest/>Web editor</a><li><a href=/download/archive/>Release archive</a><li><a href=https://github.com/godotengine>Source code</a></ul></div><div class=col><h2>Project</h2><ul><li><a href=/blog/>Blog</a><li><a href=/code-of-conduct/>Code of conduct</a><li><a href=/governance/>Governance</a><li><a href=/teams/>Teams</a><li><a href=/priorities/>Priorities</a><li><a href=/community/>Communities</a></ul></div><div class=col><h2>Resources</h2><ul><li><a href=https://godotengine.org/asset-library/asset>Asset library</a><li><a href=/press/>Press kit</a><li><a href=/showcase/>Showcase</a><li><a href=/education/>Education</a></ul></div><div class=col><h2>Foundation</h2><ul><li><a href=https://godot.foundation/>About</a><li><a href=https://fund.godotengine.org>Donate</a><li><a href=/license/>License</a><li><a href=/privacy-policy/>Privacy policy</a><li><a href=/contact/>Contact us</a></ul></div></div><hr><div class=credits-and-socials><p>© 2007-2025 Juan Linietsky, Ariel Manzur and <a href=https://github.com/godotengine/godot/blob/master/AUTHORS.md target=_blank rel=noopener>contributors</a>. Hosted by the <a href=https://godot.foundation/ target=_blank rel=noopener>Godot Foundation</a>. Website <a href=https://github.com/godotengine/godot-website target=_blank rel=noopener>source code on GitHub</a>.<div class=social><a href=https://github.com/godotengine target=_blank rel=noopener title=GitHub><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6.0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6.0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3.0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1.0-6.2-.3-40.4-.3-61.4.0.0-70 15-84.7-29.8.0.0-11.4-29.1-27.8-36.6.0.0-22.9-15.7 1.6-15.4.0.0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5.0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9.0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4.0 33.7-.3 75.4-.3 83.6.0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6.0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9.0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
</a><a href=https://bsky.app/profile/godotengine.org target=_blank rel=noopener title=Bluesky><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M407.8 294.7c-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3zM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3 61.6-9.4 37.5-1.7 21.6 5.5 3.3 13.8.0 41.9.0 58.4S9.1 194 15 213.9c19.5 65.7 89.1 87.9 153.2 80.7 3.3-.5 6.6-.9 10-1.4-3.3.5-6.6 1-10 1.4-93.9 14-177.3 48.2-67.9 169.9C220.6 589.1 265.1 437.8 288 361.1c22.9 76.7 49.2 222.5 185.6 103.4 102.4-103.4 28.1-156-65.8-169.9-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3 64.1 7.1 133.6-15.1 153.2-80.7C566.9 194 576 75 576 58.4s-3.3-44.7-21.6-52.9c-15.8-7.1-40-14.9-103.2 29.8C385.1 81.9 314.1 176.4 288 227.1z"/></svg>
</a><a href=https://mastodon.gamedev.place/@godotengine target=_blank rel=noopener title=Mastodon><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5.0.0.0-63.7 28.5-63.7 125.7.0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5.0 01-.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6V190.1c0-49.7-64-51.6-64 6.9v62.5H201V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/></svg>
</a><a href=https://discord.gg/godotengine target=_blank rel=noopener title=Discord><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M524.5 69.8a1.5 1.5.0 00-.8-.7A485.1 485.1.0 00404.1 32a1.8 1.8.0 00-1.9.9 337.5 337.5.0 00-14.9 30.6 447.8 447.8.0 00-134.4.0 309.5 309.5.0 00-15.1-30.6 1.9 1.9.0 00-1.9-.9A483.7 483.7.0 00116.1 69.1a1.7 1.7.0 00-.8.7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 00.8 1.4A487.7 487.7.0 00176 479.9a1.9 1.9.0 002.1-.7 348.2 348.2.0 0030-48.8 1.9 1.9.0 00-1-2.6 321.2 321.2.0 01-45.9-21.9 1.9 1.9.0 01-.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8.0 011.9-.3c96.2 43.9 200.4 43.9 295.5.0a1.8 1.8.0 011.9.2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9.0 01-.2 3.1 301.4 301.4.0 01-45.9 21.8 1.9 1.9.0 00-1 2.6 391.1 391.1.0 0030 48.8 1.9 1.9.0 002.1.7 486 486 0 00147.2-74.1 1.9 1.9.0 00.8-1.4c12.2-126.7-20.6-236.8-87-334.5zm-302 267.8c-29 0-52.8-26.6-52.8-59.2s23.4-59.3 52.8-59.3c29.7.0 53.3 26.8 52.8 59.2.0 32.7-23.4 59.3-52.8 59.3zm195.4.0c-29 0-52.8-26.6-52.8-59.2s23.3-59.3 52.8-59.3c29.7.0 53.3 26.8 52.8 59.2.0 32.7-23.2 59.3-52.8 59.3z"/></svg>
</a><a href=https://www.reddit.com/r/godot title=Reddit target=_blank rel=noopener><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 256C0 114.6 114.6.0 256 0S512 114.6 512 256 397.4 512 256 512H37.1c-13.7.0-20.5-16.5-10.9-26.2L75 437C28.7 390.7.0 326.7.0 256zM349.6 153.6c23.6.0 42.7-19.1 42.7-42.7s-19.1-42.7-42.7-42.7c-20.6.0-37.8 14.6-41.8 34-34.5 3.7-61.4 33-61.4 68.4v.2c-37.5 1.6-71.8 12.3-99 29.1-10.1-7.8-22.8-12.5-36.5-12.5-33 0-59.8 26.8-59.8 59.8.0 24 14.1 44.6 34.4 54.1 2 69.4 77.6 125.2 170.6 125.2s168.7-55.9 170.6-125.3c20.2-9.6 34.1-30.2 34.1-54 0-33-26.8-59.8-59.8-59.8-13.7.0-26.3 4.6-36.4 12.4-27.4-17-62.1-27.7-1e2-29.1v-.2c0-25.4 18.9-46.5 43.4-49.9 4.4 18.8 21.3 32.8 41.5 32.8zM177.1 246.9c16.7.0 29.5 17.6 28.5 39.3s-13.5 29.6-30.3 29.6-31.4-8.8-30.4-30.5S160.3 247 177 247zm190.1 38.3c1 21.7-13.7 30.5-30.4 30.5s-29.3-7.9-30.3-29.6c-1-21.7 11.8-39.3 28.5-39.3s31.2 16.6 32.1 38.3zm-48.1 56.7c-10.3 24.6-34.6 41.9-63 41.9s-52.7-17.3-63-41.9c-1.2-2.9.8-6.2 3.9-6.5 18.4-1.9 38.3-2.9 59.1-2.9s40.7 1 59.1 2.9c3.1.3 5.1 3.6 3.9 6.5z"/></svg>
</a><a href=/rss.xml title=RSS target=_blank rel=noopener><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M64 32C28.7 32 0 60.7.0 96V416c0 35.3 28.7 64 64 64h320c35.3.0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24 137 0 248 111 248 248 0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-2e2-2e2-2e2-13.3.0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24 83.9.0 152 68.1 152 152 0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104-13.3.0-24-10.7-24-24zm0 120a32 32 0 1164 0 32 32 0 11-64 0z"/></svg></a></div></div></div></footer><script defer src=/assets/js/localize.js?3></script><script defer src=/assets/js/tobii.min.js></script><script defer src=/assets/js/highlight.min.js?1></script><script defer src=/assets/js/highlight.gdscript.min.js?1></script><script>document.addEventListener("DOMContentLoaded",()=>{document.querySelectorAll("pre code").forEach(e=>{hljs.highlightBlock(e)}),document.querySelectorAll("[data-post-date]").forEach(e=>{Date.parse(e.dataset.postDate)>Date.now()-1e3*60*60*48&&e.classList.add("post-recent-highlight")}),new Tobii({zoom:!1});const e=document.querySelectorAll(".set-os-download-url");for(let t=0;t<e.length;t++){const n=e[t];let o="download";"version"in n.dataset&&n.dataset.version==="3"&&(o="download/3.x");let s="windows";navigator.platform.indexOf("Mac")!==-1?s="macos":navigator.platform.indexOf("Linux")!==-1&&(s="linux"),n.href=`/${o}/${s}/`}})</script>