木匣子

Web/Game/Programming/Life etc.

在 macOS 上使用 eGPU 的体验

前阵子在处理动森二维码的服装预览模型的时候,玩了一下 Blender。于是又勾起了我小时候玩 3D Max 的回忆。于是好想给手上这台 Macbook Pro 2016 配个显卡,深入学习一下 3D 建模。

于是在网络上搜索了好一阵,发现可以使用 eGPU(外置显卡坞)将独立显卡接到 Macbook 上使用。苹果的官网有一篇文章介绍了目前各个版本的系统支持的 eGPU 以及显卡的情况。

仔细看了一圈下来,发现由于历史原因近几年新出的 Macbook 并不支持 nvidia 显卡。看来是没法享受最近要发布的 RTX 3080 系列,只能选择 AMD 显卡。经过简单的预算,最终选择了 Razer Core XAMD RX 5700 XT 来体验一番。

经过一个多星期的等待,收齐了两个包裹,兴奋地开箱安装。Razer Core X 比想像中的笨重,体积堪比一个小型机箱。但是非常简单易用,全程无需镙丝刀就把显卡装上。内置 650W 的电源,可以给大号显卡和笔记本同时供电。使用也很方便,只需要将 USB-C 线接到 Macbook Pro 的雷电接口就行了。后悔没有加点钱买带 4 个 USB 的 Razer Core X Chroma,这样还可以省去使用另外的 USB Hub。苹果对 AMD 显卡的支持也是即插即用,不需要再安装额外的驱动,相当省心。

但是,后面的体验但没有那么愉快。下载安装了最新的 Blender 2.90,然后一路摸索到 Edit > Preferences… > System > Cycles Render Devices 才发现 OpenCL 面板竟然找不到显卡,原因是 macOS 为了推广自家的 Metal 标准,在 10.14 移除了对 OpenCL 的支持。这样的话,这 eGPU 对 blender for macOS 岂不是毫无用武之地了,T_T 哭。

经过一翻研究,发现 AMD 官方有个叫 Radeon ProRender 的产品。它提供了 Blender 插件并实现了 Metal 接口的渲染加速。看起来挺不错的,于是下载来试了试,说不定能曲线救图。

安装完插件后,需要在 Blender 中切换到 ProRender 渲染引擎。于是我尝试渲染了一些 Demo,结果 Blender 闪退到让人弃坑。不过我还是坚持做完了一个 tutorial。第二天想想,这闪退应该有什么解决方案。于是终于在 AMD 的论坛让我找到了解决方案:将 Noise Threshold 优化关闭即可(设置为 0)。

得以使用 ProRender 继续学习 Blender。不过在使用的过程中发现 ProRender 并不支持全部 Blender 的渲染结点,也不能访问一些模型数据。导致渲染出来的效果往往跟教程中有些许差异。还好我不是很在意这些,多数渲染问题都可以在技术上变通。好再 eGPU 是工作起来了。

游戏引擎的渲染引擎是否会有类似的渲染的差异,回头再研究。另外我试了一些支持 macOS 的游戏,例如 Tomb Rider、StarCraft II,特效全开问题不大。但是有些游戏几乎跑不起来,例如 Fortnite,直接卡死在大厅(应该是 CPU 没跟上的问题)[1]。大部分大作只支持 Windows,macOS 上几乎没啥可玩。

我还试在了 Windows 机器上使用这个 eGPU。Blender 依然无法在 Windows 使用 OpenCL 加速,论坛上很多使用 AMD 显卡的人都有这个问题。看来要搞 3D Modeling 还是老老实实使用 Windows + Nvidia 显卡比较省心。最近有两个新闻:1)Nvidia 收购了 ARM。2)Apple 未来将使用 ARM 作为新机器的内核。不知道未来 Apple 和 Nvidia 是否会重归于好,重新支持上 Nvidia 的显卡呢?让我们拟目以待。


  1. 后来又试了几次,测试结果是每次退出游戏并重新进行游戏后都会有大概10多分钟的 MetalCompileService 编译时间,之后重复游戏加载比较快。 ↩︎

给 Apollo-Link 打补丁 II

继上回给 Apollo Link 打补丁过去了几个月,本想翻看一下之前的那个 Pull Request 是否已经被 Merge,结果没想到原来的 Apollo Link 库已经被弃用了。新版的 Apollo Link 被重构后集成到了 @apollo/client 包中。不过并没有修复前面 PR 提到的问题。更糟糕的是之前使用的打补丁的方式无法起作用了。新的 selectHttpOptionsAndBody() 函数被专门放置到一个独立的 module 文件中。并且在提供的 cjs bundle 里,它直接被 createHttpLink 函数引用。所以之前的替换方法没办法在函数被引用前进行「狸猫换太子」。

那么是否有其它成本比较低的方式进行补救?于是我简单的搜索了一下 Webpack 的官方文档,很快找到了一个叫 NormalModuleReplacementPlugin 的内置插件。使用它可以非常方便地替换指定的模块文件。

new webpack.NormalModuleReplacementPlugin(
  resourceRegExp,
  newResource
);

