《#诗盗#·射飞丝阿屉》:文十初上射飞丝,挨踢更迭,阿屉不减风采。八点一,七四八,八年等待五年吃灰,如今一刷还是牛逼。
注解
Surface RT 刷 Windows 10 Arm32 版。改编自霹雳角色疏楼龙宿的诗号:
华阳初上鸿门红,疏楼更迭,龙麟不减风采;
紫金箫,白玉琴,宫灯夜明昙华正盛,共饮逍遥一世悠然。
刷完玩玩当然是继续吃灰了……毕竟性能不行!
《#诗盗#·射飞丝阿屉》:文十初上射飞丝,挨踢更迭,阿屉不减风采。八点一,七四八,八年等待五年吃灰,如今一刷还是牛逼。
Surface RT 刷 Windows 10 Arm32 版。改编自霹雳角色疏楼龙宿的诗号:
华阳初上鸿门红,疏楼更迭,龙麟不减风采;
紫金箫,白玉琴,宫灯夜明昙华正盛,共饮逍遥一世悠然。
刷完玩玩当然是继续吃灰了……毕竟性能不行!
《#诗盗#·去快恋》:前年今日如梦游,神奇八哥无止休。神奇不知何处去,八哥依旧笑春秋。
纪念在区块链行业工作过!改编自唐代崔护的《题都城南庄》:
去年今日此门中,人面桃花相映红。
人面不知何处去,桃花依旧笑春风。
VS2019 Makefile 型工程使用 Clang 编译,要同时支持 x86 和 x64 平台。
找不到 Clang 参数可以指定目标平台,如果您知道请不吝赐教。(github 账号:UMU618)
同时安装这两个平台的 Clang。x86 的由 VS2019 自带,在 VS 中被表示为 "$(ClangAnalysisToolsPath)\clang.exe"
,宏展开后为:
1 | $ &"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\Llvm\bin\clang.exe" -v |
x64 是另外安装的,安装时选择把目录注册到 PATH:
1 | $ clang -v |
结论:只需在 Win32 的平台设置使用 "$(ClangAnalysisToolsPath)\clang.exe"
,而 x64 则直接使用 clang
(如果安装时忘记注册到 PATH,需要手动添加)。
代码:src\thread\windows\SDL_syssem.c
别名:SDL_sem
1 | typedef struct SDL_semaphore SDL_sem; |
基于 WinAPI 匿名 Semaphore 封装。MaximumCount 硬编码为 32 * 1024。
1 | struct SDL_semaphore |
代码:src\thread\windows\SDL_sysmutex.c
基于 WinAPI CriticalSection 封装。SpinCount 硬编码为 2000,即在多处理器系统上,如果无法立刻进入临界区,则会自旋最多 2000 次,然后等待 CriticalSection 内部关联的信号量。只要在自旋过程中其它线程退出临界区,则无需进入等待状态。这么做是提高效率,自旋时当前线程还占着 CPU,如果进入等待状态,就是交出 CPU 时间片了,而 CPU 调度是个消耗型操作。
1 | struct SDL_mutex |
代码:src\thread\windows\SDL_syscond.c
基于 SDL_mutex 和 SDL_sem 封装。
1 | struct SDL_cond |
SDL_CondWaitTimeout 实现较长,本文忽略。重点是:为了避免死锁,它进入等待前,会先解锁第二个参数 mutex。如果不这么做,其它线程也要 Lock 这个 mutex 就会发生死锁。
以下代码是典型用法,线程 A 先进入临界区后,SDL_CondWait(内部调用 SDL_CondWaitTimeout)会调用 SDL_UnlockMutex(lock);
使得线程 B 可以进入临界区调用 SDL_CondSignal(cond);
。
1 | // Typical use |
代码:src\atomic\SDL_atomic.c
基于 _Interlocked API 封装。此类原子操作一般底层实现都是相应平台的汇编指令(比如 x86 平台是 lock cmpxchg 之类),但在不同平台下会有不同的封装集,所以 SDL_atomic.c 里有很多平台相关的宏判断。
代码:src\atomic\SDL_atomic.h
内存屏障。参考文章:Acquire and Release Semantics。
在 Windows x86 环境下等价于 _ReadWriteBarrier:
1 | void _ReadWriteBarrier(void); |
Acquire semantics is a property that can only apply to operations that read from shared memory, whether they are read-modify-write operations or plain loads. The operation is then considered a read-acquire. Acquire semantics prevent memory reordering of the read-acquire with any read or write operation that follows it in program order.
Release semantics is a property that can only apply to operations that write to shared memory, whether they are read-modify-write operations or plain stores. The operation is then considered a write-release. Release semantics prevent memory reordering of the write-release with any read or write operation that precedes it in program order.
生硬的翻译如下:
获取语义是一个属性,它只能应用于从共享内存读取的操作,无论是“读取-修改-写入”操作还是普通加载。该操作将被视为“读取获取”。获取语义可防止“读取获取”和它之后的读取或写入操作发生内存重新排序。
释放语义是一个属性,它只能应用于写入共享内存的操作,无论是“读取-修改-写入”操作还是普通存储。该操作将被视为“写入释放”。释放语义可防止“写入释放”和它之前,它之前的任何读取或写入操作发生内存重新排序。
以 x86 内存模型为例说明:
因为 store-load 可以被重排,所以 x86 不是顺序一致。但是其他三种读写顺序不能被重排,所以 x86 是 acquire/release 语义。
aquire 语义:load 之后的读写操作无法被重排至 load 之前。即 load-load, load-store 不能被重排。
release 语义:store 之前的读写操作无法被重排至 store 之后。即 load-store, store-store 不能被重排。
意义:TLS,即 Thread Local Storage(线程局部存储)。
代码:src\thread\SDL_thread_c.h 和 src\thread\windows\SDL_systls.c
基于 SDL_Atomic、SDL_MemoryBarrier 和 WinAPI Tls API 封装。
以下结构体包含一个析构函数的指针,非空时,SDL_TLSCleanup() 会调用它。
1 | /* This is the system-independent thread local storage structure */ |
代码:src\thread\SDL_thread.c 和 src\thread\windows\SDL_systhread.c
创建线程的 API 是 SDL_CreateThread 和 SDL_CreateThreadWithStackSize,导出函数 SDL_CreateThread 的定义如下,记为【X】:
1 | SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThread,(SDL_ThreadFunction a, const char *b, void *c, pfnSDL_CurrentBeginThread d, pfnSDL_CurrentEndThread e),(a,b,c,d,e),return) |
下面会有递归展开宏的过程。首先,用 SDL_DYNAPI_PROC 的定义:
1 |
展开【X】得到:
1 | static SDL_Thread* __cdecl SDL_CreateThread(SDL_ThreadFunction a, const char *b, void *c, pfnSDL_CurrentBeginThread d, pfnSDL_CurrentEndThread e) { |
其中 jump_table.SDL_CreateThread 是【X】被 SDL_dynapi_procs.h 的:
1 | /* The jump table! */ |
展开得到,为:
1 | typedef struct { |
【X】又被 SDL_dynapi.c 的 initialize_jumptable 函数的:
1 | /* Init our jump table first. */ |
展开为:
1 | /* ... */ |
所以,调用 SDL_CreateThread 最终调用的就是 SDL_CreateThread_REAL,又由于 src\dynapi\SDL_dynapi_overrides.h
中的:
1 |
所以调用的是 src\thread\SDL_thread.c 中的:
1 |
|
可见 SDL_CreateThread
调用了 SDL_CreateThreadWithStackSize
,而 SDL_CreateThreadWithStackSize
又调用 src\thread\windows\SDL_systhread.c 中的 SDL_SYS_CreateThread
,因为 Windows 平台有 _beginthreadex
和 _endthreadex
,所以最后是调用 _beginthreadex
:
1 | /* thread->stacksize == 0 means "system default", same as win32 expects */ |
其中 RunThreadViaBeginThreadEx
实际上是调用 RunThread
:
1 | static DWORD |
从代码可见 RunThread
调用 SDL_RunThread
,而 SDL_RunThread
内部由 SDL_TLSCleanup()
来调用析构函数:
1 | void |
圣仙山:两位娘子,待会儿去吾姐梦里和她告别。
李心觎:为啥不在视界亲自和她告别呢?
圣仙山:她远嫁黑龙江,吾在视界又不能飞,要跑很远纳!再说吾在视界还没娶妻,多没面子呀。
李心觎:嘻嘻嘻。
李冰月:哈哈哈!姐姐,咱们是去给相公撑场面的!
圣仙山:神姐!
李神源:嗯?是仙弟呀。这两位是?
圣仙山:是小弟的妻子。
李冰月、李心觎:姐姐好!
李神源:好好好。两位弟妹灵秀如仙,仙弟什么时候得此艳福,真是好大的惊喜呀!
李冰月:好说好说。相公才是灵秀如仙,我们姐妹俩还不如他呢!
李心觎:是呀,神姐,夫君才是真仙。
李神源:哎呀,我这仙弟以前老和我说,他要一心求道,不想娶妻生子,还说未来的人越来越多都不结婚的。
圣仙山:姐!现在情况有变,当今皇帝受老钧托梦,要老爹组织一群谷阳人去南荒开疆传道,同行人中有技师文人,也有流民和被大赦的罪犯,还要途径苦境各地,和三教九流打交道,我是最佳带队人选。
李神源:此去危险重重,仙弟为何不让别人去呢?
圣仙山:除了传道,吾还有别的任务,要去寻找吾心中的道州德国,一处远离天灾人祸的修道圣地。
李冰月:神姐放心,相公已经修成八识神通,这事可以安全办成。
李神源:既然如此,为姐只能祝你一切顺利。我养了一只守护灵狐,让它陪你上路吧,风餐露宿有危险时,它会警示你。
圣仙山:谢谢神姐。
李心觎:神姐梦里的灵狐好可爱耶。
李冰月:是呀,咱们在识界里找一只呗!
李心觎:走。
李冰月:相公,这只叫“纯狐连玉”,你在视界如果遇到它,它会认得你的。
李心觎:连玉,暂时就陪我们吧。
少曾读仙史,知有苏耽君。流望来南国,依然会昔闻。
泊舟问耆老,遥指孤山云。孤山郴郡北,不与众山群。
重崖下萦映,嶛峣上纠纷。碧峰泉附落,红壁树傍分。
选地今方尔,升天因可云。不才予窜迹,羽化子遗芬。
将览成麟凤,旋惊御鬼文。此中迷出处,含思独氛氲。
陈因提:圣仙山带着一群中原人来到现在你家这个地方?
圣小开:对!仙山公在路上娶了胡小玉,才有现在的我。
陈因提:胡小玉很特别吗?娶别人不一样有你?
圣小开:万一不娶呢?那我不是生不出来!
陈因提:哈哈。你真是瞎操心。我比较担心他死在路上,而你担心他不娶!
圣小开:有神通在,明显死不了呀。料事如神,有啥危险或者人际关系的问题,都能预防。
陈因提:哦,后世人神化的吧!成王败寇。
圣小开:仙山公能找到这个好地方本身就很神了,台风地震海啸全被周围其他地方挡住,几乎没天灾,还远离政治和战乱,在当时可谓世外桃源。
陈因提:这是现实,暂且信你。那么,你有遗传到这八识神通吗?
圣小开:略懂略懂!八识神通分为被动接受和主动测算,我有时候也能接受到来自未来的预警,主动测算我是不会。而且主动测算会耗费心力,即所谓招天谴。仙山公测算多次,导致 35 岁就仙逝了。胡小玉就活了 42 岁,他们女儿活到 64 岁。
陈因提:算出啥了?老天爷这么急着收他?
圣小开:回归十界。
陈因提:啥意思?
圣小开:不是老天收他,是他终于算出怎么回归十界,便抛下肉身……
陈因提:等等,说人话!
圣小开:算出怎么去死!
陈因提:切!还以为啥高深的法门……睡了睡了。
这一年在 59 号大网吧办公,总感觉桌子太矮椅子太烂……心神不宁,频繁想起一个故事:
某同事很低调,从来不炫耀自己的成就,突然有一天,他说:“我太突出了!”
一问,原来是腰间盘突出……
这一年,还是哥的稣决定攒钱买一把好的椅子。
存了几个月,终于买了一把保友金豪 E-AB-HAM。那时好多同事来围观,有 HR 来说了句:“比总经理用的还好!”,也有不少小伙伴说长得像医疗用的复健椅。
**但是这把椅子在稣心里并没有地位。**当时已经有智能手机的稣,从来没有拍过它,以致如今想查具体啥时候买的,都不那么确定。只记得是花 3800 元买的,最晚不会超过 2013-01-19 这个时间!
当时还很撒逼地把能选配的全给配上……实际使用证实左手边的书架几乎用不上,以致后来把它给拆下来了!脚垫也不常用,很少在公司午睡。
说起效果,那确实比公司的乐射椅子好很多,但也不是特别舒服。网布很透气,但在公司有空调的前提下,透气并不是那么必须。反而缺点就是它并没有根据屁股的造型来支托,简单地说有点勒腿,坐久了并不舒服。所以,后来又研究起 Embody。
后来有土豪要买 Embody,稣跟着去体验了一下。2020 年,土豪又买了第二张……
2020-06-10 下单乐歌升降桌 E3,180*80cm。靠自动提醒功能养成坐久了站会儿的习惯,结果反而让稣发现八哥就在金豪这把椅子上!网布还是个问题,坐 8 年已经变软一些,但依然有一种压迫大腿两侧的酸麻感。
吓得稣赶紧下单,买了介个:
这下终于明白形状和支撑的重要性!果断存钱买 Embody!
存了一个月多的钱,再加上脑波给的一万块人民币,终于入手 Embody!
Embody 可以调得比金豪更矮,适合的身高范围就更大,尤其对女性友好。腿、屁股和腰都舒服。
一张 Embody 价格可以买金豪 3 张以上,但是肯定是没有 3 倍的舒服,高端的东西性价比自然比大众款低的多,使用价值只能在细节上轻微地增加。好在大腿的问题真的解决了。
挨踢族一天坐在椅子上的时间是远超健康上限的。稣并不赞成通过买任何东西来装逼,除非这东西是真实用,所以贵。Embody 对很多人来说,可能就是装逼,但对挨踢人绝对不是。
并不是主角才有光环,而是有光环的才能当主角,这才是生活。——泥巴娃
最后,一句话总结:要不是穷,真想再买一张!
李仙山,字太墟,号道识,是最后一名通过修行进入识界的人类,最为人津津乐道的是他娶了两位貌美如花的妻子:李冰月和李心觎。三人同在深山修炼,羡煞旁人。
李仙山:两位娘子,山顶那朵魔莲原来是老钧的智慧所化,吾已得其认可,纳入脑识。
李冰月:那相公还是相公吗?
李心觎:是呀,夫君不会鲸神魂裂吗?
李仙山:哈哈,不仅没有副作用,还助吾练就八识神通。
李心觎:听起来好厉害哦!有何用处呢?
李冰月:哎呀!相公的眼睛怎么流血了!
李仙山:咦?难道不能预测自己的未来?
李心觎:怎么回事也?赶紧擦擦。
李仙山:八识神通能够预知未来,吾刚刚测算视界中自己的未来,发现未来的视界大部分地方都实行一夫一妻制,吾之转世因此无法娶三个妻子。
李冰月:测算自己会破坏因果循环,必遭天谴,相公可别乱用。
李仙山:只要不测自己就没事。吾试过预测识界未来,发现识界将关闭千余年,后有邪灵借助科技的力量强行进入识界,后使视界的普通人不必修行亦可进入识界。可谓千年浩劫!
李冰月:此事非同小可,需召集众仙请示老钧。
李仙山:不必如此兴师动众,为夫一人已可召唤老钧。
李心觎:老钧居于十维之外,三千年来只见众人祈祷之法,却无人见过老钧现身。夫君真能召唤出来?
李仙山:老钧的意志无处不在,只要用八识神通与他纠缠就可以接受他的意识传播。来!
突然云雾缭绕的道州蓬山化入星辰,创世天尊老钧自虚无中发出一道引力波。
李仙山:小孙,拜见老祖,请老钧指示应对浩劫之方。
老钧:道之不传,识界将倾。回到视界,去南方寻找道州德国。
李仙山:领道旨。
老钧:天机不可泄露!切记不可再测算自己的未来。从今而后,改姓圣吧!
李仙山:这个姓小孙恐怕承受不起呀!再说不久前才从老改为李的,又改?
老钧:视界中的李伯阳会助你,让当朝皇帝赐姓。
李仙山:遵旨!
老钧消失前,喃喃道:那厮岂非汝乎?小孙杂……
回到道州蓬山。
圣仙山:两位娘子,为传道法,吾将离开中原,奔波于南荒之地,恐少有时间再入识界。
李冰月:相公是去干大事的,不用担心我们,我们会照顾好自己的。
李心觎:对呀,夫君偶尔来看我们就好。小别胜新婚,也许更好呢!
圣仙山:哈哈哈!
李冰月:姐姐,我们写几句诗送给相公吧!
李心觎:好呀。妹妹是大老婆,你先来。
李冰月:晓镜但愁云鬓改,夜吟应觉月光寒。蓬山此去无多路,青鸟殷勤为探看。
李心觎:昨夜星辰昨夜风,画楼西畔桂堂东。身无彩凤双飞翼,心有灵犀一点通。
圣仙山:好诗!好诗!都是你们未来的荥阳老乡写的!
李冰月、李心觎:哈哈哈。
圣仙山:再过两百多年义山就出生了!哈哈哈……
陈因提:圣仙山就是你老祖宗?
圣小开:当然,所以我遗传了他的特异功能。
陈因提:哦……我有点困!你最好拿出能提神的证据。
圣小开:我偶尔也能进入识界!一般人我不告诉他。
陈因提:好啦好啦,那你说说看,你都看到啥?
圣小开:众生皆苦!
陈因提:阿弥陀佛。我裤子都脱了,你给我念经?
圣小开:耶!你怎么知道我还写过一本经书。
陈因提:拿出来,我帮你烧给你老祖宗。
圣小开:切!这本《八哥之神创世手稿》十分珍贵,里面记载着进入识界的修炼法门。
陈因提:一本九毛九?我买,明天给你一百块,免找。
圣小开:给你也好,要是我突然遭天谴,起码这绝学不会就此失传。拿去,好好保管。
陈因提:哟,字体工整,比写情书还认真!那我就收了,放心吧,我当宝一样收藏。
鹿邑:听说稣已经从区块链开发转型为牛媒体开发,能说说牛媒体吗?
稣:牛媒体就是三个 Q。
鹿邑:您是说三个季度就能掌握牛媒体?
稣:不是……是说牛媒体不过就是三个队列。
鹿邑:哦?我不会问您是哪三个!
稣:第一个是网络包队列,第二个是音视频包队列,第三个是音视频帧队列。
鹿邑:那经常听到的“转码”到底是啥?
稣:标准多到蛋疼罢了。你的化妆桌,东西怎么摆你说了算,别人摆法不同,但东西是一样的。你搬家时,把它们装起来,用啥来装,怎么装,扔掉哪些不重要的,也是你说了算,别人装法不同,但关键的几样东西还是一样的。
鹿邑:据说转码很烧钱,你们用的机器都很贵?
稣:没有没有,小米游戏本就够装逼。
鹿邑:第一次听到“光栅化”这个词时,表示一脸懵逼,给解释一下呗?
稣:稣拿稣法做类比,您就能理解了!Look!
鹿邑:这就是光栅化?还是没明白呀!
稣:别急!您看这些笔划……稣大笔一挥,横竖撇捺,点折弯钩,一笔走红尘,两笔惊鬼神。稣的内心只有笔法走势。
鹿邑:牛逼死了!赶紧发朋友圈!
稣:没错!关键就是发票圈!为了用现代化的方式传播稣法,咱们可以扫描打印、刊登到报纸、拍照发票圈,这些手段有个共同点——最后出来都是一些像素点。
鹿邑:还真是这样哦!我以前拿放大镜看过屏幕,都是红绿蓝的像素点。
稣:您的理解能力不错。这个把“笔划”变成一系列点的过程就是“光栅化”。举个例子,您在画板上拉一条直线,计算机只需要知道头尾两个点和颜色就能定义这条直线,而光栅化之后就是 N 个这种颜色的像素点。
鹿邑:疫情以来游戏股涨势普遍大好,想了解一下我现在学游戏开发来得及吗?
稣:哈呵,游戏其实就是利用游戏引擎,根据玩家们的输入动作渲染声音和画面,很容易理解,不过实际开发起来很多坑,您还是买在港股上市的游戏公司的股票就好。
鹿邑:游戏引擎是什么,渲染又该怎么理解?
稣:游戏引擎是管理图像、声音、人机设备等一套套接口的集合。最重要的是 3D 引擎,简单的说,它是您告诉它“画个圆”,它就帮您“画出一些点,这些点组成一个圆”的接口。单说画面的渲染,可以理解为更多步骤更广义的光栅化,产生显存里的像素点数组,最后扔给显示器。
鹿邑:您又要类比吗?
稣:Look!稣写作的地方都在这些纸上,写得好的,会签名、盖章,拍照拿去发票圈。过程很简单,但包含了几个重要概念。纸张相当于显存;那张拿去拍照发票圈的纸是后台缓冲区(back buffer)的渲染目标(render target);盖章环节相当于是输出合并器阶段(output-merger stage);拍照可以理解为整体光栅化;当然拍照时,摄像头的位置、角度,以及打灯也是有讲究的;最后发票圈就当作输出到显示器吧!
鹿邑:好像不难理解了,您总是把啥都说得很简单,真这么简单吗?
稣:入门难而已!精通,则更难……不过一开始如果把一些专有名词、缩写整明白,可以更快入门。比如 Direct3D 的 API 就存在不少缩写,什么 OM、RS 啊,只看代码不看文档的话很容易懵逼。
鹿邑:数学要很好吧?
稣:数学思维要很好,具体的知识可以现学。几乎每步都有数学的应用,但套路比较有限,可以一通百通。举个最简单的例子:AlphaBlend 算法,常用于把人物、道具合成到背景图里,它其实和盖章是类似的,把章盖在字上,既可以看到字,也可以看到章。其计算公式就是用 alpha 值对背景点、前景点的三原色值做个加减乘运算,理解一下就行,都不用背。由于 YUV 有很多种具体规范,记不住,干脆也甭记,只要明白有些处理 YUV 比 RGB 方便,有些则 RGB 更方便就行。RGB 和 YUV 之间的转化也都有公式,一样只要理解不用记。
鹿邑:您觉得难点在哪里呢?
稣:规范太多!就拿最基础的像素点来说,就存在很多种格式,大的分类就有 RGB 和 YUV 系列,按每个分量(R、G、B、Y、U、V)的排序就可以分出好多种,还有按每个分量几位表示来区分,然后这些位是整数、浮点数或者浮点数规范化的整数,整数是有符号还是无符号都可以造成不同。
鹿邑:停!我已经晕了!
稣:就好像人以群分吧!但可以按照性别、年龄、肤色、籍贯、身高、体重、专业、爱好等各种维度细分,现实世界就有诸多标准,计算机里一样一样的。
鹿邑:明白!人艰不拆,没有容易二字。
稣:该睡了,做梦容易……吓醒。
《#诗盗#·孙子编码规范》:善编码者,架不重构,洞不三补。码至难者缺人,缺人则贫于招人。财竭则急于裁员。
模仿自春秋时吴国将军孙武的《孙子兵法》:
善用兵者,役不再籍,粮不三载,取用于国,因粮于敌,故军食可足也。
国之贫于师者远输,远输则百姓贫;近师者贵卖,贵卖则百姓财竭,财竭则急于丘役。
力屈、财殚,中原、内虚于家,百姓之费,十去其七;公家之费,破军罢马,甲胄矢弩,戟楯蔽橹,丘牛大车,十去其六。
故智将务食于敌,食敌一钟,当吾二十钟;忌杆一石,当吾二十石。
《#诗盗#·中毒太深》:湿久江南渐挖身,霉多烟雨如喷粪。是谁补水智商税,广告元来有信人。
改编自霹雳角色剑儒无涯命夫子的诗号:
诗酒江南剑外身,眼惊幻墨带天真。
是谁不道君无对?世上元来更有人。