<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Home on Blurred code</title>
    <link>/</link>
    <description>Recent content in Home on Blurred code</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-CN</language>
    <atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>UE | Android游戏内集成Renderdoc</title>
      <link>/2026/07/0cf171b5/</link>
      <pubDate>Wed, 29 Jul 2026 22:55:47 +0800</pubDate>
      <guid>/2026/07/0cf171b5/</guid><description>动机 在真机上调试抓帧总的来说有两个痛点。 RenderDoc 抓帧时经常因为各种原因断掉。比如公司的测试机 Type-C 口经过不知道多少人反复插拔早就松松垮垮的了，WiFi 调试也不稳定，AP一漫游就断了，adb 在弱网环境下容易丢包断连。 条件抓取和部分抓取不好实现。内部抓取很容易做到满足某个条件时抓一帧，或者只抓某一帧的某个部分（如 Basepass），但外部抓取就很难做到。 做法 网上搜了一下，看见钱康来的博客已经记录过他们的探索了，至少这条路是行得通的。 高版本安卓注入 RenderDoc | Loading &amp;amp; Learning 我对 GLES 不感兴趣，所以反而要好做得多——毕竟 GLES 抓取需要 Hook 所有 GL API，而 Vulkan 有清晰的 Loader 和 Layer 概念。只需要做到操作： 把 libVkLayer_GLES_RenderDoc.so打到包里去 在 Vulkan 的 vkCreateInstance 处把 Layer 加进去。 libVkLayer_GLES_RenderDoc.so 在哪里 RenderDoc 的 APK 里...</description>
    </item>
    <item>
      <title>UE_Vulkan_TimeStampQuery计时和一个竞态条件的bug</title>
      <link>/notes/8bb7b105/</link>
      <pubDate>Mon, 20 Jul 2026 22:25:45 +0800</pubDate>
      <guid>/notes/8bb7b105/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 Info Engine Version: 4.26.2 FRealtimeGPUProfiler / FVulkanGPUTiming / FVulkanGPUProfiler 虚幻引擎有三套计时代码，职责混在一起，梳理如下： FVulkanGPUTiming：底层的 Primitive，一些引擎功能依赖它计时 FVulkanGPUProfiler：只用来响应 ProfileGPU 命令的 GPU Profiler FRealtimeGPUProfiler：最新的一套，拥有 stat / insights 数据写入等功能，但 query 的管理是依靠自己调用 RHI 底层接口来管理，是 RHI 层上面的东西，与 Vulkan 平台无关 FVulkan GPU Timing E:\ue\Engine\Source\Runtime\VulkanRHI\Private\VulkanGPUProfiler.h class FVulkanGPUTiming : public...</description>
    </item>
    <item>
      <title>UE | 在部分机器上 Dev 包明显出现 RHI Thread 性能劣化的问题</title>
      <link>/2026/05/7e7e4b87/</link>
      <pubDate>Sat, 23 May 2026 12:48:06 +0800</pubDate>
      <guid>/2026/05/7e7e4b87/</guid><description>受影响版本：5.4.3 及之后的版本。 其实 Epic 的 PR 里已经有人发现这个问题了。 Added a hotfix for Vulkan draw markers and drastically improved performance on low-end Android devices in dev builds by Adlerkampf · Pull Request #12541 · EpicGames/UnrealEngine Update VulkanLayers.cpp : Fix Android Vulkan Performance by residentour · Pull Request #12361 · EpicGames/UnrealEngine 任意合并一个都可以修复这个问题，不清楚为什么 Epic 都拒绝合并。 问题的原因 从 5.4.3 开始，UE 默认在 DEBUG/Development 包里启用了 Draw Marker 功能： #define VULKAN_SHOULD_ENABLE_DRAW_MARKERS (UE_BUILD_DEVEL...</description>
    </item>
    <item>
      <title>UE | Android AGDE 找不到Adb机器的问题</title>
      <link>/2026/05/5ae13e5c/</link>
      <pubDate>Sat, 23 May 2026 12:20:39 +0800</pubDate>
      <guid>/2026/05/5ae13e5c/</guid><description>首次连接不上 这种情况大概率是因为本地存在多个不同版本的 adb。比如，RenderDoc 里通常自带一份 adb，Android Studio 里也会带一份，某些厂商工具，比如 Snapdragon Profiler，也可能额外提供一份 adb。 adb 对版本比较敏感，不同版本之间反复切换或连接，可能会导致设备一直处于断连状态。 建议把本地的 adb 统一到同一个版本，尽量避免多个来源混用。 第二次连接不上 这种情况通常出现在 adb 是通过 Wi-Fi 连接时，设备在下一次连接前更换了 IP 地址或端口。 首先要确认在 adb 下确实能看到目标设备，而且状态不是断开的。 另一个常见的 AGDE 问题是：上一次连接的是 A 机器，这一次想连接 B 机器，但 AGDE 仍然使用了上一次缓存的 A 机器 IP 地址，结果就连不上了。 这时可以删除以下文件，然后重新生成一个干净的工程，以彻底清理本地 Visual Studio 对ip地址的缓存： UnrealProject\&amp;lt;ProjectName&amp;gt;\.vs UnrealProject\&amp;lt;ProjectName&amp;gt;...</description>
    </item>
    <item>
      <title>Nvrhi|Vulkan的VolatileConstantBuffer设计</title>
      <link>/notes/rhi/d5158078/</link>
      <pubDate>Tue, 05 May 2026 19:44:48 +0800</pubDate>
      <guid>/notes/rhi/d5158078/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 RenderPass nvrhi的RenderPass从SetGraphicsState开启，在CmdList-&amp;gt;Close时结束， 因此一个渲染Pass的典型示例如下： CmdList-&amp;gt;Open(); CmdList-&amp;gt;WriteBuffer(...) CmdList-&amp;gt;ClearTexture(...) CmdList-&amp;gt;SetGraphicsState(...)// 打开RenderPass，执行大量的bind set绑定 CmdList-&amp;gt;Draw(...); CmdList-&amp;gt;Close(); m_Device-&amp;gt;ExecuteCmdList(..); 有很多API都只能在RenderPass外执行，最典型的是各种IO操作，比如读写Texture/Buffer/Query等，Vulkan API都要求在RenderPass外执行。 因此SetGraphicsState这个API的调用时机是有讲究的，最好在...</description>
    </item>
    <item>
      <title>PBRT | Tagged Pointer模拟多态</title>
      <link>/2026/04/d900c760/</link>
      <pubDate>Sun, 26 Apr 2026 15:06:04 +0800</pubDate>
      <guid>/2026/04/d900c760/</guid><description>[TOC] 必要性 在GPU Kernel上要支持虚函数有若干限制： 对象必须构造到GPU上，不能跨越host/device边界调用函数（在global/device scope内通过new构造，不能在host构造然后cudamemcpy上去） 链接的时候跨cu链接虚函数很麻烦 Tagged Pointer：现代体系架构里，指针只用了48位，5级页表只能用40位 目前的体系架构里，高7位是空着的（但可能被其他硬件功能占用，比如HWASAN）。这些空着的比特位可以用来标记类型信息，但支持的类型数量有限制。 用法 // Primitive Definition class Primitive : public TaggedPointer&amp;lt;SimplePrimitive, GeometricPrimitive, TransformedPrimitive, AnimatedPrimitive, BVHAggregate, KdTreeAggregate&amp;gt; { public: // 继承TaggedPointer的默认构造函数 using TaggedPointer::TaggedP...</description>
    </item>
    <item>
      <title>UE | 一个错误的%格式化字符串引起的卡死</title>
      <link>/2026/04/9fd71eb9/</link>
      <pubDate>Thu, 02 Apr 2026 22:24:58 +0800</pubDate>
      <guid>/2026/04/9fd71eb9/</guid><description>真机打包后的包，在 vivo 某台手机上频繁出现卡死无响应。 经过多次实验后，发现每次卡死前都会出现一条固定日志（在 Android 上表现为 ANR，应用失去响应）。 nativeBatteryEvent(stat = xxx https://github.com/EpicGames/UnrealEngine/blob/7643252dbc0e275505d4afe5094778fc80aae0c0/Engine/Source/Runtime/Core/Private/Android/AndroidPlatformMisc.cpp#L366 这条日志是“非充分但必要条件”：出现它不一定会卡死，但发生卡死时一定会出现它。 仔细看了这段代码后，发现这里的 printf 存在未定义行为： struct FBatteryReceiver: Java::Lang::FObject { static constexpr FAnsiStringView ClassName = &amp;quot;com/epicgames/unreal/BatteryReceiver&amp;quot;; static void...</description>
    </item>
    <item>
      <title>UE | Vulkan_下 UE Editor 操作界面卡顿问题</title>
      <link>/2026/04/2f208232/</link>
      <pubDate>Wed, 01 Apr 2026 02:11:31 +0000</pubDate>
      <guid>/2026/04/2f208232/</guid><description>通过-vulkan -sm5启动 每次操作界面可以肉眼注意到slate的界面出现模糊然后卡顿2s后变清晰 经过定位发现是卡在swapchain的重建上 关闭cvar r.vulkan.keepswapchain后立马好转 怀疑是驱动问题 Vulkan Spec这么说&amp;gt; oldSwapchain is VK_NULL_HANDLE, or the existing non-retired swapchain currently associated with surface. Providing a valid oldSwapchain **may** aid in the resource reuse, and also allows the application to still present any images that are already acquired from it. 看起来是个没啥用的优化，主要是驱动可能会帮忙处理旧交换链的销毁和同步。 但是这里直接WaitGPUIdle应该也没啥问题 ...</description>
    </item>
    <item>
      <title>UE | Mobile Preview部分材质出现Create PSO失败的问题</title>
      <link>/2026/03/7f1014a7/</link>
      <pubDate>Sat, 14 Mar 2026 17:31:13 +0800</pubDate>
      <guid>/2026/03/7f1014a7/</guid><description>[TOC] 项目里有一部分材质在切换到 Mobile Preview 后，会在进入渲染阶段时崩溃。 崩溃时日志会出现如下报错： error X8000: D3D11 Internal Compiler Error: Invalid Bytecode: Incompatible min precision type for operand #1 of opcode #233 (counts are 1-based). Expected int or uint. 要复现这个 Bug，虚幻版本需要在 5.5 及以后，并且必须包含 https://github.com/EpicGames/UnrealEngine/commit/77ae86207ab4270c2030828002d8e732e37fbfb1 这个提交。 原因 其实这个提交里已经包含了对部分可能触发该 Bug 情况的处理，但还遗漏了 LocalVertexFactory。 虚幻在编译 Mobile Preview 的 shader 时，会先走 DX11 的 fxc.exe。如果 fxc.exe 编译失败，就会回退到 dxc.ex...</description>
    </item>
    <item>
      <title>UE | AVolume::EncompassesPoint永远失败的问题</title>
      <link>/2026/02/3adb5b11/</link>
      <pubDate>Sun, 08 Feb 2026 15:07:16 +0800</pubDate>
      <guid>/2026/02/3adb5b11/</guid><description>EncompassesPoint always fails when called with a Navigation Mesh Bounds Volume due to the Body Instance not being valid - Development / Programming &amp;amp; Scripting - Epic Developer Community Forums 最近碰到了和这个老哥类似的问题，任何一个继承自AVolume的子类在判断Actor-&amp;gt;GetActorLocation是否被Volume覆盖的时候总是返回false，即使是明显的case。 前情提要大概是需要在Build HLOD的时候对部分区域Actor进行特殊处理，所以从AVolume继承了一个子类，然后通过摆盒子来圈定空间范围。 Debugging 深入debug了下，发现从某个版本开始AVolume::EncompassesPoint换掉了基于AABB检查的方法，而是依靠物理引擎来判断是否有交集。 注意这里判断点是否位于Volume内都是用的GetSquaredDistanceToC...</description>
    </item>
    <item>
      <title>Mesa | 交叉编译v3dv驱动到raspberry pi5上</title>
      <link>/2026/01/6ed1e1c5/</link>
      <pubDate>Sun, 25 Jan 2026 18:58:24 +0800</pubDate>
      <guid>/2026/01/6ed1e1c5/</guid><description>本文含有一定的AI润色 想要直接在Pi上编译v3dv也不是不行，target并不是很多，大约500多个c target，一会也就编完了。 直接编译的话可以参考 Maíra Canal | Cross-Compiling CTS for the Raspberry Pi 4 虽然Maíra Canal这篇博客的重点是交叉编译CTS(我的下一个难题...) Mesa的交叉编译准备 Build Machine: Ubuntu 2404 LTS (WSL2 x86_64) Target Machine: Raspberry Pi OS (Debian 13 Trexie aarch64) 准备交叉编译环境 首先，在 WSL2 Ubuntu 中启用 arm64 架构并安装交叉编译器及依赖库。 # 启用 arm64 架构支持 sudo dpkg --add-architecture arm64 sudo apt update 注意 apt sourcelist的配置，ubuntu的其他架构的二进制不在默认的ubuntu源里，而是在ubuntu-ports源里，所以要在source list里指明不...</description>
    </item>
    <item>
      <title>Mobile | 集成HWCPipe(libGPUCounter)来监测性能</title>
      <link>/2025/11/9f36f844/</link>
      <pubDate>Sun, 30 Nov 2025 06:58:39 +0000</pubDate>
      <guid>/2025/11/9f36f844/</guid><description>最近在抓移动端的数据的时候还是经常感觉到一些痛点，尤其是虚幻在移动端分析性能的工具比较欠缺，主要还是要依靠厂商提供的工具和SDK。 项目组有钱的话可以考虑接入UWA或者Perfdog，节约很多事情，没钱就得自立更生了.. 不过UWA和Perfdog也有一些问题就是数据必须得传到他们自己的网站看，没法在游戏内实时看。有的时候在游戏内想调cvar来看性能指标的变化就不太方便了，只能录下来再对比。 Arm Streamline(对应mali芯片)倒是提供了比较方便的实时分析工具，只要用Arm Streamline启动应用，会绘制实时数据的采集图片(带宽需要结束trace以后才能分析数据)，Snapdragon Profiler 有个realtime模式也可以做到这个。 游戏内集成内录工具 如果想要做一些性能统计或者脚本化抓取的功能，最好还是有一个游戏内录工具比较好。 比如战斗开始前录制，战斗结束后停止录制，用厂商的工具就比较难做到了，只能先录制一整段，后期再手动截取一段数据。 Mali: libGPUCounter 考察了一下现有的工具发现Arm的GPUCounters数据是开源的，再饭 A...</description>
    </item>
    <item>
      <title>UE | 下一款Android开发机不一定是Android</title>
      <link>/2025/10/3e667544/</link>
      <pubDate>Thu, 09 Oct 2025 22:32:58 +0800</pubDate>
      <guid>/2025/10/3e667544/</guid><description>最近看了下Epic在Unreal Fest2024和Unreal Fest2025的两篇关于移动端的workflow的介绍，又尝试了一些新的东西。 移动端真机调试一直是一个我工作的一个痛点..无论是Android还是iOS.. 关于虚幻在模拟器上运行的可行性 UE5.X后对模拟器支持的比较好了，无论是Android Studio还是iOS Simulator都可以比较好的模拟了。 Android 模拟器支持 Mac模拟器支持 Mac我们用的是下面那种，打包成IPA以后直接安装并调试(只能适用于M芯片的mac) 最后一条不支持GPU Debugger是可以改的，项目里已经验证过了，把XCode Capture Plugin给改改就可以做到抓帧直接写入到文件，然后读取抓帧后的文件。 第一种模拟方式意义不大，主要是GPU特性支持不全。 这里重点讲下怎么用模拟器跑Android，iOS其实用 Xcode的designed for iPad的模式就可以比较无缝的跑了。 关于使用模拟器跑Android游戏 关于x86模拟器和arm模拟器 x86模拟器 x86上跑测没问题，gpu直通，cpu靠lib...</description>
    </item>
    <item>
      <title>UE | SingleLayerWater和Translucent截断的问题</title>
      <link>/2025/08/d4c72dd2/</link>
      <pubDate>Mon, 18 Aug 2025 23:44:30 +0800</pubDate>
      <guid>/2025/08/d4c72dd2/</guid><description>SLW or Not? 选择SLW的理由: 虚幻提供的一整套水体渲染解决方案，天然支持水上，水下以及真实感水体渲染 原生开箱即用，和water系统结合紧密 不用处理复杂场景下的半透排序问题(水/瀑布多个片在某些视角下半透排序可能会出问题) 选择半透水材质的理由: 不写深度，不会产生遮挡问题 NPR风格有一些其他游戏可以参考 Single Layer Water 截断半透明物体的问题 首先，这是否是个问题 关于SLW截断半透明是否需要处理这个取决于策划和美术的需求... 不过如果是个多端互通的游戏，最好还是考虑处理下，因为在移动端上SLW会退化成一个非常简单的半透渲染，它不会产生截断其他半透的效果。而在PC端上SLW是一个Opaque的渲染方式 + 写深度，所以会在深度测试阶段截断很多半透明的问题，这里会产生不统一的渲染效果。 以塞尔达为例，其磁力命中宝箱时产生的命中特效是一个圆圈，但是显然这个圆圈特效被水面渲染截断了。但是实际从游戏体验上来说，并不会造成很明显的视觉瑕疵。 如果要解决，如何解决 碰到了和这个朋友碰到的同样的问题，这里记录一下解决方案。 UE渲染学习（1）- Single...</description>
    </item>
    <item>
      <title>Donut | 有意思的Bent Normal</title>
      <link>/notes/rhi/f0273e37/</link>
      <pubDate>Tue, 05 Aug 2025 22:38:28 +0800</pubDate>
      <guid>/notes/rhi/f0273e37/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 bent normal https://github.com/NVIDIA-RTX/Donut/blob/6d3855607bd3b4e06f9fa64b659da7dcaabff11b/include/donut/shaders/utils.hlsli#L139 donut框架里，着色的时候有一个有意思的函数是GetBentNormal(即抄即用),研究了一下 // Smart bent normal for ray tracing // See appendix A.3 in https://arxiv.org/pdf/1705.01263.pdf float3 getBentNormal(float3 geometryNormal, float3 shadingNormal, float3 viewDirection) { // Flip the normal in case we&#39;re looking at the geometry from its ba...</description>
    </item>
    <item>
      <title>UE | Variable Shading Rate的定制和应用</title>
      <link>/2025/06/491d0591/</link>
      <pubDate>Sun, 29 Jun 2025 14:34:27 +0800</pubDate>
      <guid>/2025/06/491d0591/</guid><description>虚幻从5.4以后提供了完整的VRS Tier2支持，并且在Nanite的场景下额外提供了对Nanite特定优化的Software Variable Shading Rate的支持。 具体可以见这篇博客的介绍 Nanite-enabled Variable Rate Shading in Unreal Engine 5.4 在Nanite场景下，Software的模式会更适合Nanite的渲染模式。 Variable Rate Shading Tier1 / Tier2? Variable-rate shading (VRS) - Win32 apps | Microsoft Learn 微软的文章可能是对VRS的来龙去脉，支持特性讲的最清楚的，由于这个特性是由硬件支持的，所以DX12的API也可以无缝迁移到其他API上。 另外一个可以参考的资料是VK_KHR_fragment_shading_rate的提案 Vulkan-Docs/proposals/VK_KHR_fragment_shading_rate.adoc at main · KhronosGroup/Vulkan-Docs...</description>
    </item>
    <item>
      <title>UE | HLOD 让部分Moveable的Actor也参与HLOD Build</title>
      <link>/2025/06/69bb9694/</link>
      <pubDate>Sat, 14 Jun 2025 21:08:47 +0800</pubDate>
      <guid>/2025/06/69bb9694/</guid><description>这算是一个我们项目碰到的一个&amp;quot;伪&amp;quot;需求吧。有一部分Mesh在场景里可能按照Sequence K的动画的方式移动(因为涉及到物理所以不能使用材质WPO来处理)，但是这类Mesh属于一个巨型建筑的组成部分，所以如果HLOD把这些Movable的Actor剔除了，远处的剪影效果就不对了。 解决方案 UE里，一个StaticMeshComponent被排除出HLOD的判断逻辑是通过IsHLODRelevant()函数来实现的。这个函数会检查Component的Mobility属性，如果是Movable，则默认返回false。 实现上是新加了一种Volume,然后放了一个 AHLODMovableRelevantVolume的盒子在场景里，在这个盒子里的物体即使是Movable也会被认为是HLOD Relevant，但是在Commandlet实际Build HLOD时还是会忽略这个Actor。 具体修改方案如下: // UStaticMeshComponent.cpp IsHLODRelevant ... if (Mobility == EComponentMobility:...</description>
    </item>
    <item>
      <title>UE | HLOD 绕开Landscape Build Crash</title>
      <link>/2025/06/309c585a/</link>
      <pubDate>Sat, 14 Jun 2025 21:03:13 +0800</pubDate>
      <guid>/2025/06/309c585a/</guid><description>这是一个在5.3.2上碰到的问题，不清楚更新一点的引擎还会不会有。 Build HLOD中如果Landscape参与了Build那么可能会出现 Build中断崩溃的情况。 这个在UDN上也能翻到对应的其他人的提问， https://udn.unrealengine.com/s/question/0D54z00009dZSD3CAO/engine-crashing-when-trying-to-build-hlods-in-53 崩溃的堆栈类似如下，主要是崩溃在ALandscapeProxy::ExportToRawMeshDataCopyNew() [2023.09.18-00.39.57:482][222]LogWindows: Windows GetLastError: The operation completed successfully. (0) [2023.09.18-00.40.04:668][222]LogWindows: Error: === Critical error: === [2023.09.18-00.40.04:668][222]LogWindows: E...</description>
    </item>
    <item>
      <title>UE | HLOD优化 加快Build速度</title>
      <link>/2025/06/3902fd43/</link>
      <pubDate>Fri, 13 Jun 2025 21:30:24 +0800</pubDate>
      <guid>/2025/06/3902fd43/</guid><description>使用最后一级LOD的资源 虚幻默认HLOD Builder只提供了两个选项: 始终选择LOD0的资源 会使用一个1920x1080x45FOV 和某个距离的相机的投影矩阵计算一下LOD，使用这一级LOD的资源 对现在大量使用Nanite项目来说，LOD0可能是一个非常稠密的Mesh，导致Build HLOD的速度非常慢，内存要求也非常夸张，打包机性能差点可能都编不出来。 这里可以Hack一下引擎，选择Mesh的最后一级LOD，至少在日常迭代期可以使用这种HLOD，可以有效加快HLOD的Build速度。 分布式Build 参考: Distributing HLOD Building | Epic Developer Community 虚幻给了一个像模像样的BuildWorldPartitionHLODs.xml的BuildGraph示例作为分布式编译的参考，但是没看懂怎么用的。对BuildGraph也不太熟。不过BuildGraph里也是调用的commandlet的指令，所以我们可以跟一下里面到底做了什么。 在HLOD Builder的Commandlet的源码注释里能翻到一些更多的信...</description>
    </item>
    <item>
      <title>UE | HLOD优化 纠正错误的效果</title>
      <link>/2025/06/49830e01/</link>
      <pubDate>Sat, 07 Jun 2025 22:05:28 +0800</pubDate>
      <guid>/2025/06/49830e01/</guid><description>HLOD builder UE有好几套不同的合并Mesh，Baking材质的实现，Modeling Tool里有一套，UE4时期集成Simplygon有一套， HLOD这里也有好几种不同的baking method。 甚至存在一些相同类名相同功能的类但是实现不同的...咱也不敢想咱也不敢问 Mesh HLODBuilder UE现在用的比较多的主要有个4个HLOD Builder: Simplify Mesh HLODBuilder Merge Actors HLODBuilder Batch Instance HLODBuilder Approximate Mesh HLODBuilder 这四个类的入口有两个地方： 第一个入口在选中Actors以后点顶部菜单的Actors -&amp;gt; Merge Actors可以进入。 这里可以对单独的Actor进行合并操作，并且精心调制合并的参数（实际上这个手段更适合用来做一些debug操作，毕竟一个地图几十万个Actors谁会去一个一个的选啊) 第二个入口在顶部菜单的Build-&amp;gt;BuildHLODs，这个是对整个地图的Actors进行B...</description>
    </item>
    <item>
      <title>UE | Mobile Load 开源freedreno Vulkan驱动</title>
      <link>/2025/05/af26cfac/</link>
      <pubDate>Sun, 25 May 2025 12:04:21 +0800</pubDate>
      <guid>/2025/05/af26cfac/</guid><description>之前看到Android上的switch模拟器有这个挺有意思的功能，就研究了一下，是否可以不使用系统的驱动，Load第三方的Vulkan驱动来绕过一些系统驱动。 结论是： 可以，虚幻跑起来没什么问题。但是从商业角度来看，由于绕开了Android一些安全措施，有Google Play的上架风险。 UE Vulkan Loader 从虚幻的代码来看，vulkan的lib加载也不是很复杂的点，Android平台只有1-2个dlopen，加载了libvulkan.so的地方。这几个点需要修改一下，加载第三方驱动。 第三方驱动可以从这里下到，这个repo里存储了一些开源驱动(libfreedreno.so)，还有一些是从手机厂商的驱动中提取出来的libvulkan.so，比如Meta Quest 2的libvulkan.so。 Releases · K11MCH1/AdrenoToolsDrivers 绕开dlopen的白名单机制 从Android 7 以后，谷歌不允许apk随意再dlopen so文件了。参考https://developer.android.com/about/versions...</description>
    </item>
    <item>
      <title>UE5 | Mobile GPUScene 超过LWC Tile的Actor的显示问题</title>
      <link>/2025/04/77029615/</link>
      <pubDate>Sat, 12 Apr 2025 14:04:32 +0800</pubDate>
      <guid>/2025/04/77029615/</guid><description>注意这个Bug和UE5.x的Mobile GPUScene的实现方案强相关，忘了是5.x改到有Bug的实现方案的，UE5.3中为了绕开Mali GPU Vertex Shader不能读取SSBO的问题选择了把一部分紧凑的GPUScene上的数据(准确的说，每个Primitive 80个字节，5个float4) Encode到了Vertex Attribute上作为VS计算坐标的输入。 UE5.4以后Mobile GPUScene又改了实现，我还没细看，据说是改成了用UBO的方式来传递数据。 简单的说，Mobile GPUScene上VS读取的数据里没有LWC坐标的TilePosition的信息(需要额外的三个int)，所以UE5.3的Mobile GPUScene只能表示LWC TileSize内的Actor坐标(大约20公里)，超出这个坐标算出来的坐标是错误的。 Bug现象 新建一个Cube Actor移动到这个位置 XYZ (X=-2870208.313199,Y=718670.497198,Z=342.168112) Scale (X=10000.000000,Y=10000.0...</description>
    </item>
    <item>
      <title>UE | Mobile: Mesh和Material的UV套数不匹配的问题</title>
      <link>/2025/03/bf8a3658/</link>
      <pubDate>Sat, 22 Mar 2025 14:51:16 +0800</pubDate>
      <guid>/2025/03/bf8a3658/</guid><description>最近发现一些Mesh只有1-2套UV，如果TA错误的使用了需要3-4套UV的材质，那么在PC预览上看起来是正常的，切换到Mobile Preview或者真机上看到的效果都不太对。 调试半天以后确定是UV的问题，去UDN上翻了一下发现也有人报告类似的问题。 5.2 Bug in FStaticMeshVertexBuffer::BindPackedTexCoordVertexBuffer 先说结论: UE是刻意改成这样的，认为这种不规范的行为导致的bug是可以接受的。 问题出现在 LocalVertexFactory.ush里 当材质出现了偶数对的UV时，虚幻会将两个float2的UV Pack到一个float4. #if NUM_MATERIAL_TEXCOORDS_VERTEX #if !MANUAL_VERTEX_FETCH // These used to be packed texcoord arrays, but these cause problems with alighnment on some Vulkan drivers #if NUM_MATERIAL_TEXCO...</description>
    </item>
    <item>
      <title>UE | Mobile: Prepass Or Not?</title>
      <link>/2025/03/239ae6a3/</link>
      <pubDate>Sat, 15 Mar 2025 14:04:22 +0800</pubDate>
      <guid>/2025/03/239ae6a3/</guid><description>首先可以看看一篇其他人写的好博客。 参考：To z-prepass or not to z-prepass – Interplay of Light 为什么对Mask材质开PrePass有性能优势 Overdraw的产生: 由于mask材质里会有clip指令出现，所以管线里没法做early-z，只能跑完整的PS，这样会导致大量的Pixel Shader Overdraw. 为什么Opaque物体不会产生? 也会产生Overdraw。但是由于引擎里Opaque物体基本都做了从前往后排序，所以在绘制过程中由于Opaque物体可以经过early-Z优化，有大概率被earlyz pixel killing. 为什么开启Prepass对mask材质会有好处 Engine\Shaders\Private\DepthOnlyPixelShader.usf 虚幻渲染PrePass的时候用的，会根据材质生成一个 DepthOnlyPixelShader.usf,主要是给阴影和PrePass用的。 这个材质只计算Pixel Location 和 Clip相关的信息，不进行复杂的颜色计算，相当于一个轻量级的...</description>
    </item>
    <item>
      <title>Mobile | 通过UAV和RTV Clear纹理的测试</title>
      <link>/2025/02/e20059a6/</link>
      <pubDate>Wed, 12 Feb 2025 02:12:58 +0000</pubDate>
      <guid>/2025/02/e20059a6/</guid><description>Clear By UAV / RTV 高通845 XIAOMi 8 PF_RGBA8 Res UAV RTV 64x64 14us 11us 256 37 21 512 116 52 1024 443 170 2048 1698 621 4096 6.7ms 2.4ms 8192 24.9ms 10.08ms 测试代码 测试数据用Snapdragon Profiler抓出来的 void FShaderDemoModule::RandomGraphicsTest_Renderthread(FPostOpaqueRenderParameters&amp;amp; Parameters) { FRDGBuilder* RDGBuilderPtr = Parameters.GraphBuilder; static const std::array TextureSizes{64, 256, 512, 1024, 2048, 4096, 8192}; auto ClearTest = [RDGBuilderPtr](bool bClearByUAV) { auto&amp;amp;&amp;amp; RDGBuilde...</description>
    </item>
    <item>
      <title>2024: 狂飙</title>
      <link>/misc/c293dd8b/</link>
      <pubDate>Tue, 31 Dec 2024 20:21:55 +0800</pubDate>
      <guid>/misc/c293dd8b/</guid><description>又到了一年一度写总结的时候了！ 然而我下午还在艰难的调试Android Crash的问题...好在下班前修好了，真是顺顺又利利啊！ 聊聊生活 &amp;amp; 健康 今年不得不把这个主题作为第一个主题了，虽然好像没有生什么严重的病。 年初的时候被发烧狠狠袭击了一波（其实我怀疑是二阳了，但是家里没抗原了也就床上直挺挺的躺了两天给熬过去了），连带着干废了我两天年假。 体检报告还是一如既往的...嗯，好起来了！脂肪肝从中度变成了轻度，体重也有所降低，今年在家里有好好做饭。工作变得可以6点过下班以后，回家还可以顺路买点打折蔬菜做点晚饭，至少在晚饭上整挺好。 六月到九月还陆陆续续跑了一段时间步，九月总共跑了52公里吧，还挺开心的。国庆节摆烂以后随着天气转冷，这个跑步的习惯也没保持下来，不过也没关系，明年再继续。什么时候都不晚。 聊聊工作 今年是在完美的第二年了嗯，体感上比去年刚入职那会忙太多了。一方面是因为经常到处在代码里挖坑，经常做了一些比较底层的改动会影响一大片同事，导致不得不紧急修Bug。 另一方面也是因为主要做性能相关的工作，又涉及到 PC Client / Server / Mobile等不...</description>
    </item>
    <item>
      <title>UE | WPO Disable Distance的使用</title>
      <link>/2024/12/af05ecba/</link>
      <pubDate>Sat, 21 Dec 2024 03:58:50 +0000</pubDate>
      <guid>/2024/12/af05ecba/</guid><description>这个功能在5.3才被彻底在普通管线上实现，在 5.2 之前这个功能是 nanite only 的。 可以根据距离停止WPO的计算，根据 Epic 某篇文章说在堡垒之夜有非常好的性能收益，一时之间找不到这个文章了，可能是这篇. Bringing Nanite to Fortnite Battle Royale in Chapter 4 - Unreal Engine 从原理上来说不复杂： 在 Shader 生成的时候，在计算 WPO 的shader 代码生成的时候外层包裹一个 If 条件 根据 GPUScene 上的剔除结果（逐Instance 剔除）来标记一个 flag，根据 flag 来确定是否跳过 WPO 计算 UStaticMeshComponent::WorldPositionOffsetDisableDistance 经过层层传递，从U类到StaticMeshProxy类，再到FPrimitiveUniformShaderParametersBuilder void FPrimitiveSceneProxy::BuildUniformShaderParameters(FPri...</description>
    </item>
    <item>
      <title>BRDF | 白炉测试</title>
      <link>/2024/12/4d821343/</link>
      <pubDate>Sun, 08 Dec 2024 08:39:56 +0000</pubDate>
      <guid>/2024/12/4d821343/</guid><description>有一个很有意思的文件，bsdftest.cpp 其实这就是我一直想实现的白炉测试了。 (uniform incoming radiance: 1.0) BRDF能量守恒 \[ \int_{\Omega} f(\omega_i, \omega_o) \cos(\omega_i,n) d\omega_i Lambert BRDF 当\(\rho = 1.0\)时候，这个BRDF是完全不会丢失能量的。 强弱白炉测试 原始论文见: https://www.jcgt.org/published/0003/02/03/paper.pdf C++实现见: https://github.com/knarkowicz/FurnaceTest/blob/master/main.cpp 要看懂这个文件，首先要看懂 几何遮蔽函数。 我们在实时渲染里写的那个只是个简化的方案。 PBRT给了一个比较正常的公式 https://www.pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models#MaskingandShadowing 另外白炉测试的时候: 假设...</description>
    </item>
    <item>
      <title>BVH | HLBVH建树方法</title>
      <link>/2024/12/92aa5f22/</link>
      <pubDate>Sat, 07 Dec 2024 07:36:04 +0000</pubDate>
      <guid>/2024/12/92aa5f22/</guid><description>HLBVH HLBVH为什么快: 普通BVH建树是自顶向下，必须要知道上一层的bounds大小才能继续建树。而HLBVH通过莫顿编码可以快速的划分不同Primitives到不同的cluster,这样允许并行独立的处理不同cluster,然后再合并成BVH. 优点： 允许一定程度的并行建树，速度快 缺点： 利用 Morton 码能够二分空间的性质来建树，每一层二分的算法实质上等于按最简单的二分轴的方式，质量很差 morton编码的一些性质 PBRT是这样描述的: which map nearby points in dimensions to nearby points along the 1D line, where there is an obvious ordering function. After the primitives have been sorted, spatially nearby clusters of primitives are in contiguous segments of the sorted array. 莫顿编码能够把高维数据映射到一维，并且在空...</description>
    </item>
    <item>
      <title>BVH | Naive和SAH建树方法</title>
      <link>/2024/11/0e73fe60/</link>
      <pubDate>Sat, 30 Nov 2024 18:41:41 +0800</pubDate>
      <guid>/2024/11/0e73fe60/</guid><description>Naive 建树方法 二分Bounds法 每次取得BVH节点的最长轴，将其Box沿着最长轴一分为二，落在不同子节点的Primitive分别放入两个子节点中。 case SplitMethod::Middle: { // Partition primitives through node&#39;s midpoint Float pmid = (centroidBounds.pMin[dim] + centroidBounds.pMax[dim]) / 2; auto midIter = std::partition(bvhPrimitives.begin(), bvhPrimitives.end(), [dim, pmid](const BVHPrimitive &amp;amp;pi) { return pi.Centroid()[dim] &amp;lt; pmid; }); mid = midIter - bvhPrimitives.begin(); // For lots of prims with large overlapping bounding boxes, this // may fail ...</description>
    </item>
    <item>
      <title>Rider在虚幻的调试技巧</title>
      <link>/2024/11/4bdaa5f3/</link>
      <pubDate>Wed, 27 Nov 2024 02:32:17 +0000</pubDate>
      <guid>/2024/11/4bdaa5f3/</guid><description>查看全局变量 VS的语法 UnrealEditor-Core!GConfig UnrealEditor-Engine!GPlayInEditorContextString Rider的语法: {,,UnrealEditor-UnrealEd.dll}::GEditor-&amp;gt;PreviewPlatform.PreviewFeatureLevel {,,UnrealEditor-RHI.dll}::GMaxRHIFeatureLevel {,,UnrealEditor-UnrealEd.dll}::GEditor-&amp;gt;PreviewPlatform.PreviewFeatureLevel {,,UnrealEditor-Core}::PrintScriptCallstack() // 查看蓝图堆栈 {,,UnrealEditor-Engine.dll}::GPlayInEditorContextString 条件断点 FString: wcsstr((wchar_t*)MyString.Data.AllocatorInstance.Data, L&amp;quot;Search subst...</description>
    </item>
    <item>
      <title>UE | 虚幻里浮点数的Round模式</title>
      <link>/2024/11/6565b5f8/</link>
      <pubDate>Tue, 26 Nov 2024 00:00:11 +0800</pubDate>
      <guid>/2024/11/6565b5f8/</guid><description>IEEE 754 Floating-Point Rounding Mode (MIT/GNU Scheme 12.1) IEEE 754允许4种浮点round模式: round-to-nearest 等同于十进制下的四舍五入。 一个比较典型的特征是，在0.5的时候，会round到最近的偶数。 比如2.5和1.5都会round到2.0。 对应Round函数 toward-zero: 不管是负数还是正数都是直接截断尾部，比如1.5和-1.5都会round到1.0和-1.0。 对应Trunc函数 round up: 始终朝上舍入，正数尾部阶段并进位，负数直接截断尾部。 比如1.3往上round到2.0，-1.3直接round到-1.0。 对应Ceil函数 round down: 始终朝下舍入，正数直接截断尾部，负数尾部阶段并进位。 比如1.3直接round到1.0，-1.3往下round到-2.0。 对应Floor函数 Cpp种类型强转时的取整策略 https://en.cppreference.com/w/cpp/language/implicit_conversion#Floating-...</description>
    </item>
    <item>
      <title>Puerts | v8::InternalField 大小和UStruct的包装成本</title>
      <link>/2024/11/b3ae7426/</link>
      <pubDate>Sun, 24 Nov 2024 06:05:08 +0000</pubDate>
      <guid>/2024/11/b3ae7426/</guid><description>SMI v8要求存入的void*必须按2字节对齐，也就是最后一位必须是0 很容易想到这个是v8压缩指针要求。 进去看一下 void v8::Object::SetAlignedPointerInInternalField(int index, void* value) { auto obj = Utils::OpenDirectHandle(this); const char* location = &amp;quot;v8::Object::SetAlignedPointerInInternalField()&amp;quot;; if (!InternalFieldOK(obj, index, location)) return; i::DisallowGarbageCollection no_gc; Utils::ApiCheck(i::EmbedderDataSlot(i::Cast&amp;lt;i::JSObject&amp;gt;(*obj), index) .store_aligned_pointer(obj-&amp;gt;GetIsolate(), *obj, value), location, &amp;quo...</description>
    </item>
    <item>
      <title>KDE_Wayland_Nvidia登陆后黑屏的问题</title>
      <link>/2024/11/d02b0f52/</link>
      <pubDate>Sun, 03 Nov 2024 21:39:42 +0800</pubDate>
      <guid>/2024/11/d02b0f52/</guid><description>这两天重新装了Manjaro KDE作为桌面。因为双显示器的缩放问题(x11只能设置全局缩放不能设置per monitor缩放)只能用wayland。 结果装了Nvidia的专有驱动以后登陆进去就黑屏。(其实Mesa的NVK倒也不是不能用，只是我起了虚幻以后发现nvk可能性能还是不太行，有点卡卡的)。 查了半天从manjaro的论坛里发现了一个类似症状的帖子: https://forum.manjaro.org/t/cannot-use-wayland-with-kde-on-nvidia/163318/8 翻了一下有大佬给了解决方案 No plasma interface with kernel 6.9 + Nvidia gpu + Wayland If you encouter a black screen with no inteface after login in, it’s probably a problem with simpledrm loading. To solve it add nvidia_drm.fbdev=1 to /etc/default/grub in...</description>
    </item>
    <item>
      <title>UE | 解决Rider在Unreal Android配置下大片飘红的问题</title>
      <link>/2024/09/8739b0fb/</link>
      <pubDate>Sun, 15 Sep 2024 20:35:43 +0800</pubDate>
      <guid>/2024/09/8739b0fb/</guid><description>Rider打开Unreal Android 大片爆红 Rider在平时开发虚幻的时候很好用，但是一旦用来开发和调试Android平台的虚幻代码时，就会出现大片红色的情况，这是因为Rider无法正确解析Android平台的头文件路径。 我们平时的工作流基本上是在Windows上开发，然后交叉编译到Android平台，但是虚幻的UBT没有正确在Android平台导出对应NDK的头文件路径，导致Rider无法正确解析交叉编译的头文件路径。 Jetbrains的Issue里也有人提了这个问题，UBT只导出了各个模块的头文件和引擎的头文件路径，但是没有导出Android的系统库的索引路径，导致大片大片的找不到头文件的问题。 &amp;quot;EnvironmentIncludePaths&amp;quot;:[ &amp;quot;C:\\Program Files\\Epic Games\\UE_4.26\\Engine\\Source&amp;quot;, &amp;quot;C:\\NVPACK\\android-sdk-windows\\ndk\\21.1.6352462\\sources\\android\\native_...</description>
    </item>
    <item>
      <title>UE | UnrealInsights 截图采用Jpg数据</title>
      <link>/2024/09/e1141481/</link>
      <pubDate>Sun, 15 Sep 2024 12:54:54 +0800</pubDate>
      <guid>/2024/09/e1141481/</guid><description>背景: 我们在录制Unreal Insights时为了方便查看性能热点时候正在做什么，开了一个后台线程在定时截屏并传输数据给insights存储到Screenshot Channel里。但是最近注意到这样生成的utrace文件体积增长的非常快，很不利于存储的和分发，调查了一下原因。 TraceScreenshot接口 FTraceScreenshot::TraceScreenshot接受一个未压缩过的TArray&amp;lt;FColor&amp;gt;或者TArray&amp;lt;FLinearColor&amp;gt;作为原始数据，内部会根据数据类型选择不同的压缩格式。 FColor: PNG FLinearColor: exr 默认情况下FColor是压缩成PNG，压缩效率有点低，一张1280x720的数据要增长1M多。 void ImageUtilsCompressImageArrayWrapper(int32 ImageWidth, int32 ImageHeight, const TArrayView64&amp;lt;const FColor&amp;gt;&amp;amp; SrcData, TArray64&amp;lt;ui...</description>
    </item>
    <item>
      <title>Mesa | u_vector的实现</title>
      <link>/2024/09/07cd164d/</link>
      <pubDate>Wed, 11 Sep 2024 00:05:57 +0800</pubDate>
      <guid>/2024/09/07cd164d/</guid><description>可以看u_vector.c里的注释: * A dynamically growable, circular buffer. Elements are added at head and * removed from tail. head and tail are free-running uint32_t indices and we * only compute the modulo with size when accessing the array. This way, * number of bytes in the queue is always head - tail, even in case of * wraparound. */ 主要特点: 可以动态扩容 ring buffer 实现位于 src/util/u_vector.c 和std::vector的异同点 ： 相同: 尾部追加时可扩容，扩容系数2 不同: 不保证数据从0偏移量开始，head指针有可能有偏移量 不允许随机访问，只能头尾访问. 数据在内存上可能有环回，不连续，不能直接memcpy /* |1|2|3|...</description>
    </item>
    <item>
      <title>Mesa | VK_EXT_PRIVATE_DATA的实现</title>
      <link>/2024/09/7cf58aab/</link>
      <pubDate>Mon, 02 Sep 2024 20:54:33 +0800</pubDate>
      <guid>/2024/09/7cf58aab/</guid><description>这个扩展允许在任意的VK_OBJECT上绑定若干个8字节的数据。 主要用法是: 通过vkCreatePrivateDataSlotEXT创建一个slot,这个slot内部结构是不透明的，但是实际上里面就是一个index(应用层不可见) 这个slot可以被作为索引访问修改一个VK_OBJECT上的PRIVATE_DATA 问题1: 为什么Index要做成不透明的 这个Index实际上是存放在vkDevice上的一个成员变量(驱动层), 每次创建一个slot,这个index就会自增1。 这样可以避免不同的Layer申请到同一个Index。 每个Layer和应用每次调用vkCreatePrivateDataSlotEXT都会创建一个自增的index的slot。 问题2: 数据存放在哪， 数据结构是什么 数据实际存放在所有vulkan object的基类 vk_object上，数据结构是sparse array。 通过index可以稀疏的创建和删除数据。 问题3：为什么API需要传入ObjectType 既然PrivateData在所有的VK_OBJECT上，理论上是可以在任何的VK_OBJE...</description>
    </item>
    <item>
      <title>UE5 | ChildActorComponent Transient Child在PIE下消失的问题</title>
      <link>/2024/08/e488f1cb/</link>
      <pubDate>Thu, 15 Aug 2024 22:49:58 +0800</pubDate>
      <guid>/2024/08/e488f1cb/</guid><description>To be Transient or Not To be? /** * Should the spawned actor be marked as transient? * @note The spawned actor will also be marked transient if this component or its owner actor are transient, regardless of the state of this flag. */ UPROPERTY(EditDefaultsOnly, Category=ChildActorComponent) uint8 bChildActorIsTransient:1; 这个Flag主要作用是给Spawn出来的ChildActor加上Transient标记。 带上Transient标记，所有序列化和SavePackage之类的函数都会跳过这个ChildActor。 适合ChildActor可能会进行一些会导致PackageDirty的情况。 Bug: PIE下ChildActor消失? 如果带上Transient，摆...</description>
    </item>
    <item>
      <title>UE Panner节点移动端Half精度问题</title>
      <link>/2024/08/3ffbfa69/</link>
      <pubDate>Sun, 11 Aug 2024 19:31:32 +0800</pubDate>
      <guid>/2024/08/3ffbfa69/</guid><description>Panner节点主要是用于实现材质中纹理的平滑滚动的效果，可以用在水花等地方。 最近发现在移动端上，随着时间的推移，用了该节点的材质会滚动得越来越慢，越来越卡顿，应该是精度问题。 Panner节点的实现 panner节点的实现位于 UMaterialExpressionPanner::Compile，它自身并没有什么特殊的HLSL代码，只是组合了一些基本算子。 勾上了bFractionalPart: UV = UV + frac(Time * Speed) 没有勾上bFractionalPart: UV = UV + Time * Speed 问题的源头 连一个最简单的材质，直接打开DirectX Mobile来看翻译出来的hlsl half Local1 = (View_GameTime * 0.94999999); half Local2 = frac(Local1); half2 Local3 = Parameters.TexCoords[0].xy; half2 Local4 = half2( Local2 ,frac(0.00000000)); half2 Local5 = ...</description>
    </item>
    <item>
      <title>UE5 | 一个Mobile小于4x4的法线贴图采样错误的问题</title>
      <link>/2024/07/59e1920f/</link>
      <pubDate>Fri, 05 Jul 2024 23:44:10 +0800</pubDate>
      <guid>/2024/07/59e1920f/</guid><description>最近注意到在移动端上Build出来的HLOD看上去光照不太对，明显感觉法线出了点问题。但是在PIE和PC包里看上去都没问题。 把Android上的Shader抓出来，起了一个Vulkan-RHI的Editor调到Mobile Render，然后抓帧把Shader打出来一行一行的diff.. 翻了一下代码以后发现两边对 Normap Map 的采样处理的不太一样 */ MaterialFloat4 UnpackNormalMap( MaterialFloat4 TextureSample ) { #if DXT5_NORMALMAPS // DirectX MaterialFloat2 NormalXY = TextureSample.ag; #elif LA_NORMALMAPS // ASTC.. MaterialFloat2 NormalXY = TextureSample.ra; #else MaterialFloat2 NormalXY = TextureSample.rg; #endif NormalXY = NormalXY * MaterialFloat2(2.0f,2....</description>
    </item>
    <item>
      <title>使用FWeakObjectPtr安全判断UObject失效</title>
      <link>/2024/06/a57858d2/</link>
      <pubDate>Sat, 29 Jun 2024 13:00:20 +0800</pubDate>
      <guid>/2024/06/a57858d2/</guid><description>最近碰到一个比较奇葩的问题，就是渲染线程里绑定材质Material Uniform的地方居然保存了UTexture*的裸指针。 具体可以见 Engine\Source\Runtime\Engine\Private\Materials\MaterialInstanceSupport.h 然后在切换地图的时候，有可能因为切地图UE强杀Actor的原因导致Actor动态创建的UTexture*被回收，这里保存的指针就成了悬空指针。(具体的成因我还没想清楚，因为理论上来说Material那里也记录了Texture的引用，不过现象就是在切地图的时候有几率碰见崩溃)。 具体崩溃的堆栈在于void FUniformExpressionSet::FillUniformBuffer 仔细阅读这段代码,似乎Epic也发现这里可能会产生悬空指针，只是他们也没弄明白这块到底是怎么产生的...我翻了一下UDN，也有人碰见过类似的问题，不过他那个原因是因为他绑定的UTexture忘记了加UProperty,导致被GC回收了，和我这还不太一样...我这里UTexture明明放在蓝图的变量怎么都被回收了.. IsVa...</description>
    </item>
    <item>
      <title>Unreal Including IndirectDraw Primitives In Stats(DirectX12 Only)</title>
      <link>/2024/06/aeefcfc1/</link>
      <pubDate>Mon, 17 Jun 2024 23:12:00 +0800</pubDate>
      <guid>/2024/06/aeefcfc1/</guid><description>Stat Unit / Stat RHI IndirectDraw is widely used in Unreal (especially on Desktop Deferred Renderer). It&#39;s a common practice to use IndirectDraw to draw a large number of objects with a single draw call, with GPU Culling on GPUScene. With GPUScene enabled, HISM and ISM and landscape will be drawn with IndirectDraw. However, the instances drawn with IndirectDraw are decided by Compute Shader, the result is stored in a GPU Buffer and then used in the IndirectDraw call. There is no simple way to de...</description>
    </item>
    <item>
      <title>UE | 移动端谨慎使用Occlusion/Timer Query</title>
      <link>/2024/04/42bea8b2/</link>
      <pubDate>Sat, 27 Apr 2024 15:29:06 +0800</pubDate>
      <guid>/2024/04/42bea8b2/</guid><description>最近在项目里连续遇到了几个关于Query的问题，这里记录一下.. Occlusion Query Hardware Occlusion Query在桌面端是一个很可靠的功能， GPU Gems里有好几篇文章都介绍过这个功能用来做Object级别的剔除，也是从零几年就被广泛使用的老技术了，稳定可靠。 参考：Chapter 6. Hardware Occlusion Queries Made Useful | NVIDIA Developer 但是在移动端还是发现他比较不可靠，主要问题集中在: 发起一次Occlusion Query需要至少3次API Call，Query多的时候可能会导致性能问题 部分API和硬件对Occlusion Query的数量有隐式限制。 先谈第一个问题，以vk作为例子，发起一次Occlusion Query至少需要以下API Call: vkCmdBeginQuery(commandBuffer, queryPool, 0, 0); // draw something // End occlusion query vkCmdEndQuery(commandBuf...</description>
    </item>
    <item>
      <title>UE5: Strange Magenta Pixels Sampled in ASTC6x6 On Android</title>
      <link>/2024/04/ea7c59b3/</link>
      <pubDate>Thu, 25 Apr 2024 23:52:24 +0800</pubDate>
      <guid>/2024/04/ea7c59b3/</guid><description>Recently I noticed that some strange magenta pixels (1,0,1,1) appeared on objects in Android Packages. After a quick investigation through Renderdoc, I noticed some textures compressed in ASTC6x6 format have there mipmaps lower than 4x4 filled with magenta pixels. I&#39;m not sure which way is correct to handle the 4x4 mipmaps and lower for ASTC6x6. These small mipmaps should probably be filled with the correct down-sampled pixels (but still take up 6x6 space), or simply not generate them. However, ...</description>
    </item>
    <item>
      <title>一个有意思的UE SwitchParam Dynamic Branch 导致驱动崩溃/渲染错误问题</title>
      <link>/2024/04/79b3d5d0/</link>
      <pubDate>Thu, 25 Apr 2024 00:10:18 +0800</pubDate>
      <guid>/2024/04/79b3d5d0/</guid><description>UE在5.2(也许5.3)推出的一个功能，将材质里的SwitchParam变成了Dynamic Branch(但是不支持动态修改，只允许在MI处修改)，主要目的是充分利用现代GPU对于Uniform Variable + Branch的优化，对于Uniform变量作为if condition的情况下，动态分支并不会带来warp divergence。 而StaticSwitch的问题在于会导致材质变体指数级上升，尤其是切换Shader变体可能会导致PSO的重新编译和加载，开销可能远大于Shader里走一个动态分支。 Dynamic Branch的实现 Dynamic Branch的代码生成是在int32 FHLSLMaterialTranslator::DynamicBranch(int32 Condition, int32 A, int32 B)这个函数里，生成的代码类似于 float3 branch1 = ...; float3 branch2 = ...; float3 staticResult; [branch] switch (condition){ default: sta...</description>
    </item>
    <item>
      <title>Puerts | 利用V8机制进行并行GC</title>
      <link>/2024/04/f1685bcf/</link>
      <pubDate>Sat, 13 Apr 2024 16:18:20 +0800</pubDate>
      <guid>/2024/04/f1685bcf/</guid><description>这里讨论的东西其实是利用v8的机制，所以理论上nodejs的也能用上。 Finalizer的性能问题和可靠性问题 在用脚本语言和宿主语言交互的时候，不可避免的会创建许多临时的js object对象，比如在ts里调用各种C++函数，返回的结果都必须包装为js object。 最早Puerts全部使用的是v8::PersistentBase::SetWeak的方法，这也是v8的hello world文档里讲的方法(https://v8.dev/docs/embed)。 它允许在C++里指向js object的Persistant handle并设置为WeakPtr，当这个WeakPtr是最后一个存在的reference的时候，v8会调用我们指定的callback来执行资源释放的操作。 它的问题主要是: v8不保证这个callback一定会被执行，有可能会有资源泄露(Finalizer问题，许多GC语言都会有这个问题，有的语言甚至允许在finalizer里重新复活这个对象) 执行的时候是在主线程，大量的小对象回收会阻塞主线程很长时间 ArrayBuffer BackStoring v8 8加...</description>
    </item>
    <item>
      <title>UE半精度下SpirV-Cross生成了错误的Metal Shader</title>
      <link>/2024/04/a86c2360/</link>
      <pubDate>Sat, 13 Apr 2024 14:55:20 +0800</pubDate>
      <guid>/2024/04/a86c2360/</guid><description>UE5 Metal后端打开半精度Shader编译支持 虚幻默认在IOS上是禁用半精度的，但是实际上手机上需要这个功能。 禁用的地方在于 BaseEngine.ini里 [/Script/IOSRuntimeSettings.IOSRuntimeSettings] ForceFloats=True 在游戏里的DefaultEngine.ini找个地方重新覆盖这个变量为false即可打开半精度支持。 SpirV-Cross生成了错误的Metal Shader 打开半精度后发现有部分材质有 error: call to &#39;clamp&#39; is ambiguous的编译错误，仔细调查后发现问题在于SpirV-Cross生成的MSL有问题。 虚幻编译MSL的基本流程是通过ShaderConductor调用 hlsl --(dxc)-&amp;gt; spirv -- (spirv-cross) --&amp;gt; msl. 通过简单的二分查找，发现这个问题出现在我们TA连的一些材质里会使用了 arctangent2这个节点，后面经过一系列的计算然后最后经过saturate就必然出现这个问题。 找了个最简单的材质...</description>
    </item>
    <item>
      <title>UE调试Shader编译失败的问题</title>
      <link>/2024/03/04dbd0cf/</link>
      <pubDate>Sat, 30 Mar 2024 00:20:49 +0800</pubDate>
      <guid>/2024/03/04dbd0cf/</guid><description>最近碰到了打包UE的时候碰见了编译Spir-v Shader的时候，DXC直接抛Internal Error然后导致ShaderCompileWorker进程崩溃的情况，这种情况下甚至不会导出编译失败的Shader的源码。 花了一段时间排查解决了，记录一下备忘。 有一些自定义编译Shader编译过程参数的时候也可以参考下面的备忘。 调试Shader编译错误的时候，我们需要 正在编译的Shader源码 当前的编译选项 编译结果和错误警告信息 ConsoleVariables.ini 禁用单独进程编译 ;r.Shaders.AllowCompilingThroughWorkers=0 这行注释掉，我们需要在Editor里调试ShaderCompile的过程 Spir-V Shader 的编译入口 Vulkan Shader的编译入口处在 UE5_master\Engine\Source\Developer\VulkanShaderFormat\Private\VulkanShaderCompiler.cpp static bool CompileWithShaderConductor UE...</description>
    </item>
    <item>
      <title>Shader Generation for Binning Pass</title>
      <link>/2024/03/cc00bf0c/</link>
      <pubDate>Thu, 21 Mar 2024 21:53:02 +0800</pubDate>
      <guid>/2024/03/cc00bf0c/</guid><description>What is Binning Pass? Reference: Tile-based rendering Binning Pass is a special vertex pass which marks which bin a triangle visible belongs to. It&#39;s a common tech widely used in Mobile GPU to reduce the amount of fragment shader invocations. How is Binning Pass implemented? I&#39;ve been curious about it for a long time, especially when I ran into a performance issue(Snapdragon Profiler will show how long Binning Pass is) about it. It is implemented in driver by the GPU vendor and not public. There...</description>
    </item>
    <item>
      <title>UE 谨慎使用SetCollisionProfileName和SetCollisionEnabled</title>
      <link>/2024/02/3303b9f3/</link>
      <pubDate>Sun, 18 Feb 2024 22:28:57 +0800</pubDate>
      <guid>/2024/02/3303b9f3/</guid><description>我的一个测试工程里有个测试场景是大批量的调整Component的碰撞属性，用了SetCollisionProfileName和SetCollisionEnabled来实现。 但是在测试的过程中发现了一个问题，这两个函数的调用会导致性能急剧下降。 SetCollisionProfileName UPrimitiveComponent::SetCollisionProfileName会带来约50us-100us的卡顿，Component数量一多很容易到毫秒级别的耗时。 主要的耗时在FBodyInstance::UpdatePhysicsFilterData() SetCollisionEnabled 这个函数更夸张，可能会导致100us-200us的卡顿，可能会导致PhysicsState重建(类似于SceneProxy的MarkRenderStateDirty)。 如果用的物理引擎是Chaos可以参考下面的文章进行hack一下，phsyx或者havok就只能自求多福了.. 参考：Fast Chaos Collision Toggling | voithos.io 结论 不要在tick或者...</description>
    </item>
    <item>
      <title>一个JS的class fields导致在v8 9.4(nodejs 16)上的性能退化的问题</title>
      <link>/2024/02/ca4cc3e1/</link>
      <pubDate>Sat, 10 Feb 2024 15:32:34 +0800</pubDate>
      <guid>/2024/02/ca4cc3e1/</guid><description>最近仍然在做一些关于v8的性能测试，用到的v8的版本是9.4，对应nodejs的16版本。 在测试以下代码的时候发现一个有意思的性能问题。 class Point { X: number; Y: number; Z: number; constructor(x: number, y: number, z: number) { this.X = x; this.Y = y; this.Z = z; } }; let start_time = new Date().getTime(); for (let i = 0; i &amp;lt; 100_0000; i++) { let p = new Point(1, 2, 3); } let end_time = new Date().getTime(); console.log(&amp;quot;Time: &amp;quot; + (end_time - start_time) + &amp;quot;ms&amp;quot;) 问题原因 v8 9.4在处理以下形式的js class声明的时候有性能问题，只要包含了class fields的声明，性能就会急剧劣化。 class...</description>
    </item>
    <item>
      <title>Nvrhi | Push Constant的处理</title>
      <link>/notes/rhi/b152921e/</link>
      <pubDate>Thu, 18 Jan 2024 22:41:41 +0800</pubDate>
      <guid>/notes/rhi/b152921e/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 Push Constant Push Constant是Vulkan的术语，用来指代一个小的buffer，可以被设置在管线上而无需绑定，Shader可以直接访问。 比较适合用来存一些易变而又不太大的数据。 在rhi的处理里，要封装一个Push Constant，最麻烦的是Dx11 / OpenGL，主要是上一代图形API没有暴露这个概念。只能用Constant Buffer去模拟。 在实现上: API Native Concept Limit Vk Push Constant Spec要求最少128Bytes Dx12 Root Signature(Root Constants) 256Bytes Dx11 Constant Buffer 比较大，但是需要占据一个Slot Push Constant在硬件上的映射 其实软件封装不是重要的，重要的是Push Constant在硬件上是怎么映射的，如此小的尺寸限制往往暗示着它在硬件上可能有专门的高速Cache。 翻了一...</description>
    </item>
    <item>
      <title>Halton序列和Faure_Permutations</title>
      <link>/2024/01/0f112f8b/</link>
      <pubDate>Wed, 17 Jan 2024 23:40:49 +0800</pubDate>
      <guid>/2024/01/0f112f8b/</guid><description> Note 文章内图片来自参考资料1的课件。 从N维空间任取一块空间， 这块空间和整体空间的比值 [0,1] 落在这块空间里的点和所有采样点的比值 任取一块空间，所有取的空间上面两个值的最大绝对差值就是&amp;quot;差异&amp;quot; 假设完全均匀分布(格子状)，那么差异应该接近0。 Van Der Corput 序列 以2为底的序列 以2为底的表示有快速算法，观察其二进制表示，可以观察到其radical inverse是二进制位彻底翻转，然后前面加0.（相当于除以一个2^(n)次幂, n为表示这个数需要的二进制位）。 比如 100 = 4 --radical invese--&amp;gt; 001 = 1 转换为(0.001)_2 = 1 / 8 所以以2为底的VDC序列有一个快速的移位算法 float radicalInverse_VdC(uint bits) { bits = (bits &amp;lt;&amp;lt; 16u) | (bits &amp;gt;&amp;gt; 16u); bits = ((bits &amp;amp; 0x55555555u) &amp;lt;&amp;lt; 1u) | ((bits &amp;amp; 0xAA...</description>
    </item>
    <item>
      <title>一个RenderDoc调试VK_KHR_synchronization2崩溃的问题</title>
      <link>/2024/01/e7923477/</link>
      <pubDate>Sun, 14 Jan 2024 17:13:38 +0800</pubDate>
      <guid>/2024/01/e7923477/</guid><description>把有问题的RDC File放在这里 https://drive.google.com/file/d/1XCYulZg0esli__yX0cRjsvjCKrAeMpJo/view?usp=sharing 这两天在研究KHR Sync2的barrier写法，并且尝试从Core的vkCmdPipelineBarrier的写法切换到Sync2里的写法。好处是把StageFlags也收拢到了每个barrier的表示里。 举个例子 比如我有两个buffer需要同步 第一个buffer是由compute shader产生，到第二个Pass的VS使用。 第2个buffer是由Copy操作产生，到第二个Pass的PS使用。 那么同步的stageFlags应该是这样的 buffer1: srcStageMask: eComputeShader dstStageMask: eVertexShader buffer2: srcStageMask: eTransfer dstStageMask: eFragmentShader 在vkCmdPipelineBarrier里这个只能拆成两次调用，而在sync2里可...</description>
    </item>
    <item>
      <title>我的2023: Unreal的一年</title>
      <link>/misc/3e54c858/</link>
      <pubDate>Sat, 30 Dec 2023 13:52:57 +0800</pubDate>
      <guid>/misc/3e54c858/</guid><description>在2022的灰色基调后面，今年总算是多了一点鲜艳的颜色。 今年最大的改变也许是口罩令终于结束了。我在2022年12月的上半旬随着口罩令的解除也随着大军中招了，好在家里药箱里倒是有之前留下来的对乙酰氨基酚和布洛芬，还有一些缓解咽喉疼痛的含片。症状最明显的两三天基本上是躺在床上靠点外卖过活，然后每隔六七个小时准时开始起高热，这个时候就开始狠狠嗑药。过了两三天总算是满血复活了。 聊聊目标 苟住工作这一条既算是失败又是成功了吧..尽管一度通过活水逃过了一次裁员，最后还是没能逃过降本增效，领了大礼包离开了腾讯。不过好在基本无缝衔接了下一份工作，来到了完美。并且从另一种角度来看也算是大成功中的大成功，因为家庭的原因我一直在思考从上海or深圳换工作地点到成都的事情。趁着这次机会也是成功换到了成都工作，彻底完成了这个小目标。 保持健身这一条只能说成功了一部分，我确实一直有在健身，体重从80+公斤降到了77左右，并且维持了较好的身体健康(今年完全没有生病过)，但是离预期的肌肉男还差得远，再接再厉。 讲好英语这条没做好，明年继续努力吧！ 博客120篇的话完成了一部分吧，今年大概写了20+篇左右，离30篇的...</description>
    </item>
    <item>
      <title>Puerts 编译带调试符号的debug版v8</title>
      <link>/2023/12/964342de/</link>
      <pubDate>Sat, 23 Dec 2023 15:18:42 +0800</pubDate>
      <guid>/2023/12/964342de/</guid><description>最近一段时间一直在和PuerTs打交道，有的时候碰见问题想跟到v8里看一下v8的具体实现细节。 Puerts的官方提供编译好的二进制，开箱即用。 从官方下下来的v8是release版，但是压缩包里带了个pdb，本来以为是可以直接挂上调试符号的，但是用VS的调试器挂上pdb以后仍然没法单步进去，只能反汇编调试，跟着v8一些简单的符号调，有点痛苦。 翻了一下也不只是我碰见了个这个问题 参考：v8.dll.pdb的符号文件无法加载 · Issue #7 · puerts/backend-v8 深入调查了一下，原因可能是因为编译v8的时候用的Release版，而且看样子还加了strip_debug_info之类的标记，对MSVC的pdb和谷歌的gn 这套不熟悉，不太确定到底是哪个标记导致的。 call gn gen out.gn\x64.release -args=&amp;quot;target_os=&amp;quot;&amp;quot;win&amp;quot;&amp;quot; target_cpu=&amp;quot;&amp;quot;x64&amp;quot;&amp;quot; v8_use_external_startup_data=fals...</description>
    </item>
    <item>
      <title>UE Cull Distance Volume 杂记</title>
      <link>/2023/12/547b212a/</link>
      <pubDate>Sun, 03 Dec 2023 23:41:57 +0800</pubDate>
      <guid>/2023/12/547b212a/</guid><description>UE的距离剔除不是默认打开的，需要在场景里摆一个Cull Distance Volume打开才生效。 参考：Cull Distance Volume | Unreal Engine 4.27 Documentation 官方文档写的还算详细，补充几点杂记。 在编辑器下预览Cull Distance Volume 需要按G进入GameView，隐藏所有的gizmo或者要进入PIE的情况下才能正确预览cull distance生效。 生效的条件 Actor是Static Actor需要勾选bAllowCullDistanceVolume 没有勾选HiddenInGame，bVisible也是true 具体应用的距离 tldr: 以Bounds的直径为准，找到abs差最接近的Size到CullDistance的映射。 详细过程: Volume里保存了一堆FCullDistanceSizePair结构体， 分别表示Size -&amp;gt; CullDistance的映射 核心循环体是 获得PrimitiveComponent的BoundsRadius, 乘以2获得直径 遍历FCullDistanc...</description>
    </item>
    <item>
      <title>Nvrhi的RefCounter和侵入式计数</title>
      <link>/notes/rhi/e7f14935/</link>
      <pubDate>Sat, 04 Nov 2023 17:12:30 +0800</pubDate>
      <guid>/notes/rhi/e7f14935/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 RefCountPtr 介绍可以参考: 参考：DirectX11--ComPtr智能指针 - X_Jun - 博客园 DX的类都继承自IUnknown,除掉可以查询接口并转型以外，比较大特点是自带引用计数。 vulkan的资源需要手动管理引用计数，不用的时候归还回去。 统一这两种方式的方式是用侵入式Ptr。 仿造实现一个ComPtr。 另外一个好处是侵入式指针是zero-space，由于它不包含任何成员。 https://github.com/NVIDIAGameWorks/nvrhi/blob/556eb6e22e5c5a61f09b84ad26945d76b0172dfa/include/nvrhi/common/resource.h#L127 微软也有一个不包含Windows SDK专属功能的实现在DirectX12 for WSL的头文件里，https://github.com/microsoft/DirectX-Headers/blob/48f23952...</description>
    </item>
    <item>
      <title>Donut virtual filesystem的设计</title>
      <link>/notes/rhi/0385ff99/</link>
      <pubDate>Sat, 04 Nov 2023 17:08:17 +0800</pubDate>
      <guid>/notes/rhi/0385ff99/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 donut有一个vfs的设计，思想可能来自于linux，继承自VFS的只需要实现最基本的几个接口就可以了。 它主要是解决一个渲染框架去读取资源的问题。 一般渲染框架都会带一个类似的方案，比如根据自身的exe所在的位置去搜索Resources目录之类的。 fs::path的路径格式 native format generic_format 在posix下两种没有区别。 在windows下，generic_format会用/，而native-format会用\\ https://github.com/MeouSker77/Cpp17/blob/master/markdown/src/ch20.md 一个有点意思的测试程序 #include &amp;lt;filesystem&amp;gt; #include &amp;lt;iostream&amp;gt; namespace fs = std::filesystem; int main() { std::string strpath = &amp;quo...</description>
    </item>
    <item>
      <title>UE 降低编译线程数</title>
      <link>/2023/11/36d363d7/</link>
      <pubDate>Fri, 03 Nov 2023 23:28:08 +0800</pubDate>
      <guid>/2023/11/36d363d7/</guid><description>简单做个备忘。 与在公司不同，在家编译UE的时候通常会一边干其他事情一边等编译。 如果虚幻把所有线程都占满就没法干其他事情了(刷B站或者看其他代码都卡)。 编译CPP的线程控制 BuildConfiguration.xml可以控制UBT的一些行为，所以可以用来控制编译线程数。这个文件有几个不同目录，工程目录下的只会影响工程，而C:\Users\&amp;lt;User&amp;gt;AppData\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml目录下的会影响全局。 在家里没有xge其实只需要设置LocalExecutor就够了。 稍微解释下: ProcessorCountMultiplier 默认值是1.0, 对于有超线程的CPU(主流CPU应该都有吧)，极限榨干性能可以设置为2，可以把整个CPU都打满，系统会很卡。反之降低就可以把CPU核占用给降下来。 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt; &amp;lt;Configuration xmln...</description>
    </item>
    <item>
      <title>用Unreal Insights 查看UE Cooking过程</title>
      <link>/2023/10/85fda58f/</link>
      <pubDate>Fri, 20 Oct 2023 21:16:51 +0800</pubDate>
      <guid>/2023/10/85fda58f/</guid><description>Cooking Profiling可以用来查打包过程中Cooking打包过程为什么慢的问题。 可以用Unreal Insights来看。 官方虽然有一个文档，但是写的不清不楚的，一直没弄明白该怎么跑起来。 参考：Unreal Cooking Insights in Unreal Engine 5 | Unreal Engine 5.2 Documentation 摸索了一下，参考了Unreal Japan的一篇文章1，才弄明白: 在Editor下Cook是没办法看到Cooking Insights的，需要从命令行拉起UnrealEditor-cmd.exe来Cook资源 参考脚本 先打开UnrealInsights, UE4Editor-Cmd.exe拉起来的时候会反向链接UnrealInsights 按照下面的脚本拉起命令行编辑器来Cook资源，对应路径需要自己调整 UE4 set ENGINE_PATH=&amp;quot;D:\Release-4.27\Engine\Binaries\Win64\UE4Editor-Cmd.exe&amp;quot; set PROJECT_PATH=&amp;quot...</description>
    </item>
    <item>
      <title>UE的Allocator以及StompAllocator的实现</title>
      <link>/2023/10/d7a3cb82/</link>
      <pubDate>Fri, 06 Oct 2023 23:50:30 +0800</pubDate>
      <guid>/2023/10/d7a3cb82/</guid><description>虚幻默认带有好几种不同的Allocator实现，用来实现不同的malloc/free策略。有一些调试用的Allocator可以用来查内存相关的bug。 Windows下的Allocator选择 编辑器下，UE4默认使用TBB Allocator,TBB不可用的情况下到Mimalloc (UE5默认Mimalloc)。非shipping情况下，可以靠传参设置allocator -ansimalloc,绕开所有的allocator,用操作系统原生的new/delete,方便valgrind等工具 -tbbmalloc,使用TBB Allocator 打包版本默认使用Binned2/Binned3 Allocator 调试用allocator -stompmalloc 分配虚拟内存的时候不会复用已有的地址，分配内存的时候会分配一个额外的保护页，越界写会立刻崩溃 -stompmalloc2 ue5 only，没看实现 PoisonMallocProxy 在UE打包的时候的Development版本会有，内存回收以后会设置成一个特殊的值，防止野指针 简化版的选择逻辑如下 FMalloc* FWi...</description>
    </item>
    <item>
      <title>UE InstancedStaticMeshComponent 备忘</title>
      <link>/notes/a17e09fb/</link>
      <pubDate>Sat, 16 Sep 2023 00:01:36 +0800</pubDate>
      <guid>/notes/a17e09fb/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 Info Engine Version: 4.26.2 看代码之前列了一些问题，总结在这里: ISM的剔除逻辑/ISM的LOD计算过程: 结论: ISM大量复用了SM的逻辑，剔除/LOD计算都用的SM的逻辑，由于ISM可能会被大量摆放，其Bounds可能很大，其LOD可能得到一个很差的结果，大概率长期保持LOD0~1。 ISM的Instances的Buffers存在哪里在: 存在FStaticMeshInstanceBuffer这个结构体上，这个结构体在FInstancedStaticMeshSceneProxy-&amp;gt;FInstancedStaticMeshRenderData-&amp;gt;PerInstanceRenderData-&amp;gt;InstanceBuffer ISM的TransformBuffer默认精度为Half4,如何转成FP32 https://gist.github.com/BlurryLight/28852a23ca793185778bdf3...</description>
    </item>
    <item>
      <title>在博客里贴UE蓝图的一个示例</title>
      <link>/2023/09/271fd62d/</link>
      <pubDate>Wed, 13 Sep 2023 22:32:19 +0800</pubDate>
      <guid>/2023/09/271fd62d/</guid><description>折腾了一下在网页端渲染蓝图，主要是有的时候可能会贴点代码。 蓝图直接复制，编辑器会帮忙格式化成文本形式表示。 一个有趣的事实是编辑器里复制一个节点也是走的这个流程 void FBlueprintEditor::CopySelectedNodes() { // Export the selected nodes and place the text on the clipboard const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes(); FString ExportedText; for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter) { if(UEdGraphNode* Node = Cast&amp;lt;UEdGraphNode&amp;gt;(*SelectedIter)) { Node-&amp;gt;PrepareForCopying(); } } // 序列化节点为文本 F...</description>
    </item>
    <item>
      <title>Unreal Loads Packages Slowly On Actor Deletion</title>
      <link>/2023/08/692f9003/</link>
      <pubDate>Fri, 04 Aug 2023 23:37:06 +0800</pubDate>
      <guid>/2023/08/692f9003/</guid><description>Recently, I&#39;ve been annoyed by a long loading when deleting actors in an active level. It happens only in studio project with a fairly large amount of assets. The behavior is weird: when creating a new template level, then trying to delete an actor from this level, unreal strangely begins to calculate references, and a lot of packages are loaded, taking about 20 seconds. This happens the first time an actor is deleted since editor startup, and the next deletions are as fast as usual. 20 seconds ...</description>
    </item>
    <item>
      <title>Deploy A Local SVN Repo For Unreal Source Control</title>
      <link>/2023/08/4cec92aa/</link>
      <pubDate>Wed, 02 Aug 2023 23:06:12 +0800</pubDate>
      <guid>/2023/08/4cec92aa/</guid><description>Perforce is the first class source control system for Unreal Engine, Epic itself, along with many other game studios, including my current studio, are using it. Epic develops an excellent tool to work with Perforce UnrealGameSync. Although perforce is the best choice for Unreal Engine, it&#39;s not free, and it&#39;s not easy to setup a local perforce server for personal use. It&#39;s common for developers, and also artists, to create some Local and small repos for prototyping ,bug testing and learning. For...</description>
    </item>
    <item>
      <title>CMake Snippets</title>
      <link>/notes/utils/ae3cb50c/</link>
      <pubDate>Fri, 28 Jul 2023 05:05:12 +0000</pubDate>
      <guid>/notes/utils/ae3cb50c/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 跨平台创建类似于Symlink take from cmake - replacement of create_symlink in windows - Stack Overflow 在Windows上有一个替代品 Directory Junction。 对于单机用户基本上没区别。 区别在于 对于一个Remote Directory里的路径，symlink会解析到本地，junction会在server解析 symlink可以指向文件，junction只能指向目录 see: https://superuser.com/a/343079 if(NOT EXISTS ${CMAKE_BINARY_DIR}/bin/media) if (UNIX) execute_process(COMMAND &amp;quot;${CMAKE_COMMAND}&amp;quot; -E create_symlink ${CMAKE_SOURCE_DIR}/media ${CMAKE_BINARY_D...</description>
    </item>
    <item>
      <title>D3D11_Filter 枚举组装</title>
      <link>/2023/07/203e2ba7/</link>
      <pubDate>Mon, 24 Jul 2023 00:56:12 +0800</pubDate>
      <guid>/2023/07/203e2ba7/</guid><description>今天才注意到D3D11_FILTER的枚举的数字是由构造的规律的，头文件还提供了方便的构造宏。这里速记一下以备忘。 DX11的Sampler Filter目测由9个Bit组成。 前6个bit分别是 mip/mag/min, 每个占2个bit，实际上只会用一个bit(linear/point) 第7个bit是ANISOTROPIC的标记位，Anisotropic存在的时候，后6个bit只能为010101,即三个linear 8-9的2个bit用来表示D3D11_FILTER_REDUCTION_TYPE . 两个例子: D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15,,拆解为0b00&#39;0&#39;01&#39;01&#39;01，即min/mag/mip都是linear，reduction type是standard。 D3D11_FILTER_MINIMUM_ANISOTROPIC = 0x155,,拆解为0b10&#39;1&#39;01&#39;01&#39;01，即min/mag/mip都是linear，anisotropic bit为1， reduction type是minimul(2)。 d3d11...</description>
    </item>
    <item>
      <title>Fix DirectX Graphics Tools On Win10 Failed To Install</title>
      <link>/2023/07/190d19e2/</link>
      <pubDate>Wed, 12 Jul 2023 22:43:08 +0800</pubDate>
      <guid>/2023/07/190d19e2/</guid><description>中文关键词: 修复DirectX图形工具无法安装的问题 Graphics Tool Graphics Tool is one of the essential components of Graphics Programming on Windows. During the installation of Visual Studio, the installer tries to automatically install Graphics Tool, but things are not always going well. In complicated network environment, such as in a company that has a strict firewall, or in the Chinese mainland which is blocked by the GFW, it is somewhat difficult to install Graphics Tools. UseWUServer = 0 This method is suitable ...</description>
    </item>
    <item>
      <title>限定长度的vector容器实现</title>
      <link>/2023/06/676d5850/</link>
      <pubDate>Mon, 26 Jun 2023 23:03:43 +0800</pubDate>
      <guid>/2023/06/676d5850/</guid><description>标准库一直缺少限定上限的vector实现，但是在实际一些场景里也还是有用的。 比如在图形API里，一些API能够绑定的数量是有上限的。 比如DX11的OMSetRenderTargets, void OMSetRenderTargets( [in] UINT NumViews, [in, optional] ID3D11RenderTargetView * const *ppRenderTargetViews, [in, optional] ID3D11DepthStencilView *pDepthStencilView ); 其NumViews必须小于等于 D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT，一般而言等于8。 一个朴素的容器结构是 template &amp;lt;typename T, size_t N&amp;gt; struct limited_vector{ T data[N]; size_t Num; }; limited_vector&amp;lt;ComPtr&amp;lt;ID3D11RenderTargetView&amp;gt;,D3D11_SIMULTANE...</description>
    </item>
    <item>
      <title>Generate Renderdoc Python-API Stubs</title>
      <link>/2023/06/b12cd5f8/</link>
      <pubDate>Sat, 17 Jun 2023 00:58:00 +0800</pubDate>
      <guid>/2023/06/b12cd5f8/</guid><description>Which Mode? Renderdoc has a friendly Python API for writing custom scripts. It allows both modes: QRenderDoc is the host, scripts are embeded. Scripts runs in its UI Python Shell Python Interpreter is the host, Renderdoc is library. In this case, scripts can freely use third-party libraries, and writes just like a normal Python script. With custom Python scripts, developers have free access to all the data stored in a capture, and can analyze some additional information, such as how many drawcal...</description>
    </item>
    <item>
      <title>禁止Hugo的goldmark后端转义英文引号为&amp;rsquo</title>
      <link>/2023/06/02dd9af9/</link>
      <pubDate>Thu, 15 Jun 2023 20:29:56 +0800</pubDate>
      <guid>/2023/06/02dd9af9/</guid><description>根据主题的不同，我有时候会使用英文写作博客。 平时由于字体的原因，我还没注意到hugo的会把markdown中的&#39;转义为&amp;amp;rsquo。 直到某一天注意到引号好像渲染的不太对，把字体切换到sans-serif就更明显了。 左:异常引号 右:正常引号 对前端完全不懂，在主题里的代码里翻了一下也没有找到相关的代码。 随后把渲染的后端从goldmark换成了mmark，发现问题就消失了，初步判断是hugo的渲染后端的问题。 打开html源码看了一眼，所有的&#39;引号都被生成了&amp;amp;rsquo。 以hugo + goldmark + rsquo为关键词搜了一下，在官网的文档找到了相关的设置(吐槽一下，官网的文档没有历史版本，想找一下0.68.3的文档都很难找) Configure Markup | Hugo 在老版本(0.68.3)的版本下，配置和官网文档上的有点不一样，不过怀疑的方向是对的，在老版本直接关了就行。 在config.toml里加入，见diff [markup.goldmark.extensions] typographer = false ...</description>
    </item>
    <item>
      <title>Build Mesa3D on MinGW64 in 2023</title>
      <link>/2023/06/4c4e845f/</link>
      <pubDate>Tue, 13 Jun 2023 22:44:53 +0800</pubDate>
      <guid>/2023/06/4c4e845f/</guid><description>Background It&#39;s been a long time since I wrote the first line of OpenGL code. However, I&#39;ve never read the implementation or debugging in driver. Mesa3D is a hodgepodge of Open Source driver implementation, and is widely used on Linux Desktop. It&#39;s a good place to start. For Linux Users I miss my old golden days of working on Linux Desktops. It&#39;s trivial to compile it on Linux, just follow the official tutorials and it should be OK. For WSL1 Users Don&#39;t waste time on it. It&#39;s not worth it. Ref：D...</description>
    </item>
    <item>
      <title>UE Programmatically Modifying Material Nodes</title>
      <link>/2023/06/64b1b73b/</link>
      <pubDate>Sun, 11 Jun 2023 12:56:00 +0800</pubDate>
      <guid>/2023/06/64b1b73b/</guid><description>Note: I&#39;ve tested the code on UE5 as well. There are some minor API changes, but the logic still applies. Introduction Recently, I&#39;ve a need to create a fairly complicated material in a programmatically way. Even if the desired material is created by Tech-Artists, I need to modify some nodes in it automatically. After some searching, there is already a great article about Adding nodes to material, see Ref：UE4 - Programmatically create a new material and inner nodes - Isara Tech. However, there i...</description>
    </item>
    <item>
      <title>Nvrhi | ShaderTool</title>
      <link>/notes/rhi/6e6d9502/</link>
      <pubDate>Mon, 29 May 2023 22:59:53 +0800</pubDate>
      <guid>/notes/rhi/6e6d9502/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 ShaderBlob is an asset format defined by NVRHI which packages multiple shader variants into a single blob file. The tool is originally a submodule of NVRHI(but a standalone tool), and now it has been moved to a standalone repo. For a RHI a similar tool is always needed for compiling &amp;amp; managing shaders, especially for RHIs targeing Vulkan/DX since the formats they need are different. Reference：NVIDIAGameWorks/ShaderMake: Shader Com...</description>
    </item>
    <item>
      <title>alianas/alignof/#pragma pack的一点测试</title>
      <link>/2023/05/9da9c22d/</link>
      <pubDate>Mon, 22 May 2023 00:08:05 +0800</pubDate>
      <guid>/2023/05/9da9c22d/</guid><description>pragma pack 在x64下其对齐标准为 struct Test { char a; // offset 0 int b; // offset 4 char c; // offset 8 double d; // offset 16 }; sizeof(Test) == 24; #pragma pack 主要控制类内元素间的对齐。 这是个非标语法，但是主流的编译器都支持。 #pragma pack(push,1) struct Test { char a; // offset 0 int b; // offset 1 char c; // offset 5 double d; // offset 6 }; #pragma pack(pop) static_assert(sizeof(Test) == 14); static_assert(offsetof(Test,a) == 0); static_assert(offsetof(Test,b) == 1); static_assert(offsetof(Test,c) == 5); static_assert(offsetof(...</description>
    </item>
    <item>
      <title>Quick Reference About Asset Name/Path in UE</title>
      <link>/2023/05/7a83b54b/</link>
      <pubDate>Sat, 20 May 2023 11:41:47 +0800</pubDate>
      <guid>/2023/05/7a83b54b/</guid><description>There are tons of different functions that return AssetName/AssetPath/PackagePath/PackageName, but there are subtle differences in the strings returned by different APIs. What&#39;s worse, there are also many different APIs for manipulating assets that accept randomly formatted path strings. Some of them accept Virtual Path defined by UE, while others may accept System Path. I&#39;m tired of using Go to Definition to check what format an API needs. So I made a table to record what an API will return/nee...</description>
    </item>
    <item>
      <title>关于x86下的几种调用约定的一点测试</title>
      <link>/2023/04/3648c0f9/</link>
      <pubDate>Sun, 16 Apr 2023 23:44:20 +0800</pubDate>
      <guid>/2023/04/3648c0f9/</guid><description>写了个简单的例子来测试 _cdecl,_stdcall,_fastcall的区别，见https://godbolt.org/z/ao9v3Tj7Y int __cdecl add(int a,int b,...) { return a + b; } int __stdcall add2(int a,int b,int,int,int,int,int,int,int) { return a + b; } int __fastcall add3(int a,int b,int c) { return a + b + c; } int main() { int _ = add(1,2,3,4,5,6,7); _ = add2(1,2,3,4,5,6,7,8,9); _ = add3(1,2,3); return 0; } 结论: x64下都是走fastcall。即使手动加了标识符，也是走fastcall x86下不加标识符的情况下，走`__cdeclc 不同的调用约定有不同的name mangling方式。 int __cdecl add(int a,int b,...) { return ...</description>
    </item>
    <item>
      <title>UE4 Compilation Process Affinity with Intel 12th Gen CPU on Windows 10</title>
      <link>/2023/04/bc4cbce3/</link>
      <pubDate>Thu, 13 Apr 2023 23:58:44 +0800</pubDate>
      <guid>/2023/04/bc4cbce3/</guid><description>Slow Compilation Today, I&#39;ve compiled a UE 4.26 release on an Intel i7 12700 CPU (a heterogeneous archtecture with 8-Cores and 4 E-cores ) running the latest Win10 22H2 system. However, the compilation was noticeably slower than on an older AMD CPU. With the task manager, I observed my cpu utilization was between 30% and 50% and it appeared all compilers were running on the E-Cores. After some research, I found there is another similar problem on Epic Forum Intel 12th Gen Shader Compilation Proc...</description>
    </item>
    <item>
      <title>Why glClipControl is crucial for reverse-Z implementation in OpenGL</title>
      <link>/2023/03/9f52e5cc/</link>
      <pubDate>Wed, 22 Mar 2023 20:00:13 +0800</pubDate>
      <guid>/2023/03/9f52e5cc/</guid><description> Reverse-Z is a widely used technique for optimizing depth buffer precision in modern video games. If you are looking for a detailed tutorial on how to implement Reverse-Z in OpenGL, Blog reversed-z-in-opengl is an excellent starting point. In short, the process involves several steps, including: Use glClipControl to adjust z-range in NDC from [-1,1] to [0,1] Adjust Project Matrix to project far plane on 0 and near plane on 1 Change Depth Comparison function to GL_GREATER/GL_GEQUAL Clear Depth B...</description>
    </item>
    <item>
      <title>Discover Unreal Engine APIs Offline with Zeal</title>
      <link>/2023/03/d009f04a/</link>
      <pubDate>Sat, 11 Mar 2023 20:26:44 +0800</pubDate>
      <guid>/2023/03/d009f04a/</guid><description>For days I have been using an offline documentation browser named zealdocs/zeal to search for and browse the APIs I need while programming. Zeal is an open-source offline documentation, which is compatible with the popular MacOS-Only paied documentation tool named Dash. Zeal offers several open-box documentations, including CPP and Lua documentation. Unfortunately, there is no official UE documentation availbale from Epic Games or Zeal. Some developpers have taken it upon themselves to grab cont...</description>
    </item>
    <item>
      <title>Rendering Plane Reflected Objects</title>
      <link>/2023/03/c193add0/</link>
      <pubDate>Sat, 11 Mar 2023 00:51:05 +0800</pubDate>
      <guid>/2023/03/c193add0/</guid><description>One approach to implementing a plane mirror is to render objects twice: once in their original positions, and again as a mirror reflection. Reflected matrix with a given plane DirectX offers a convenient function XMMatrixReflect for reflecting objects. The function takes normalized Point-Normal form of plane as an argument, which is \( ax + by + cz + d = 0, \vec{n} = \{a,b,c\}\). It then returns a matrix can be used to mirror object vertices. The Pseudocode is auto plane = vec4(0,0,1,0); // x-y ...</description>
    </item>
    <item>
      <title>脆弱的2022底: 派遣，裁员与逃离</title>
      <link>/misc/5664e986/</link>
      <pubDate>Thu, 19 Jan 2023 21:54:06 +0800</pubDate>
      <guid>/misc/5664e986/</guid><description>续着谈谈我的2022 | Blurred code文章继续写吧。 本来在做年终总结的时候想着不要写很多负面的东西，也就没有写一些坏的事情。 结果到了1月初接连发生了一连串的坏事，导致有一段时间都没睡好，就把这些坏事写下来吧。 我本来在天美上海某个二次元项目组工作，一度非常喜欢这个项目(在渲染质量上我认为是领先许多市面上的游戏的)。 尽管作为校招新人加入这个项目半年，这期间内也努力的做了一些贡献。 十二月初的时候直接上级告知项目组需要派遣两个程序去另外一个团队支援，虽然名义上是支援，但是在沟通中感觉上级对归期也不确定，对那边的工作内容也不确定。当时就隐约觉得，没有归期的支援也许就是永久性的调动了。 明明前两天还在和mentor讨论手上的需求应该怎么推进，突然就被项目组告知出局了。 在一段时间里都在思考，是不是我做的不好还是什么原因。也没有答案了。 受疫情影响导致调动晚了一段时间，元旦过后才到了深圳，开始和这边的团队开始工作。 1月上旬先后召开了工作室年会和天美年会，主题也都半句不离降本增效。 在工作室年会上，看了项目的内部PV感觉效果还不错。 然而在第二天，却受到了直接上级和HRBP的约...</description>
    </item>
    <item>
      <title>Monkey语言 | 3. 字节码与栈式VM</title>
      <link>/notes/monkey/56b5a9fd/</link>
      <pubDate>Sat, 07 Jan 2023 17:05:30 +0800</pubDate>
      <guid>/notes/monkey/56b5a9fd/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 结构 graph TD A[Source Code] B[AST] C[Evaluator] D[Result] E[ByteCode] F[Virtual Machine] subgraph 前端 A--Lexer&amp;Parser--B end subgraph 后端 B--C B--Compiler--E subgraph 解释器 C--D end subgraph 编译器 E--F F--D end end 采用bytecode + VM的方案会比树上求值的解释器(tree-walking interp)更快。 因为最简单的tree-walker解释器本质上是在对一个比较深的树(AST)以递归的方式做后续遍历，不仅内存连续性不好，也不方便做一些很容易就能做的优化，比如常量消除，而且每走一步都需要将走到的Ast的节点转换为求值器内部的数据表示，需要不断的malloc/free。 而生成bytecode后，VM可以将所有的常量收集起来，采用索引的方式索引常量，并且...</description>
    </item>
    <item>
      <title>谈谈我的2022</title>
      <link>/misc/81827c32/</link>
      <pubDate>Thu, 22 Dec 2022 12:32:03 +0800</pubDate>
      <guid>/misc/81827c32/</guid><description>先插一个今年最喜欢的BGM(也许也是去年最喜欢的)，来自一个R18游戏 Succubus Academia。 这篇博客接着硕士三年的Github继续写吧。 今年发生的事情也比较多，简单的说可以划分为学校阶段和工作阶段吧。 学校阶段 平稳的度过了硕士的最后一段时间，其实过的并不是很开心。 之前也有提到过，主要的不开心原因有三个: 没有进步，每天都在为了毕业论文劳心，论文后期基本是在A4雕花，没有新的内容可以写了 互联网形势不好，每天都有坏消息，非常担心不能入职 疫情反复，生活工作都面临很大的不确定性，学校也时不时添个堵 总而言之不太喜欢这段时间的生活，答完辩以后急匆匆的就从学校里离校了，不太想继续待在这里了。 学院学校的毕业照也没参加也没要，草草的和一些朋友同学拍了点合照之类就离开了学校，结束了学生时代。 站在2022的年底，心里的愤怒平歇以后想来求学生涯结束的未免太过潦草，甚至连学校都没再走一圈。 连国党败退台湾的时候都带走了一抔黄土。 想来虽然种种不爽，但是也是在这个学校生活了七年。 除掉求学时间以外的东西(很惭愧，并没有怎么在课业上学习)，至少在这里生活了很长一段时间。 总而言之，...</description>
    </item>
    <item>
      <title>Talking about Hitches in UE4 LoadingScreen</title>
      <link>/2022/12/6ec75576/</link>
      <pubDate>Sat, 10 Dec 2022 23:45:16 +0800</pubDate>
      <guid>/2022/12/6ec75576/</guid><description> 中文版: 谈谈UE4 LoadingScreen卡顿问题 This blog is just some casual thoughts around one problem I&#39;ve met. I believe there are ways to solve it, However I&#39;ve yet found. Background Loading Screen is an common component of games, preventing users from getting bored by wait game loading. Unreal provides MoviePlayer module for the task. It allows for playing an mp4 movie and presenting an animated slate(UMG is actually supported, but needs to modifier engine to suppress an ensure in 4.26, see Appendix: Suppo...</description>
    </item>
    <item>
      <title>UE4 Hide Stats Rendering</title>
      <link>/2022/11/3408630c/</link>
      <pubDate>Mon, 28 Nov 2022 23:09:05 +0800</pubDate>
      <guid>/2022/11/3408630c/</guid><description> 中文版: UE4 隐藏统计数据Stats渲染 Recently there was a need for hide UE4 stats rendering. When stats are enabled, the stats data are collected and meanwhile tables are rendering into viewport canvas. The need is to collect stats data background without rendering them on the canvas, which obscures other primitives. Using stats none or other similar methods like [Solved] How to disable fps counter in rendered level sequence? - Development / Rendering - Unreal Engine Forums is not possible because it also st...</description>
    </item>
    <item>
      <title>UE4 Get Binary Build Timestamp</title>
      <link>/2022/11/6b21234f/</link>
      <pubDate>Sun, 27 Nov 2022 14:25:37 +0800</pubDate>
      <guid>/2022/11/6b21234f/</guid><description> 中文版: UE4获取二进制编译时间戳 Recently, we need a string representation of timestamps to tag the built UE4 game binary. To test random topics I package game about ~10 versions a day, and we need a string tag to distinguish different versions. The expected string is v20220101T235959, which in UE format is v%Y%m%dT%H%M%S. Get Compile Time Timestamp The trick is that we must record the BuiltTime in somewhere when compilers are doing their work. We cannot use clock() or any other similar methods to get the ti...</description>
    </item>
    <item>
      <title>UE4生成zip压缩文件</title>
      <link>/2022/11/b7ea1d63/</link>
      <pubDate>Sun, 20 Nov 2022 16:39:41 +0800</pubDate>
      <guid>/2022/11/b7ea1d63/</guid><description>最近有用UE生成zip文件的需求。 FZipArchiveWriter UE提供一个非常简单的RAII风格的ZIP类FZipArchiveWriter，只有三个API（Engine/Source/Developer/FileUtilities/Public/FileUtilities/ZipArchiveWriter.h） FZipArchiveWriter(IFileHandle* InFile); ~FZipArchiveWriter(); void AddFile(const FString&amp;amp; Filename, const TArray&amp;lt;uint8&amp;gt;&amp;amp; Data, const FDateTime&amp;amp; Timestamp); 构造函数需要传入一个InFile指针，指明压缩文件需要保存的路径 通过AddFile往Zip文件里添加文件名和路径 析构函数自动保存 该类使用起来非常简单，但是其具有几个问题。 最大问题是没有实现压缩算法，只是单纯的实现了Zip的文件格式，里面保存的数据是未经压缩的。 模块在Developer目录下，该目录下的文件是不能被打...</description>
    </item>
    <item>
      <title>HBAO的Unity实现杂记</title>
      <link>/2022/10/e9d23ec7/</link>
      <pubDate>Sat, 15 Oct 2022 22:13:09 +0800</pubDate>
      <guid>/2022/10/e9d23ec7/</guid><description>HBAO实现杂记 主要参考原始的Image-Space Horizon-Based Ambient Occlusion.pdf。 主要思想是以在半圆内朝着不同的方向步进，步进的方向越崎岖，则最后的AO值越大。 核心公式在第12页， Tangent Bias 为了减少步进的表面不平带来的jitter问题，加入bias。 在一些几何体面数不够的时候(本来应该是一个光滑的曲面)，会在三角面片的接缝出现一些AO的计算。 因此需要一个Bias参数用来忽略一些较小的AO值(通过抬升Tangent向量实现)。 要获得每个像素的切线方向只有估算。在估算某个像素对应的位置的切线时候，我们只有用屏幕空间周围的像素来估算。 PPT里用的是dpdx,dpdy的方式进行估计，其思想类似于函数求导时的有限差分方法。 测试了一下，使用对称差分的方式获得的效果比较好。代码类似于 float3 tangent = FetchViewPos(input.uv + dir * _MainTex_TexelSize.xy) - FetchViewPos(input.uv - dir * _MainTex_TexelSize....</description>
    </item>
    <item>
      <title>Monkey语言 | 2.evaluator</title>
      <link>/notes/monkey/e6a47ab4/</link>
      <pubDate>Mon, 26 Sep 2022 00:09:52 +0800</pubDate>
      <guid>/notes/monkey/e6a47ab4/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 树遍历求值 求值，也就是解释器的后端，有许多优化方法。 但是这里实现的是最简单、最慢的递归AST求值的方法。 从AST的Root出发，应该算是DFS？ 假设一段伪代码 &amp;lt;aaaaa&amp;gt;; &amp;lt;bbbbb&amp;gt;; 那么先要递归求值完毕&amp;lt;aaaaa&amp;gt;中的所有表达式，得到这一句的结果后才能进一步求值&amp;lt;bbbbb&amp;gt;语句。 更进一步用伪代码展示，其核心入口为一个Eval函数，根据碰见不同的Ast节点采取不同的求值。 var Eval(astNode) { switch(node) { case Ast.IntegerLiteral node: return node.Value; case Ast.PrefixExpression node: // &amp;lt;op&amp;gt;&amp;lt;right&amp;gt; var right = Eval(node.right); return EvalPrefix(node.op,right); case A...</description>
    </item>
    <item>
      <title>Monkey语言 | 1. Lexer &amp; Parser</title>
      <link>/notes/monkey/85f52114/</link>
      <pubDate>Wed, 14 Sep 2022 21:52:05 +0800</pubDate>
      <guid>/notes/monkey/85f52114/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 最近在阅读Writing An Interpreter In Go。 Lexer graph LR A[source] -- B[Lexer] B--Tokens--C[Parser] C--Ast--D[Evaluator] D--Value Lexer把原始的代码(包含空格、注释、换行符等额外的符号)转换为Parser关注的一连串的词，以方便解析器将一连串的Token转换为抽象语法树(AST)。 然而Lexer/Parser分离只是一种抽象方法，实际上并没有什么限制必须要分层制作。 一些简易的配置文件的Parser(比如ini,json)由于其语法足够简单，可以不经过Lexer阶段，从头到尾一次扫描得到解析结果。 然而这样混杂在一起会导致在Parser的代码里会有许多关于处理空格、换行符、注释以及循环读取标识符(identifier)等代码，并且在碰见错误的时候不方便提示错误信息(混在一起只能提供当前和之前字符的信息，而经过Lexer阶段可以报错当前和之前的T...</description>
    </item>
    <item>
      <title>浮点数有效位数</title>
      <link>/2022/08/79b6a364/</link>
      <pubDate>Tue, 30 Aug 2022 00:48:13 +0800</pubDate>
      <guid>/2022/08/79b6a364/</guid><description>十进制下的有效位数定义 以科学计数法记录， 十进制下， \( 1.23456 \times 10^3 \) 的有效数字为6位。 如果一个变量x的范围为[0,123456],那么这个变量x的有效数字至少为5位，但是其不能表示所有6位有效数字的数。 如果一个变量x的有效数字为5位，那么它不能区分1.23456和1.23457。 如果我们定义一个32位十进制浮点数，其尾数为23位。 那么其尾码为 10^24，由于科学计数法中指数部分不影响有效位数，所以其可以写作 \[ 1.(10^{23}) * 10^E \] 其有效位数为24位。 float32 在只考虑规格化(normalized)的浮点数的情况下(因为非规格化的浮点数的尾数不额外补1，其可表示的尾数比规格化的少一个比特，所以无需讨论他)，尾数部分可以用23个比特表示，加上额外的补1，总共可以认为是24个比特。 24个比特的尾数换算成10进制，其为 \[ 2^{24} = 16777216 \] 规格化的浮点数写作 \(sign * M * 2^{E}\) 的形式，因此其有效位数单纯由\(M\)决定。 由此 \[ 10^7在规格化的情况...</description>
    </item>
    <item>
      <title>Bind的简单实现剖析</title>
      <link>/2022/08/402a1cee/</link>
      <pubDate>Mon, 22 Aug 2022 00:20:21 +0800</pubDate>
      <guid>/2022/08/402a1cee/</guid><description>本来想剖析下UE的Delegate的实现，想到一直想看下bind的实现就干脆看看bind的。 翻了下EASTL，发现其拒绝实现bind，因为lambda是bind的上位替换(见effective modern cpp item 34 条款三十四：考虑lambda而非std::bind)。 又翻了下llvm的libcxx，标准库里的代码真的很难看懂。。 根据(https://gist.github.com/Redchards/c5be14c2998f1ca1d757)改了一个版本的bind实现。 int foo(int a,int b,int c) { return a + b + c; } auto func = bind(foo,1,2,std::placeholder::_1); func(3); // return 1 + 2 + 3 = 6 就从这个gist开始吧。 bind 简单的说，bind由于部分实参需要延迟绑定(通过占位符占位的实参)，所以需要保存构造bind对象时的参数列表，并在第二次调用的时候将占位符替换为实参并进行实际的调用。 graph TD A[&#34;bind(f...</description>
    </item>
    <item>
      <title>C&#43;&#43;的MemoryOrder以及Fence</title>
      <link>/2022/08/21e0de5a/</link>
      <pubDate>Tue, 16 Aug 2022 01:28:02 +0800</pubDate>
      <guid>/2022/08/21e0de5a/</guid><description>Memory Order 借Wiki的定义， Memory ordering describes the order of accesses to computer memory by a CPU. The term can refer either to the memory ordering generated by the compiler during compile time, or to the memory ordering generated by a CPU during runtime. 内存顺序可以指编译器乱序或者运行期乱序。 编译期乱序 编译器在编译的时候会尝试重排指令以执行优化。 比如gcc在这段的代码:https://godbolt.org/z/enPGxeMjf int g_a = 0; int g_b = 0; void foo() { g_a = g_b + 1; g_b = 1; } void bar() { while (g_b == 0) continue; assert(g_a == 1);// maybe fail. } 以上代码可能会被优化...</description>
    </item>
    <item>
      <title>从NDC到View Space坐标推导</title>
      <link>/2022/08/b46b0bd8/</link>
      <pubDate>Mon, 15 Aug 2022 00:01:28 +0800</pubDate>
      <guid>/2022/08/b46b0bd8/</guid><description>从NDC到ViewSpace/WorldSpace的方式有好几种。 利用Project_Inv到ViewSpace，然后再用View_Inv到WorldSpace。 用摄像机的世界坐标以及屏幕射线插值方法实现 Inverse Projection 一种从NDC坐标到ViewPos的代码可以写做(见：https://stackoverflow.com/questions/11277501/how-to-recover-view-space-position-given-view-space-depth-value-and-ndc-xy) mat4 inversePrjMat = inverse( prjMat ); vec4 viewPosH = inversePrjMat * vec3( ndc_x, ndc_y, 2.0 * zdepth - 1.0, 1.0 ) vec3 viewPos = viewPos.xyz / viewPos.w; 自己尝试着用Sympy推了一下能推出近似的结果，但是不能完全消元，不知道是哪弄错了还是怎么，推导过程见附录。 这篇文章介绍了这一段代码的干净代...</description>
    </item>
    <item>
      <title>UE4的临界区</title>
      <link>/2022/07/158130ce/</link>
      <pubDate>Wed, 27 Jul 2022 01:08:41 +0800</pubDate>
      <guid>/2022/07/158130ce/</guid><description>由于UE是多线程渲染结构，所以在主线程更新某个参数，而渲染线程需要某个参数的时候，往往需要加锁。 其实C++在C++11以后提供了统一的锁结构，std::mutex。 不过UE还是自己实现了一套,毕竟C++的来的太迟了。 Windows平台 UE的临界区分为3类，一类是普通的临界区，一类是SystemWide的，一类是读写锁。 typedef FWindowsCriticalSection FCriticalSection; typedef FWindowsSystemWideCriticalSection FSystemWideCriticalSection; typedef FWindowsRWLock FRWLock; FWindowsCriticalSection FWindowsCriticalSection主要依赖Windows提供的CriticalSection完成，大致对应pthread_mutex功能。 注意windows的mutex和linux的完全是两码事，windows的mutex是系统全局的，大致对应linux系统的named filelock。 其他进程也能...</description>
    </item>
    <item>
      <title>TSubclassOf的作用与实现</title>
      <link>/2022/07/48413275/</link>
      <pubDate>Thu, 21 Jul 2022 00:49:10 +0800</pubDate>
      <guid>/2022/07/48413275/</guid><description>TSubclassOf的作用 UE官方文档里描述了一个通过TSubclassOf在蓝图里限制下拉框选取范围的例子，不过感觉也不是讲的很清楚。 UClass描述了一个UObject类的反射信息。 通过obj-&amp;gt;GetClass()能够获取其UClass信息。 UE中的引用分为obj reference和class reference。 一个朴素的UClass*可以指代任意UObject，所以在蓝图中选择类型的时候不好选。 UPROPERTY(EditAnywhere,BlueprintReadWrite,Category=&amp;quot;Class&amp;quot;) UClass* SMBody_Class; 而TSubclassOf可以限定只引用某个类型及其Child类型的类型。 比如 UPROPERTY(EditAnywhere,BlueprintReadWrite,Category=&amp;quot;Class&amp;quot;) TSubclassOf&amp;lt;ACharacter&amp;gt; SMBody_Class2; 如果在蓝图中选中了一个UClass或者一个TSubClassof，可以在运行...</description>
    </item>
    <item>
      <title>UE4中的断言</title>
      <link>/2022/07/1e49fb86/</link>
      <pubDate>Sun, 17 Jul 2022 15:15:59 +0800</pubDate>
      <guid>/2022/07/1e49fb86/</guid><description>编译flags 从Engine\Source\Runtime\Core\Public\Misc\Build.h中定义了一系列的编译宏(这些宏的内容应该可以在UBT里重新定义)。 和断言有关的包括 宏 作用 DO_GUARD_SLOW 编译checkSlow,checkfSlow和verifySlow DO_CHECK 编译check族和verify族函数 DO_ENSURE 编译ensure族函数 其中 Debug模式 DEBUG模式下所有的断言默认开启 #define DO_GUARD_SLOW 1 #define DO_CHECK 1 #define DO_ENSURE 1 Development模式 Development下，SLOW的被禁用(不懂为什么起名叫SLOW，起名叫DEBUG不好吗)。 Shipping和Test模式 Test模式下和SHIPPING without editor下差不多。 在Shipping模式下分为两种，如果是带editor的情况下说明是在调试打包， 此时定义为 #define DO_GUARD_SLOW 0 #define DO_CHECK 1 #...</description>
    </item>
    <item>
      <title>分析enable_shared_from_this</title>
      <link>/2022/07/b1767390/</link>
      <pubDate>Thu, 14 Jul 2022 01:13:26 +0800</pubDate>
      <guid>/2022/07/b1767390/</guid><description>std::shared_ptr在以下情况下会触发未定义行为(double free) //错误用法 struct A { std::shared_ptr&amp;lt;A&amp;gt; GetSharedPtr() { return std::make_shared&amp;lt;A&amp;gt;(); } } std::shared_ptr&amp;lt;A&amp;gt; ptr1= std::make_shared&amp;lt;A&amp;gt;(); ptr2 = ptr1-&amp;gt;GetSharedPtr(); //ptr2 and ptr1 will both free the object 由于shared_ptr是非侵入式的，所以被管理的对象内部不保存引用计数状态，也无法知道自己正在被shared_ptr管理。 这种用法返回的shared_ptr并不知道还有另外一个shared_ptr正在管理这个对象，导致一个对象关联了多个不同的引用计数器，导致多重释放。 要解决这个问题只有侵入对象本身,在对象内部关联引用计数器，使得在调用GetSharedPtr函数的时候通知正在管理自身的SharedPtr更新引用计数器。 源码剖析 代码剖...</description>
    </item>
    <item>
      <title>硕士三年的Github</title>
      <link>/misc/50954ba8/</link>
      <pubDate>Mon, 30 May 2022 22:03:54 +0800</pubDate>
      <guid>/misc/50954ba8/</guid><description>终于毕业了，一直想根据github理一下，从本科到硕士期间每个阶段在学习些什么在做些什么。 2015 - 2018 github基本全黑，处于未入门的阶段。这段时间陆陆续续学了一些Python，能写一些一两百行的东西。 2019年 2019年3-4月左右 打算入门C++，思来想去觉得从写一个小的STL开始比较合适，可见FakeSTL From Scratch | C++ 从0开始写FakeSTL [目录]。 代码主要参照于EASTL。在读代码的过程中顺带修了个EASTL的bug(fix clang never using constexpr #326)。大概尝试着写了unique_ptr，shared_ptr，vector，list，以avl_tree为底层结构的map(未完整)等组件。尝试着写了个有状态的Allocator但是似乎有bug，写内存分配器是个艺术活，当时还把持不住。 总得来说这段时间还是菜鸡一个，但是第一次尝试写单元测试和模版编程，也算收获颇丰。 2019年5-6月 这段时间主要在写本科毕设，大概是用Qt画了点界面。 第一次尝试写Latex。在前人的基础上修改出了一个符...</description>
    </item>
    <item>
      <title>顺序无关半透明物体渲染OIT</title>
      <link>/2022/05/4c17b61d/</link>
      <pubDate>Sat, 07 May 2022 23:54:19 +0800</pubDate>
      <guid>/2022/05/4c17b61d/</guid><description>半透明物体和深度 正确绘制半透明物体渲染需要技巧。 一种顺序相关的渲染方式是 渲染所有不透明物体 关闭深度写入 排序所有透明物体 从后到前渲染透明物体 打开深度写入 渲染半透明物体需要考虑深度测试，因为这些物体可能被不透明物体遮挡。所以要先渲染不透明物体，然后再渲染半透明物体。 但是在渲染时需要关闭深度写入。 在能够完美按照从后到前顺序渲染的时候，实际上是实现了朴素版本的画家算法，所以写zbuffer没啥意义。 但是从后向前渲染的时候如果出现半透明物体交叉的情况，这种情况是排序不了的，会出现后渲染的物体一部分像素被剔除，关闭深度写入能避免这个问题(但是在做后处理的时候会碰见其他问题，比如DOF这种依赖深度的后处理会对半透明物体，也有可能混合颜色会出问题)。 OIT https://jcgt.org/published/0002/02/09/paper.pdf 单次blend的公式可以写作如下，其中\(C_1\)是SRC的像素颜色和alpha通道值\(\alpha_1\)预相乘的结果。 \[ C_f = C_1 + (1 - \alpha_1)C_0 \] 上面这个公式递推一下，可以把多...</description>
    </item>
    <item>
      <title>Unity TAA实现杂记(附录带开箱即用代码)</title>
      <link>/2022/05/9f1444db/</link>
      <pubDate>Fri, 06 May 2022 18:21:10 +0800</pubDate>
      <guid>/2022/05/9f1444db/</guid><description>TAA 主要理论参考资料可以参考Inside的分享GDC Vault - Temporal Reprojection Anti-Aliasing in INSIDE，主要实现代码可以参考Unity的Post Processing v2的实现，相较于Inside的实现其更加干净，而且更容易看懂。 框架 几个注意的点: 输入的所有数据都是jitter后的 unjitter只发生在混合阶段，用以采样_MainTex，也即是jitter后的color buf。为了采样unjitter的数据，需要调整uv坐标。 reproj是找到当前帧的像素在之前帧的位置，有一些细节要处理(depth dilate) Jitter 视椎体 要注意的点是Jitter实际上是亚像素级别的轻微偏移裁剪近平面，形成Temporal上的超采样。 _Jitter = new Vector2( 2.0f * (HaltonSequence[Index].x - 0.5f) / camera.pixelWidth, 2.0f * (HaltonSequence[Index].y - 0.5f) / camera.pixelHe...</description>
    </item>
    <item>
      <title>根据文字显示乱码猜测当前编码</title>
      <link>/2022/04/7210c1a5/</link>
      <pubDate>Thu, 14 Apr 2022 00:58:13 +0800</pubDate>
      <guid>/2022/04/7210c1a5/</guid><description>用UTF-8编码的这句话来测试。这个字符串包含emoji，emoji部分无法被编码到gbk,big5等ANSI编码。 这是编码测试fontTest👿 编解码的结果写入到UTF-8编码的txt，并通过支持UTF-8的编辑器查看。 # -*- coding: utf-8 -*- test_str = &amp;quot;这是编码测试fontTest👿&amp;quot; def print_test(encoding,decoding): print(&amp;quot;{} encoding {} decoding&amp;quot;.format(encoding,decoding)) # 对于encoding过程中出现的字符串，用？替代 # 对于decoding过程中出现的字符串，用�(U+FFFD)替代 print(test_str.encode(encoding,errors=&#39;replace&#39;).decode(decoding,errors=&#39;replace&#39;)) print(&amp;quot;UTF-8编码被错误用其他编码解释&amp;quot;) print_test(&#39;utf-8&#39;,&#39;gbk&#39;) print_test...</description>
    </item>
    <item>
      <title>MSVC调试器显示UTF 8中文字符串</title>
      <link>/2022/04/8a85b633/</link>
      <pubDate>Tue, 12 Apr 2022 22:16:17 +0800</pubDate>
      <guid>/2022/04/8a85b633/</guid><description>C++20以前没有std::u8string，虽然msvc有一个扩展(/Zc:char8_t-)可以用，但是最好别碰(https://docs.microsoft.com/en-us/cpp/build/reference/zc-char8-t?view=msvc-170)。 那个扩展允许写出这种代码，第一条是扩展的内容，这个扩展和c++二十加进来的u8符号是不兼容的，会导致升级编译器的时候出现break change。 const char* str = u8&amp;quot;hello中国&amp;quot;; // error in cpp20 const char8_t* str = u8&amp;quot;hello中国&amp;quot;; //valid in cpp20 目前最好的方案还是通过/utf-8标志指定MSVC编译器把代码里的字符串都当做utf-8处理，并且代码都保存在utf-8格式。 但是std::string并不包含encoding信息，所以MSVC的debugger不知道std::string里的编码文字，其会把里面的文字尝试用locale所在的编码解释，中文的话会是GBK编码。 如...</description>
    </item>
    <item>
      <title>Win32窗口程序打开Console终端</title>
      <link>/2022/03/cb7ce10f/</link>
      <pubDate>Thu, 31 Mar 2022 18:29:34 +0800</pubDate>
      <guid>/2022/03/cb7ce10f/</guid><description>默认的Win32窗口程序(subsystem:Windows)，入口点为WinMain的程序是不会显示Console终端的，意味着std::cout一系列的函数都不能用。 要想在执行程序的时候同时打开黑框(Console)有两种方式 改变Link符号和入口点 可以通过以下的链接指令修改入口点到mainCRTStartup，也可以在Properties-&amp;gt;Linker-&amp;gt;System-&amp;gt;Subsystem里修改为Console，这样入口点也会调整到mainCRTStartup。 #ifdef _MSC_VER # pragma comment(linker, &amp;quot;/subsystem:windows /ENTRY:mainCRTStartup&amp;quot;) #endif 此时程序启动的时候会正常调用int main函数，相较于winMain函数其主要少了重要的hInstance参数，可以通过GetModuleHandle(0)获得当前窗口的hInstance。 int main()和winMain()的区别可以见: 程序入口函数 main 和 WinMain i...</description>
    </item>
    <item>
      <title>Catlike Coding | Chapter 4 Directional Shadows</title>
      <link>/notes/catlikecodingsrp/e36ff115/</link>
      <pubDate>Tue, 08 Mar 2022 19:14:50 +0800</pubDate>
      <guid>/notes/catlikecodingsrp/e36ff115/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 Rendering Shadows Shadow Settings 需要建立一个ShadowSettings类以保存shadowmap属性，如最远的距离maxDistance和纹理大小。 把ShadowSettings作为[SerializedField]成员添加到CustomRenderPipelineAsset里去(chapter 1),则可以通过inspector调节其属性。 Passing Along Settings 把ShadowSettings结构体层层传递，最后传递给Camera.Render方法，并传递给lighing.Setup和Cull方法。 Cull方法中out ScriptableCullingParameters p中可以填充Mathf.Min(shadowDistance,camera.farClipPlane)的值，对于相机可视范围以外渲染阴影是无意义的。 Shadows Class 单独采用一个类Shadow来管理shadowma...</description>
    </item>
    <item>
      <title>Catlike Coding | Chapter 3 Directional Lights</title>
      <link>/notes/catlikecodingsrp/d791911c/</link>
      <pubDate>Tue, 08 Mar 2022 19:12:47 +0800</pubDate>
      <guid>/notes/catlikecodingsrp/d791911c/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 Lighting Lit Shader Pass { Tags { &amp;quot;LightMode&amp;quot; = &amp;quot;CustomLit&amp;quot; } … } 在Pass里设置的LightMode比较重要，这是在C#里通过new ShaderTagId(&amp;quot;CustomLit&amp;quot;);获取的shaderTagId，并且需要通过drawingSettings.SetShaderPassName(1, litShaderTagId);以使得该管线可以通过这个shader渲染。 Normal Vectors 在vshader的输入里使用NORMAL语义以获得正确的法线输入。 v2f结构体里的变量的语义可以自己定(不要和其他的语义重了就行)，比如可以定义个VAR_NORMAL,VAR_BASE_UV，也可以像buildin管线的惯例用TEXCOORDX。 Interpolated Normals 老生常谈的问题，因为三角面片插值法线的时候可能插值...</description>
    </item>
    <item>
      <title>Catlike Coding | Chapter 2 Drawcalls</title>
      <link>/notes/catlikecodingsrp/c2282dd8/</link>
      <pubDate>Tue, 08 Mar 2022 19:12:44 +0800</pubDate>
      <guid>/notes/catlikecodingsrp/c2282dd8/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误认识或偏颇观点，亦或采用只有自身能够理解的记录。 # Chapter 2 Drawcalls Shader 基础 没什么好说的。 坐标和纹理坐标使用float,其他的都用half就足够了。 桌面端不支持真的half，都是float，移动端很讲究这个。 sementaics: POSITION : float3 输入的坐标 SV_POSITION: float4 vs输出坐标 SV_TARGET: float4 fs输出 常用的hlsl #include &amp;quot;Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl&amp;quot; #include &amp;quot;Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl&amp;quot; 要在inspector里设置变量需要在Properties里添加，并在使用时候需要先声明一个同名的变量。 Batching drawc...</description>
    </item>
    <item>
      <title>Catlike Coding | Chapter 1 Pipeline Asset</title>
      <link>/notes/catlikecodingsrp/18139ebf/</link>
      <pubDate>Tue, 08 Mar 2022 19:00:42 +0800</pubDate>
      <guid>/notes/catlikecodingsrp/18139ebf/</guid><description> 笔记栏文章声明 Warning 笔记栏所记录文章往往未经校对，或包含错误观点以及认知，亦或采用只有自身能够理解的表达。 这是来自对Catlike Coding的SRP教程的整理，约等于简单的复制粘贴，其License为MIT-0，版权作者为Jasper Flick。 目录包括 Chapter 1 Pipeline Asset Chapter 2 Drawwcalls Chapter 3 Directional Lights Chapter 4 Directional Shadows ... Chapter 1 Pipeline Asset Pipeline Asset 需要创建一个Asset文件，没什么特殊的就是一个保存设置的序列化文件，里面有一个虚函数CreatePipeline作为工厂函数返回一个IRenderPipeline抽象类，该工厂函数需要被override。 menuName允许在右键菜单中加入一个新的Rendering的菜单。 [CreateAssetMenu(menuName = &amp;quot;Rendering/Custom Render Pipeline&amp;quot...</description>
    </item>
    <item>
      <title>搜索行尾为CRLF换行符的所有文件</title>
      <link>/2022/03/265a6886/</link>
      <pubDate>Sun, 06 Mar 2022 20:45:25 +0800</pubDate>
      <guid>/2022/03/265a6886/</guid><description>autoCRLF的问题 在多个平台工作的时候经常需要注意换行符的问题，Windows习惯使用CRLF(\r\n)作为换行符，而Linux习惯采用LF(\n)，MacOS没用过没有发言权。 尽管现在不同平台的现代IDE和编辑器都能正确处理换行符的问题，但是有必要在同一项目里采用相同的换行符。我个人的习惯是不管在任何平台都采用LF换行符。 在Windows平台下由于默认的换行符为CRLF，因此在Unity或者在Powershell里新建文件都会默认以CRLF结尾。 将CRLF结尾的文件提交到Git里会提示一个警告，要求设置autoCRLF，大概就是checkin的文件会帮你自动把CRLF转到LF，然后checkout的时候会帮你自动把LF转到CRLF。我个人不喜欢这个选项，很多人也不喜欢，比如GitHub 第一坑：换行符自动转换。git会偷偷摸摸的更改你的换行符，而且在有些落后的diff工具里，不会标识换行符发生了改变，导致你面对diff摸不着头脑。 寻找CRLF 遗憾的是Git不会提示你哪些文件是CRLF结尾的，每当收到这个警告就必须挨着排查到底哪个文件是CRLF的，重复劳动很心碎。 迫...</description>
    </item>
    <item>
      <title>Hugo的GitInfo为nil的问题</title>
      <link>/2022/02/4f3428ec/</link>
      <pubDate>Fri, 18 Feb 2022 15:11:32 +0800</pubDate>
      <guid>/2022/02/4f3428ec/</guid><description>我现在的博客程序用的是Hugo，并且版本Pin在了0.68.3，因为高版本似乎禁用了mmark这个格式但是我现在写Katex需要它。 最近试图增加一个新的功能，是追踪文章的LastMod更新时间，并且添加commit hash值和commit msg。 对应的代码差不多类似于,需要用到Hugo提供的.GitInfo结构体(https://gohugo.io/variables/git/)。 {{ if and (.Site.Params.GitRepo.enable) (.GitInfo)}} &amp;lt;p class=&amp;quot;date&amp;quot; title=&amp;quot;Commit: {{ .GitInfo.Subject }}&amp;quot;&amp;gt;LastMod:&amp;lt;a href=&amp;quot;{{ .Site.Params.GitRepo.Host }}/{{ .GitInfo.AbbreviatedHash}}&amp;quot;&amp;gt;{{ $lastmod }}&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt; {{ else }} &amp;lt;p class=&amp;quot;date&amp;quot...</description>
    </item>
    <item>
      <title>多重重要性采样与路径跟踪</title>
      <link>/2022/02/bd19f3eb/</link>
      <pubDate>Tue, 08 Feb 2022 15:48:11 +0800</pubDate>
      <guid>/2022/02/bd19f3eb/</guid><description>Veach在其博士论文里详细阐述了MIS的理论Multiple Importance Sampling，并给出了一段简单的无偏性证明，但是其过于精简导致我一直没看懂无偏性的证明这一块，尤其是其无偏性的证明需要\(\sum_i\omega_i = 1\)这个条件，花了点时间想清楚。 多重重要性采样 Airguanz同学给出了渲染方程中应用MIS的无偏性证明,不过其符号比较复杂，花了点时间才看懂，因此自己写个笔记吧，也从自己的思路里出发。 \(f(x)g(x)\)函数的估计 从PBRT中取一小节内容作为例子。 假设两个已知形式函数\(f(x)\)和\(g(x)\)，要求的积分为\(\int f(x)g(x) dx\),已知其重要性采样的pdf为\(p_f(x)\)和\(p_g(x)\)那么对其的多重重要性采样可以写作 \[ F = \frac{1}{n_{f}} \sum_{i=1}^{n_{f}} \frac{f\left(X_{i}\right) g\left(X_{i}\right) w_{f}\left(X_{i}\right)}{p_{f}\left(X_{i}\right)}+...</description>
    </item>
    <item>
      <title>Links</title>
      <link>/links/</link>
      <pubDate>Sun, 06 Feb 2022 00:00:00 +0000</pubDate>
      <guid>/links/</guid><description>只记录技术博客。 烟雨迷离半世殇 愿天下心诚剑士人人可剑开天门! 无境 梦想成为家里蹲的游戏开发萌新 </description>
    </item>
    <item>
      <title>为什么延迟渲染和MSAA不搭</title>
      <link>/2022/02/5b548f07/</link>
      <pubDate>Fri, 04 Feb 2022 15:10:42 +0800</pubDate>
      <guid>/2022/02/5b548f07/</guid><description>MSAA与延迟渲染 翻了下之前在Games101上写的MSAA的实现，发现错的离谱(逃。 改了一下以后顺手就把MSAA和延迟渲染这部分写了。 MSAA的原理 MSAA的buffer里保存的是着色中心点的颜色，buffer的大小为屏幕大小乘以子sample的数量，其需要保存\(n\)个sample的深度和sample的颜色。 其渲染流程如如下 假设先渲染蓝色三角形，后渲染黄色三角形 渲染蓝色三角形时，先计算像素中心点着色，计算结果为紫色。对四个采样点进行深度测试和测试是否在三角形内。下面两个sample通过测试，所以把紫色复制到这两个sample的buf上。 渲染黄色三角形，进行同样的测试，黄色三角形只有一个采样点位于三角形内。 所有的三角形渲染完成后，对所有的像素的子sample的缓冲区进行resolve，常见的resolve方法是取平均。 GBuffer与MSAA MSAA的子sample保存的颜色是像素中心点的颜色，其resolve阶段在所有三角形的fshader运行着色以后进行。 延迟渲染的第二个阶段已经丢失了几何信息(其实际是渲染一个quad或者一个大三角形)，所以在延迟渲染的...</description>
    </item>
    <item>
      <title>Windows上CMake&#43;Lua编译和luarocks环境配置</title>
      <link>/2022/01/5455335f/</link>
      <pubDate>Mon, 31 Jan 2022 14:30:13 +0800</pubDate>
      <guid>/2022/01/5455335f/</guid><description>Windows10 珍爱生命，远离预编译的包。编译Lua环境相对容易，只需要一个支持ANSI C的编译器环境，所以推荐自己编译，否则可能由于预编译的包的工具链和本机上的工具链版本不一样导致奇奇怪怪的问题。 预编译的包可能会出现Luarocks不识别Lua5x.dll的情况 luarocks的许多包安装时需要C工具链参与，并且要和Lua5x.dll链接。 Lua + CMake编译 Lua原生只提供makefile，而且是Linux下的makefile,有人提供了MSVC工具链可以调用的NMAKE工程文件,没有尝试，可以试试。 因为我想要自动化下载源代码，编译和打包这个流程，所以干脆就用cmake搞了一下。 Lua的源代码里不考虑MSVC的私货dllexport那一坨，所以在Win下编译要格外注意符号导出，或者就别搞动态链接库，直接打包成静态库(有个坑就是luac必须静态链接lualib，用动态链接找不到符号)。 自己fork了一份代码改了下cmake,工程在https://github.com/BlurryLight/Lua-with-cmake，支持从环境变量LUA_VERSION导...</description>
    </item>
    <item>
      <title>基于LTC方法的面光源渲染</title>
      <link>/2022/01/bbf5cca9/</link>
      <pubDate>Sat, 22 Jan 2022 22:07:04 +0800</pubDate>
      <guid>/2022/01/bbf5cca9/</guid><description>渲染方程的解析解 (注:这部分来着Prof Ravi的CSE168 Homework2) 渲染方程的完整形式，其中假定 \(\omega_i\) 是指向光源的，\(\omega_o\) 指向视角方向,符号 \(V\) 包含光线的可见性。 \[ \textcolor{red}{L_r(p,\omega_r)} = \textcolor{blue}{L_e(p,\omega_o)} + \int_{\Omega_P} \textcolor{blue}{f_r(\omega_i \Rarr \omega_r )}\textcolor{red}{ L_i(p,\omega)} \textcolor{blue}{\cos\theta_i \textcolor{blue}{V(\omega_i)} \text{d}\omega_i}\\ where: \cos\theta_i = \mathbf{n} \cdot \mathbf{w_i} \] 如果我们不考虑自发光项，忽略光线传播中的被遮挡情况(意味着没有阴影)，着色表面Lambert漫反射的时候，公式可以简化为如下，其中漫反射brdf为常数，\...</description>
    </item>
    <item>
      <title>生者常戚戚</title>
      <link>/misc/76dd3298/</link>
      <pubDate>Mon, 13 Dec 2021 11:34:17 +0000</pubDate>
      <guid>/misc/76dd3298/</guid><description>无他，唯接力尔。 愿逝者在另一个世界也能心潮澎湃。 </description>
    </item>
    <item>
      <title>PBR渲染中的能量补偿</title>
      <link>/2021/08/4df3fd6f/</link>
      <pubDate>Mon, 09 Aug 2021 11:20:31 +0800</pubDate>
      <guid>/2021/08/4df3fd6f/</guid><description> 菲涅尔项 描述了以某个角度看向微表面有多少能量被反射。 NDF项，描述了微观法线的统计分布。给定参数粗糙度，法线和半程向量h，NDF给出了整个微表面朝向h的统计分布。 G项是一个0,1之间的量，给定法线n,观测角度v和粗糙度，返回有多少比例的面可以同时被入射方向和出射方向观察到，通常来说这个值约等于1，只有在接近glazing angle的时候，会大幅减少，因为此时接近于平视微表面，有大量的面被遮挡。 能量损失 来自filament. 在建模BRDF的时候只考虑了单次弹射，没有考虑多次反射，在高粗糙度的表面容易有能量损失。 直观理解就是在高粗糙度的时候G项会更接近于0。 能量补偿 Kulla2017提出可以通过额外的近似项来补偿损失的能量。 先进行白炉测试，假设从四面八方入射的radians都是1，并且假设物体的表面F0为1,进行积分后打表。反应了在给定粗糙度和\(\cos \theta\)上的出射能量。 构造一个新的brdf，使得它的积分结果为\(1 - E(u)\). 把新的brdf加到原来的brdf上。 详细推导 (to be continued) ...</description>
    </item>
    <item>
      <title>面试异闻录</title>
      <link>/misc/fa15ea61/</link>
      <pubDate>Wed, 28 Jul 2021 00:28:00 +0800</pubDate>
      <guid>/misc/fa15ea61/</guid><description>聊以记录大厂招聘的一些故事吧,此篇致敬我的2018年计算机实习和秋招经历（微软、阿里、腾讯、网易游戏、今日头条等）, 虽然素未蒙面，但是这篇在本科的时候曾经激励了我,间接促使走上了这条路吧。 前言 大概从疫情期间(2020年春)决定离开学校走向工业界的道路，诱因很多，主要原因是想要稳定和充裕的经济来源。 分析了一波当时拥有的知识和兴趣爱好，决定走游戏开发这条路比较适合，比较契合当时拥有的技能栈(C++, 图形学基础知识)，同时也有符合预期的职业发展。 本文不会讨论各厂的薪资，也不会讨论具体的面试题目，姑且当做杂记吧。 实习生篇 公司 地点 岗位 批次 结果 腾讯 北极光 上海 渲染向游戏开发 内推 Offer 快手 Y-tech 北京 渲染特效和AI 内推 二面跪 祖龙 北京 游戏客户端 网申 简历挂 最终结果： 接腾讯Offer 腾讯 北极光工作室 腾讯是第一个面试的公司也是第一个发Offer的公司。实习面试比较少，主要是准备比较少 + 腾讯进度非常快。 大概在3月8日左右投的腾讯实习，投之前和WXG的一个朋友聊了聊，他有意找光子的朋友内推我去腾讯光子工作室，对深圳没有特别的好感所以...</description>
    </item>
    <item>
      <title>xv6的mmap的实现</title>
      <link>/2021/07/f586ed81/</link>
      <pubDate>Sun, 18 Jul 2021 17:03:00 +0800</pubDate>
      <guid>/2021/07/f586ed81/</guid><description>很长一段时间没整理xv6的相关笔记了。mmap是Linux下的一个比较常用的api，可以将磁盘的文件映射到虚拟内存中。 实现mmap需要用到lazy分配的机制，这样才允许mmap映射远大于内存的文件到虚拟内存中。 Lazy 分配的实现 常规的添加两个syscall用于mmap和munmap。mit的实验中不需要实现mmap的第一个参数addr,交由内核来决定，最后一个参数offset不需要实现，flag只需要实现map_private和map_shared。 在proc中新建struct vma作为slots，由于mmap的测试量比较小，官方给了提示可以直接开栈上数组，不用走kalloc分配堆内存。在mmap的syscall里处理逻辑， struct vma* v = 0; for(int i = 0; i &amp;lt; 16;i++) { if(!p-&amp;gt;vmas[i].valid_) // we find one { v = &amp;amp;(p-&amp;gt;vmas[i]); v-&amp;gt;valid_ = 1; addr = p-&amp;gt;sz; v-&amp;gt;addr_ = addr; v-...</description>
    </item>
    <item>
      <title>PBR渲染: Cook-Torrance的实现与补充</title>
      <link>/2021/05/dec701b2/</link>
      <pubDate>Sat, 15 May 2021 23:08:49 +0800</pubDate>
      <guid>/2021/05/dec701b2/</guid><description>Cook-Torrance反射方程 原始的Cook-Torrance反射方程来自论文1. (注：原文的公式里的分母少了一个4) \( \begin{aligned} &amp;L_{o}\left(p, \omega_{o}\right) =\int_{\Omega^+}\left(k_{d} f_d(\omega_i \rArr \omega_o)+ k_{s}f_{s}(\omega_i \rArr \omega_o)\right) L_{i}\left(p, \omega_{i}\right) (n \cdot \omega_{i}) d \omega_{i}\\ \text{where:}\\ &amp;f_d(\omega_i \rArr \omega_o) = \textcolor{red}{\frac{c}{\pi}}\\ &amp;f_s(\omega_i \rArr \omega_o) = \frac{D F G}{4\left(\omega_{o} \cdot n\right)\left(\omega_{i} \cdot n\right)}\\ &amp;k_d + k_s = 1 \end{al...</description>
    </item>
    <item>
      <title>C&#43;&#43;跨平台新体验: Vcpkg Manifest与Github Actions</title>
      <link>/2021/05/4756a873/</link>
      <pubDate>Wed, 05 May 2021 16:36:54 +0800</pubDate>
      <guid>/2021/05/4756a873/</guid><description>C++的跨平台体验一直比较糟糕，尤其是涉及到需要链接大量的第三方库的时候，整个项目的依赖管理变得相当复杂。 究其根本，问题的源头是C++一直未在ABI的问题上统一，导致在不同系统，不同编译器，不同编译器版本，以及不同的编译设置上编译出来的东西，彼此ABI不兼容以至于不能链接或者链接后运行报错，这消耗了大量的C++开发者的时间。即使是富有经验的CPP老手，在处理编译、链接的问题上也得小心翼翼。 由此C++的第三方库的管理也就成了悬而未决的疑难问题，自C++发明40年来，这些书本上不会告诉你的知识会让一代又一代的初学者不断的趟坑。 常见的C++第三方库的管理包含如下方式： git submodules CMake ExternalProject 开发者自行写prebuild.sh/cmd/py 将子模块的源码放在third_party文件夹下 利用系统的包管理器(apt/pacman/dnf) 以上方案均不能让人满意，1-4方法需要开发者对每一个子模块的编译过程都要掌握，这个过程是你现在想要引入A你得先要学会编译A（需要对A库的所有options，flags都弄清楚），然后A又依赖B，C，...</description>
    </item>
    <item>
      <title>渲染方程与蒙特卡罗积分与重要性采样</title>
      <link>/2021/03/a49ec510/</link>
      <pubDate>Wed, 03 Mar 2021 13:55:18 +0800</pubDate>
      <guid>/2021/03/a49ec510/</guid><description>渲染方程的可解性 先上渲染方程 \[ \textcolor{red}{L_r(p,\omega_r)} = \textcolor{blue}{L_e(p,\omega_o)} + \int_{\Omega^+} \textcolor{blue}{f_r(\omega_i \Rarr \omega_r )}\textcolor{red}{ L_i(p,\omega)} \textcolor{blue}{\cos\theta_i \text{d}\omega_i}\\ where: \cos\theta_i = \mathbf{n} \cdot \mathbf{w_i} \] 方程中蓝色的量代表已知量，红色代表未知量。 左边和右边的\(\mathbf{L}\)项都是未知的，其他项都是已知的。 根据Linear Operator Theory，可以整理为一个线性系统的方程 \[ \textcolor{red}L = \textcolor{blue}E + \textcolor{blue}K\textcolor{red}L \] 简单的理解可以理解为\(L\)代表一个列向量，包含了场景中所有的...</description>
    </item>
    <item>
      <title>xv6的用户态线程(协程)</title>
      <link>/2021/02/98bbdc4e/</link>
      <pubDate>Tue, 23 Feb 2021 20:22:46 +0800</pubDate>
      <guid>/2021/02/98bbdc4e/</guid><description>来自MIT6S081的多线程lab, 需要在用户态实现一个“多线程”，实际上是一个简单的协程的实现。其实这个lab挺容易的，因为只需要弄懂保存上下文就可以很容易的解决整个问题。 在协程切换之间，我们需要保存不同协程的运行上下文，其中需要保存的包括 所有callee-saved的寄存器，caller-saved的寄存器不用保存，编译器会帮我们生成相关的代码，注意，以下代码是平台相关的(risc-v)。 ra寄存器，当指令ret被调用的时候，指令寄存器pc会被重置到ra所保存的地址。 sp寄存器，也就是栈寄存器。这里的实现是有栈协程，所以每个协程拥有独立的栈区。这里很容易犯错，如果我们开辟一个char stack[SIZE],那么sp寄存器应该被设置为stack + SIZE, 也就是数组的末端，地址的高位。因为对栈内存的使用都是从高位向地位地址。 结构体 由此我们可以写出协程的定义 struct context { uint64 ra; uint64 sp; // callee-saved uint64 s0; uint64 s1; uint64 s2; uint64 s3; uint6...</description>
    </item>
    <item>
      <title>xv6的文件系统设计</title>
      <link>/2021/01/xv6%E7%9A%84%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</link>
      <pubDate>Thu, 21 Jan 2021 20:32:15 +0800</pubDate>
      <guid>/2021/01/xv6%E7%9A%84%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid><description>xv6的文件系统大概介于ext2和ext3之间吧，相较于ext2增加了日志(logging)部分，可以确保异常中断下下次重启硬盘可以恢复未写入的数据， 相较于真实的文件系统，xv6的文件系统采用朴素的线性结构而不是真实场景中的B+树来维持磁盘索引，查找文件是O(n)复杂度。 xv6的文件系统呈现层状的结构(与网络协议的结构类似)，进程需要从最顶层的fd查找到最底层，并且底层的inode，cache等结构对应用程序是完全透明的，其中比较重要的是buffer-cache层和logging层。 buffer层 其实buffer层没什么特别好说的，一个环状的链表，维持一个固定的head入口，每个被更新的块会被插到链表的head-&amp;gt;next，每次查找最不常使用的块只需要查找head-&amp;gt;prev就可以了。因为根据程序的局部性，如果一个块被访问了，那么接下来重复访问它和它周围数据的几率会比较大。 // 每个数据块要维持引用计数，valid标志 struct buf { int valid; // has data been read from disk? int disk; // doe...</description>
    </item>
    <item>
      <title>LRU Cache的简单实现</title>
      <link>/2021/01/lru_cache%E7%9A%84%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Sun, 10 Jan 2021 20:20:31 +0800</pubDate>
      <guid>/2021/01/lru_cache%E7%9A%84%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0/</guid><description>CS:APP的最后一个proxy lab需要实现一个LRU cache用于代理服务器的缓存。 定义一个数据块 data_block，用于存放socket的数据。 #define MAX_CACHE_SIZE 1049000 #define MAX_OBJECT_SIZE 102400 struct data_block { char data[MAX_OBJECT_SIZE]; uint32_t size; }; 常见的LRU实现包含一个链表，用于存放数据块，一个hash表，用于存放key到链表节点的指针的映射关系(其实我感觉优先队列也可以)。 如果没有hash表，每次查找cache需要用O(n)的时间。 链表可以自己在数据节点实现，实现只需要实现两个辅助函数detach_node,insert_node(pos)用于实现将节点从链表中间断开，插入头部。 class LRU_cache { private: std::list&amp;lt;data_block&amp;gt; data_; // store references of key in cache std::unordered_map&amp;...</description>
    </item>
    <item>
      <title>xv6的lock,sleep,wakeup的实现</title>
      <link>/2020/12/xv6_lock_sleep_wakeup_implement/</link>
      <pubDate>Sat, 12 Dec 2020 14:37:26 +0800</pubDate>
      <guid>/2020/12/xv6_lock_sleep_wakeup_implement/</guid><description>自旋锁的实现 在xv6中实现有两种锁，自旋锁和睡眠锁，其中睡眠锁的实现是依靠自旋锁来实现的。自旋锁的实现相当的简单，我们首先考虑一个错误的锁的实现。 struct spinlock { uint locked; }; 错误的加锁实现: void acquire_lock(struct spinlock *lk) { for(;;) { if(lk-&amp;gt;locked == 0) //&amp;lt;--------------- potential race condition { lk-&amp;gt;locked = 1; break; } } } 这个实现的问题在于，检测locked和lk-&amp;gt;locked=1这两个操作之间不是原子的，导致可能出现竞态条件。如果有两个CPU同时看到lk-&amp;gt;locked==0,那么会有两个cpu同时加锁，违反了锁的独占性。 许多硬件的实现提供了一些原子指令来帮助我们实现锁，这些指令在不同的指令集上虽然长得不一样。gcc帮我们实现了一个函数__sync_lock_test_and_set, 使得我们无需为每个架构都编写一次汇编。 因此正确的锁实现应该是 ...</description>
    </item>
    <item>
      <title>CPP线程池的实现</title>
      <link>/2020/12/cpp%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Tue, 01 Dec 2020 20:51:22 +0800</pubDate>
      <guid>/2020/12/cpp%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid><description>在DiRender中有一份 C++线程池 ，主要用于渲染中的图像的分块并行。 这个线程池大概是从 thread pool 或者其他的项目中改过来的。 为什么需要有线程池 在早期的我的某个版本渲染器中，是没有线程池的，起初为了实现并行渲染我设计的每一行像素一个线程，一张200x200的图片要使用200个线程，按照每个线程2MB的栈内存开销，开销400M。但是当图片到2000x2000的分辨率的时候，内存的开销就相当大了。其实线程池的思想还是比较容易理解的：有一定数量的线程，当消费者角色不断的从一个线程共享的队列取出任务执行。 但是实现起来还是有一定的难度，主要是线程池需要执行的是具有不同签名的函数，而我们只有一个容器，所以必须要对任务进行装箱，涉及到模板编程。 装箱的实现 template &amp;lt;typename F, typename... Args&amp;gt; decltype(auto) ThreadPool::enqueue_task(F &amp;amp;&amp;amp;func, Args &amp;amp;&amp;amp;... args) { using res_type = typename std...</description>
    </item>
    <item>
      <title>xv6 fork的实现</title>
      <link>/2020/11/xv6fork%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Mon, 23 Nov 2020 14:20:32 +0800</pubDate>
      <guid>/2020/11/xv6fork%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid><description>Posix中规定的fork的签名很简单，这个函数的作用是复制一个新的进程，子进程和父进程被复制出来是一样的，Linux的实现还会采用cow复制，也就是共享一份物理地址空间，直到有写入发生的时候才实际上复制被污染的页。xv6的一个lab也是要实现cow复制。这个函数最有意思的一个特点是，该函数会返回两个值，对父进程返回子进程的pid,而对子进程返回0，所以常见的fork的编程范式是 //pid_t fork(void); pid_t pid = fork(); if(pid) // parent process { //do something in parent process } else { //do something in child process } 简单的函数签名下蕴含了相当深入的知识———进程调度，不妨问一个问题：为什么fork函数能够返回&amp;quot;两个值&amp;quot;？ xv6的进程调度 进程调度的时机 在xv6中，进程调度由时间中断(timer interrupt)控制，时间中断发生后，内核在usertrap函数中捕获该中断，然后跳转到yield函数，yield的函...</description>
    </item>
    <item>
      <title>xv6的内核态与用户态的切换</title>
      <link>/2020/11/xv6%E5%86%85%E6%A0%B8%E6%80%81%E4%B8%8E%E7%94%A8%E6%88%B7%E6%80%81%E7%9A%84%E5%88%87%E6%8D%A2/</link>
      <pubDate>Tue, 17 Nov 2020 15:00:03 +0800</pubDate>
      <guid>/2020/11/xv6%E5%86%85%E6%A0%B8%E6%80%81%E4%B8%8E%E7%94%A8%E6%88%B7%E6%80%81%E7%9A%84%E5%88%87%E6%8D%A2/</guid><description>当一个进程需要调用kernel提供的服务的时候，他们调用一个system call， 在x86上一个system call大概类似于int 0x80的一条指令，而在RSIC-V的xv6调用syscall的方式是通过ecall指令, 并把进行的系统调用的编号放到a7寄存器。ecall会修改特权等级，并且进入到由内核控制的某个函数入口。 .global fork fork: li a7, SYS_fork ecall ret 从User进入Kernel 当一个ecall指令被调用，首先跳到uservec的函数。 这个函数具有两个特征 必须由汇编编写，因为它需要直接操作寄存器。从用户态进入到内核态，需要在进程内部保存所有用户态的寄存器，否则进入内核以后没办法再返回来。 这个函数必须位于一个内核的页表和用户的页表相同的虚拟地址，因为这个函数需要切换用户页表到内核页表，切换完了以后要能继续工作。 因此内核和每一个用户进程的页表都拥有一个叫做TRAMPOLINE的映射，他们的虚拟地址和物理地址是一样的，在这一页里包含了uservec和userret函数。 每一个进程的proc结构体内，有一个tra...</description>
    </item>
    <item>
      <title>About Me</title>
      <link>/about/</link>
      <pubDate>Tue, 17 Nov 2020 00:00:00 +0000</pubDate>
      <guid>/about/</guid><description>In code we trust. 😀 // GitHubCalendar(&#34;.calendar&#34;, &#34;BlurryLight&#34;); // or enable responsive functionality GitHubCalendar(&#34;.calendar&#34;, &#34;BlurryLight&#34;, { responsive: true,tooltips: true}); </description>
    </item>
    <item>
      <title>C&#43;&#43;的虚函数,虚表与多继承</title>
      <link>/2020/10/%E8%99%9A%E5%87%BD%E6%95%B0_%E8%99%9A%E8%A1%A8_%E5%A4%9A%E7%BB%A7%E6%89%BF/</link>
      <pubDate>Thu, 29 Oct 2020 17:50:14 +0800</pubDate>
      <guid>/2020/10/%E8%99%9A%E5%87%BD%E6%95%B0_%E8%99%9A%E8%A1%A8_%E5%A4%9A%E7%BB%A7%E6%89%BF/</guid><description>虚函数 考虑到面向对象设计中，当基类base派生出的Derived类的方法覆盖了基类中的方法时，使用基类的指针去访问此方法，可能出现: 调用基类的方法(静态绑定) 调用子类的方法 (动态绑定，迟绑定，运行期绑定等各种名称) 虚函数是用来实现动态绑定的方法，它允许使用基类指针指代一系列的子类对象，在调用函数的时候分发调用到实际的子类中去。 一份没有虚函数的示例： class Animal { public: void bark() { std::cout&amp;lt;&amp;lt;&amp;quot;Animal::bark not implemented&amp;quot;&amp;lt;&amp;lt;std::endl; } }; class Dog : public Animal { public: void bark() { std::cout&amp;lt;&amp;lt;&amp;quot;Dog bark&amp;quot;&amp;lt;&amp;lt;std::endl; } }; int main() { Animal* b = new Dog(); b-&amp;gt;bark(); //Animal::bark not implemented return 0...</description>
    </item>
    <item>
      <title>整数和浮点数的机器级表示</title>
      <link>/2020/09/%E6%95%B4%E6%95%B0%E5%92%8C%E6%B5%AE%E7%82%B9%E6%95%B0%E7%9A%84%E6%9C%BA%E5%99%A8%E7%BA%A7%E8%A1%A8%E7%A4%BA/</link>
      <pubDate>Mon, 21 Sep 2020 15:47:44 +0800</pubDate>
      <guid>/2020/09/%E6%95%B4%E6%95%B0%E5%92%8C%E6%B5%AE%E7%82%B9%E6%95%B0%E7%9A%84%E6%9C%BA%E5%99%A8%E7%BA%A7%E8%A1%A8%E7%A4%BA/</guid><description>文章节选于csapp 3rd edition. 整数表示 无符号整数表示 无符号整数是最简单的。由每一位二进制的0/1和二次幂累加而成。 \[ X=\sum_{i=0}x_i 2^i \] 二进制串0001映射到\(0 \times 2^3 + \cdots + 1 \times 2^0 = 1\). 容易推出，uint32的最大值是\(2^32 -1\). 有符号整数表示 现代计算机基本采用补码表示，反码和原码已经近乎退出历史舞台了。补码的最高有效位是1时，它的权重是\(-2^{w-1}\). 容易推出，0xFF等于-1，而0x7F是8位有符号的最大数127,0x80是8位有符号的最小数-128。补码在0处和正溢出的时候都有比较良好的性质，可以仅靠加法就可实现从-1(0xFF)到0(0x00)的进位。 补码的Tmin等于-Tmax-1,所以其区间是不对称的。取相反数、绝对值的时候要注意处理边界情况。补码的相反数等于所有位取反再+1，所以0x80取反的结果是0x7F,加一后会回到0x80,这暗示了对Tmin取反会回到Tmin，因为发生了溢出。 扩展和截断数字的位表示 扩展数字 无符号数...</description>
    </item>
    <item>
      <title>Manjaro下texlive环境配置指北</title>
      <link>/2020/08/linux%E4%B8%8Btexlive%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8C%97/</link>
      <pubDate>Sat, 15 Aug 2020 17:58:25 +0800</pubDate>
      <guid>/2020/08/linux%E4%B8%8Btexlive%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8C%97/</guid><description>Linux下安装texlive环境大致分为两种方式，一种是从包管理器中安装，二是从下载已经打包好的texlive镜像。 在Ubuntu这种带版本的发行版我可能会偏向第一种，因为安装简单，但是在Manjaro这种滚动发行版上最好选择第二种方案。原因有两点，第一texlive会经常随着系统的升级而滚动升级，但是风险是万一在赶论文ddl的时候把texlive滚坏了就会很麻烦。第二个是texlive带有自己的包管理器tlmgr，这个和arch系的包管理器pacman并不兼容，因此需要一个特别的与pacman兼容的包管理器。(PS: Arch下的Python也有这个问题，用python3-pip升级由pacman安装的Python包以后可能会导致pacman无法管理这部分文件)。 Texlive安装 这部分没什么好说的，在喜欢的镜像处下载texlive的完整安装包texlive2020.iso(我用的阿里云镜像）,挂载后安装。 发现ctan的texlive镜像只会保留最新的texlive版本。犹他大学的ftp服务器有所有历史版本和当前版本的texlive镜像，可以自由选择。我一般习惯用2020的...</description>
    </item>
    <item>
      <title>Debug你的光线追踪渲染器</title>
      <link>/2020/07/debug%E4%BD%A0%E7%9A%84%E5%85%89%E7%BA%BF%E8%BF%BD%E8%B8%AA%E6%B8%B2%E6%9F%93%E5%99%A8/</link>
      <pubDate>Wed, 01 Jul 2020 18:39:12 +0800</pubDate>
      <guid>/2020/07/debug%E4%BD%A0%E7%9A%84%E5%85%89%E7%BA%BF%E8%BF%BD%E8%B8%AA%E6%B8%B2%E6%9F%93%E5%99%A8/</guid><description>光线追踪渲染debug很困难，当然实时渲染debug也很难。有的时候材质实现的不太对，可能很长一段时间都看不出来。渲染环境下Debugger基本是不可用的，多线程+十万/百万条光线加上递归的求解算法，断点打上也看不出毛病在哪里。条件断点等偶尔有所帮助，不过总体帮助不大，渲染中常见的debugger方法还是刻意构造一些简单场景(全是镜子的房间、两个相切的球)，或者输出法向量图等来debug。 我们可以实现一些小工具来帮助debug。比如这里的例子。我实现了一个可视化工具，渲染器在debug模式下会使用spdlog记录每一束光线的origin(原点)，isect.coords(击中的物体表面), isect.normal(物体表面的法向量)，输出到log文件中。然后Debugger去parse这个log文件，用OpenGL的Geometry Shader画出每根光线。 Debugger的实现依赖OpenGL，简单采用一个BlinnPhong就能得到还不错的效果，没有阴影。可以用ImGUI做一些简单的GUI，因为全部的光线显示出来就太多了，我们可能只是为了观察某个物体的表面的反射、折射情况，...</description>
    </item>
    <item>
      <title>辐射度量学与渲染方程</title>
      <link>/2020/05/%E8%BE%90%E5%B0%84%E5%BA%A6%E9%87%8F%E5%AD%A6%E4%B8%8E%E6%B8%B2%E6%9F%93%E6%96%B9%E7%A8%8B/</link>
      <pubDate>Thu, 28 May 2020 15:48:07 +0800</pubDate>
      <guid>/2020/05/%E8%BE%90%E5%B0%84%E5%BA%A6%E9%87%8F%E5%AD%A6%E4%B8%8E%E6%B8%B2%E6%9F%93%E6%96%B9%E7%A8%8B/</guid><description>辐射度量学提供了一系列方便的概念来描述渲染中的光学问题，所以渲染中大量运用到这些符号(与物理含义)。渲染中的光学做了一些简化。 光线不会自相交 光线沿直线传播 光线是线性可加的 光线不会闪烁 光线保持能量守恒 衍射和干涉等波动光学的概念几乎不考虑 辐射度量学 Energy 能量 单位焦耳，物理里最常见的符号之一。某个波长的光线能量实质上是含有光子的数量。一个光子所带的能量可以用以下公式来刻画，\(\lambda\)为波长,h为普朗克常数: \( Q = \frac{hc}{\lambda} \) Flux 称为power(功率)，单位瓦特(w), 又可以被称为Radiant flux,单位流明(lm)。Flux可以通过能量对时间做微分得到 \[ \Phi = \frac{\text{d}Q}{\text{d}t} \] 一个在空间中的球形光源每单位时间内对周围360度空间内辐射的能量就是功率, 也就是Flux。 Irradiance 给定一个有限的面积, 我们可以得到单位时间内单位面积所得到的能量。 \[ E = \frac{Q}{At} = \frac{\Phi}{A} \] 考虑一...</description>
    </item>
    <item>
      <title>对随机变量的采样</title>
      <link>/2020/05/%E5%AF%B9%E9%9A%8F%E6%9C%BA%E5%8F%98%E9%87%8F%E7%9A%84%E9%87%87%E6%A0%B7/</link>
      <pubDate>Sat, 23 May 2020 22:12:52 +0800</pubDate>
      <guid>/2020/05/%E5%AF%B9%E9%9A%8F%E6%9C%BA%E5%8F%98%E9%87%8F%E7%9A%84%E9%87%87%E6%A0%B7/</guid><description>首先,先上Rendering Equation: \[ L_o(p,\omega_o) = L_e(p,\omega_o) + \int_{\Omega +}L_i(p,\omega _i)f_r(p,\omega_i,\omega_o)(n \cdot \omega_i) \text{d}\omega_i \] 其中包含有含有递归定义的L项和对半球面的积分项,这个式子是没有解析解的,数值方法可以使用蒙特卡罗积分,而蒙特卡罗积分就涉及到对随机变量的采样. 逆采样方法 如果\(X\)是连续型随机变量,其概率密度函数\(p(x)\)已知, 则在定义域内可以积分得到其累积分布函数(CDF)为: \[ P(x) = \int_0^x p(x&#39;)\text{d}x&#39; \] 计算其反函数,令一随机变量\(u \in [0,1)\),则有 \[ P(P^{-1}(x)) = u \] 解可得: \[ P^{-1}(x) = g(u) \] 其中\(P^{-1}(x)\)即为服从\(P(x)\)分布的样本, 而这个样本可以从均匀分布的\(u\)中导出. 一维随机变量的采样 设概率密度\(p(x) = ...</description>
    </item>
    <item>
      <title>直线与三角形相交Moller Trumbore算法推导</title>
      <link>/2020/04/%E7%9B%B4%E7%BA%BF%E4%B8%8E%E4%B8%89%E8%A7%92%E5%BD%A2%E7%9B%B8%E4%BA%A4moller-trumbore%E7%AE%97%E6%B3%95%E6%8E%A8%E5%AF%BC/</link>
      <pubDate>Fri, 03 Apr 2020 20:24:25 +0800</pubDate>
      <guid>/2020/04/%E7%9B%B4%E7%BA%BF%E4%B8%8E%E4%B8%89%E8%A7%92%E5%BD%A2%E7%9B%B8%E4%BA%A4moller-trumbore%E7%AE%97%E6%B3%95%E6%8E%A8%E5%AF%BC/</guid><description>Moller Trumbore算法是一种快速求解直线与三角形求交的算法，通过向量与矩阵计算可以快速得出交点与重心坐标。要推导它还比较麻烦，需要用到向量的混合积和克拉莫法则。 引理 引理1: 三阶方阵的行列式等于三个列向量的混合积。 \[ \begin{aligned} &amp;\mathbf{a \cdot (b \times c)} = \mathbf{b \cdot (c \times a)} = \mathbf{c \cdot (a \times b)}&amp; = \\ &amp;\mathbf{a \cdot -(c \times b)} = \mathbf{b \cdot -(a \times c)} = \mathbf{c \cdot -(b \times a)}&amp; = \end{aligned} \begin{vmatrix} a_1 &amp; b_1 &amp; c_1\\ a_2 &amp; b_2 &amp; c_2\\ a_3 &amp; b_3 &amp; c_3 \end{vmatrix} \] 引理2: 克拉莫法则: 如果一个线性方程组 \(\mathbf{Ax = c}\), 其中A是可逆方阵，\( \mathbf{x...</description>
    </item>
    <item>
      <title>AABB包围盒快速变换方式</title>
      <link>/2020/03/aabb%E5%8C%85%E5%9B%B4%E7%9B%92%E5%BF%AB%E9%80%9F%E5%8F%98%E6%8D%A2%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Tue, 24 Mar 2020 20:57:36 +0800</pubDate>
      <guid>/2020/03/aabb%E5%8C%85%E5%9B%B4%E7%9B%92%E5%BF%AB%E9%80%9F%E5%8F%98%E6%8D%A2%E6%96%B9%E5%BC%8F/</guid><description>这个问题来源于PBRT的第二章的一个练习题，朴素的AABB包围盒的变换方式是对8个顶点做同等变换，然后在8个顶点中找最小的xyz和最大的xyz来构成新的AABB顶点pMin,pMax。PBRT指出有更高效的方法来变换AABB，稍微搜索了并实现了一下。 参考:http://dev.theomader.com/transform-bounding-boxes/ 考虑一个AABB的构造应该是两个对角点pMin和pMax，假设AABB的中点是\(\mathbf{c} = (c_x,c_y,c_t)^T\),以及到两个对角点的offset: \(\mathbf{r} = (r_x,r_y,r_z)^T\) \[ \mathbf{Box} = [\text{pMin,pMax}] = [c - r, c + r] = \begin{bmatrix} min \begin{pmatrix} c_x \pm r_x \\ c_y \pm r_y \\ c_z \pm r_z \\ \end{pmatrix} &amp; max \begin{pmatrix} c_x \pm r_x \\ c_y \pm r_...</description>
    </item>
    <item>
      <title>Linux的socket指北</title>
      <link>/2020/03/linux%E7%9A%84socket%E6%8C%87%E5%8C%97/</link>
      <pubDate>Mon, 16 Mar 2020 22:13:26 +0800</pubDate>
      <guid>/2020/03/linux%E7%9A%84socket%E6%8C%87%E5%8C%97/</guid><description>socket概览 这是继signal, pthread后的第三篇，socket系列，代表的是进程间通讯和网络通讯。我没有读过unp，对网络编程一窍不通(虽然我挺想学的)。 主要着重记录socket一些基础概念以便以后翻阅，用途也主要用于IPC。IPC(进程间通讯)有很多种方式，FIFO,system V IPC,Posix IPC，socket, signal,PIPE等，各有优劣，都是时代的眼泪，不同的主机(微机，巨型机)，不同的操作系统贡献了不同的方式。我个人比较喜欢用signal的方式，因为写起来简单，但是仅限于简单的通知某进程。如果在进程间要传递数据，socket是不二之选，因为它很容易被扩展到网络上的不同主机上。socket虽然起源于BSD, 但是现在在SUSv3标准里，所以不用担心移植性。 提到socket就不能不提到IO复用的概念，主要指select,poll以及Linux的epoll，BSD的kqueue系列函数。这允许我们同时对许多许多个IO进行监控(socket也是一种IO操作)，现代高性能网络库的基石。不过这篇文章里不会讲epoll的概念。 一个socket通讯建...</description>
    </item>
    <item>
      <title>Pthread指北</title>
      <link>/2020/02/pthread%E6%8C%87%E5%8C%97/</link>
      <pubDate>Tue, 25 Feb 2020 23:35:58 +0800</pubDate>
      <guid>/2020/02/pthread%E6%8C%87%E5%8C%97/</guid><description>Pthread简介 Pthread,也即Posix thread，顾名思义，是posix的一部分，跨平台的线程库。Linux上的现在的实现是Native POSIX Threads Library,也即NPTL. Thread是一种并行的机制，在多核处理器上，不同的线程可以被调度到不同的核上运行，从而获得接近线性增长的效率提升。在单核处理器上，使用多线程程序在IO密集型程序上也能获得收益，因为在IO阻塞的时候，切换到其他线程可以继续工作，从而提高程序的有效运行效率（而不是傻傻的阻塞等待IO，在Python中由于GIL的存在，Python的多线程不能调度到其他核运行。所有的线程独享自己的栈空间，共享同一个堆地址空间，这会带来一些变量使用上的便利，避免频繁的IPC通讯，同时也带来了严重的风险：race condition将会导致所有线程工作不正常，或者程序异常终止。 线程继承和不继承的属性 当一个线程启动的时候，它会从其他线程中继承一些信息。典型的如: 进程有关的信息(pid,ppid,pgid) 打开的文件描述符(fd) 信号处理方式 timer相关信息，对进程的资源限制，nice va...</description>
    </item>
    <item>
      <title>Linux的signal指北</title>
      <link>/2020/02/linux%E7%9A%84signal%E6%8C%87%E5%8C%97/</link>
      <pubDate>Wed, 19 Feb 2020 17:53:26 +0800</pubDate>
      <guid>/2020/02/linux%E7%9A%84signal%E6%8C%87%E5%8C%97/</guid><description>预计此系列有三篇文章，分别记录信号(signal), 线程(pthread) 和 套接字(socket) 方面的内容，作为学习知识的整理记录。只讨论Linux下面的API和表现。 信号简介 信号是一种通知进程的手段，源头可能是从kernel递送到进程，可能是自己给自己信号，也有可能是由其他进程发送过来(kill -9). 不同信号是通过不同的魔数区分开的(SIGTERM 15号信号，SIGKILL 9号信号)，不同的信号有不同默认的含义。信号还可以用来传递一些信息(很少用)。 信号通常是异步的，程序可能在任何代码段接受到递送过来的信号，部分代码可能会被打断，当被打断的时候，部分操作(比如sleep)会失败，并且errno会被置为EINTR，比如在阻塞等待socket的时候就可能会被信号打断。 默认下，多数信号的响应操作是终止(可能会导出core dump),所以发信号前一定要注意不要有竞争(race condition).如果在一个程序注册好handler之前就发起了信号，可能会导致程序直接终止。 传统程序使用signal函数(Linux也可以)，但是现在更推荐使用sigaction函...</description>
    </item>
    <item>
      <title>一个有锁线程安全的队列实现</title>
      <link>/2020/02/%E4%B8%80%E4%B8%AA%E6%9C%89%E9%94%81%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Mon, 03 Feb 2020 19:28:27 +0800</pubDate>
      <guid>/2020/02/%E4%B8%80%E4%B8%AA%E6%9C%89%E9%94%81%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0/</guid><description>最近在学习pthread的过程中，也算是重温了多线程编程的一些知识。关于单生产者单消费者的模式，最佳实践应该是circle buffer，可以无锁操作,可是STL里没有提供该容器。github上有一个高性能的readwritequeue1,实现了lock-free的queue，benchmark看了一下性能还可以，API接口也很好看。Boost库的lockfree里也有一个queue，不过没有用过。 在实际中，如果在queue上不是瓶颈，想自己封一下的话,也就几十行就能把std::queue封成线程安全的，主要要用到C++11里的条件变量和互斥锁。 benchmark下，大概比刚刚提到的lockfree的慢50倍吧:)。 #pragma once #include &amp;lt;condition_variable&amp;gt; #include &amp;lt;memory&amp;gt; #include &amp;lt;mutex&amp;gt; #include &amp;lt;queue&amp;gt; #include &amp;lt;thread&amp;gt; template &amp;lt;typename T&amp;gt; class safe_que...</description>
    </item>
    <item>
      <title>一次numpy读取txt文件优化</title>
      <link>/2020/01/%E4%B8%80%E6%AC%A1numpy%E8%AF%BB%E5%8F%96txt%E6%96%87%E4%BB%B6%E4%BC%98%E5%8C%96/</link>
      <pubDate>Wed, 01 Jan 2020 20:04:49 +0800</pubDate>
      <guid>/2020/01/%E4%B8%80%E6%AC%A1numpy%E8%AF%BB%E5%8F%96txt%E6%96%87%E4%BB%B6%E4%BC%98%E5%8C%96/</guid><description>简单介绍下背景，有大约600个txt，大小在1M左右，实际内容是4列的浮点数，也就是csv格式，以空格分割。想用python读到内存里转成numpy.array格式，踩了一个numpy.loadtxt的坑。 先下结论:永远不要用numpy.loadtxt,非常非常慢1。 环境： Manjaro x64 HDD 5400X python 3.7 I7 4720HQ 2.6GHZ numpy.loadtxt有多慢 先用numpy跑一次loadtxt import os from timeit import default_timer as timer import numpy as np path_list = os.listdir(&#39;.&#39;) start=timer() [np.loadtxt(x) for x in path_list] print(timer() - start) 结果:139.54152867000084 139秒，emm 换用pandas.read_csv看看 import os from timeit import default_timer as timer i...</description>
    </item>
    <item>
      <title>X11VNC搭建指北</title>
      <link>/2019/11/x11vnc%E6%90%AD%E5%BB%BA%E6%8C%87%E5%8C%97/</link>
      <pubDate>Sat, 30 Nov 2019 13:10:51 +0800</pubDate>
      <guid>/2019/11/x11vnc%E6%90%AD%E5%BB%BA%E6%8C%87%E5%8C%97/</guid><description>vnc作为开源协议，实现的有很多，包括出名的realVNC,tightVNC以及它的fork tigerVNC，不同的vnc后端也不少，主要分为另起一个x和直接获取当前的x画面两种。 我的需求是从外网穿透学校的防火墙以及路由器NAT，访问位于实验室的笔记本，并且监视一些还没有完成的工作，所以我需要 1.穿透内网 2.获取当前的X画面 穿透内网的部分frp很好配置，而x11vnc配置起来要花点功夫，主要是在systemed中配置起来有点麻烦。 生成密码 x11vnc -storepasswd 生成后的密码默认在家目录，如果有root权限可以拷贝到/etc/下，这样方便systemd读取 x11vnc的systemed配置 主要注意两点: xfce4使用的dm是lightdm，所以要从/var/run/lightdm/root/:0中获取权限 配置shared和forever,以允许多个vnc viewer访问 不要开ncache,会导致不支持的客户端，比如手机上的realvnc客户端，获取到错误的分辨率 要加-repeat标签，否则无法连续输入多个按键 [Unit] Descriptio...</description>
    </item>
    <item>
      <title>经典光照模型:Phong光照模型以及BlinnPhong光照模型</title>
      <link>/2019/10/phong_and_blinn_phong/</link>
      <pubDate>Mon, 28 Oct 2019 16:27:19 +0800</pubDate>
      <guid>/2019/10/phong_and_blinn_phong/</guid><description>Phong 光照模型 Phong Lighting Model在1975年，由Phong提出，以他的名字冠名。是一种局部光照的模型，他认为一个光照模型可以用三种不同的部分组成，主要包括ambient,diffuse,specular(环境光，漫反射和高光)。 ambient光照 环境光照是考虑到即使在不可见光源的地方，经过各种反射，总会带有微弱的光照。光线追踪的算法会更加准确，但是在这里采用一种简化的方法。就是将光源的颜色，乘以一个很小的系数，再乘以物体的颜色。 vec3 ambient = 0.1 * lightColor * objectColor; diffuse光照 漫反射光照是考虑到，离光源越近，直接受光源照射的片段，理应比其他片段更亮。这种概念可以用两个向量表示，分别是片段到光源的位移向量与片段表面的单位法向量，他们的向量积表示了一种衡量漫反射的强度的量。 vec3 lightDir = lightPos - FragPos; float diffuse = dot(norm,lightDir) specular光照 光线在表面经过反射后，会形成一束反射光。 摄像机正处于反...</description>
    </item>
    <item>
      <title>UEFI&#43;win7&#43;Linux双启动踩坑日记</title>
      <link>/2019/10/uefi-win7-linux%E5%8F%8C%E5%90%AF%E5%8A%A8%E8%B8%A9%E5%9D%91%E6%97%A5%E8%AE%B0/</link>
      <pubDate>Thu, 17 Oct 2019 13:40:01 +0800</pubDate>
      <guid>/2019/10/uefi-win7-linux%E5%8F%8C%E5%90%AF%E5%8A%A8%E8%B8%A9%E5%9D%91%E6%97%A5%E8%AE%B0/</guid><description>启动黑屏 在服务器上装了一个Ubuntu server，安装盘启动黑屏，其实就应该想到是驱动有问题，因为服务器的显卡是Quadro M4000,但是没有想那块去，排查了一段时间以后才想起来我之前的笔记本也出现过启动黑屏。 在grub的启动菜单时，按e进入编辑模式，在boot一栏加入nomodeset，成功启动。 进入系统以后，可以编辑/etc/default/grub,修改GRUB_CMDLINE_LINUX_DEFAULT一栏，一般这里有ro quiet等参数，加入nomodeset以后，启动就没问题了。 windows覆盖grub问题 重点想谈谈这个，以前用MBR的时候还没有发现windows这么蠢，后来发现换成UEFI以后，windows更霸道了，强制覆盖grub不说，MBR时代的时候也覆盖，用Live CD修一下就消停了，UEFI重启一次就覆盖一次，有点意思。 查了一下Ubuntu的论坛，刚好有人提到如何解决启动项被覆盖的方法。 第一种 修改Windows BCD 来让Ubuntu默认启动 bcdedit /set {bootmgr} path \EFI\ubuntu\grub...</description>
    </item>
    <item>
      <title>读Machine-Learning-Yearning杂记</title>
      <link>/2019/10/%E8%AF%BBmachine-learning-yearning%E6%9D%82%E8%AE%B0/</link>
      <pubDate>Sat, 05 Oct 2019 15:09:49 +0800</pubDate>
      <guid>/2019/10/%E8%AF%BBmachine-learning-yearning%E6%9D%82%E8%AE%B0/</guid><description>用差不多两个下午的时间读了这本120页的小册子，Andrew NG的诚意之作，并不是tutorial之类的巨头，而是娓娓道来的经验之谈，重点讨论了如何处理数据，如何判断算法哪里需要改进和如何分析错误三大块内容。读了一遍以后，自感只收获了不到一成，但仍可以写一篇小的杂记，以便后来查阅。 数据处理 数据集可以分为训练集(training set),开发集(dev set/validation set)和测试集(test set). 开发集主要用来调节参数和改变学习算法，测试集主要用来评估性能。开发集不是必须的。训练集/测试集往往 70% / 30%，如果有开发集的话可以划分为 70% / 15% / 15%。 训练集可以取自不同来源，而开发集和测试集应该尽可能的符合处理问题的实际分布，开发集和测试集应当符合同一分布。 开发集的规模应当在1000 ～ 10000个之间为佳。样本太小可能导致无法收敛，或者无法观察到细微的提升。 使用单一指标评估算法。常用的包含准确度(precision)和召回率(recall),两者呈负相关。可以采用F1 score来结合两者。 误差分析 可以将开发集分为ey...</description>
    </item>
    <item>
      <title>LookAt的实现</title>
      <link>/2019/09/lookat%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Sat, 14 Sep 2019 21:28:47 +0800</pubDate>
      <guid>/2019/09/lookat%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid><description>一个物体要在OpenGL中被渲染出现，需要经过经典的MVP变换，主要是从物体坐标系，通过Model Matrix，变换到世界坐标系，然后通过View Matrix，也叫相机坐标系，转换为某个视角所看到的内容，最后通过projection Matrix，做仿射变换，最后还有一步OpenGL隐藏的剪裁步骤，裁减掉不在投影区域内的像素点。 流程图可以看1 其中，glm::lookat函数是glm库提供的一个工具函数，可以用来计算view matrix。给定三个特殊向量，包括相机的position向量，相机所看的物体的位置target向量以及整个世界坐标系的up向量，可以计算出view矩阵。 其中P是相机的位置向量，U是需要计算的，相机的up向量，R也是需要计算的，相机坐标系的右向量，D是最好计算的，D是direction向量，是从物体到相机的向量。 因此代码可以写成如下， //* a solution to glm::lookat auto look_at = [](glm::vec3 position, glm::vec3 target, glm::vec3 worldup) -&amp;gt; ...</description>
    </item>
    <item>
      <title>我选择了Manjaro</title>
      <link>/2019/09/%E6%88%91%E9%80%89%E6%8B%A9%E4%BA%86manjaro/</link>
      <pubDate>Sat, 14 Sep 2019 16:48:10 +0800</pubDate>
      <guid>/2019/09/%E6%88%91%E9%80%89%E6%8B%A9%E4%BA%86manjaro/</guid><description>我在过去的四年内一直使用Debian,大约从Debian7的末期开始使用，到第八个stable版本jessie，到第九个stable版本strech，以及第10个版本，我记不大清第十个版本的名字了，因为我只是从strech跨大版本升级上来玩弄了一会，便一鼓作气的升级到了testing，并一直保持着滚动更新。 Debian是一个很好的发行版，鉴于它是最大的上游之一，无数的面向Linux的开源软件，往往都会提供一个deb包，即使是商业软件，在提供个tar.gz之余，往往可以翻到deb。这无疑是对Debian作为最古老的发行版之一的尊重。Debian也确实值得尊重，Free的理念根值在方方面面中。Debian作为上游，支持绝大多数架构，以至于在某段时间内，我能接触到的所有属于我的电子设备，都安装上了Debian（包括一台实际上基于Gentoo的chromebook）。 为什么选用Manjaro呢？诱因是我在我的win10上安装了msys2，msys2上有一个port的pacman可以用，引起了我的好奇。导火索是Debian的一次滚动更新把内核滚动到了kernal 5.2，导致可怜的Bumbl...</description>
    </item>
    <item>
      <title>Kinect_V1在Debian testing的配置指北</title>
      <link>/2019/08/kinect_v1%E5%9C%A8debian_testing%E7%9A%84%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8C%97/</link>
      <pubDate>Thu, 29 Aug 2019 17:07:59 +0800</pubDate>
      <guid>/2019/08/kinect_v1%E5%9C%A8debian_testing%E7%9A%84%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8C%97/</guid><description>坑在哪里 在Linux下驱动Kinect V1现在有两种方式，一种是使用OpenNI + SensorKinect + Nite的方案，一种是使用OpenNI2 + libfreenect的方案，第一种我没有尝试，第二种的话，Debian有坑。 Debian的包管理自带有LibOpenNI-dev和LibOpenNI2-dev，这个是PCL库的前置依赖，理论上来说，通过apt装上libfreenect-dev就可以了。然而，OpenNI2与libfreenect连接， 需要在libfreenect的编译选项里打开BUILD_OPENNI2_DRIVER，然而Debian自带的库不带这一点，因此需要手动编译，不然会出现这样的错误。 SimpleViewer: Device open failed: DeviceOpen using default: no devices found 解决方案 根据OpenNI2-FreenectDriver的链接，首选需要一个&amp;gt;=2.2.033版本的OpenNI2。 可以从github下载预编译版本，也可以自己手动编译。然后需要手动编译libfre...</description>
    </item>
    <item>
      <title>图形学中常见的变换</title>
      <link>/2019/08/matrix_transformation/</link>
      <pubDate>Fri, 09 Aug 2019 17:33:03 +0800</pubDate>
      <guid>/2019/08/matrix_transformation/</guid><description>引言: https://learnopengl.com/Getting-started/Transformations 这里讲得不错。这篇文章不过是对链接指向地址的简单摘要。此外，推荐&amp;lt;Foundation of 3D computer graphics&amp;gt;，前六章的数学基础讲得不错。 为什么要用齐次坐标？ Why use homogeneous matrix? 图形学中常用4x4矩阵来进行变换，因为 1. 升维后的向量能够帮助我们区分点和向量, 2 在齐次坐标下可以统一线性变换和平移变换。 考虑如下 \( \vec{v} \) \[ \left[ \begin{array}{cc} x_{1} \\ y_{1} \\ z_{1} \end{array} \right] \] 它可能代表一个点，也可能代表一个向量。 一个向量和一个向量的一些操作是有物理含义的，如\(\vec x + \vec y\)，代表\(\vec x\)与\(\vec y\)向量方向的串接。而两个点相加的操作是无意义的，你也不可能对一个点做放大(scale)的线性变换，同理，一个向量代表着某种方向，对某个...</description>
    </item>
    <item>
      <title>关于C&#43;&#43;的命名约定</title>
      <link>/2019/07/%E5%85%B3%E4%BA%8Ec-%E7%9A%84%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A/</link>
      <pubDate>Tue, 30 Jul 2019 18:59:25 +0800</pubDate>
      <guid>/2019/07/%E5%85%B3%E4%BA%8Ec-%E7%9A%84%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A/</guid><description>谷歌命名约定 本篇文章几乎照搬于谷歌命名约定，部分根据个人习惯有所改动，可供参考。 文件名 文件名全部小写,采用_连接单词。如my_useful_tools.cc是一个可以接受的命名。头文件一律采用h结尾，包含有内联函数，或者模板实现的头文件可以采用hpp结尾，实现文件一律采用cc后缀。 类型命名 类，结构体，类型别名(typedef),枚举(enum)采用驼峰命名法。如MyExcitingClass，不允许下划线。 变量命名 所有变量，包括函数参数，全部采用小写字母+下划线。 类的成员变量使用下划线_结尾。如 class MyExcitingClass { public: .... private: std::mutex lock_; std::string char_buffer_; //good. Don&#39;t use charbuffer_ or CharBuffer_ } 谷歌建议结构体变量像正常变量一样命名，但是我认为应该同类等同，因为大量的C++代码不加区分的使用类与结构体。 常量 用static const，#define,const和constexpr以及确定有常量语义...</description>
    </item>
    <item>
      <title>Debian10双显卡安装Anaconda&#43;Cuda9&#43;Pytorch</title>
      <link>/2019/06/debian10%E5%8F%8C%E6%98%BE%E5%8D%A1%E5%AE%89%E8%A3%85anaconda-cuda9-pytorch/</link>
      <pubDate>Tue, 11 Jun 2019 16:46:04 +0800</pubDate>
      <guid>/2019/06/debian10%E5%8F%8C%E6%98%BE%E5%8D%A1%E5%AE%89%E8%A3%85anaconda-cuda9-pytorch/</guid><description>在笔记本上搭建Pytorch的环境还算比较麻烦的,我的笔记本带核显和独显，在Linux下配置起来要稍微麻烦点，主要要解决的问题包括三个： 在Debian10上安装显卡驱动和cuda，正确驱动独显和核显 安装Anaconda管理Python的虚拟环境，避免污染系统默认的Python环境（可以使用Docker替代 安装对应版本的Pytorch Nvidia驱动的安装 Linux下双显卡驱动是老大难问题了。不外乎三种方案。 BIOS直接屏蔽核显，启用独显，装官方的显卡驱动，这种情况最简单，但是独显会一直启用，导致笔记本续航下降。 Nvidia-prime， 这应该是C社与Nvidia合作的结果。采用prime-select intel|nvidia切换独显和核显，缺点是每次切换必须要注销或者重启来使切换生效。 Bumblebee或Nvidia xrun。默认使用集显工作，独显电源被关闭。需要使用独显的时候optirun/nvidia-xrun application手动调用独显运行。 优点是无缝调用集显独显，缺点是大黄蜂有性能损失，Nvidia xrun比大黄蜂配置更加复杂。 这里选用Bum...</description>
    </item>
    <item>
      <title>谈谈constexpr,inline和static</title>
      <link>/2019/06/%E8%B0%88%E8%B0%88constexprinline%E5%92%8Cstatic/</link>
      <pubDate>Sat, 08 Jun 2019 16:16:18 +0800</pubDate>
      <guid>/2019/06/%E8%B0%88%E8%B0%88constexprinline%E5%92%8Cstatic/</guid><description>constexpr，inline和static可能是C++里最让人迷惑的几个关键词了，如同vector&amp;lt;bool&amp;gt;既不是一个vector，里面也不存放bool一样。 当出现组合static constexpr,static inline等这种组合的时候，更是让人摸不着头脑。 constexpr和const的区别 简单来说，const的含义是readonly variable，constexpr代表的是真的constant，用法如同纯C里的#define A 100一样。 如果你想要真正的常量,那就请使用constexpr。 考虑如下代码 #code 1 volatile constexpr int a = 5; int *p = (int *)&amp;amp;a; *p = 100; printf(&amp;quot;a = %d, *p = %d\n&amp;quot;, a, *p); #code 2 volatile const int a = 5; int *p = (int *)&amp;amp;a; *p = 100; printf(&amp;quot;a = %d, *p = %d\n&amp;quot;...</description>
    </item>
    <item>
      <title>FakeSTL From Scratch | AVL TREE的实现</title>
      <link>/2019/06/fakestl_from_scratch____avl_tree%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Tue, 04 Jun 2019 16:57:12 +0800</pubDate>
      <guid>/2019/06/fakestl_from_scratch____avl_tree%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid><description>Why AVL Tree? STL里平衡二叉树应用的很广泛，主要是标准规定了查找、插入和删除的复杂度。set,multiset,map,multimap下面都是平衡二叉 树，主流的实现版本都是用rbtree来实现。红黑树理论上在插入和删除的时候都比AVL树性能更好一点，在查找的时候AVL树性能更好， 因为AVL树有更严格的平衡条件。 在写tinySTL中我选择了AVL树而不是红黑树，具体原因有以下两点： 红黑树的插入好写，删除的各种case多的要命。有多麻烦写过的人都知道。相反AVL的删除和插入都类似，只需要处理LL，LR，RL和RR四种情况，其中两种是对称的，实际上只有两种情况。 AVL树和RBtree的性能究竟有多大差距也有争议，如AVL/RBTREE 实际比较。 AVL树的实现 AVL节点定义和辅助函数 一个包含有高度信息和节点数目信息的AVL树可以定义为以下的数据结构 struct node { int data; int height; int n; //左右子树的总节点数目 struct node* parent; struct node* lChild; struct n...</description>
    </item>
    <item>
      <title>博客迁移到Hugo &amp; github pages</title>
      <link>/2019/06/%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB%E5%88%B0hugo-github-pages/</link>
      <pubDate>Sun, 02 Jun 2019 10:30:01 +0800</pubDate>
      <guid>/2019/06/%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB%E5%88%B0hugo-github-pages/</guid><description>迁移原因 原来的服务器体验了一把GFW VIP待遇，先是被TCP阻断，随后直接IP block了。幸亏没有被DNS污染，不然就浪费了我辛辛苦苦想的域名 了。不过服务器维护起来也很费劲，加上原有的typecho的stable版停留在17年不更新了，Dev分支虽然在推进但是不想做小白鼠。 一劳永逸迁移到hugo平台，源文件用git管理，免去备份数据库的烦恼。 迁移过程 首先用AlanDecode/Typecho-Plugin-Tp2MD插件（感谢作者的付出），格式 需要自己手动修改一下成hugo需要的格式，默认的是hexo格式的，有些细微的差别。然后就是标准的建站流程了。 ...</description>
    </item>
    <item>
      <title>谈谈observer_ptr</title>
      <link>/2019/05/459/</link>
      <pubDate>Sat, 25 May 2019 00:00:00 +0000</pubDate>
      <guid>/2019/05/459/</guid><description>observer_ptr是于14年的提案N42821提出的一种“世界上最蠢的智能指针&amp;quot;，现在的observer_ptr在std::experimental::memory里，当然也可以自己写一个，observer_ptr的代码简单到”代码即文档“的级别了，但是目前来看observer_ptr应该是进不了标准库了，因为Bjarne Stroustrup在提案P1408R02:里有力的驳斥了observer_ptr。 What is observer_ptr 和智能指针家族的其他兄弟们一样(weak_ptr,shared_ptr,unique_ptr)，observer_ptr也是为了处理资源管理问题而诞生的。C++在语言层面上没有提供一种只读指针，当我们需要用指针来指代某个数据而使用raw_pointer，也即T*时，我们需要时刻紧绷心弦，一旦对本意为只读的指针错误的使用了delete，就会造成意料之外的资源释放。 一种可行的方案是采用自定义删除器的unique_ptr，如 template &amp;lt;typename T&amp;gt; using read_only_ptr = un...</description>
    </item>
    <item>
      <title>Debian 字体回落到AR PL UKai、AR PL UMing的解决方法</title>
      <link>/2019/05/457/</link>
      <pubDate>Thu, 23 May 2019 00:00:00 +0000</pubDate>
      <guid>/2019/05/457/</guid><description>Debian现在中文经过社区的不断努力已经解决的很好了，但是Firefox在浏览某些网页中，偶尔会见到不和谐的字体。 比如在这种 A Brief Introduction to DDD 网页中，在没有CSS规定样式，或者CSS没有规定字体的时候，会显示的很丑。用Firefox的审查元素看了下，字体被回落到了AR PL UKai。可能是安装TexLive或者Sougou Pinyin的时候apt自动装上的。 解决办法 1.直接删除 sudo apt-get remove fonts-arphic-ukai fonts-arphic-uming 2.如果想保留字体 到/etc/fonts/conf.d中，将含有fonts-arphic-*的配置文件全部删除。这些都是软连接，指向/etc/fonts/conf.avail，需要用的时候可以重新将conf.avail/里的配置链接到conf.d/ ...</description>
    </item>
    <item>
      <title>Huffman编码与解码</title>
      <link>/2019/05/454/</link>
      <pubDate>Mon, 20 May 2019 00:00:00 +0000</pubDate>
      <guid>/2019/05/454/</guid><description>Huffman编码在数据结构里是必学的部分。要实现霍夫曼编码首先要实现霍夫曼树（一个简单的教程，它是一个带权最优编码树，主要特性有两点： 所有数据都在叶节点 出现概率越高的字符(权重越高)离根节点越近 这就为编码和解码提供了思路。编码时，先新建森林，每一个树上只有单独的权重和节点，然后两两合并为新的树，最后合并为一颗整数。从根节点往下递归，左分支为0，右分支为1，得到编码。 如图所示： 解码时思路也相同，从形如0010101的bitset中解码时，需要预先知道霍夫曼树的全体信息。从根节点开始遍历，每一次遍历检查是否为叶节点，为叶节点时提取数据并回到根节点，解码一个字符。 从森林中合并为树 while(forest.size() &amp;gt; 1) { std::sort(forest.begin(),forest.end(),[&amp;amp;](node* a,node* b){return a-&amp;gt;weight &amp;lt; b-&amp;gt;weight;}); ptr = new node; ptr-&amp;gt;weight = forest.at(0)-&amp;gt;weight + forest.a...</description>
    </item>
    <item>
      <title>FakeSTL From Scratch | Iterator and Traits(迭代器与类型萃取)</title>
      <link>/2019/04/452-1/</link>
      <pubDate>Sat, 13 Apr 2019 00:00:00 +0000</pubDate>
      <guid>/2019/04/452-1/</guid><description>Iterator 迭代器是STL特有的一个概念,翻译的比较绕口,更加通俗的翻译可以翻译成游标. 它提供一种统一和抽象的方式,使用迭代器可以用于访问和修改STL容器的每一个元素,而无需暴露STL容器的内部实现. 迭代器提供和指针一样的dereference和member access的功能,所有的迭代器都重载了*和-&amp;gt;运算符,使他们工作的更像指针. 事实上,一般的STL实现里,vector容器的iterator就是T*. 在这里推荐一篇博客带你深入理解STL之迭代器和Traits技法. Iterator与重载决议 下面讲一个工程问题,如何利用迭代器特性来帮助重载决议. C++模板函数的重载决议问题. //construct 1 iterator insert( const_iterator pos, size_type n, const value_type &amp;amp;value ) //construct 2 template&amp;lt;typename InputIterator&amp;gt; iterator insert( const_iterator pos, InputItera...</description>
    </item>
    <item>
      <title>FakeSTL From Scratch | 编写Allocator</title>
      <link>/2019/04/450/</link>
      <pubDate>Fri, 05 Apr 2019 00:00:00 +0000</pubDate>
      <guid>/2019/04/450/</guid><description>Allocator模版是所有 标准库容器里默认的内存分配器。在实现容器前，首先要实现Allocator以管理容器的内存。文中涉及到的函数标准一律以C11为准。 Allocator的要求 根据cppreference :: std::Allocator，一个最简单的Allocator应当具备如下成员。 类型 定义 value_type T pointer T* const_pointer const T* reference T&amp;amp; const_reference const T&amp;amp; difference_type std::ptrdiff_t size_type std::size_t 同时具备以下的成员函数 template&amp;lt; class U &amp;gt; struct rebind { typedef allocator&amp;lt;U&amp;gt; other; } Allocator() = default; Allocator(const Allocator&amp;amp; other); template &amp;lt;typename U&amp;gt; Allocator(const A...</description>
    </item>
    <item>
      <title>FakeSTL From Scratch  | C&#43;&#43; 从0开始写FakeSTL [目录]</title>
      <link>/2019/03/443/</link>
      <pubDate>Sun, 31 Mar 2019 00:00:00 +0000</pubDate>
      <guid>/2019/03/443/</guid><description>引言 我的项目地址 ： BlurryLight/PDSTL 目前处于边学边写（半抄半写）状态。 我的知识水平：cpp beginner STL 我先推荐一本《C++标准库 第二版》，第二版包含了C11的内容，对右值，shared_ptr,Lambda等内容都进行了详尽的阐述。这是一本介于字典(cppreference)和教学书籍（cpp primer），感觉更贴近于effective系列，包含了许多标准库的惯用法。 STL主要由六个部分组成，主要是allocator,container,algorithm,iterator,functors,adapters。和常见的OO编程范式差别较大，对容器操作主要通过algorithm + iterator + container的组合来完成。 如果你对我上面讲的六个组件比较陌生，建议买一本介绍标准库的书读。 需要的能力 基本的数据结构（红黑树，跳表这种少见的数据结构可以边学边写，本身写FakeSTL的最大收货也集中在数据结构这一块） 基本掌握C++模版 （最好能理解C++的类型推导原则，知道模版的引用折叠规则） 知道STL的大概实现 （至少要知...</description>
    </item>
    <item>
      <title>建立简单的带单元测试的CMake项目</title>
      <link>/2019/03/452/</link>
      <pubDate>Sun, 24 Mar 2019 00:00:00 +0000</pubDate>
      <guid>/2019/03/452/</guid><description>前言 Cmake是一个跨平台的C/C++项目组织管理工具，虽然许多IDE都有私有的项目管理工具，但是在现在2019年，各大IDE基本都支持了CMAKE的管理，所以如果有跨平台的需求，使用CMAKE管理是最方便的。 包含单元测试的简单工程 CMAKE支持gtest,cppunit这种单元测试框架，当然也可以使用断言自定义单元测试，我在一个小项目里用了一个纯c宏编写的小型单元测试 github/minunit，与Cmake的搭配很好。 一个典型的项目目录 │ .gitignore │ CMakeLists.txt ├─include │ xxxxx.h ├─src │ main.cpp ├─build │ ... └─test xxx_test.cpp CMakeLists.txt 其中test目录可以作为子项目，和主目录分开编译，避免main函数重复。 主目录的CMakeLists.txt CMake主要通过分析CMakeLists.txt所规定的条件来生成对应平台的Makefile，所以CMakeLists.txt需要手动编写。 cmake_minimum_required(VERSI...</description>
    </item>
    <item>
      <title>浅谈RAII和智能指针</title>
      <link>/2019/03/438/</link>
      <pubDate>Sat, 02 Mar 2019 00:00:00 +0000</pubDate>
      <guid>/2019/03/438/</guid><description>不管cpp有多少被诟病的地方，但是RAII是一个很伟大的发明。可以说不懂RAII，连C++的门槛都还没入。 《cpp primer》在第12章的动态内存中讲解了很多智能指针的使用，参考着另外一本国内的《C++泛型》的教材书上的代码，记个笔记。 RAII RAII(Resource Acquisition is Initialization)就是把资源（内存，文件，socket）等等和一个对象的生命周期绑定起来，当对象的生命周期到的时候，自动释放资源，避免手动new/delete管理内存。 在CPP的实现中，是讲裸指针以及指针的操作封装成一个类，通过类的生命周期管理来控制资源的释放。下面列出最简单的一个管理资源的模版 template &amp;lt;typename T&amp;gt; class My_ptr { private: T *_ptr; public: My_ptr(T *p):_ptr(p) { std::cout&amp;lt;&amp;lt;&amp;quot;Wrapped&amp;quot;&amp;lt;&amp;lt;std::endl; } T&amp;amp; operator*() { return *_ptr; } T*...</description>
    </item>
    <item>
      <title>从Thunar中在xfce-terminal打开VIM</title>
      <link>/2019/02/437/</link>
      <pubDate>Tue, 05 Feb 2019 00:00:00 +0000</pubDate>
      <guid>/2019/02/437/</guid><description>VIM在xfce的默认设置中,如果用图形化调用,则是默认打开Xterm来显示,Xterm显示效果比xfce-terminal效果差很多. 所以可以修改thunar调用vim的方式. 打开/usr/share/applications/vim.desktop 找到Exec行,改成 Exec=xfce4-terminal -e &amp;quot;vim %F&amp;quot; 然后把下一行的terminal从true改成false. </description>
    </item>
    <item>
      <title>CPP Exercises</title>
      <link>/2019/01/435/</link>
      <pubDate>Mon, 07 Jan 2019 00:00:00 +0000</pubDate>
      <guid>/2019/01/435/</guid><description>做一些CPP的练习。 不讲究奇技淫巧，不讲究运行速度。 以练习C++11的语法和强调代码的可读性为主要目标。 按照标准实现strlen,strcpy,strncpy,strcat,strncat,strcmp size_t strlen1(const char* str) { size_t tmp; while(*str++) tmp++; return tmp; } //baddress指针存储了目标数组开始的地址 char* strcpy1(char* dest,const char* src) { if((dest==NULL) ||(src==NULL)) return NULL; auto baddress = dest; while(*src!=&#39;\0&#39;) *dest++ = *src++; return baddress; } char* strncpy1(char* dest,const char* src,size_t count) { if((dest==NULL) ||(src==NULL)) return NULL; auto baddress = dest; ...</description>
    </item>
    <item>
      <title>C&#43;&#43; lambda初探</title>
      <link>/2018/01/434/</link>
      <pubDate>Wed, 24 Jan 2018 00:00:00 +0000</pubDate>
      <guid>/2018/01/434/</guid><description>lambda表达式主要用于一次性的函数，最常见的应用就是在remove_if,find_if这种需要predicator的函数中了。 其结构为 [函数对象参数] (函数参数) mutable或exception声明 -&amp;gt; 返回值类型 {函数体} 其中[]标志Lambda的开始，[]中可以增加一些参数。 [=]表示以拷贝的方式传递此lambda所在作用域的全部变量 [&amp;amp;]引用传递 [=,&amp;amp;value]其他变量全用值传递，value变量用引用传递 [this]可以传递this指针，可以在函数体中调用this-&amp;gt;something 可以用-&amp;gt;标明函数返回值变量，用法同C11标准相同，简单函数可以省略，lambda可以自动推断。 伪代码 std::list&amp;lt;int&amp;gt; lst = {1,2,3,4,5,6}; int value = 4; lst.remove_if([&amp;amp;value](const int&amp;amp; elem){return elem==value;}); lst.remove_if([](const int&amp;amp; elem...</description>
    </item>
    <item>
      <title>C&#43;&#43;模板函数的重载决议问题</title>
      <link>/2018/01/433/</link>
      <pubDate>Wed, 24 Jan 2018 00:00:00 +0000</pubDate>
      <guid>/2018/01/433/</guid><description>事情的起因是我在尝试实现std::list的过程中，我发现了其insert函数并不如我想象中的顺序工作。 伪代码如下 //construct 1 iterator insert( const_iterator pos, size_type n, const value_type &amp;amp;value ) //construct 2 template&amp;lt;typename InputIterator&amp;gt; iterator insert( const_iterator pos, InputIterator first, InputIterator last ) //test insert(pos,1,2); insert(pos,1.5,2); 第一句会调用第二个构造函数，而第二句话会调用第一个构造函数。 Google了一下，意外发现zhihu上有类似的讨论 C++ 重载匹配的一个问题？ - 知乎 https://www.zhihu.com/question/31491227 其问题出在，insert(pos,1,2)在第二个构造函数中，可以被构造成两个int，更加符合条件。为了避免...</description>
    </item>
    <item>
      <title>Qt使用预编译头PCH及多核编译加快编译</title>
      <link>/2017/09/427/</link>
      <pubDate>Sun, 03 Sep 2017 00:00:00 +0000</pubDate>
      <guid>/2017/09/427/</guid><description>最近在用Qt做一些小项目的时候，发现Qt的编译很慢，理论上来说不应该有这样的问题的。查了一下Qt的文档以及在知乎上的问题为什么 Qt Creator 的编译如此之慢？，调整了一些设置以后好多了。 问题主要是出在在没有设置的情况下每一次编译Qt都会编译所有文件，这是不应该的。 预编译头文件 在project.pro文件中，加入以下两行 CONFIG += c++11 precompile_header PRECOMPILED_HEADER = stable.h 在stable。h中加入头文件，在开发阶段可以直接加入QWidgets,QtGui等重量级头文件，此后编译中就不会再对这些头文件进行处理了。注意，stable.h文件不要被包含在项目的任意角落里面。 make启动多核编译 在项目——构建设置——构建步骤——make的参数中，加入-jX选项，其中X为CPU线程数+1，如我的I7是四核八线程，此处应该为-j9 注:make的选项仅适用于MinGw编译。 MSVC编译器可以在pro文件中加入（未测试） QMAKE_CXXFLAGS += /MP ...</description>
    </item>
    <item>
      <title>Qt5.8 Patch: Missing qtmultimediadefs.h</title>
      <link>/2017/08/425/</link>
      <pubDate>Tue, 22 Aug 2017 00:00:00 +0000</pubDate>
      <guid>/2017/08/425/</guid><description>详情请见： https://bugreports.qt.io/browse/QTBUG-58432 这是被列在Qt5.8的等级为P1的Bug report,原因是在某次merge中移除了这个qtmultimediadefs.h这个文件，很多头文件的声明被更名到qtmultimediaglobal.h，，并进行了一些其他的修改，但是在打包成安装包的过程中出现了一些问题，加粗部分的变动并没有打进安装包，导致所有引用了qtmultimedia功能的程序均无法编译。 在Linux中可以利用包管理器解决 这是Debian的软件包信息 https://packages.debian.org/stretch/qtmultimedia5-dev 在Windows中就要麻烦一点了 首先需要找到include的目录 我的目录在E:\Qt\Qt5.8.0\5.8\mingw53_32\include\QtMultimedia中，找到qtmultimediadefs.h文件。 将 https://codereview.qt-project.org/#/c/184100/2/src/multimedia/qtm...</description>
    </item>
    <item>
      <title>Debian利用Squid3搭建Pac代理，端口25</title>
      <link>/2017/08/423/</link>
      <pubDate>Sat, 12 Aug 2017 00:00:00 +0000</pubDate>
      <guid>/2017/08/423/</guid><description>虽然平时都是用shadowsocks，但是偶尔在没有条件的时候也会用到Pac来救急（通常是在我的chromebook上）,在网上找PAC不是个办法（也不安全），于是就萌生了搭建个PAC的想法。 顺着代码来，一行行复制 Debian sudo apt install squid3 curl www.cutinlove.com/squid.conf &amp;gt; /etc/squid3/squid.conf mkdir -p /var/cache/squid chmod -R 777 /var/cache/squid service squid3 stop squid3 -z service squid3 restart 这样squid3的服务就搭建完了，接下来只需要配置上pac就好了 可以直接下载到本地,将第一行的地址填上服务器的IP https://raw.githubusercontent.com/rptec/squid-PAC/master/1.pac 也可以在服务端的网站可访问的目录配置好PAC，直接填上网站路径的PAC就可以了 如果不能正常工作，请在防火墙将25端口放行，或者检查V...</description>
    </item>
    <item>
      <title>Git push 失败，提示&#39;receive.denyCurrentBranch&#39; configuration variable to &#39;refuse&#39;.</title>
      <link>/2017/08/420/</link>
      <pubDate>Tue, 01 Aug 2017 00:00:00 +0000</pubDate>
      <guid>/2017/08/420/</guid><description> 在VPS上新建了一个Git仓库，本地开发测试完了以后直接通过Git Push上去，不用SCP传了 但是却提示remote: error: &#39;receive.denyCurrentBranch&#39; configuration variable to &#39;refuse&#39;. 查了一下，这是Git新建了仓库以后默认不允许push操作 可以使用 git config receive.denyCurrentBranch=ignore 即可解决 </description>
    </item>
    <item>
      <title>Debian 9 stretch 解决网易云音乐(NetEase-music)的依赖问题</title>
      <link>/2017/04/415/</link>
      <pubDate>Sun, 09 Apr 2017 00:00:00 +0000</pubDate>
      <guid>/2017/04/415/</guid><description> 明明记得stretch已经冻结了，于是无聊的时候直接从jessie升级上了stretch,内核升级到4.9带来了一堆问题，但是最麻烦的还是一些旧的软件包被废弃了，导致一些软件的依赖满足不了。 网易云音乐 提供多种版本的客户端下载，我一开始记得用Deepin的版本比较好，这次也沿用了deepin的版本。Ubuntu 16.04的没测试，但是据说也可以通过同样的方法安装上。 1.安装软件包 sudo apt isntall -f 一般用于解决依赖问题，不过这次由于依赖被废弃的原因所以就没办法了。先用dpkg -i安装上下载的软件包，然后记录下缺少的依赖，再用apt remove 卸载掉网易云音乐，最后用apt install安装刚才缺少的依赖。最后会卡在libqt5libqgtk2这个依赖上（如果用ubuntu的deb的话应该还会缺失libfontconfig1） 2.手动解决依赖 没有可用的软件包 libqt5libqgtk2，但是它被其它的软件包引用了。 这可能意味着这个缺失的软件包可能已被废弃， 或者只能在其他发布源中找到 然而下列软件包会取代它： qt5-style-plugin...</description>
    </item>
    <item>
      <title>造轮子能走多远？Ubuntu最终放弃了Unity</title>
      <link>/2017/04/414/</link>
      <pubDate>Sat, 08 Apr 2017 00:00:00 +0000</pubDate>
      <guid>/2017/04/414/</guid><description> 长久以来，Ubuntu一直是我讨厌的发行版。虽然我一向不参与发行版之间的无止境的争吵，但是Ubuntu是例外。“分裂”，“吸血”，“商业化”等一直是我对它的固有印象。但是如今这么一个发行版最终在造轮子的路上走到了头，碰的头破血流，竟还是忍不住一丝感慨。 unity8 Unity8是C社投入了非常大力气的一个项目，目的是为了在手机，平板，PC以及甚至包括电视等设备上达成统一的操作体验。凡是经历过跨平台GUI开发的都知道这是一个多么宏伟的工程，何况是一个操作系统的GUI，本来就是一个非常大的工程量了。Youtube上有各种各样的演示，包括Ubuntu率先支持争议比较大的snap软件包格式，都是为了这一个宏伟的目标。准确地说，在两年前或许这个目标看起来还不错，但是如今平板电脑的出货量血崩，手机基本被安卓和IOS绑定的情况下(即使是微软接连打出了windows10 mobile,uwp应用等王牌后，也未捞得好处)，这个计划基本上算是空中楼阁了。 创新，还是分裂 In the community, our efforts were seen fragmentation not innovati...</description>
    </item>
    <item>
      <title>Crouton安装Debian 9&#43;xfce4后出现触摸板不能轻触点击的问题</title>
      <link>/2017/04/413/</link>
      <pubDate>Mon, 03 Apr 2017 00:00:00 +0000</pubDate>
      <guid>/2017/04/413/</guid><description>这个问题蛮少见的，因为完整安装Debian的话应该是会带这个驱动，但是利用Crouton安装出来的Debian没有，所以导致我的Chromebook在Linux下触摸板工作的不太正常。 sudo apt install xserver-xorg-input-synaptics 然后在xfce4的设置—鼠标与触摸板中会多出触摸板的选项。 </description>
    </item>
    <item>
      <title>RSA与AES加密杂谈——兼Python实现</title>
      <link>/2016/05/rsa%E4%B8%8Eaes%E5%8A%A0%E5%AF%86%E6%9D%82%E8%B0%88-%E5%85%BCpython%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Sun, 22 May 2016 00:00:00 +0000</pubDate>
      <guid>/2016/05/rsa%E4%B8%8Eaes%E5%8A%A0%E5%AF%86%E6%9D%82%E8%B0%88-%E5%85%BCpython%E5%AE%9E%E7%8E%B0/</guid><description>本文代码部分大量参考于http://www.jianshu.com/p/6a39610122fa 使用的Pycryto库的API：https://www.dlitz.net/software/pycrypto/apidoc/ 环境要求:Python 2.7,Pycryto库 话外言：有那么长一段时间一直没写博客了，大概是由于变懒了和近期在学JavaScript。作为一个初入一门语言的小菜鸟着实不敢在JS领域发表什么言论，尽管在Python上也是菜鸟一个但至少还能写一写东西来娱乐自己。在晃悠了接近一月有余，总算决定不写不行了。恰好最近看了一点密码学的东西，就决定来写一写加密的杂谈。 预警：本文可能充满理论上的错误和各路神论以及私人情感。 一。为什么要加密 这个问题太难回答了，大概和为什么你需要一个保险柜一样。 加密可以用于保证安全性，但是其它一些技术在保障通信安全方面仍然是必须的，尤其是关于数据完整性和信息验证。例如，信息验证码（MAC）或者数字签名。另一方面的考虑是为了应付流量分析。 尤其是在网络环境比较恶劣的国内，加密的应用似乎无处不在。比如本博客的小绿锁，代表本网站传输的内容是加密...</description>
    </item>
    <item>
      <title>Python2生成简单验证码</title>
      <link>/2016/04/python2%E7%94%9F%E6%88%90%E7%AE%80%E5%8D%95%E9%AA%8C%E8%AF%81%E7%A0%81/</link>
      <pubDate>Sat, 23 Apr 2016 00:00:00 +0000</pubDate>
      <guid>/2016/04/python2%E7%94%9F%E6%88%90%E7%AE%80%E5%8D%95%E9%AA%8C%E8%AF%81%E7%A0%81/</guid><description>使用python2.7环境编写，原因是Python2.7内置了PIL库，而Python3需要手动安装Pillow库，且Pillow还依赖几个其他的库。功能很简单，所以就不想折腾了。 生成验证码分为下列步骤： 生成随机字符串 用PIL的ImageDraw生成空白画布和背景 画出字符 填充噪点 用滤镜对图片进行模糊处理 #!/usr/bin/env python # encoding: utf-8 from PIL import Image, ImageDraw, ImageFont, ImageFilter import random import string def get_chars(): char_list=[random.choice(string.ascii_letters) for i in range(4)] return (char_list) def get_color(): return(random.randint(30,106),random.randint(30,100),random.randint(30,100)) def create_pic_code(...</description>
    </item>
    <item>
      <title>解决Debian和windows相差八小时的问题</title>
      <link>/2016/04/%E8%A7%A3%E5%86%B3debian%E5%92%8Cwindows%E7%9B%B8%E5%B7%AE%E5%85%AB%E5%B0%8F%E6%97%B6%E7%9A%84%E9%97%AE%E9%A2%98/</link>
      <pubDate>Sun, 10 Apr 2016 00:00:00 +0000</pubDate>
      <guid>/2016/04/%E8%A7%A3%E5%86%B3debian%E5%92%8Cwindows%E7%9B%B8%E5%B7%AE%E5%85%AB%E5%B0%8F%E6%97%B6%E7%9A%84%E9%97%AE%E9%A2%98/</guid><description>windows在查询时间时，采用读取CMOS时间作为标准时间,而Linux如果采用UTC（世界协调时）时间，则会在读取CMOS时间后，按照时区（北京时间为东八区）来计算时间，所以如果不小心在Linux中启用了UTC时间，就总是会和windows相差八小时。 解决办法如下： 在Debian7以后，关于时间的配置文件从/etc/default/rcS中移到了/etc/adjtime。 vim /etc/adjtime 将UTC替换为LOCAL 如果没有adjtime这个文件 sudo hwclock --adjust#生成文件 完成以后， sudo hwclock --hctosys 将时间写入CMOS ...</description>
    </item>
    <item>
      <title>关于chromebook在安装crouton后没有声音的解决办法</title>
      <link>/2016/03/%E5%85%B3%E4%BA%8Echromebook%E5%9C%A8%E5%AE%89%E8%A3%85crouton%E5%90%8E%E6%B2%A1%E6%9C%89%E5%A3%B0%E9%9F%B3%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
      <pubDate>Fri, 25 Mar 2016 00:00:00 +0000</pubDate>
      <guid>/2016/03/%E5%85%B3%E4%BA%8Echromebook%E5%9C%A8%E5%AE%89%E8%A3%85crouton%E5%90%8E%E6%B2%A1%E6%9C%89%E5%A3%B0%E9%9F%B3%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid><description> 最近去弄了一台chromebook回来，13寸1080p屏幕，8小时以上的超级续航，不到200刀的价格，Linux原生支持，这价格也没有谁可以比了. 用Crouton安装了熟悉的Debian后，遇到了没有声音的问题，alsa也无法正常工作，初步判断是驱动出了问题 在Crouton的issue发现了和我同样问题的人，解决办法如下： sudo sh -e ~/Downloads/crouton -u -n jessie ＃升级chroot到最新版 友情提示：这里是需要翻墙的。这里在升级的过程中需要下载一个声卡驱动，而这个声卡驱动需要经过谷歌的服务器。可以先用ChromeOs连上VPN,或者使用修改过的Crouton，或者手动下载那个驱动，然后修改Crouton脚本直接指定位置。 附上备注： 备份： sudo edit-chroot -b chrootname 注意：打包文件***.tar.gz放在Downloads文件夹，但是一定要把它拷贝到别处或者上传云盘，否则万一chrome OS崩溃，这个文件夹的东西就全没了。 恢复: sudo sh -e ~/Downloads/crouton ...</description>
    </item>
    <item>
      <title>关于Py2exe对Pyqt5.uic报错的解决</title>
      <link>/2016/03/%E5%85%B3%E4%BA%8Epy2exe%E5%AF%B9pyqt5%E6%89%93%E5%8C%85%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E5%9D%91/</link>
      <pubDate>Sat, 05 Mar 2016 00:00:00 +0000</pubDate>
      <guid>/2016/03/%E5%85%B3%E4%BA%8Epy2exe%E5%AF%B9pyqt5%E6%89%93%E5%8C%85%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E5%9D%91/</guid><description>先上代码 from distutils.core import setup import py2exe import sys #this allows to run it with a simple double click. sys.argv.append(&#39;py2exe&#39;) py2exe_options = { &amp;quot;includes&amp;quot;: [&amp;quot;sip&amp;quot;,&#39;mainwindow.h&#39;], #&amp;quot;excludes&amp;quot;:[&#39;PyQt5.uic&#39;], &amp;quot;dll_excludes&amp;quot;: [&amp;quot;MSVCP90.dll&amp;quot;,],#排除此文件 &amp;quot;compressed&amp;quot;: 1, &amp;quot;optimize&amp;quot;: 2, &amp;quot;ascii&amp;quot;: 0, &amp;quot;bundle_files&amp;quot;: 1, } setup( name = &#39;sample&#39;, version = &#39;1.0&#39;, windows = [&#39;gui.py&#39;], data_files=[(&amp;quot;&amp;...</description>
    </item>
    <item>
      <title>Python利用Qtcreator和Pyqt快速创建GUI</title>
      <link>/2016/02/python-%E5%88%A9%E7%94%A8qtcreator%E5%92%8Cpyqt%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BAgui/</link>
      <pubDate>Sun, 28 Feb 2016 00:00:00 +0000</pubDate>
      <guid>/2016/02/python-%E5%88%A9%E7%94%A8qtcreator%E5%92%8Cpyqt%E5%BF%AB%E9%80%9F%E5%88%9B%E5%BB%BAgui/</guid><description>利用qtcreator创建简单的界面很容易，生成一个xml格式化的ui文件，再在python里面载入这个ui文件即可快速编写GUI。关于按钮点击等编写qtcreator同样可以完成。 输入以下代码: #!/usr/bin/env python3 # encoding: utf-8 from PyQt5 import uic,QtWidgets import sys #Enter file path qtCreatorFile = &amp;quot;dialog.ui&amp;quot; Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile) class build(Ui_MainWindow,QtWidgets.QMainWindow): def __init__(self,parent = None): QtWidgets.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.setupUi(self) def start(): app = QtWidgets.QApp...</description>
    </item>
    <item>
      <title>Debian开放AP热点</title>
      <link>/2015/11/debian%E5%BC%80%E6%94%BEap%E7%83%AD%E7%82%B9/</link>
      <pubDate>Thu, 19 Nov 2015 00:00:00 +0000</pubDate>
      <guid>/2015/11/debian%E5%BC%80%E6%94%BEap%E7%83%AD%E7%82%B9/</guid><description>4.Debian开放AP热点 Ubuntu建立AP热点 由于Ubuntu和Debian那暧昧的关系，我天真的以为能够照搬这个教程。结果出现了卡在Starting wifi这个步骤。 分析原因有： 1.Debian的源里面没有ap-hotspot的包，而Ubuntu有。我在Debian上是在百度搜索了一个DEB安装上的，所有可能是版本冲突。 2.AP-hotspot在Debian上有兼容问题。 3.由于我在Ubuntu下无线网卡驱动是修改了设置，而在Debian中是编译安装了新的网卡驱动，可能存在AP-hotspot和网卡驱动的兼容问题。 在Debian下，Gnome自带的热点管理器默认的是AP模式，不再是Ubuntu下的AD-hoc模式，所以用gnome的热点管理器也可以正常工作的。 不过有以下问题： 1.热点采用WEP加密，远不如WPA2方式安全。 2.密码随机，不能自己更改。 3.没有提示，设备断开或者连入，或者管理设备都没有。 4.感觉信号比用hotspod建立的差。 所以在将就了几天后决定还是回到AP-hostpod。（链接到github） 然而： While it may s...</description>
    </item>
    <item>
      <title>man?不，你需要cheat</title>
      <link>/2015/10/man%E4%B8%8D-%E4%BD%A0%E9%9C%80%E8%A6%81cheat/</link>
      <pubDate>Fri, 30 Oct 2015 00:00:00 +0000</pubDate>
      <guid>/2015/10/man%E4%B8%8D-%E4%BD%A0%E9%9C%80%E8%A6%81cheat/</guid><description>Linux界有一个经典的俗语，不懂就问男人。 即man命令。 但是，man实在是太冗长了。 比如 man tar TAR(1) BSD General Commands Manual TAR(1) NAME tar — The GNU version of the tar archiving utility SYNOPSIS tar [-] A --catenate --concatenate | c --create | d --diff --compare | --delete | r --append | t --list | --test-label | u --update | x --extract --get [options] [pathname ...] DESCRIPTION Tar stores and extracts files from a tape or disk archive. The first argument to tar should be a function; either one of the letters Acdrtux, or on...</description>
    </item>
  </channel>
</rss>