于是我们将 @apollo/client 包中的 selectHttpOptionsAndBody.js 复制一份,并作以下修改:

  1. 在头部导入 stripIgnoredCharacters ;
  2. 在生成 body.query 的地方使用 stripIgnoredCharacters 压缩 graphql 查询;

Diff 结果如下:

3d2
< import { stripIgnoredCharacters } from "graphql";
38c37
<         body.query = stripIgnoredCharacters(print(query));
---
>         body.query = print(query);

将修改后的文件放到 /path/to/project/patch/selectHttpOptionsAndBody.js 然后在 Webpack 配置中增加一个插件:

plugins: {[
    ..., 
    new webpack.NormalModuleReplacementPlugin(
      /\/node_modules\/@apollo\/client\/link\/http\/selectHttpOptionsAndBody\.js/,
      path.resolve(process.cwd(), './patch/selectHttpOptionsAndBody.js'),
    ),
]},

重新运行 Webpack 构建,但发现并未能如愿将补丁打上。检查 Sourcemap 发现 Webpack 引入的是 @apollo/client 打包好的 commonjs 模块(*.cjs.js)而非实际的源码。

经过一番排查,发现 @apollo/client/link/httppackage.json 描述如下:

{
  "name": "@apollo/client/link/http",
  "main": "http.cjs.js",
  "module": "index.js",
  "types": "index.d.ts"
}

可见当前 Webpack 使用的是 main 字段的入口,而非 module 字段的入口。根据 Webpack 的 resolve 配置说明,我们可以使用 #resolvemainfields 来修改入口顺序。于是在 Webpack 配置中找到 mainFields 并作如下调整:

resolve: {
    mainFields: ['module', 'browser', 'main', 'jsnext:main'],
    ...,
}

调整的依据:

  1. 优先使用 es module,虽然会稍微增加一些构建时间,但可以利用 tree-shaking 减小包的体积;
  2. 对于不支持 es module 的库,尝试使用 browser 或 main 入口,即预编译的浏览器包或者 commonjs 的入口;

再次构建,检查 Sourcemap 后发现 selectHttpOptionsAndBody.js 已经变成我们打了补丁的版本了。

最后为了保证依赖的一致性,将 @apollo/client 的版本锁定。下次升级时需要检查补丁是否需要修订。

动森二维码生成器:服装类(后篇)

前篇写到动森二维码生成器的二维码划分规则以及预览模型的 UV 贴图。根据我的设想,接下来我要在 Aseprite 画板上将这个模型渲染出来,并将用户绘制的图案通过 UV 贴图的方式绘制到模型上。而目前我所拥有的东西只有:调整过 UV 贴图坐标的 6 个衣服模型、在 Aseprite 读取和写入像素的 Lua API,还有记忆中模糊的图形学知识。本文简单记录一下我当时的思路,以及遇到的一些细节问题,如果对实现有兴趣,可以直接访问 Github 上的该项目

动森二维码生成器:服装类(前篇)

在开发动森二维码生成器的时候,对于服装类设计的生成,一开始我并没有很好的想法。游戏中对贴图的是分块绘制的,并且有一个动态模型可以展示衣服绘制的效果。对比同类工具 Animal Crossing Pattern Tool(下称 ACPT),他们也提供了 3D 模型用于展示贴图效果。但要如何在 Aseprite 中实现展示 3D 模型,我并不是很有把握,毕竟这是一款用于绘制 2D 像素图的工具。另外如何给贴图分块,也需要深入分析。本文先记录一下贴图的划分,之后再写一篇分析 3D 模型的渲染。

动森二维码生成器 for Aseprite

最近翻到一本不错的像素书:由 Nostarch Press 出版的 Make Your Own Pixel Art。Nostrach Press 专门出版极客类的书籍,里面有很多看到标题就有兴趣读下去的书,非常适合我。

而这本 Make Your Own Pixel Art 的作者 Janeifer 拥有 30 年的像素画经验,在书中非常系统地讲解了像素画入门的各个方面,还提供了不错的练习素材。并且书中使用的编辑器正是我非常喜欢的 Aseprite。正好最近时间比较多,就又拿起来画板和鼠标瞎画点东西。

而更让我惊喜的是,这款买了很久的小而专业的像素绘画软件,居然支持自己编写插件。从 Aseprite v1.2.10-beta2 版本开始,作者引入了 Lua 语言来扩展编辑器,允许用户编写一些定制化的功能

于是我开始琢磨着用它做点什么。正好最近在玩动物森友会,游戏里允许玩家自己创做像素风格的图案用来装饰各种物品、衣服、地面。另外,新版的 Animal Crossing: New Horizons 可以通过手机 App 扫描早期版本的 Animal Crossing: New Leaf (下称 ACNL) 玩家绘制的图案的二维码,导入这些现存的图案。

但要知道,不像 3DS 上的 ACNL 可以使用双屏 + 画笔来做画。在 Nintendo Switch 上使用手柄做画有多难受,可想而知。虽然基础画板只有 32 x 32 像素,但是切换颜色、工具等操作,实再不如在电脑上作画来得舒服。我想,如果能直接在 Aseprite 这种专业软件中作画,然后直接生成二维码,再传送到游戏里使用,岂不是非常方便。

