<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://netdex.org/feed.xml" rel="self" type="application/atom+xml"/><link href="https://netdex.org/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-09-07T21:07:59-04:00</updated><id>https://netdex.org/feed.xml</id><title type="html">netdex</title><subtitle>netdex</subtitle><author><name>netdex</name></author><entry><title type="html">Overclocking Ryzen 5000, suffering edition™</title><link href="https://netdex.org/2022/11/05/overclock-ryzen-5000/" rel="alternate" type="text/html" title="Overclocking Ryzen 5000, suffering edition™"/><published>2022-11-05T00:00:00-04:00</published><updated>2022-11-05T00:00:00-04:00</updated><id>https://netdex.org/2022/11/05/overclock-ryzen-5000</id><content type="html" xml:base="https://netdex.org/2022/11/05/overclock-ryzen-5000/"><![CDATA[<div class="toc-source"> <ul id="markdown-toc"> <li><a href="#the-tradeoff" id="markdown-toc-the-tradeoff">The tradeoff</a></li> <li><a href="#we-need-to-go-faster" id="markdown-toc-we-need-to-go-faster">We need to go faster</a></li> <li><a href="#precision-boost-overdrive" id="markdown-toc-precision-boost-overdrive">Precision Boost Overdrive</a> <ul> <li><a href="#limits" id="markdown-toc-limits">Limits</a></li> <li><a href="#curve-optimizer" id="markdown-toc-curve-optimizer">Curve Optimizer</a></li> <li><a href="#scalar" id="markdown-toc-scalar">Scalar</a></li> <li><a href="#auto-oc" id="markdown-toc-auto-oc">Auto OC</a></li> <li><a href="#thermal-limit" id="markdown-toc-thermal-limit">Thermal Limit</a></li> </ul> </li> <li><a href="#was-it-worth-it" id="markdown-toc-was-it-worth-it">Was it worth it?</a></li> <li><a href="#references" id="markdown-toc-references">References</a></li> </ul> </div> <p class="lead">Overclocking computer components has traditionally been a delicate balance of power, thermals, voltage, and clock speed.</p> <h2 id="the-tradeoff">The tradeoff</h2> <p>The higher the clock speed, the more operations your processor can complete within a given period of time. However, it takes a minimum amount of time for the logic in your processor to be driven to a given logic level. If your clock speed is too fast, you’ll start violating timing requirements and cause instability. Increasing voltage reduces the amount of time it takes to settle to a given logic level, subsequently increasing your maximum theoretical clock speed. Unfortunately, it also increases power consumption. The cost of electricity aside, the components on your board are only capable of delivering power up to their specified limits. Before you reach that limit though, the increase in power consumption forces you to mind your thermals. Not only to avoid exceeding their maximum rated temperature, but also since silicon performs worse at higher temperatures.</p> <p>To make things worse, your processor dynamically scales its clock speed based on load. This is especially important on battery power since hurtling along at maximum clock speed is a waste of energy at idle. Reducing clock speed lets us achieve the same result with less voltage, and thus less power. This means, for every single clock speed our processor could be at, we need to ensure:</p> <ul> <li>The voltage is sufficient such that logic levels settle fast enough</li> <li>The voltage is low enough such that it doesn’t use too much power</li> <li>The power is low enough such that the processor (and any power delivery) doesn’t get too hot</li> <li>Some other details I’ll leave out (voltage is low enough such that electro-migration doesn’t happen, some considerations related to the process node etc.)</li> </ul> <p>Of course, there are clock speeds our processor won’t be able to achieve since at least one of the conditions will be violated. For example, there will most certainly be a frequency where the voltage you’d need to drive the processor at to achieve it would cause it to overheat or incur damage.</p> <p>Something I glossed over is cooling, which is what buys the thermal headroom in the first place. If your components are air-cooled, you’ll also need to make a trade-off between fan speed, cooling performance, and noise. I could write an entire article about cooling, so I’ll leave it at that.</p> <h2 id="we-need-to-go-faster">We need to go faster</h2> <p>Modern processors are capable of on-demand boosting, which allows them to temporarily increase their clock speeds past their base specification depending on load. Many workflows tend to be bursty in nature, such as browsing the web. Boosting allows us to adapt to bursty workloads by temporarily increasing clock speeds, improving perceivable latency amongst other things. Sometimes you could make the argument that completing work faster allows the processor to return to an idle state quicker.</p> <p>Why not stay at boost clocks indefinitely? We are abusing the fact that the processor takes time to heat up due to thermal mass, so it’s possible to achieve higher than normal clocks for a short period of time. There is no equilibrium though - eventually the processor will get too hot to continue running at boost frequencies, at which point it needs to decide between lowering its frequency or returning to sand.</p> <h2 id="precision-boost-overdrive">Precision Boost Overdrive</h2> <p>The latest Ryzen processors from AMD have a feature named Precision Boost (and Precision Boost 2), which balances several of the factors I mentioned above to scale clock speeds on a per-core basis. If you’re interested in the details, consider looking it up because how Precision Boost works has been covered to death.</p> <p>Precision Boost Overdrive (PBO) is a feature which allows you to overclock your processor by tweaking the parameters used by Precision Boost. This differs from traditional overclocking in which we typically set either the maximum boost or base frequency to a fixed value, then tune voltage to an appropriate fixed value or auto-offset. While this might be easier, we are leaving a lot on the table for reasons I’ll briefly go over later. Here are the knobs and dials which PBO gives us to tune the behavior of Precision Boost:</p> <h3 id="limits">Limits</h3> <dl> <dt>Package Power Tracking (PPT)</dt> <dd>The maximum power that can be delivered to the CPU socket, in watts.</dd> <dt>Thermal Design Current (TDC)</dt> <dd>The maximum current that can be delivered in sustained loads, when constrained by thermals, in amps.</dd> <dt>Electrical Design Current (EDC)</dt> <dd>The maximum current that can be delivered in transient loads, when constrained by electrical characteristics, in amps.</dd> </dl> <p>PBO allows us to tweak the power and current limits applied to the CPU on a global basis. Raising power limits allows cores to operate on higher clock speeds by indirectly giving them more budget to be driven at higher voltages. Of course, there’s a limit to how high we can push them, which is thermals again. Not only of the CPU, but also the regulators and other components on the motherboard.</p> <h3 id="curve-optimizer">Curve Optimizer</h3> <dl> <dt>Voltage-Frequency Curve</dt> <dd>A relation which specifies the minimum voltage required to drive the processor at a given frequency.</dd> </dl> <p>Curve optimizer allows us to apply an offset to the voltage-frequency curve. This is similar to undervolting by applying a negative auto-offset, except curve optimizer allows us to do it on a per-core basis. This is important because even though they are on the same die, you will see that some cores require less voltage to run at a certain frequency. By achieving the same frequency but with less voltage, we unlock thermal headroom which allows us to remain at higher frequencies for a longer period of time.</p> <p>The downside is that it is notoriously difficult to ensure your configuration is stable. With traditional overclocking, you could be fairly confident that your overclock is stable by just running a torture test, since the voltage is fixed at a point which should be sufficient for any frequency the CPU could possibly encounter. This means that the maximum frequency is where we would most likely see instability. Of course, this means our voltages are unnecessarily high for lower frequencies, costing us precious thermal headroom and power consumption. The situation is a bit better with auto-offset voltages, but the offset is constrained by the worst core on the processor and terrible implementations by motherboard vendors.</p> <p>Since we are applying the offset to a <em>curve</em>, we effectively have to test every frequency on every core in order to ensure system stability.</p> <p><img src="/assets/images/posts/overclock-ryzen-5000/vf1.svg" alt="A voltage-frequency curve that dips below the minimum stable voltage at low frequencies"/> For example, if the green line represents the minimum voltage required at a given frequency for the core to be stable, and the red line is the current voltage-frequency curve, we would see instability at lower frequencies (i.e. when the core is idle!)</p> <p>We can’t move the green line since it is a physical property of the core, but we can move the red line around using curve optimizer by applying a voltage offset.</p> <p><img src="/assets/images/posts/overclock-ryzen-5000/vf2.svg" alt="The same curve raised by 15mV so that it clears the minimum at every frequency"/></p> <p>Fifteen millivolts is enough to clear the green line everywhere, at the cost of exactly the headroom we were trying to win. The interesting direction is the other one: push the red line down as far as it will go without crossing the green line at <em>any</em> frequency, on <em>any</em> core.</p> <p>There exist tools such as CoreCycler which helps exercise different parts of the voltage-frequency curve on a per-core basis, to help with verifying stability. After a bunch of tedious work, we can arrive at a set of core offsets which will generally reduce the amount of power our CPU consumes, giving it more headroom.</p> <p><img src="/assets/images/posts/overclock-ryzen-5000/cotune.png" alt="Spreadsheet of per-core curve optimizer offsets across six CoreCycler runs"/></p> <p>It’s worth mentioning that CoreCycler creates synthetic conditions which aren’t realistic, which is why you’ll sometimes see instability with CoreCycler but no issues under normal use. Sometimes you’ll even need to apply positive offsets, which implies that the processor was unstable out of the box!</p> <h3 id="scalar">Scalar</h3> <dl> <dt>Scalar Multiplier</dt> <dd>How much silicon wear the boost algorithm is allowed to accept when it decides how long to hold a high voltage, from 1X to 10X.</dd> </dl> <p>Precision Boost carries an internal model of how fast the silicon wears out. Time at high voltage wears it faster than time at low voltage, and the algorithm keeps the total over the part’s expected life to something it considers safe. Scalar tells it to allow more: at 10X, AMD’s framing is that you’re accepting roughly ten times the degradation rate you’d otherwise see.</p> <p>So what does it actually buy? Time. The core sits nearer the top of the curve for longer before the algorithm pulls it back. It doesn’t raise the ceiling, and it won’t make a core stable at a voltage that wasn’t stable before. It buys that time by holding voltage higher, which costs the same thermal headroom that was pulling the core down in the first place. On air, that mostly cancels out. I left it at 1X, though I’ve never seen the degradation claim measured by anyone outside AMD, and I don’t know how you’d measure it in under a decade.</p> <h3 id="auto-oc">Auto OC</h3> <dl> <dt>Maximum Boost Offset</dt> <dd>An offset applied to the maximum boost frequency the processor can achieve globally, in 25 MHz steps, up to +200 MHz.</dd> </dl> <p>This is the one knob in PBO that behaves like traditional overclocking: it raises a number, and the number is a ceiling. It’s also the knob that does the least, because a ceiling only matters if we were touching it. Under an all-core load we never are. PPT or thermals run out long before frequency does. It shows up in one- and two-thread work, where a single core has the whole power and thermal budget to itself, which is conveniently most of what a desktop does.</p> <p>The catch is that raising the ceiling doesn’t grant us the voltage to reach it. The curve still says what it says, and the new top of it wants more voltage than the old top did. That’s why curve optimizer and maximum boost offset are recommended together and rarely alone: the offset asks for frequency we have no headroom for, and curve optimizer is what produces the headroom.</p> <h3 id="thermal-limit">Thermal Limit</h3> <dl> <dt>Maximum Temperature</dt> <dd>The die temperature the boost algorithm targets, measured from a network of sensors spread across the die and reported as the hottest of them.</dd> </dl> <p>The name suggests a safety valve. It is really a target: the algorithm raises clocks until it runs into a limit, and on a decent cooler with the power limits already raised, the limit it runs into is this one. A chip sitting at 90C under sustained load isn’t in distress. Buy a better cooler and it won’t run any cooler. It will run faster at the same temperature.</p> <p>Which means the knob only works in one direction. We can’t set it above the part’s TjMax (90C on the 5900X and 5950X, 95C on the 5600X and 5800X), but we can set it lower. Cap it at something like 75C and we spend less time at high clocks, but the fans never have to ramp. Mind that the reported number is a maximum over a sensor network, sampled quickly, so it spikes. Key a fan curve straight off it and you get a fan that screams every time a background task wakes up.</p> <h2 id="was-it-worth-it">Was it worth it?</h2> <p>The screenshot above is six rounds of CoreCycler across twelve cores at 185 W PPT, 125 A TDC and 170 A EDC, and it took most of a weekend: a few hours of the machine being unusable, then a crash or a clean pass, then one number changed and around again.</p> <p>The cores converged unevenly. Six settled at -27, the rest at -20, -11, -9, -4, 0 and +7. The low-numbered cores are the ones AMD’s ordering marks as the best of the batch, which makes them the cores a single-threaded load lands on, and also the ones that tolerate the least undervolt. The fast cores are the fragile cores.</p> <p>And then there’s core 3, which needed <strong>+7</strong>. A positive offset: that core couldn’t hold its own stock curve out of the box, and had been quietly returning wrong answers for however long I’d owned it. Nothing had visibly crashed.</p> <p>I never benchmarked this properly before and after, so I can’t put a number on what the tuning bought. Probably single digits in the things that respond at all. Core 3 was unstable at stock, and isn’t now.</p> <h2 id="references">References</h2> <ul> <li><a href="https://skatterbencher.com/2021/08/18/skatterbencher-26-amd-ryzen-9-5900x-overclocked-to-5223-mhz">SkatterBencher #26: AMD Ryzen 9 5900X overclocked to 5223 MHz</a></li> <li><a href="https://www.gamersnexus.net/guides/3491-explaining-precision-boost-overdrive-benchmarks-auto-oc">GamersNexus: Explaining Precision Boost Overdrive, benchmarks &amp; auto OC</a></li> <li><a href="https://www.youtube.com/watch?v=NTkRuZ3uSO4">Unhinged Systems: Finally Overclocking my Ryzen 9 5950X - Ryzen 5000</a></li> </ul>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Overclocking computer components has traditionally been a delicate balance of power, thermals, voltage, and clock speed.]]></summary></entry><entry><title type="html">5GHz Access Point with Intel WiFi</title><link href="https://netdex.org/2022/07/20/iwlwifi-5g-ap/" rel="alternate" type="text/html" title="5GHz Access Point with Intel WiFi"/><published>2022-07-20T00:00:00-04:00</published><updated>2022-07-20T00:00:00-04:00</updated><id>https://netdex.org/2022/07/20/iwlwifi-5g-ap</id><content type="html" xml:base="https://netdex.org/2022/07/20/iwlwifi-5g-ap/"><![CDATA[<p>I have a thin client that I wanted to turn into a WiFi access point. “Consumer APs are basically just really slow computers. Then, shouldn’t a moderately slow computer be a really fast AP?” In theory, it checked out. Just slap <a href="https://openwrt.org">OpenWrt</a> onto it or something of that ilk, and Bob’s your uncle.</p> <p>My thin client has built-in WiFi, which simplifies things somewhat because there aren’t any PCIe expansion slots to spare after shoving a second ethernet card into it. The wireless adapter in question is an Intel Wireless-AC 9560 in M.2 form factor, boasting 2x2 802.11ac support – enabling high-throughput transfers over the 5GHz band. On Linux, this adapter is controlled by the iwlwifi driver.</p> <p><img src="/assets/images/posts/iwlwifi-5g-ap/luci-channel-52.png" alt="LuCI wireless configuration, set to AC mode on channel 52 at 80 MHz width"/></p> <p>I proceeded to download the necessary drivers and firmware on my OpenWrt instance, which was simple enough. Then, I created a 5GHz access point through LuCI, pulled out my phone, and scanned for networks. However, no matter how long I waited, nothing appeared. I couldn’t possibly know at that moment how much time I’d waste trying to get this to work.</p> <p>Looking at the logs, it seems to be failing in the DFS CAC stage.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Thu Jul 21 21:53:49 2022 daemon.notice hostapd: Remove interface 'wlan0'
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: interface state DISABLED-&gt;DISABLED
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: AP-DISABLED
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: CTRL-EVENT-TERMINATING
Thu Jul 21 21:53:49 2022 daemon.err hostapd: hostapd_free_hapd_data: Interface wlan0 wasn't started
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: nl80211: deinit ifname=wlan0 disabled_11b_rates=0
Thu Jul 21 21:53:49 2022 daemon.notice netifd: Wireless device 'radio0' is now down
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: Configuration file: /var/run/hostapd-phy0.conf (phy wlan0) -&gt; new PHY
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: interface state UNINITIALIZED-&gt;COUNTRY_UPDATE
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: interface state COUNTRY_UPDATE-&gt;HT_SCAN
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlan0: interface state HT_SCAN-&gt;DFS
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlan0: DFS-CAC-START freq=5260 chan=52 sec_chan=1, width=0, seg0=54, seg1=0, cac_time=60s
Thu Jul 21 21:53:50 2022 daemon.err hostapd: DFS start_dfs_cac() failed, -1
Thu Jul 21 21:53:50 2022 daemon.err hostapd: Interface initialization failed
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlan0: interface state DFS-DISABLED
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlan0: AP-DISABLED
</code></pre></div></div> <p><a href="https://www.asus.com/support/FAQ/1045936/">DFS</a> is a technology that allows 5GHz WiFi to coexist with certain radar technologies which share the same radio spectra. It works by performing a Channel Availability Check (CAC) before transmitting on any DFS channel. This check involves listening on the channel for radar activity. Whether or not a channel is DFS or not depends on your country’s local ordinance, formally referred to as <em>regulatory domain</em> (or regdom for short). It seems that for whatever reason, DFS CAC doesn’t work with this combination of wireless adapter and driver. I wouldn’t be surprised if it’s not even implemented, because CAC is only used in AP mode. This network adapter belongs in laptops, which never enter AP mode in normal operation.</p> <p>Yeah, so DFS seems to be broken. And the channel is marked DFS because of the regdom. Where did the regdom come from?</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@OpenWrt:~# iw reg get
global
country 00: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (2457 - 2482 @ 20), (N/A, 20), (N/A), AUTO-BW, PASSIVE-SCAN
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM, PASSIVE-SCAN
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW, PASSIVE-SCAN
        (5490 - 5730 @ 160), (N/A, 20), (0 ms), DFS, PASSIVE-SCAN
        (5735 - 5835 @ 80), (N/A, 20), (N/A), PASSIVE-SCAN
        (57240 - 63720 @ 2160), (N/A, 0), (N/A)