动森图鉴+开发笔记

这两个月受到疫情影响,从四月中开始在家办公,四月底开始降薪,并且到五月初的这两个多星期进入了做三休四的节奏,每周强制休二天年假,不知何时是头。

由于在家工作,有更多的时间做自己想做的事。为了给自己减压,于是入了动物之森 Switch 版开始捡树枝。玩了几个星期发现其实自己并不是很喜欢联机,硬是把一个休闲社交游戏玩成了单机游戏。

不过动森的探索性还是挺强的,光是收集小动物就可以折腾很久。而且游戏时间与现实同步,每过一个月就会有新的物种出现。有时候需要查一下攻略才知道最近新出了啥动物。

在网上浏览这些攻略的时候就想,为啥大家都把数据做成表格的形式,非常不直观,不如做成像游戏中的图鉴,还可以加上自己的改进。例如其它网站上对鱼的影子的描述,只是用文字写着 “Smallest (1)”、“Small (2)”…“Latest (6)” 这样让人摸不着头脑的表述。

给 Apollo-Link 打补丁

公司最近上线了一个主要由我负责的 React 项目。Frontend Team Lead 希望把 Graphql 请求由 POST 方式改成 GET 方式,这样就可以借助 CDN 进行缓存。

由于项目中使用的是 Apollo Client,于是只需要简单地对 ApolloHttpLink 加上一条配置即可:

const client = new ApolloClient({
  link: ApolloLink.from([
    // ...
    createHttpLink({
      // ...
      useGETForQueries: true,
    }),
  ]),
  cache: new InMemoryCache(),
});

export default client;

不过我检查了一下项目中的一些 query 定义。有些 query 还是挺大的,其中最长的那个请求有 2288 个字符。我印象中 Internet Explorer 似乎有最长 URL 限制,于是下载了个微软官网的 IE11 虚拟机,随手做了一下测试。

「攻略」JetBrains Quest S1E3

书接上回,续 JetBrains Quest S1E2 之后,3月13日 JetBrains Quest 发布了本次最后一个挑战。因为感冒的原因,休息了一天,在周六深夜才发现题目已经出来了,又精神了起来。迷面如下:

Last Quest #JetBrainsQuest ⚔
SGF2ZSB5b3Ugc2VlbiB0aGUgcG9zdCBvbiBvdXIgSW5zdGFncmFtIGFjY291bnQ/
剧透警告!!!本文记录了挑战 III 的攻略过程。如果有兴趣自行通关,请暂时不要往下读!!!

「攻略」JetBrains Quest S1E2

书接上回,续 JetBrains Quest S1E1 之后,@JetBrains 发布了挑战第二弹。废话少说,来看迷面:

Time for the next #JetBrainsQuest!
.spleh A+lrtC/dmC .thgis fo tuo si ti semitemos ,etihw si txet nehw sa drah kooL .tseretni wohs dluohs uoy ecalp a si ,dessecorp si xat hctuD erehw esac ehT .sedih tseuq fo txen eht erehw si ,deificeps era segaugnal cificeps-niamod tcudorp ehT
剧透警告!!!本文记录了挑战 II 的攻略过程。如果有兴趣自行通关,请暂时不要往下读!!!

「攻略」JetBrains Quest S1E1

换工作后,新的公司没有提供 Jetbrains IDE 的包年订阅。在这四个多月里,秉承入乡随俗的习惯,我跟新团队一起用 Visual Studio Code 进行项目开发。一开始非常不顺手,后来安装了各种插件并慢慢适应,勉强还能工作。虽然 VSC 也是可以将就的,但就是没有 Webstorm/Phpstorm[1] 来得爽。

Jetbrains 的 IDE 功能强大,就连内置的版本控制都比其它独立的 App 要强大得多。为了配合 VSC,我还得挑选其它的 Git 客户端来协作,先后用了 Source TreeGit KrakenFork

  • Source Tree - 免费,但是 Diff / Merge 功能不够强大,没有 fast-forward 之类的功能。
  • Git Kraken - 非常强大,前期免费,但是后来改成个人版 30 天试用,到期后开始对私有仓库收费了。
  • Fork - 免费,小清新。比 Source Tree 易用,有同分支 fast-forward 功能,但不能跨分支。

折腾了一圈,到最后还打算自费回到 JetBrains IDE 的怀抱。

来得早不如来得巧,3月9日,@JetBrains 发布了第一个挑战信,内容如下:

JetBrains Quest begins… #JetBrainsQuest
48 61 76 65 20 79 6f 75 20 73 65 65 6e 20 74 68 65 20 73 6f 75 72 63 65 20 63 6f 64 65 20 6f 66 20 74 68 65 20 4a 65 74 42 72 61 69 6e 73 20 77 65 62 73 69 74 65 3f

作为解迷爱好者,一下就精神起来了。

剧透警告!!!本文记录了挑战 I 的攻略过程。如果有兴趣自行通关,请暂时不要往下读!!!