...
</code></pre></div></div> <p>The default regdom is “world”, which is an overly restrictive regdom that should fly no matter what country you’re in. Let’s set it to somewhere that has some 5GHz channels that aren’t DFS, <a href="https://en.wikipedia.org/wiki/List_of_WLAN_channels#5_GHz_(802.11a/h/j/n/ac/ax)">like India</a>.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@OpenWrt:~# iw reg get
global
country IN: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 30), (N/A)
        (5150 - 5250 @ 80), (N/A, 30), (N/A)
        (5250 - 5350 @ 80), (N/A, 24), (N/A)
        (5470 - 5725 @ 160), (N/A, 24), (N/A)
        (5725 - 5875 @ 80), (N/A, 30), (N/A)

phy#0 (self-managed)
country 00: DFS-UNSET
        (2402 - 2437 @ 40), (6, 22), (N/A), AUTO-BW, NO-HT40MINUS, NO-80MHZ, NO-160MHZ
        (2422 - 2462 @ 40), (6, 22), (N/A), AUTO-BW, NO-80MHZ, NO-160MHZ
        (2447 - 2482 @ 40), (6, 22), (N/A), AUTO-BW, NO-HT40PLUS, NO-80MHZ, NO-160MHZ
        (5170 - 5190 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, PASSIVE-SCAN
        (5190 - 5210 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, PASSIVE-SCAN
        (5210 - 5230 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, PASSIVE-SCAN
        (5230 - 5250 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, PASSIVE-SCAN
        (5250 - 5270 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5270 - 5290 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5290 - 5310 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5310 - 5330 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5490 - 5510 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5510 - 5530 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5530 - 5550 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5550 - 5570 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5570 - 5590 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5590 - 5610 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5610 - 5630 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5630 - 5650 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5650 - 5670 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5670 - 5690 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5690 - 5710 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5710 - 5730 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5735 - 5755 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5755 - 5775 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5775 - 5795 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5795 - 5815 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5815 - 5835 @ 20), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-HT40PLUS, NO-80MHZ, NO-160MHZ, PASSIVE-SCAN
</code></pre></div></div> <p>That didn’t change anything. The adapter is ignoring our regdom. To be more precise, it’s applying the global regdom as a mask, which can only further restrict the adapter’s self-managed regdom. Not that you could further restrict it much, since it’s already super restrictive. Every single 5GHz channel is marked DFS. Even worse, they are also marked PASSIVE-SCAN, also known as “no IR” (initiating radiation). This is a no-go for AP mode, since PASSIVE-SCAN disallows transmission unless the adapter is associated with an AP.</p> <p>Now, where does the adapter’s self-managed regdom come from? Turns out it comes from a feature called Location Aware Regulatory (LAR). LAR is a feature of these Intel WiFi adapters which allows it to infer its regulatory domain from its environment. If LAR works <a href="https://bugzilla.kernel.org/show_bug.cgi?id=206469#c2">reasonably</a> <a href="https://bugzilla.kernel.org/show_bug.cgi?id=208071">well</a>, then it makes sense to infer regdom rather than let it be user configurable in order to ensure compliance with local laws. There used to be a kernel module parameter for iwlwifi called <code class="language-plaintext highlighter-rouge">lar_disable</code> which allowed disabling LAR. However, it has been removed since Linux 5.4 as it <a href="https://bugzilla.kernel.org/show_bug.cgi?id=205695">wasn’t meant to be user-facing</a>. Today, there is no way to disable LAR.</p> <p>To summarize, we can’t create an AP on 5GHz because all 5GHz channels are DFS channels (and also because they are all PASSIVE-SCAN). DFS is broken with this adapter. They are DFS channels because of the regdom. We can’t change the regdom because it is inferred with LAR. We can’t disable LAR, because the config parameter was removed. We can’t have nice things.</p> <p>At the end of this wild goose chase, I found <a href="https://web.archive.org/web/https://www.reddit.com/r/linuxmasterrace/comments/qv0qlj/duty_calls_linux_edition/hkugqqk/?utm_source=reddit&amp;utm_medium=web2x&amp;context=3">this Reddit post</a>. If it wasn’t for this post, I would be halfway down this list already.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[I have a thin client that I wanted to turn into a WiFi access point. “Consumer APs are basically just really slow computers. Then, shouldn’t a moderately slow computer be a really fast AP?” In theory, it checked out. Just slap OpenWrt onto it or something of that ilk, and Bob’s your uncle.]]></summary></entry><entry><title type="html">RX 5600 XT BIOS Unlock, 2 GHz and beyond</title><link href="https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock/" rel="alternate" type="text/html" title="RX 5600 XT BIOS Unlock, 2 GHz and beyond"/><published>2021-02-09T00:00:00-05:00</published><updated>2021-02-09T00:00:00-05:00</updated><id>https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock</id><content type="html" xml:base="https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock/"><![CDATA[<div class="toc-source"> <ul id="markdown-toc"> <li><a href="#prerequisites" id="markdown-toc-prerequisites">Prerequisites</a></li> <li><a href="#creating-the-overdrive-unlock-firmware" id="markdown-toc-creating-the-overdrive-unlock-firmware">Creating the overdrive unlock firmware</a> <ul> <li><a href="#mpt-profile" id="markdown-toc-mpt-profile">MPT profile</a></li> <li><a href="#rbe-modifications" id="markdown-toc-rbe-modifications">RBE modifications</a></li> </ul> </li> <li><a href="#flashing" id="markdown-toc-flashing">Flashing</a></li> <li><a href="#references" id="markdown-toc-references">References</a></li> </ul> </div> <p>We are all aware of the previous fiascos <a href="https://www.techspot.com/news/85113-amd-recommends-radeon-rx-5600-xt-owners-update.html">the RX 5600 XT has been a part of</a>. As a quick refresher, AMD released a firmware update for the RX 5600 XT to make it more competitive at its price point. This was in response to a price dip from NVIDIA on cards competing in the same price range. The update boosted clocks and power limits, pushing the silicon further than it was originally validated for. Despite this, AMD still limited the performance of the RX 5600 XT via firmware and the driver by imposing a hard limit on key parameters<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. Attempting to push the card further (for example, with soft PowerPlay tables) causes the card to instantly duck to the lowest clock speed. </p> <p>To go beyond the limits and break the 2 GHz barrier we need to bypass both the firmware and driver restrictions. Recently, <a href="https://www.reddit.com/user/BITBY_RU/">u/BITBY_RU</a> released bona fide unlock firmware which is capable of bypassing the firmware restrictions placed on this card. Their intention is for cryptocurrency mining, although they serve their purpose for boosting gaming performance as well.</p> <p>The RX 5600 XT actually has a lot of overclocking room, capable of competing with the RX 5700 and RX 5700 XT. The amount of room ultimately depends on the silicon quality, which varies from part to part. You may get better or worse results depending on the silicon lottery.</p> <p class="message message-warning"> <b>DISCLAIMER:</b> By following the instructions presented in this article, you are doing so at your own risk. The author is not liable for any consequences resulting from the procedures and information in this article, which are provided as-is. All software and firmware linked to in this article are distributed and hosted by third-parties. The author does not endorse any third-party linked to in this article. Modifying the VBIOS of your graphics card will void your warranty. Running your hardware outside of manufacturer tolerances for an extended period of time may lead to instability and/or permanent damage. <u>It is possible that your graphics card could be bricked.</u> </p> <p>Before continuing, I highly recommend reading through <a href="https://web.archive.org/web/20231003041802/https://gamersnexus.net/guides/3550-guide-how-to-flash-amd-gpu-vbios-rx-5600-xt">the GamersNexus guide to flashing VBIOS for RX 5600 XT</a>, which contains useful information regarding recovery in case you brick your card.</p> <h2 id="prerequisites">Prerequisites</h2> <ul> <li><a href="https://www.igorslab.de/en/download-area-new-version-of-morepowertool-mpt-and-final-release-of-redbioseditor-rbe/">ATIFLASH v2.93+</a> <strong>EXACTLY</strong>, do not use newer versions (the “+” is part of the version name, it doesn’t mean “including versions newer than v2.93”!)</li> <li><a href="https://www.igorslab.de/en/red-bios-editor-and-morepowertool-adjust-and-optimize-your-vbios-and-even-more-stable-overclocking-navi-unlimited/">MorePowerTool (MPT) and Red BIOS Editor (RBE)</a></li> <li>Unlock firmware for your respective card from <a href="https://web.archive.org/web/20210414150150/https://bitby.ru/bios-rx5600xt-mod-en.php">bitby.ru</a></li> </ul> <p>Please read through the disclaimers in the linked download pages.</p> <h2 id="creating-the-overdrive-unlock-firmware">Creating the overdrive unlock firmware</h2> <p>The unlock firmware provided by bitby.ru comes with <a href="https://www.reddit.com/user/BITBY_RU/comments/l4njxh/unlocked_bios_for_gigabyte_rx_5600_xt_6_gb_gaming/gkzppxz/">default overdrive limits</a>. Therefore, we will need to create an MPT profile with our desired overdrive limits and apply it to the unlock firmware.</p> <h3 id="mpt-profile">MPT profile</h3> <p>First, open MPT and open the unlock firmware you downloaded earlier. You will want to modify the overdrive limits and power/voltage to higher values so that you will be able to overclock it later. For reference, here are the values I chose:</p> <p><img src="/assets/images/posts/rx-5600-xt-bios-unlock/mpt-overdrive-limits.png" alt="MorePowerTool with GFX Maximum Clock raised to 2100 MHz and Maximum Voltage GFX to 1200 mV"/></p> <p>The most notable changes are to the <em>GFX Maximum Clock</em> and <em>Maximum Voltage GFX</em>. I do not recommend changing <em>Power Limit GPU</em> or <em>TDC Limit GFX</em> unless you have the thermal headroom.</p> <p>After you are done configuring, <strong>DO NOT</strong> click <em>Write SPPT</em>. Instead, click <em>Save</em> and save the MPT profile somewhere.</p> <h3 id="rbe-modifications">RBE modifications</h3> <p>Next, open RBE and load the unlock firmware. You will want to change the GPU ID to <em>5700XT</em>, which will cause the driver to believe the card is an RX 5700 XT and bypass the driver restrictions. Next, navigate to the <em>PowerPlay</em> tab and load the MPT profile you created earlier. Click <em>Save</em> and save this modified unlock firmware somewhere.</p> <p><img src="/assets/images/posts/rx-5600-xt-bios-unlock/rbe-load-mpt-file.png" alt="Red BIOS Editor with the GPU ID set to 5700XT, and the Load MPT File button on the PowerPlay tab"/></p> <h2 id="flashing">Flashing</h2> <p>You will now flash the modified unlock firmware onto your graphics card. <strong>Ensure you have read the disclaimer above before proceeding.</strong></p> <p>First, open an elevated command prompt. You can do so by searching “cmd” in the start menu, right clicking it, and choosing “Run as administrator”.</p> <p><img src="/assets/images/posts/rx-5600-xt-bios-unlock/run-cmd-as-admin.png" alt="Running Command Prompt as administrator from the start menu"/></p> <p>In the elevated command prompt, change directory to where you extracted ATIFLASH v2.93+ using <code class="language-plaintext highlighter-rouge">cd /d &lt;path&gt;</code>. For example, I extracted it to my desktop, so I would run <code class="language-plaintext highlighter-rouge">cd /d "C:\Users\netdex\Desktop\293plus"</code>.</p> <p>Next, copy your modified unlock firmware into this directory. <strong>Make sure there are not any spaces in the name.</strong> For example, the contents of my directory now look like this, where <code class="language-plaintext highlighter-rouge">ulfakempt.rom</code> is my modified unlock firmware file.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;dir
 Directory of C:\Users\netdex\Desktop\293plus

02/07/2021  12:36 PM    &lt;DIR&gt;          .
02/07/2021  12:36 PM    &lt;DIR&gt;          ..
02/07/2021  11:12 AM           377,344 amdvbflash.exe
02/07/2021  11:12 AM            12,048 atidgllk.sys
02/07/2021  11:12 AM            22,800 atikia64.sys
02/07/2021  11:12 AM            14,608 atillk64.sys
02/07/2021  11:12 AM             6,446 doc.txt
02/07/2021  11:12 AM               218 how-flash.txt
02/08/2021  09:00 AM           524,288 ulfakempt.rom
              10 File(s)      2,532,546 bytes
               2 Dir(s)  27,770,851,328 bytes free
</code></pre></div></div> <p>In the elevated command prompt, run <code class="language-plaintext highlighter-rouge">amdvbflash -h</code>. The output should resemble the following:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;amdvbflash
--- amdvbflash v2.93+ ---
-h, -?, /h, /?          Help (this screen)

Format: amdvbflash [command] [parameter1] [parameter2] [parameter3] &lt;option/s&gt;
--- snip ---
</code></pre></div></div> <p><strong>Ensure that the version printed is <code class="language-plaintext highlighter-rouge">amdvbflash v2.93+</code> before proceeding.</strong></p> <p>Now, run <code class="language-plaintext highlighter-rouge">amdvbflash -i</code>. The output should resemble the following:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;amdvbflash -i

adapter bn dn fn dID       asic           flash      romsize test    bios p/n
======= == == == ==== =============== ============== ======= ==== ==============
   0    28 00 00 731F Navi10          W25Q80          100000 pass       -
</code></pre></div></div> <p>Under the adapter column is the GPU ID for each respective GPU in your system. Note which GPU ID corresponds to your RX 5600 XT (shown as “Navi10” here).</p> <p>Before you continue, make a backup of your current firmware in case something goes wrong. You can do so by running <code class="language-plaintext highlighter-rouge">amdvbflash -s &lt;GPU ID&gt; bios0.rom</code> in the elevated command prompt, which will save the current firmware into a file called <code class="language-plaintext highlighter-rouge">bios0.rom</code>.</p> <p>Now, we will proceed to actually flash the modified unlock firmware onto your graphics card. <strong>This is the point of no return.</strong></p> <p>In the elevated command prompt, run <code class="language-plaintext highlighter-rouge">amdvbflash -f -p &lt;GPU ID&gt; &lt;MODIFIED_UNLOCK_VBIOS.ROM&gt;</code>. For example, I would run <code class="language-plaintext highlighter-rouge">amdvbflash -f -p 0 ulfakempt.rom</code>. Wait for the flash to successfully finish, and restart your computer when prompted. If everything went well, congratulations! Your RX 5600 XT is now fully unlocked, and you can overclock it to your heart’s desire.</p> <p>Tuning your overclock is outside of the scope of this article. For reference, the overclock I was able to achieve is shown below.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> The limiting factor for me is power consumption/thermals. Again, your results may vary and depend on silicon quality.</p> <p><img src="/assets/images/posts/rx-5600-xt-bios-unlock/wattman-tuning.png" alt="Radeon Software performance tuning, with a three-point voltage-frequency curve topping out at 2051 MHz and 1099 mV"/></p> <h2 id="references">References</h2> <ul> <li><a href="https://www.reddit.com/r/overclocking/comments/j9zuyb/tutorial_how_to_unlock_rx_5600_xt/">Tutorial how to unlock rx 5600 xt</a></li> <li><a href="https://www.reddit.com/user/BITBY_RU/comments/l4njxh/unlocked_bios_for_gigabyte_rx_5600_xt_6_gb_gaming/">Unlocked bios for Gigabyte RX 5600 XT 6GB GAMING OC 3 Fan GV-R56XTGAMING Samsung + Micron</a></li> <li><a href="https://www.reddit.com/user/BITBY_RU/">u/BITBY_RU</a></li> <li><a href="https://bitby.ru/bios-rx5600xt-mod-en.php">Unlocked modified BIOS for AMD Radeon RX 5600 XT samsung + micron + hynix</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1"> <p>1820 MHz on core clock, 1860 MHz on memory clock, and somewhere around 180 W on ASIC power – I never pinned that last one down exactly <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:2"> <p>core clock 2050 MHz @ 1.1V, mem clock 1800 MHz <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[We are all aware of the previous fiascos the RX 5600 XT has been a part of. As a quick refresher, AMD released a firmware update for the RX 5600 XT to make it more competitive at its price point. This was in response to a price dip from NVIDIA on cards competing in the same price range. The update boosted clocks and power limits, pushing the silicon further than it was originally validated for. Despite this, AMD still limited the performance of the RX 5600 XT via firmware and the driver by imposing a hard limit on key parameters[^1]. Attempting to push the card further (for example, with soft PowerPlay tables) causes the card to instantly duck to the lowest clock speed.]]></summary></entry><entry><title type="html">Using Ace.js for Pukiwiki</title><link href="https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki/" rel="alternate" type="text/html" title="Using Ace.js for Pukiwiki"/><published>2020-10-18T00:00:00-04:00</published><updated>2020-10-18T00:00:00-04:00</updated><id>https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki</id><content type="html" xml:base="https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki/"><![CDATA[<p>Out of the box, Pukiwiki’s text editor is a simple textarea. Of course, this leaves much to be desired, such as:</p> <ul> <li>Auto-indentation</li> <li>Syntax highlighting</li> <li>Line numbering</li> <li>Find/replace</li> <li>Vim bindings (a must-have)</li> <li>etc. </li> </ul> <p>We can use <a href="https://ace.c9.io/">Ace</a> to implement this functionality, by embedding the text editor in place of the default textarea used for editing.</p> <h2 id="tutorial">Tutorial</h2> <p>At the end of <code class="language-plaintext highlighter-rouge">pukiwiki.skin.php</code> before the <code class="language-plaintext highlighter-rouge">&lt;/body&gt;</code> tag, insert the following two script includes:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ace.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
</code></pre></div></div> <p>At the end of <code class="language-plaintext highlighter-rouge">main.js</code>, add the following code:</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// adapted from https://stackoverflow.com/a/19513428</span>
<span class="nf">$</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">textarea[data-editor]</span><span class="dl">'</span><span class="p">).</span><span class="nf">each</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">textarea</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">mode</span> <span class="o">=</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">editor</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">editDiv</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;div&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
            <span class="na">position</span><span class="p">:</span> <span class="dl">'</span><span class="s1">absolute</span><span class="dl">'</span><span class="p">,</span>
            <span class="na">width</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">width</span><span class="p">(),</span>
            <span class="na">height</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">height</span><span class="p">(),</span>
            <span class="dl">'</span><span class="s1">class</span><span class="dl">'</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">attr</span><span class="p">(</span><span class="dl">'</span><span class="s1">class</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">}).</span><span class="nf">insertBefore</span><span class="p">(</span><span class="nx">textarea</span><span class="p">);</span>
        <span class="nx">textarea</span><span class="p">.</span><span class="nf">css</span><span class="p">(</span><span class="dl">'</span><span class="s1">display</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">editor</span> <span class="o">=</span> <span class="nx">ace</span><span class="p">.</span><span class="nf">edit</span><span class="p">(</span><span class="nx">editDiv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nx">renderer</span><span class="p">.</span><span class="nf">setShowGutter</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">gutter</span><span class="dl">'</span><span class="p">));</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">setReadOnly</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span><span class="p">);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">setValue</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">val</span><span class="p">());</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">setMode</span><span class="p">(</span><span class="dl">"</span><span class="s2">ace/mode/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">mode</span><span class="p">);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">setTheme</span><span class="p">(</span><span class="dl">"</span><span class="s2">ace/theme/chrome</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">if </span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">grow</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">editor</span><span class="p">.</span><span class="nf">setOptions</span><span class="p">({</span>
                <span class="na">maxLines</span><span class="p">:</span> <span class="mi">180</span>
            <span class="p">});</span>
        <span class="p">}</span>

        <span class="c1">// copy back to textarea on form submit...</span>
        <span class="nx">textarea</span><span class="p">.</span><span class="nf">closest</span><span class="p">(</span><span class="dl">'</span><span class="s1">form</span><span class="dl">'</span><span class="p">).</span><span class="nf">submit</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
            <span class="nx">textarea</span><span class="p">.</span><span class="nf">val</span><span class="p">(</span><span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">getValue</span><span class="p">());</span>
        <span class="p">})</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div> <p>In <code class="language-plaintext highlighter-rouge">html.php</code>, change the msg textarea to this:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;textarea</span> <span class="na">name=</span><span class="s">"msg"</span> <span class="na">data-editor=</span><span class="s">"c_cpp"</span> <span class="na">data-gutter=</span><span class="s">"true"</span> <span class="na">cols=</span><span class="s">90</span> <span class="na">style=</span><span class="s">"height:80vh"</span><span class="nt">&gt;</span>$s_postdata<span class="nt">&lt;/textarea&gt;</span>
</code></pre></div></div> <p>Additionally, since we already have Ace loaded, we can use it for syntax highlighting.</p> <p>The version of Ace.js that this site uses has a special Pukiwiki syntax highlighter module which implements rudimentary support for Pukiwiki markup. My fork is available on <a href="https://github.com/Netdex/ace/blob/master/lib/ace/mode/pukiwiki_highlight_rules.js">GitHub</a>. I got a bit lazy trying to translate the context free grammar into regexes, so the result is a bit incomplete.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Out of the box, Pukiwiki’s text editor is a simple textarea. Of course, this leaves much to be desired, such as: Auto-indentation Syntax highlighting Line numbering Find/replace Vim bindings (a must-have) etc.]]></summary></entry><entry><title type="html">SMR Drives and UASP</title><link href="https://netdex.org/2020/07/20/smr-drives-and-uasp/" rel="alternate" type="text/html" title="SMR Drives and UASP"/><published>2020-07-20T00:00:00-04:00</published><updated>2020-07-20T00:00:00-04:00</updated><id>https://netdex.org/2020/07/20/smr-drives-and-uasp</id><content type="html" xml:base="https://netdex.org/2020/07/20/smr-drives-and-uasp/"><![CDATA[<p>A part of my workflow involves using <a href="https://github.com/restic">restic</a> to backup data onto an external hard drive. After a migration, I needed to perform a rather large ingestion into my restic repo (about 500GB). However, in the middle of the backup my drive suddenly disappeared without a trace. No drive letter, not even in device manager. </p> <p>Thinking it was just a fluke, I unplugged the drive and plugged it back in. It appeared normally in Explorer, and worked just fine. Ran <code class="language-plaintext highlighter-rouge">chkdsk</code> on it and it returned no errors. Weird. Ran the backup again, starting from the very beginning. The drive disappears again half an hour later.</p> <p>Something was definitely wrong, so I looked at Event Viewer to see if Windows agreed with me. I saw a number of worrying warnings…</p> <p><img src="/assets/images/posts/smr-drives-and-uasp/event-viewer.png" alt="Event Viewer showing repeated disk warnings and a UASPStor event 129, &quot;Reset to device, \Device\RaidPort3, was issued&quot;"/></p> <p>Namely, <em>UASPStor: Reset to device, \Device\RaidPort3, was issued.</em> Burn these words into your mind. Precursory search results pointed towards hardware failure or system instability. I didn’t buy it.</p> <p>I shucked the drive out of the external enclosure, connected it directly via a SATA interface, and ran the backup again. After about half an hour, the backup is still running. But the speed dropped to a whopping 1 MB/s. And the average access latency was into the minutes.</p> <p>I stopped the backup again, frustrated. In complete silence, I tried to come up with any fathomable reason why I could not backup my files. That was when I heard a peculiar noise. The clickety-clackety sound my hard drive makes when it moves to do writes.</p> <p>I open task manager, and check the current write speed to the drive. Zero. It was at this moment that everything became abundantly clear.</p> <p>My external drive probably uses <a href="https://en.wikipedia.org/wiki/Shingled_magnetic_recording">Shingled Magnetic Recording</a> (DM-SMR specifically). One fantastic aspect of SMR drives is that they have a much better data density than conventional PMR/CMR drives. One less fantastic aspect of SMR drives is that their write speeds are complete garbage.</p> <p>SMR drives abuse the fact that you can read from tracks much narrower than you can write, by stacking them on top of each other like the shingles on a roof. This makes them great for write-once, read-many applications, since read performance is not affected. However, if you want to write even a single sector to the drive, you will have to read and write the entire zone of tracks that overlap. Your 4K write just turned into a 256MB one.</p> <p>Device-managed SMR drives (DM-SMR) make the fact that they are SMR drives completely transparent to the host system. That is, they expose a normal interface and handle all the additional work that SMR drives need to do. Since SMR drives have amazingly awful write performance, many levels of caching are usually added to improve performance. For example, a DM-SMR drive may have a fairly large PMR cache, and a relatively small DRAM cache. The drive will try to optimize the use of the PMR region and the DRAM cache to avoid having SMR writes in the critical path. When idling, the drive controller will slowly siphon data from the PMR cache to the SMR drive, freeing up PMR cache space.</p> <p>What happens when the PMR cache is full? Writes go directly to the SMR drive. You know what SMR drives are really bad at? Writing. You know what SMR drives are even worse at doing? Random writing. You know what restic likes to do a lot? Yeah. The drive shits the bed so hard that the UASP controller thinks the drive is literally gone.</p> <p>Whose fault is this?</p> <ol> <li>The UASPStor driver probably needs to handle timeouts better than it does right now, since apparently minutes of access latency are a real use case.</li> <li>Drive manufacturers need to label their disks with the technology they use.</li> <li>Software writers need to optimize for what spinning disks do great: sequential reads and writes.</li> </ol> <p>Let block sizes be configurable, because sometimes I want to waste a bit of space if it means saving hours of time.</p> <p><code class="language-plaintext highlighter-rouge">&lt;/rant&gt;</code></p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[A part of my workflow involves using restic to backup data onto an external hard drive. After a migration, I needed to perform a rather large ingestion into my restic repo (about 500GB). However, in the middle of the backup my drive suddenly disappeared without a trace. No drive letter, not even in device manager.]]></summary></entry><entry><title type="html">RX 5600 XT Review</title><link href="https://netdex.org/2020/07/20/rx-5600-xt-review/" rel="alternate" type="text/html" title="RX 5600 XT Review"/><published>2020-07-20T00:00:00-04:00</published><updated>2020-07-20T00:00:00-04:00</updated><id>https://netdex.org/2020/07/20/rx-5600-xt-review</id><content type="html" xml:base="https://netdex.org/2020/07/20/rx-5600-xt-review/"><![CDATA[<div class="toc-source"> <ul id="markdown-toc"> <li><a href="#symptoms" id="markdown-toc-symptoms">Symptoms</a></li> <li><a href="#diagnosis" id="markdown-toc-diagnosis">Diagnosis</a> <ul> <li><a href="#system-memory-timings" id="markdown-toc-system-memory-timings">System Memory Timings</a></li> <li><a href="#bios" id="markdown-toc-bios">BIOS</a></li> <li><a href="#fan-curves" id="markdown-toc-fan-curves">Fan Curves</a></li> <li><a href="#chipset-drivers" id="markdown-toc-chipset-drivers">Chipset Drivers</a></li> <li><a href="#adrenalin-2020" id="markdown-toc-adrenalin-2020">Adrenalin 2020</a></li> </ul> </li> <li><a href="#the-verdict" id="markdown-toc-the-verdict">The Verdict</a></li> </ul> </div> <p>I recently acquired a Gigabyte RX 5600 XT Gaming OC, hoping that the widely reported driver issues were an overreaction from a vocal minority. Unfortunately, this was not the case - I encountered several stability issues throughout my first few weeks of using this card. This post goes over the troubleshooting process I went through while trying to get this card to work. </p> <p>As a foreword, nobody should ever go through this much effort to get a product they purchased to work properly. If it wasn’t for Canada Computers’ abysmal return policy, I would have jumped ship the instant my screen turned green.</p> <h2 id="symptoms">Symptoms</h2> <p>The most common issue I experienced with this card is the infamous black screen, characterized by the following symptoms:</p> <ul> <li>The screen suddenly turning black while other computer functionality remains (such as sound) <ul> <li>Sometimes, the cursor may still work or flicker</li> <li>On screens connected via HDMI, the screen may turn green instead</li> <li>After a few seconds, the entire system will stop responding, probably due to TDR (increasing TdrDelay causes this period of “functionality” to last longer)</li> </ul> </li> <li>Almost always happens while gaming, regardless of whether the game was graphically intensive or not <ul> <li>Has occurred randomly during other tasks, such as unlocking my workstation or watching a video in MPC-HC</li> </ul> </li> </ul> <h2 id="diagnosis">Diagnosis</h2> <h3 id="system-memory-timings">System Memory Timings</h3> <p>When I first got this card, I happened to have slightly unstable RAM timings for my system memory (about 1 bit flip after 8 passes of memtest). The memory was running at 3000 MT/s 14-16-16-16-32.</p> <p>I have since then loosened the memory timings to 2933 MT/s 16-17-17-17-35.</p> <p>Other people in the community have reported <a href="https://community.amd.com/thread/253016">similar issues</a>. It seems these cards are fairly sensitive to bad memory timings, since I did not have any issues with these memory timings before with any other cards.</p> <h3 id="bios">BIOS</h3> <p>You are probably already aware of the BIOS fiasco AMD sprung on its partners in order to make the RX 5600 XT more competitive. Gigabyte released two versions, F2 (stability) and FA0 (performance). I have not been able to achieve system stability with FA0, despite the only differences between them being VRAM speeds and power limits. You can replicate the FA0 performance solely with the F2 bios and Wattman.</p> <p><strong>Update:</strong> As of now, the FA0 VBIOS is no longer available on Gigabyte’s website.</p> <p>It turns out that there are two different factory VBIOSes from the factory, F1 and F60. It is worth noting that F1 and F60 share all parameters inspectable by MorePowerTool. Despite this, Gigabyte notes that the VBIOS upgrade path to F2 and FA0 is only supported for cards that come with the F1 VBIOS. That is, cards that come with F60 are inherently less overclockable than cards that come with F1, <a href="https://community.amd.com/thread/252648">despite being sold as the same product</a>.</p> <p>After reducing my clock speeds to F60 defaults while having F2 flashed, I no longer had any black screen issues.</p> <p><strong>Update:</strong> Gigabyte released three new VBIOS versions: F61, F3 and FA1 - one corresponding to each previous version. I could not identify any difference in key parameters between these versions. However, I am able to run F61 without any stability issues (even overclocked, nonetheless).</p> <h3 id="fan-curves">Fan Curves</h3> <p>The default fan curves are inadequate, and will almost definitely lead to thermal throttling. The throttling did not seem to be aggressive enough to avoid system instability. I ended up ramping up the fan curve by 20% PWM.</p> <h3 id="chipset-drivers">Chipset Drivers</h3> <p>I had manufacturer chipset drivers, which were slightly outdated compared to the chipset drivers directly from AMD.</p> <h3 id="adrenalin-2020">Adrenalin 2020</h3> <p>Some people have seen success from installing the display driver <a href="https://www.youtube.com/watch?v=s5rRIZ1wMAY">without the Adrenalin software</a>. This would imply that there is some component of Adrenalin which causes these black screen issues. Given the amount of introspection into games that Adrenalin does (counting average FPS, number of hours played, etc.), it would not be surprising if some of this functionality is broken.</p> <p>This would also explain why I have only experienced these black screens while playing games. All 50 or so of the black screens I have encountered were while playing a game.</p> <p>Without Adrenalin, you can still apply overclocks with tools such as OverdriveNTool. I personally had issues with tools like MSI Afterburner, as they do not give enough control over voltage curves.</p> <h2 id="the-verdict">The Verdict</h2> <p>My leading theory: the card I purchased came with the F60 VBIOS. Flashing F2 was thus unsupported, which led to the instability. The instability was caused by either power limits, memory frequency, or some VBIOS firmware that is not compatible with cards that come with F60.</p> <p>I am not hopeful that a future VBIOS with be released as an upgrade path from F60 (i.e. F61), since it is extremely likely that cards that come with F60 are just poorer bins which are not capable of 14 Gbps anyways.</p> <p>When purchasing this card, there is no way for a consumer to tell whether they are getting a card with F60 or F1 VBIOS. There are significant performance differences between these cards. Gigabyte does not make this clear, with AMD’s official site even listing this card as upgradable to 14 Gbps.</p> <p>Do not buy this card.</p> <p><strong>Update:</strong> Given the new VBIOS updates, this card is now perfectly fine. I wish Gigabyte gave some sort of statement or postponed release of the card before the new VBIOS was developed, as it would have saved me many hours of headaches.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[I recently acquired a Gigabyte RX 5600 XT Gaming OC, hoping that the widely reported driver issues were an overreaction from a vocal minority. Unfortunately, this was not the case - I encountered several stability issues throughout my first few weeks of using this card. This post goes over the troubleshooting process I went through while trying to get this card to work.]]></summary></entry><entry><title type="html">Building LineageOS 17.1 on ArchLinux</title><link href="https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux/" rel="alternate" type="text/html" title="Building LineageOS 17.1 on ArchLinux"/><published>2020-04-13T00:00:00-04:00</published><updated>2020-04-13T00:00:00-04:00</updated><id>https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux</id><content type="html" xml:base="https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux/"><![CDATA[<div class="toc-source"> <ul id="markdown-toc"> <li><a href="#prerequisites" id="markdown-toc-prerequisites">Prerequisites</a> <ul> <li><a href="#packages" id="markdown-toc-packages">Packages</a> <ul> <li><a href="#required-for-build" id="markdown-toc-required-for-build">Required for build</a></li> <li><a href="#recommended" id="markdown-toc-recommended">Recommended</a></li> </ul> </li> <li><a href="#system-considerations" id="markdown-toc-system-considerations">System considerations</a> <ul> <li><a href="#making-it-work" id="markdown-toc-making-it-work">Making it work</a> <ul> <li><a href="#reduce-simultaneous-jobs" id="markdown-toc-reduce-simultaneous-jobs">Reduce simultaneous jobs</a></li> <li><a href="#build-components-individually" id="markdown-toc-build-components-individually">Build components individually</a></li> <li><a href="#use-swap-and-zswap" id="markdown-toc-use-swap-and-zswap">Use swap and zswap</a></li> <li><a href="#caching" id="markdown-toc-caching">Caching</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a href="#build" id="markdown-toc-build">Build</a> <ul> <li><a href="#acquiring-sources-and-binaries" id="markdown-toc-acquiring-sources-and-binaries">Acquiring sources and binaries</a> <ul> <li><a href="#proprietary-blobs" id="markdown-toc-proprietary-blobs">Proprietary blobs</a></li> <li><a href="#device-specific-sources" id="markdown-toc-device-specific-sources">Device specific sources</a></li> <li><a href="#building" id="markdown-toc-building">Building</a></li> <li><a href="#troubleshooting" id="markdown-toc-troubleshooting">Troubleshooting</a> <ul> <li><a href="#various-symptoms-related-to-out-of-memory-conditions" id="markdown-toc-various-symptoms-related-to-out-of-memory-conditions">Various symptoms related to out-of-memory conditions</a></li> <li><a href="#error-vendorlineagebuildsoongandroidbp308-module-generated_kernel_includes-cmd-unknown-variable-path_override_soong" id="markdown-toc-error-vendorlineagebuildsoongandroidbp308-module-generated_kernel_includes-cmd-unknown-variable-path_override_soong"><code class="language-plaintext highlighter-rouge">error: vendor/lineage/build/soong/Android.bp:30:8: module "generated_kernel_includes": cmd: unknown variable '$(PATH_OVERRIDE_SOONG)'</code></a></li> </ul> </li> </ul> </li> </ul> </li> </ul> </div> <p>This post serves as a log of some of the hoops I had to jump through in order to get LineageOS 17.1 to build on ArchLinux, as a reminder to myself in case I need to do this again in the future. As a result, it is rather brief and incomplete. Consider filling in the gaps with the official build documentation from the <a href="https://wiki.lineageos.org/devices/mata/build">LineageOS Wiki</a>. </p> <p>This guide assumes working knowledge of ArchLinux and Android development, but makes an effort to link to relevant reading.</p> <h2 id="prerequisites">Prerequisites</h2> <h3 id="packages">Packages</h3> <h4 id="required-for-build">Required for build</h4> <ul> <li>repo</li> <li><a href="https://aur.archlinux.org/packages/lineageos-devel/">lineageos-devel</a></li> <li><a href="https://wiki.archlinux.org/index.php/Official_repositories#multilib">multilib-devel</a></li> <li>base-devel</li> <li>ttf-dejavu (Fun fact: It’s used to generate the PNG image for the LOS recovery splash screen)</li> <li>lib32-ncurses5-compat-libs</li> <li>…</li> </ul> <h4 id="recommended">Recommended</h4> <ul> <li>android-tools (platform-tools)</li> <li>android-udev (udev rules for Android ADB)</li> </ul> <h3 id="system-considerations">System considerations</h3> <p>In my experience, the following system specifications are a bare minimum for an enjoyable development experience:</p> <ul> <li>A 64-bit multiscalar processor from this decade</li> <li>200GB of solid state storage</li> <li><em>At least</em> 16 GB of RAM, at the bare minimum 8 GB with some tweaks</li> <li>A relatively cool climate and a nearby window to vent heat</li> </ul> <h4 id="making-it-work">Making it work</h4> <p>There are a number of optimizations which can be performed to <strong>make it work</strong> despite not having the best computer or internet connection:</p> <h5 id="reduce-simultaneous-jobs">Reduce simultaneous jobs</h5> <p>For <code class="language-plaintext highlighter-rouge">repo sync</code> or <code class="language-plaintext highlighter-rouge">brunch</code> consider using fewer simultaneous jobs (via the <code class="language-plaintext highlighter-rouge">-j</code> flag). This has the effect of:</p> <ul> <li>Using less memory at once</li> <li>Using less network bandwidth at once (for repo)</li> <li>Using less compute resources at once</li> <li>Making your build take proportionally longer</li> </ul> <h5 id="build-components-individually">Build components individually</h5> <p>For systems with less than 16 GB of RAM, there are some parts of the build which will OOM (I’m looking at you, metalava). These components can be selectively built individually, to reduce the number of tasks your computer is processing at once (see the top of <code class="language-plaintext highlighter-rouge">build/envsetup.sh</code> for make commands).</p> <h5 id="use-swap-and-zswap">Use swap and zswap</h5> <p>You should probably use <a href="https://wiki.archlinux.org/index.php/Swap">swap</a> on relatively fast storage in order to handle sudden spikes in memory usage.</p> <p>This includes enabling <a href="https://wiki.archlinux.org/index.php/Zswap">zswap</a>. Many build steps allocate large heaps or spawn many JVMs which fill all your resident memory. Memory compression is surprisingly effective at handling these bad actors. Without sufficient RAM and swap/zswap disabled, you will probably OOM while generating documentation with metalava.</p> <h5 id="caching">Caching</h5> <p>For multiple builds, consider enabling ccache with or without compression to reduce build time for subsequent builds.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">USE_CCACHE</span><span class="o">=</span>1
<span class="nb">export </span><span class="nv">CCACHE_COMPRESS</span><span class="o">=</span>1
ccache <span class="nt">-M</span> 20G
</code></pre></div></div> <h2 id="build">Build</h2> <h3 id="acquiring-sources-and-binaries">Acquiring sources and binaries</h3> <p>Run the following repo commands to download the sources from a hodgepodge of repositories scattered across the internet.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>repo init <span class="nt">-u</span> https://github.com/LineageOS/android.git <span class="nt">-b</span> lineage-17.1
repo <span class="nb">sync</span>
</code></pre></div></div> <p>Consider using fewer simultaneous jobs if you have a poor internet connection.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>repo <span class="nb">sync</span> <span class="nt">-j1</span> <span class="nt">-c</span>
</code></pre></div></div> <p>Make sure you keep the <code class="language-plaintext highlighter-rouge">-c</code> flag, which tells repo to only download the current branch. Otherwise, you will have a bad time.</p> <h4 id="proprietary-blobs">Proprietary blobs</h4> <p>You will need proprietary blobs from your device vendor. These can be extracted from a working device, ripped from an image, or obtained via unofficial means.</p> <p>If you end up getting blobs from a repository, consider adding them to your local manifests. This will make synchronization of your blobs work with repo.</p> <p>For example, my <code class="language-plaintext highlighter-rouge">.repo/local_manifests/roomservice.xml</code> looks like</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;manifest&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"device/essential/mata"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"LineageOS/android_device_essential_mata"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"kernel/essential/msm8998"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"LineageOS/android_kernel_essential_msm8998"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/essential"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"TheMuppets/proprietary_vendor_essential"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/manifest&gt;</span>
</code></pre></div></div> <h4 id="device-specific-sources">Device specific sources</h4> <p>To get vendor and device trees for your specific device, you will need to have breakfast. This will fail if your proprietary blobs are missing.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>build/envsetup.sh
breakfast &lt;DEVICE_CODENAME&gt;
</code></pre></div></div> <h4 id="building">Building</h4> <p>Run the following commands to create an unsigned build:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>croot
brunch &lt;DEVICE_CODENAME&gt;
</code></pre></div></div> <p>Signed builds can be created via a different process, which I will not <a href="https://wiki.lineageos.org/signing_builds.html">get into detail</a>.</p> <p>Your build artifacts will be in the <code class="language-plaintext highlighter-rouge">$OUT</code> directory.</p> <h4 id="troubleshooting">Troubleshooting</h4> <p>Something will probably break while you are trying to build. Here’s what broke for me, and how I tried to fix it.</p> <h5 id="various-symptoms-related-to-out-of-memory-conditions">Various symptoms related to out-of-memory conditions</h5> <ul> <li>Process is randomly terminated while building</li> <li><code class="language-plaintext highlighter-rouge">[ 97% 99068/101261] //frameworks/base:system-api-stubs-docs Metalava [common]</code></li> </ul> <p>It was probably killed by the OOM killer. Watch your available memory and process oom scores while building. Consider using the memory techniques mentioned above. If all else fails, try tweaking various environment variables to try to lower memory usage (e.g. Java heap size, cache sizes etc.)</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR: Failed to run command '['simg2img', '/tmp/targetfiles-oa64tct3/IMAGES/system.img', '/tmp/targetfiles-oa64tct3/IMAGES/unsparse_system.img']' (exit code 255):
Failed to read sparse file
</code></pre></div></div> <ul> <li>platform_build/build_image uses /tmp to unsparse image files. On most systems, /tmp is backed by RAM, with a maximum size of half the available RAM. The random file used by the tool is generated with <code class="language-plaintext highlighter-rouge">mktemp</code>, so use the <code class="language-plaintext highlighter-rouge">TMPDIR</code> envvar to set it to somewhere else.</li> </ul> <h5 id="error-vendorlineagebuildsoongandroidbp308-module-generated_kernel_includes-cmd-unknown-variable-path_override_soong"><code class="language-plaintext highlighter-rouge">error: vendor/lineage/build/soong/Android.bp:30:8: module "generated_kernel_includes": cmd: unknown variable '$(PATH_OVERRIDE_SOONG)'</code></h5> <p>Have you ever found an <a href="https://mlog.club/article/3069189">article in a foreign language</a> (navigate at your own risk) that seems to describe the very issue you are having, with no response? Well, I have.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Not enough ram edition (tm)]]></summary></entry><entry><title type="html">Atelier Ryza: Ever Stuttering and the Stupid Workaround</title><link href="https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround/" rel="alternate" type="text/html" title="Atelier Ryza: Ever Stuttering and the Stupid Workaround"/><published>2020-02-08T00:00:00-05:00</published><updated>2020-02-08T00:00:00-05:00</updated><id>https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround</id><content type="html" xml:base="https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround/"><![CDATA[<p>Like every other Atelier game, the PC port leaves much to be desired, especially in terms of input. A lack of mouse support, along with only direct mappings from keys to controller inputs (which are non-intuitive at best) takes a bit of time to get used to. As of version 1.03, the Steam version also has some stuttering issues. </p> <p>About every 1-2 seconds, you will probably experience some frame skips. Accompanied by these frame skips are the physics engine glitching out, which probably means the physics are tied to the rendering loop. Fantastic.</p> <p>You might be wondering: how did something so obviously broken make it past QA? Well, wonder no more.</p> <p>Every 1-2 seconds, the game polls the XINPUT library for controller input. This poll either has a timeout, or has a long handler in the event of failure. The input polling is probably done in the rendering loop, which causes the frame stutter. The people at Gust probably did their QA with a controller plugged in (they would probably get an RSI from testing with a keyboard all day), and the glaringly obvious problem slipped into release.</p> <p>How can you work around this? The easiest way would be to get a controller. If you don’t have one, then you can use <a href="http://vjoystick.sourceforge.net/site/">vJoy</a> to emulate a joystick.</p> <p><img src="/assets/images/posts/atelier-ryza-ever-stuttering-and-the-stupid-workaround/device-manager.png" alt="Device Manager's Human Interface Devices list, with vJoy Device picked out at the bottom"/></p> <p>And no, I don’t mean “use vJoy as a controller to play the game” (which would require something like x360ce to send inputs to the driver anyways). I mean “install vJoy <em>literally</em> just so the game thinks you have a controller plugged in”, so the XINPUT polling doesn’t fail. You’ll still be playing with the keyboard.</p> <p>It’s <code class="language-plaintext highlighter-rouge">$(current year)</code> folks.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Like every other Atelier game, the PC port leaves much to be desired, especially in terms of input. A lack of mouse support, along with only direct mappings from keys to controller inputs (which are non-intuitive at best) takes a bit of time to get used to. As of version 1.03, the Steam version also has some stuttering issues.]]></summary></entry><entry><title type="html">Supporting Legacy of Lunatic Kingdom w/ Twinject</title><link href="https://netdex.org/2018/03/04/extending-twinject-lolk/" rel="alternate" type="text/html" title="Supporting Legacy of Lunatic Kingdom w/ Twinject"/><published>2018-03-04T00:00:00-05:00</published><updated>2018-03-04T00:00:00-05:00</updated><id>https://netdex.org/2018/03/04/extending-twinject-lolk</id><content type="html" xml:base="https://netdex.org/2018/03/04/extending-twinject-lolk/"><![CDATA[<div class="toc-source"> <ul id="markdown-toc"> <li><a href="#video-demonstration-legacy-of-lunatic-kingdom" id="markdown-toc-video-demonstration-legacy-of-lunatic-kingdom">Video Demonstration (Legacy of Lunatic Kingdom)</a></li> <li><a href="#components" id="markdown-toc-components">Components</a> <ul> <li><a href="#graphical-control" id="markdown-toc-graphical-control">Graphical Control</a></li> <li><a href="#input-control" id="markdown-toc-input-control">Input Control</a></li> <li><a href="#specific-data-processor" id="markdown-toc-specific-data-processor">Specific Data Processor</a></li> <li><a href="#algorithm" id="markdown-toc-algorithm">Algorithm</a></li> </ul> </li> <li><a href="#implementation" id="markdown-toc-implementation">Implementation</a></li> <li><a href="#lolk-specific-data-processor" id="markdown-toc-lolk-specific-data-processor">LoLK Specific Data Processor</a> <ul> <li><a href="#collidable-locations" id="markdown-toc-collidable-locations">Collidable Locations</a> <ul> <li><a href="#obtaining-data" id="markdown-toc-obtaining-data">Obtaining Data</a></li> </ul> </li> </ul> </li> </ul> </div> <p><em>twinject</em> (<strong>t</strong>ouhou <strong>w</strong>indows <strong>inject</strong>or) is an automated player for the bullet hell games from the <em>Touhou Project</em>. Instead of using computer vision techniques to extract data from the game, a DLL is injected and the relevant game data is directly extracted through trampolined functions. </p> <p>For the unaware, bullet hells are a category of <em>Shoot ‘em Up</em> video games where the player controls a ship, which must dodge large numbers of obstacles and destroy large numbers of enemies. In the demonstration video, the fast moving projectiles are the obstacles which must be dodged - if the player hits any of these projectiles they die immediately. The player itself also fires projectiles, which damage enemies. The player is the little red sprite near the bottom of the screen. Props to ZUN for making games that people still play 20 years later.</p> <p>If you want to see a prototype, you can check out the video demonstration below.</p> <h2 id="video-demonstration-legacy-of-lunatic-kingdom"><a href="https://youtu.be/9WElfhVE-Lk">Video Demonstration (Legacy of Lunatic Kingdom)</a></h2> <p>Theoretically, Twinject supports every Windows game in the Touhou Project. In order to support a game, four main components must be implemented, which I will briefly detail below.</p> <h2 id="components">Components</h2> <h3 id="graphical-control">Graphical Control</h3> <p>Twinject requires control over the game graphics in order to draw debugging information which is useful in the development of the other components.</p> <p>All mainline Touhou games can use Direct3D9 for their graphics (older games through the dxd8-&gt;dxd9 patch). Thus, this component is not game specific and only needs to be implemented once.</p> <p>This component is implemented as an object wrapper delivered to the game via trampolining Direct3DCreate9.</p> <h3 id="input-control">Input Control</h3> <p>Twinject requires control over the game input in order to control the player.</p> <p>All mainline Touhou games can use DirectInput8 for their input (configurable with config.exe). Thus, this component is not game specific and only needs to be implemented once.</p> <p>This component is implemented as an object wrapper delivered to the game via trampolining DirectInput8Create.</p> <h3 id="specific-data-processor">Specific Data Processor</h3> <p>Twinject requires game data in order to be aware of the player’s environment. This includes,</p> <ul> <li>Player location, velocity</li> <li>Bullet locations, velocities</li> <li>Powerup locations, velocities</li> <li>Enemy locations, velocities</li> <li>Laser functions</li> </ul> <p>The game engines for each Touhou game vary wildly - this component is game specific and must be implemented for each game.</p> <p>This component is usually implemented as a function hook.</p> <h3 id="algorithm">Algorithm</h3> <p>Twinject requires an algorithm to turn game data into player inputs. The following algorithms are currently supported:</p> <ul> <li>Method of Virtual Potential Fields</li> <li>Method of Velocity Obstacles</li> <li>Method of Vector-based Velocity Projection</li> </ul> <p>I will explain these algorithms in detail in their own post.</p> <h2 id="implementation">Implementation</h2> <p>Twinject provides a layer of abstraction between components, allowing new components to be easily created and connected together to create a functional automated player for a certain game.</p> <p>Implementation of game-specific components is usually quite involved, and requires reverse-engineering the game. In this article, I will detail the required procedure to implement a game-specific component for Legacy of Lunatic Kingdom (LoLK).</p> <h2 id="lolk-specific-data-processor">LoLK Specific Data Processor</h2> <p>I will detail the specific component required for processing data in LoLK.</p> <h3 id="collidable-locations">Collidable Locations</h3> <p>The player requires the locations and size of every collidable entity, so that it may dodge them. This includes the player, bullets, lasers, etc. Collidable entities may be demarcated into two types: function-based, and point-based.</p> <p>The player and bullets are point-based, and are defined as a point with a radius (LoLK uses hit-circles).</p> <p>Lasers are function-based, and are defined as a curve with a restricted domain.</p> <p>For now, we will only talk about point-based collidable entities (PBCE).</p> <h4 id="obtaining-data">Obtaining Data</h4> <p>We want to trampoline a function which processes PBCEs once per frame. It would make sense for the game to have a function that checks for collisions between two PBCE.</p> <p><a href="http://cheater.seesaa.net/article/169529488.html">This website</a> details many memory patches that can be used to modify the functionality of Touhou games. An interesting one is</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;無敵
00456540-C3     // set byte at 00456540 to C3
</code></pre></div></div> <p>which renders the player invincible.</p> <p>Taking a look in a disassembler (IDA), we can see that setting that byte causes a function to return immediately when called.</p> <p><img src="/assets/images/posts/extending-twinject-lolk/ida-death.png" alt="IDA disassembly of $player_death_mng, annotated with a note that patching its first instruction to retn stops the player from dying"/></p> <p><em>A disassembly in IDA, showing the function that the patch applies to, and its effect. (note that any variable names and comments were added in myself to help me reverse-engineer the game, and were not present beforehand)</em></p> <p>Setting a breakpoint in Ollydbg, we can see that this function is called after the player hits an obstacle. This function then carries out the death routine (reduce lives, set invincibility for a while, put player at origin etc.)</p> <p>Checking cross references to this function, we find the following function:</p> <p><img src="/assets/images/posts/extending-twinject-lolk/ida-collision.png" alt="Hex-Rays decompilation of the collision check, computing squared euclidean distance against a character-dependent radius"/></p> <p><em>A rough decompilation of the function, with variable names annotated based on intuition</em></p> <p>We can see that this function checks for player collision with PBCEs using the square Euclidean distance check for circle collision. If the PBCEs collide, then it calls the death function we found earlier.</p> <p>We can write a wrapper function that will extract the relevant data for Twinject to use:</p> <p><img src="/assets/images/posts/extending-twinject-lolk/wrapper.png" alt="The naked __stdcall hook, hand-writing a prolog and epilog around the userpurge call convention of the original function"/></p> <p><em>Hook function for collision check function</em></p> <p>This wrapper function will be part of the component. Unfortunately the function is usercall, so we must write our own prolog and epilog code with inline assembly.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Extending Twinject's capabilities by supporting an additional game]]></summary></entry></feed>