<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>李萌的博客</title><link rel="alternate" href="https://lizi.moe/"></link><id>https://lizi.moe/</id><updated>2026-08-25</updated><author><name>moe li</name></author><entry><title>发射电波中，少话</title><link rel="alternate" href="https://lizi.moe/posts/Analysis-on-Soutaiseiriron/"></link><id>https://lizi.moe/posts/Analysis-on-Soutaiseiriron/</id><updated>2026-08-25</updated><category term="杂谈"></category><summary>关于「相対性理論」的若干“理论”</summary><content type="html">&lt;!-- digest: e1a492d5 --&gt;
&lt;p&gt;&lt;strong&gt;按：我发现我用中文写作的时候经常写的过快，导致文本时常严重欠考虑；所以试着换成了英文，从而强行拖慢我的写作速度，看看有没有什么变化。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; This article is my comments and analysis on a
certain band.&lt;/p&gt;
&lt;p&gt;About half a year ago, I ran into a Japanese band named &lt;span
lang=&quot;ja&quot;&gt;相対性理論&lt;/span&gt; (Soutaiseiriron), while I was haphazardly
looking for something to listen to on a drive. The first song I
encountered was &lt;span lang=&quot;ja&quot;&gt;スマトラ警備隊&lt;/span&gt; (&lt;em&gt;Sumatra
Keibitai&lt;/em&gt;) from the EP &lt;span lang=&quot;ja&quot;&gt;シフォン主義&lt;/span&gt; (Chiffon
Capitalism).&lt;/p&gt;
&lt;p&gt;I’ve been learning Japanese for a while, so chances are that I can
understand - at least part of - the lyrics. But &lt;em&gt;Sumatra
Keibitai&lt;/em&gt; was something different. The arrangement was thoughtfully
calculated and meticulous, while the vocal was intriguing (though hardly
provocative, at least in any conventional sense; we’ll jump back to this
topic later). But what about the lyrics? Honestly, at first glance, to
me, it seemed like complete nonsense, or at least something that lacks
even a basic degree of referential plausibility. Thus, it’s hard for me
to merely piece things together.&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;やってきた恐竜　街破壊&lt;/p&gt;
&lt;p&gt;迎え撃つわたし　サイキック&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;The beginning was quite perplexing. It seemed to be inspired by plots
of some stereotyped anime. But that did not last long:&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;太平洋　大西洋　ここ一体何平洋よ&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;盗んだわたしの記憶をかえして&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CIA KGB FBIに (CENSORED) の陰謀よ&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;誰か　わたしを逃がして&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;My first reaction was like &lt;em&gt;wait, what?&lt;/em&gt; I initially wondered
whether I had misunderstood the lyrics, and looked them up just to make
sure. Perhaps I found the reason why the censor-bots are complaining
about this song.&lt;/p&gt;
&lt;p&gt;What initially resembles the daydream of a sleepy girl suddenly takes
a sharp turn and gives way to something closer to some rather radical
political proclamation. &lt;em&gt;Give back my stolen memories! Conspiracy!
Anybody please set me free!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Yes, it &lt;em&gt;is&lt;/em&gt; tempting to dismiss all of this as pure semantic
incoherence and leave it at that; but that was perhaps too easy an
answer. This interpretation &lt;em&gt;can&lt;/em&gt; explain my confusion, but not
my &lt;em&gt;real&lt;/em&gt; experience. On the contrary, I was actually becoming
increasingly aware of the lyrics. The acronyms, the half-rhymes, the
abrupt shift in imagery, the strange juxtaposition of political paranoia
with rest of the song. The words themselves were not uncomprehensible. I
knew what all the individual phrases were; what I could not immediately
understand was what these things were doing together in a &lt;em&gt;bigger
picture&lt;/em&gt;. Calling them total nonsense, then, merely suggest the
failure of interpretation. It did not explain why, even before I can
make any scrutable sense of them, I had already begun to perceive them
as something more than fractured pieces of information. It’s possible
that, meaning is &lt;em&gt;not&lt;/em&gt; absent. It’s just not the first problem
the song asks us to solve.&lt;/p&gt;
&lt;p&gt;This distinction reminds me of Hiroki Azuma’s thesis on the so-called
&lt;em&gt;database model&lt;/em&gt; after several unsuccessful attempts to assemble
a &lt;em&gt;bigger picture&lt;/em&gt;. Azuma proposed that, &lt;em&gt;once upon a time
&lt;del&gt;in a distant galaxy&lt;/del&gt;&lt;/em&gt;, individual works can be understood
in relation to a deeper narrative or ideological construction that
preceded them. By encountering the elements visible on the surface of
the work, consumers could, in principle, gain access to this
pre-existing larger totality; under the database model, however, what
consumers face might instead be collections of identifiable and
individually consumable fragments that can be extracted, combined,
reorganized or even tweaked without being first confined to a singular
and authoritative narrative, through which they - consumers -
autonomously organize the fragments corresponding to their own
understanding and form a &lt;em&gt;false&lt;/em&gt; substitution for the &lt;em&gt;big
narrative&lt;/em&gt; (i.e. aforementioned &lt;em&gt;bigger picture&lt;/em&gt;). This is,
of course, not saying that the songs of Soutaiseiriron are direct
illustrations of Azuma’s theory, nor that their lyrics somehow
constitutes a literal database &lt;del&gt;or some high-dimensional vector
space&lt;/del&gt;. What I’m trying to convey - by introducing an analogous
structure from his arguments - is that, &lt;em&gt;what if&lt;/em&gt;, in this case,
&lt;em&gt;individual elements can precede totality&lt;/em&gt;. What if, the listener
come across recognizable fragments first, and is then forced into
putting together a graspable whole which remains contingent but perhaps
provisional? Here, the signifier arrives immediately, when I hear it,
recognize it and eventually decode its literal meaning; what arrives
later - if it arrives at all - is a relatively stable account of its
relations with other signifiers around it. That is to say, signification
may unfold asynchronously, and I’ll refer to this temporal mismatch or
asymmetry, somewhat loosely, as &lt;em&gt;deferred semiosis&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span lang=&quot;ja&quot;&gt;上海an&lt;/span&gt; (&lt;em&gt;Shanghai An&lt;/em&gt;) from the album
&lt;em&gt;TOWN AGE&lt;/em&gt; offers a useful variation on explaining this theory.
Its opening does not initially resist comprehension through the same
degree of abrupt semantic discontinuity &lt;em&gt;vis-à-vis&lt;/em&gt; &lt;em&gt;Sumatra
Keibitai&lt;/em&gt;. There is, at the beginning, at least &lt;em&gt;seemingly&lt;/em&gt;,
a relatively concrete and &lt;del&gt;mentally&lt;/del&gt; stable narrator, a certain
place and a sequence of events that do possess chronological
integrity:&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;上海について20分&lt;/p&gt;
&lt;p&gt;一体ここはどこだろう？&lt;/p&gt;
&lt;p&gt;荷物も見当たらないし&lt;/p&gt;
&lt;p&gt;切符もどこだかわからない&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;万歳させられてるけど&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一体どういうことだろう？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一つも見に覚えがないし&lt;/p&gt;
&lt;p&gt;にっちもさっちもいかなくて&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;Apparently, this is much easier to follow. If you’ve ever arrived at
an airport, it won’t be tough to gain a sense of intimacy. The narrator
cannot find their luggage, has lost track of their ticket, and appears
to have become entangled in some &lt;em&gt;ritual&lt;/em&gt; they do not understand;
everything is in a total mess. I found it quite easy to project my own
experiences arriving in Europe this summer onto the scene. And the most
interesting part (from my perspective, which I highlighted in bold), the
narrator is somehow requested to perform something described as
&lt;em&gt;banzai&lt;/em&gt;, which a listener with average knowlege of Japanese
culture &lt;del&gt;and modern history&lt;/del&gt; won’t be unaquainted with - to
raise their arms while shouting the phrase &lt;em&gt;banzai&lt;/em&gt;, and perhaps
some other gesture that the lyric itself leaves unspecified. For reasons
I cannot fully reconstruct, this reminds me, however vaguely, the
tedious airport security procedures here. Perhaps that’s what the lyrics
are trying to convey; or perhaps it’s not, and this is just my
overreading. The crucial point, however, is that the lyrics themself
&lt;em&gt;do not&lt;/em&gt; settle the question for me with a plain answer. It does
not explicitly identify the situation I’ve constructed, nor does it
coerce me into accepting any particular explanation.&lt;/p&gt;
&lt;p&gt;But neither does this mean that the text permits any possible
interpretation; its fragments impose certain constraints while failing
to determine a definitive account of what has taken place. The listener
does not invent meanings out of void; instead, the song exhibits an
&lt;em&gt;arranged&lt;/em&gt; and &lt;em&gt;curated&lt;/em&gt; collection of intelligible
elements whose underlying relationships still remain at least
&lt;em&gt;partially&lt;/em&gt; undetermined, and the listener’s task - perhaps even
a &lt;em&gt;compulsion&lt;/em&gt; - is to glue them, orchestrate them, make them
cohere, and thereby constructing a provisional totality. Still, it would
be doubious and misleading to conclude that &lt;em&gt;Sumatra Keibitai&lt;/em&gt;
and &lt;em&gt;Shanghai An&lt;/em&gt; arrive at interpretive instability via mutually
exclusive routes; &lt;em&gt;Shanghai An&lt;/em&gt; itself soon becomes increasingly
difficult to stabilize, when its apparently coherent narrative at the
beginning is interrupted by stuff like repeated phrases, wordplay,
unexplained figures and progressively uncertain relations between
events. The distinction, then, is not that one lyric is fragmented while
the other is not; no such sharp contradiction lies here. Rather, they
spread stability and instability across their respective structures.&lt;/p&gt;
&lt;p&gt;Under this explanation, &lt;em&gt;Sumatra Keibitai&lt;/em&gt; makes the
instability of its fragments perceptible almost immediately;
&lt;em&gt;Shanghai An&lt;/em&gt;, on the other hand, initially offers the listener a
&lt;em&gt;narrative foothold&lt;/em&gt;, before gradually revealing that this
foothold is insufficient to organize things that follows.&lt;/p&gt;
&lt;p&gt;At this point, another set of organizing principles begins to emerge
alongside the narrative one.&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;いちにの &lt;strong&gt;サンハイ&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;This phrase can either be heard as &lt;em&gt;1, 2, 3, go!&lt;/em&gt;, yet &lt;span
lang=&quot;ja&quot;&gt;サンハイ&lt;/span&gt; simutaneously overlaps with &lt;span
lang=&quot;ja&quot;&gt;上海&lt;/span&gt;. Until this point, Shanghai has primarily served
as the &lt;em&gt;signifier&lt;/em&gt; of a certain place where the narrator has
supposedly arrive and remain unable to properly locate themselves; here,
however, it briefly ceases to function &lt;em&gt;only&lt;/em&gt; as a geographical
name and becomes available as a sequence of smaller phonetic units that
can be segmented differently. Therefore, this line, by allowing two
different semantic paths to occupy an identical phonetic space,
inserting a pun into an otherwise stable narrative, introduced
explanatory ambiguity, leaving more capacity for listener
interpretation.&lt;/p&gt;
&lt;p&gt;The following repetitions intensify this shift in narrative as
well:&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;お命ちょうだいしましょうか？ 水先案内しましょうか？ x2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;Read in isolation, these lines invite radically different
interpretation depending on the provisional situation the listener has
constructed. To me, this can sound almost ritualistic, even
&lt;em&gt;cult-like&lt;/em&gt;; with a different frame, however, the same exaggrated
language may appear theatrical, absurdist, and even - as mentioned by a
few critic reviews I’ve read or watched before - somehow romantic. But
still, none of these readings is directly guaranteed by the lyric
itself, as explained in the previous analysis.&lt;/p&gt;
&lt;p&gt;Semantically, these propositions have few reason to be placed
together; nevertheless, their juxtaposition places them within the same
recurring grammar frame:&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;～しましょうか？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;The same pattern returns near the end of the song:&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;お命ちょうだいしましょうか？ 水先案内しましょうか？ x2&lt;/p&gt;
&lt;p&gt;美味しい話をしましょうか？ もう一つオマケしましょうか？ x2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;Again, the propositions themselves are only loosely related to some
extent. What establishes continuity between them is therefore merely the
repetition of a common syntactic form, rather than presence of a
concrete - or at least predictable - narrative progression to some
degree, as we typically see in numerous popular songs. This repetitive
form allows the phrases with semantic differences coexist in a formal
structure that repeatedly treats them as variations of the same pattern,
and perceived by the listener as a bundled whole. Thus, &lt;em&gt;Shanghai
An&lt;/em&gt; demonstrates additional principles of organization by using
phonetic or syntactic techniques that coexists with, and eventually
destabilize and disrupts, the narrative foothold offered at its
beginning.&lt;/p&gt;
&lt;p&gt;&lt;span lang=&quot;ja&quot;&gt;夏の黄金比&lt;/span&gt; (&lt;em&gt;Natsu no Ougonhi&lt;/em&gt;), from
&lt;em&gt;Chiffon Capitalism&lt;/em&gt;, pushes this tendency even further,
providing particularly revealing examples for these newly introduced
principles, when repetition begins to dominate the lyric arragement and
draws more attention to the material existence of language itself.&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;コントレックス箱買い&lt;/p&gt;
&lt;p&gt;コントレックス &lt;strong&gt;はこはこはこ買い&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;コントレックス箱買い&lt;/p&gt;
&lt;p&gt;コントレックス&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;At the beginning, &lt;span lang=&quot;ja&quot;&gt;コントレックス箱買い&lt;/span&gt; can be
processed as an ordinary proposition, at least a fragment of one. But
repetition (in bold) quickly alters its internal equilibrium. &lt;span
lang=&quot;ja&quot;&gt;箱&lt;/span&gt;, or &lt;span lang=&quot;ja&quot;&gt;はこ&lt;/span&gt; does not cease to
mean a box; yet, through repetition, it becomes perceptually available
as something more than its basic lexical meaning, with its recurrance
also highlighting its &lt;em&gt;phonetic texture&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We can see similar occurences of this technique in &lt;span
lang=&quot;ja&quot;&gt;四角革命&lt;/span&gt; (&lt;em&gt;Shikaku Kakumei&lt;/em&gt;) from the album
&lt;span lang=&quot;ja&quot;&gt;ハイファイ新書&lt;/span&gt; (&lt;em&gt;Hi-fi Anatomia&lt;/em&gt;).&lt;/p&gt;
&lt;div lang=&quot;ja&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;四角カクカク革命&lt;/strong&gt; 前夜の&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;長いあいあい間の&lt;/strong&gt; ロマンス&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;近いカイカイ開戦&lt;/strong&gt; 前夜に&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;やってくるくる車&lt;/strong&gt; の行列&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;The alignment is primarily there to make the recurring phonetic
structure visible. I also happen to think it looks nicer this
way.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, the progression of lyrics is organized just as strongly by
phonetic recurrence as by semantic continuity. Sound therefore begins to
operate as a principle capable of connecting and grouping otherwise
distinct words together.&lt;/p&gt;
&lt;p&gt;Here, despite not being completely cancelled, simply the semantic
function of the word no longer exhausts things that the listeners are
able to perceive. A peculiar doubling therefore arises, where the word
still serves its purpose of signification, while simultaneously becoming
a flexible and manipulable piece of acoustic material which can be
disassembled, modified, resegmented, reiterated, or reorganized, in a
way almost analogous to &lt;em&gt;audio samples&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This distinction of semantic function of text and its materiality is
not exclusive to music. It reminds me of a comparable relationship in a
very different medium: the textual interludes frequently appeared in
animation works directed by Akiyuki Shinbo, particularly in &lt;span
lang=&quot;ja&quot;&gt;物語&lt;/span&gt; (&lt;em&gt;Monogatari Series&lt;/em&gt;) and &lt;span
lang=&quot;ja&quot;&gt;さよなら絶望先生&lt;/span&gt; (&lt;em&gt;Sayonara, Zetsubou-Sensei
Series&lt;/em&gt;). In &lt;em&gt;Monogatari&lt;/em&gt;, for instance, Shinbo introduced
numerous interludes consisting of a monochromatic background and some
text which varies in length - sometimes merely a few characters like a
concise description of character or events that just occurred; sometimes
lengthy, like a excerpt from the original novel or the anime script.
This takes a step further in &lt;em&gt;Sayonara, Zetsubou-Sensei&lt;/em&gt;, where
not only the interludes but also the background art consists of random
text about some trending memes, celebrities or political sarcasm in
Japan. Such technique might originate from the Japanese comic strips, or
&lt;em&gt;manga&lt;/em&gt;, and is futher developed alongside the increasingly
experiments on typography, still-images and other non-animated
materials, by collected efforts of the staff from SHAFT, including
director Shinbo and animator Oishi Tatsuya, at the crossroad of the
traditional and computer-assisted animation production in 2000s. There
are still a lot of things to discuss about Shinbo’s styles, but as the
topic of this blog is not anime case study, its better to prevent more
prolixities on this subject. The crucial part is, the interludes, unlike
these in conventional movies or animations, often appears too briefly
which only allows a quick browsing under ordinary viewing conditions
(e.g. watching the anime airing on TV). This does not render the text in
these interludes completely meaningless; on the contrary, the viewers
are actually acutely aware that they &lt;em&gt;are&lt;/em&gt; reading some
meaningful text even without sufficient time to decode it completely.
However, its the the perception of typography, typesetting, the visual
contrast and the sudden interruption from moving image that arrives
before the semantic decoding of the text. In such moments, the visual
property of text or character (expecially the CJK characters) as some
&lt;em&gt;graphic material&lt;/em&gt;, seems to coexist, or even precede its
semantic functions. Drawing this back to our original analysis,
&lt;em&gt;Natsu no Ougonhi&lt;/em&gt; suggests an acoustic analogue - the phonetic
properties of the word, previously concluded as a &lt;em&gt;sample&lt;/em&gt;, might
become perceptually salient before its purpose to signify a certain
meaning.&lt;/p&gt;
&lt;p&gt;This raises another interesting question. If the lyrics are not only
semantic propositions but also phonetic material, what is the major
factor that determines in which form this material will reach the
listener? Apparently, it is the voice. As for lyrics of Soutaiseiriron,
they arrive through Yakushimaru Etsuko’s voice.&lt;/p&gt;
&lt;p&gt;Her vocal might be described as &lt;em&gt;flat, deadpan and
indifferent&lt;/em&gt; in the first place. Indeed, compared with many popular
vocalists whose performances rely on conspicuous changes in intensity,
pharsing, vocal color, etc. that guide the listener toward a relatively
recognizable emotional response, Yakushimaru often seems to
&lt;em&gt;withhold&lt;/em&gt; such guidance. However, such comments, despite not
completely unsubstantiated, are ultimately insufficient. They risk
reducing a remarkably variable vocal performance to the absence of
something that, in fact, often exists. 救心 (&lt;em&gt;Kyu-Shin&lt;/em&gt;), for
instance, is lively, playful, and almost exuberant. A more useful
description is that her vocal delivery does not necessarily arrange
affect into a familiar trajectory of accumulation, climax, and release,
thus partially redistributes the burden of emotional interpretation to
the listener. Namely, instead of determining in advance how particular
pharses ought to be felt, her delivery frequently leaves a considerable
distance between &lt;em&gt;what is being said&lt;/em&gt; and &lt;em&gt;how it is
eventually felt&lt;/em&gt;. The stablization of meaning might not be the only
thing that can arrive asynchronously; sentimental effects, too, may also
remain temporarily unresolved.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Previously, I mentioned something called &lt;em&gt;big narrative&lt;/em&gt; or
&lt;em&gt;bigger picture&lt;/em&gt;, and I failed in the pursuit for it. From the
above analysis, we can say that this bigger picture does not always
precede perception. In some cases it emerges only through perception
itself. This does not mean that every interpretation is equally valid,
nor that the listener invent random meanings out of nothing. The
fragments still impose constraints. The lyrics, sound, vocal delivery
and cultural references all delimit what can plausibly be heard. The
listener, then, is neither a passive receiver nor an entirely free
author. They occupy a position somewhere in between assembling relations
between fragments, revising provisional interpretations, and bringing
their own experiences to bear on what they hear. Without the listener’s
participation, at least some of these relations may remain temporarily
unsettled.&lt;/p&gt;
&lt;p&gt;So where, after all this, are my actual comments on the band? Well,
perhaps I would prefer to leave that conclusion temporarily unsettled,
too.&lt;/p&gt;
&lt;p&gt;以上。&lt;/p&gt;</content></entry><entry><title>关于软件学院寒假项目的若干反思</title><link rel="alternate" href="https://lizi.moe/posts/Reflections-on-NWPU-SSW-Winter-Project/"></link><id>https://lizi.moe/posts/Reflections-on-NWPU-SSW-Winter-Project/</id><updated>2026-08-21</updated><category term="杂谈"></category><summary>好了，花点时间去讨论计算机科学有趣的难题，不再讨论这件事情了。</summary><content type="html">&lt;!-- digest: 97a7e864 --&gt;
&lt;blockquote&gt;
&lt;p&gt;今年 ICLR，哈工大学大胜，西工大学惨败！哈工大发表论文 83
篇，西工大发表论文 8 篇！是谁负责？羞也不羞！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于软件学院的若干政策，我其实是不太感兴趣的。自从他们两年三回爆改保研/推免政策和综测加分政策以后我就决定，&lt;strong&gt;凡是学院的决策，我都坚决不管&lt;/strong&gt;。虽然他们每搞一个政策就要蚕食一点我的利益，但是温水煮青蛙煮了这么久以后我人已经彻底麻了，大家一起开开心心安乐死也未尝不可，happy
coding life 大大方方耍起嘛，&lt;em&gt;New Game!&lt;/em&gt;
耍起嘛，&lt;em&gt;斯特拉的魔法&lt;/em&gt;
耍起嘛，虽然有传言说哈酱当年画斯特拉的魔法的时候是想要写超级 BE
的，可惜被编辑拴了未能实施，编辑这次好心办好事了。可惜你软 5:1
的男女比例注定了搞乐队的不是少女，少女不会搞乐队，至于人民群众喜闻乐见的
les
桥段更是想都别想。西安市冬天下沙子下土干燥不行，皮肤疯狂开裂东非大裂谷说是，甘油是什么东西不知道，已经被烘干机般的空气当路边一条一脚踢死了，夏天疯狂暴晒进行轰轰烈烈的地面大烧烤，小冰期晚期的抽象气候外加杜撰出来的厄尔尼诺现象是这样的，怎么你不服气？忆昔开元全盛日，小邑犹藏万家室，唐代气象条件你在哪……陕西这里不是大饼泡大肉汤就是大面条，吃了以后前凸后翘这一块，致敬传奇匈牙利老哥欧尔班的黄金分割螺线大胃袋，而且脸上噗噗冒油，洗都洗不掉，这时候就要推荐我们的
xx 至尊洗面奶了，请注意内容包含医疗建议注意甄别，但是 xx
至尊洗面奶到底是不如我的异维A胶囊，虽然肝功疯狂爆炸但是至少是不冒油了，顺便以后酒桌不去嗯造烈酒的理由又多了一条，连吃带拿好舒服呀。虽然但是这么近那么美周末请到杀人放火长安县，来了就别想走了，大家都别想做图灵派，谁说计算机大类都是
flooded by femboy 的，没看见啊？我 dress 项目 star 了 6
年了还不止，删库跑路共患难了，当时你们都跑到哪里去了？陕西老大爷说米脂婆姨绥德汉，有人说感觉不如米脂汉，我说你是真的牛批。扯远了，总之只要学院不要跳脸我，我一般不管他们干些什么。&lt;/p&gt;
&lt;p&gt;今年寒假的时候他们后知后觉意识到整个学院的学生根本写不来代码，
&lt;strong&gt;all in LLM 是这样的，你们才发现吗？&lt;/strong&gt;
加上郑院长在他的号称 100% 抬头率的软件工程课喜提 &amp;lt;30%
的到课率以后急火攻心，紧急召开全体教师大会，决定大搞特搞代码编写运动，
&lt;strong&gt;争取一个寒假赶交超电。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;宋代帝王有经筵讲读：有单日，亦有双日，有早讲，亦有晚讲！自端午至中秋，共一百天。自冬至至来岁春分，亦有九十天！
&lt;strong&gt;我们学生躲在游戏屋，不觉得羞愧！？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不思科研之极至，立不朽之功。
&lt;strong&gt;躲在家中玩耍游戏，置一流学问于不顾！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;——Yau&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;随后，学院成立专项工作组推进各项工作有序进行，同时督导题目命制，要求题目始终反映学院
&lt;strong&gt;力图培养能编码，会开源，讲文明，树新风的新时代计算机专业学生&lt;/strong&gt;
的根本要求。此外为保证公平还限制了 LLM
的使用，特别是在文档编写中的使用。 &lt;strong&gt;虽然通知和作业要求文档也是
LLM
slop，但是他们批判性使用，到底和我们无底线无下限无理论指导无政治挂帅的使用是有本质区别的。&lt;/strong&gt;
姜文在《阳光灿烂的日子》里面借某位龙套的台词说的很明白了：“这是大毒草，小孩子不要看！”这可以说是句句在理，一句顶一万句——
&lt;strong&gt;这是 LLM，小孩子不要用！&lt;/strong&gt;
虽然大家都荷尔蒙水平已经是成年人的水平了，但是本质上说到底还是小孩子呀。随后各项要求马上印发给辅导员，向
24/25 级同学传达学习。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不愿意做寒假项目的同学，不用来参加，
&lt;strong&gt;你们不会受到任何處分!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;学院最初的要求是 &lt;strong&gt;人人要参与，班班有成果，&lt;/strong&gt; 要我们
&lt;strong&gt;下定决心，不怕牺牲，排除万难，争取胜利。&lt;/strong&gt;
然而通知一发，24
级同学一片哗然，原来你们都不准备让大家过好年了，还要做作业哦！进来都一年多了，大家都是老油条了，学院的行为模式摸得门清，就是不做还能奈他何？25
级的同学 &lt;strong&gt;刚到西安，不熟地方，&lt;/strong&gt;
学院一通软硬兼施，双管齐下，两手都抓，两手都硬，他们便纷纷为了保研评优踊跃报名。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;得分最高的，奖一块华为手表。——Yau&lt;/p&gt;
&lt;p&gt;一等奖今年将获得 5000 元，二等奖 1000 元，……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;卧槽，软院分钱！？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;……最后果然还是没有顶住诱惑呀。&lt;/p&gt;
&lt;p&gt;可供选择的项目配置是这样的：&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;cJSON / HTTP server&lt;/li&gt;
&lt;li&gt;Lua Runtime / Redis&lt;/li&gt;
&lt;li&gt;LLVM (clang-tidy)&lt;/li&gt;
&lt;li&gt;大模型应用相关&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中，Tier 1 主要供 25 级选择；Tier 2 供 24 级；Tier 3 供 25
拔尖班；Tier 4 供 24 拔尖班。&lt;/p&gt;
&lt;p&gt;看起来很全面不是吗？
可是，他们面对的是一帮代码能力极差（至少他们如此认为）的学生；
&lt;strong&gt;他们怎么会认为这些人前脚还在拿 LLM
写代码，后脚就能够独立手工开发大型工程项目了？&lt;/strong&gt; 21st Century
Schizoid Man 是这样的。特别是 25 拔尖班的项目，
&lt;strong&gt;让一群编译原理没学过的学生去搞 LLVM？认真的吗？&lt;/strong&gt;
我确实不太明白，学生突然学会写代码这件事是在任务中途哪个地方发生的。虽然我也主张任务导向学习，
&lt;strong&gt;但是布置任务的时候显然是要考虑到学生的实际水平的呀。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;后续就学院如何解决不会编译原理（及 C++）一事询问了 25
级学弟。他的回答是，学院让他们设法自学。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我和我的队友选了 Lua Runtime。然后还能怎么样，做呗。&lt;/p&gt;
&lt;p&gt;学院本来的要求是开学即答辩并评分，
&lt;strong&gt;本组在开学前即超额完成任务，反倒是学院的答辩安排一拖再拖，反复愆延，也不知道在等谁。&lt;/strong&gt;
一直拖到了本学期几乎到了半期才开始匆忙组织答辩。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;大概知道自己无耻，从头到尾，秘密行动，直到答辩了，都没有告诉过我！
&lt;strong&gt;已经到了无耻的地步。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而且答辩仅给 5 分钟。至于 5
分钟可以深入介绍什么技术，我也不知道。最后出来的结果也是令人啧啧称奇。饼画的很大，组织上完全就是和稀泥，不公开不透明。后续我现场质询负责工作的老师也是一问就是，我提出的质询意见学院全都考虑到了，
&lt;strong&gt;但是怎么实际的组织情况表现出来不像这样呢！？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如，我问到，学院有没有准备统一的评分标准，
&lt;strong&gt;特别是针对答辩这种主观性较强的环节。&lt;/strong&gt;
学院老师表示有评分标准，并向我展示了评分细则。然而，细则上面的各项指标并未在答辩
QA 环节中体现，在场评委只是随机问一些乱七八糟的问题，
&lt;strong&gt;甚至其中一位评委似乎混淆了我们报告中提到的 x86 物理寄存器和 SSA
虚拟寄存器，围绕这一问题反复追问，双方在这个实际上根本不存在的分歧上浪费了大量时间。&lt;/strong&gt;
此外，按照评分标准的要求，每一评分项应当独立打分，然而在学院老师
&lt;strong&gt;破例&lt;/strong&gt;
向我展示的最终打分表上面，几位评委根本没有针对评分项打分，而是大笔一挥直接写一个数字了事。&lt;/p&gt;
&lt;p&gt;另外，本次作业还有一项相当重要的评分标准即 &lt;strong&gt;要求 RIIR&lt;/strong&gt;
，分数占比高达
30%。本组对此是保质保量完成了的。至于其他组的实际完成情况，我不知道；问学院老师，对方也没有正面回应。由于所有项目的代码最终都没有公开，我们自然也无从验证。&lt;/p&gt;
&lt;p&gt;不过这么重要的一项评分要求，到底在最终评分中发挥了多大作用，也只有作为组织方的学院自己知道了。&lt;/p&gt;
&lt;p&gt;最后本组拿了 1000 块钱。至于拿 5000 的都是什么怪物我也不知道，
&lt;strong&gt;因为学院老师婉拒了我关于开放所有组 codebase 以供大家 peer review
的请求。毕竟，如果这个项目最终评价的是代码能力，那么看看代码本来应该是一件再自然不过的事情。&lt;/strong&gt;
当然，RIIR 任务的实际完成情况，我们也就不得而知了。&lt;/p&gt;
&lt;p&gt;另外，拔尖班项目的排名几乎全部排在后面，这让我感到无比困惑。这些项目本身的学习成本显然远高于
cJSON 一类基础任务，甚至有些学生此前连编译原理和 C++ 都没有系统学习过。
&lt;strong&gt;那么，在最终评分时，学院究竟是如何处理不同项目之间的巨大难度差异的？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果完全按照最终完成度横向比较，那么高难度项目天然需要承担更高的学习成本；但如果任务难度根本不进入评价体系，我也不知道为什么要把它们设计成不同的
Tier。质询学院老师时候我也提及了此问题，对方称答辩前的初审有做分数归一化，但是并无赋分。至于答辩的复审的打分策略呢？对方对此未作正面回答。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;你们就是这么对待学院最希望往优秀人才培养的一批学生的吗？&lt;/strong&gt;
说实话，他们做的有点过分了。&lt;/p&gt;
&lt;p&gt;本学期又搞了个什么开源贡献纳入综测，然后找了包括我在内的几号人去开会。我本来准备去质疑这个方案的，
&lt;strong&gt;即使这是软院两年来唯一一个确实利好我的政策。&lt;/strong&gt;
结果讨论了半天我才反应过来，原来他们早就决定实施了，叫我们过去大概只是征求一下意见。搞半天我反对了个寂寞，只能说给他们提几个建设性意见，但愿最后别搞的太抽象就行了……&lt;/p&gt;
&lt;p&gt;反正也不知道这个寒假作业到底有没有起到解决学生写不来代码问题的作用。好吧，至少钱是拿到了，没啥损失。&lt;/p&gt;
&lt;p&gt;以上。&lt;/p&gt;</content></entry><entry><title>我如何停止焦虑并爱上 Vulkan</title><link rel="alternate" href="https://lizi.moe/posts/How-I-Learned-to-Stop-Worrying-and-Love-Vulkan/"></link><id>https://lizi.moe/posts/How-I-Learned-to-Stop-Worrying-and-Love-Vulkan/</id><updated>2026-08-20</updated><category term="C++"></category><category term="图形学"></category><category term="Vulkan"></category><summary>Mein Fuhrer, I can walk!</summary><content type="html">&lt;!-- digest: 9e1ebd91 --&gt;
&lt;p&gt;过去的两年里面，我总是十分急切地想要去证明一些什么事情，因而孜孜不倦地乐此不疲地投身于各种堪称自虐式的项目里面。这并不意味着我是个
masochist，大概只是越来越激烈的就业和学业竞争使我愈发偏执了吧。&lt;/p&gt;
&lt;p&gt;差不多一年前的这个时候
&lt;em&gt;（另一个漫无止境的八月呀……）&lt;/em&gt;，我下了一个大愿心，那便是要在大二写出一个自己的图形引擎。一个原因是我当时陷入了某种虚无，非常想要寻找一点事情来做然后从中获得一点方向感；其次是，要是这个东西做出来了，学院期末时候有一门小组作业可以直接套，届时会比较方便，而且可以留足时间给期末复习。&lt;/p&gt;
&lt;p&gt;去年八月底九月初，我正式开始实施这个雄心勃勃的计划。&lt;/p&gt;
&lt;p&gt;我最早跟的是 &lt;em&gt;Vulkan Tutorial&lt;/em&gt; 去学的 Vulkan 编程，但是
&lt;em&gt;Tutorial&lt;/em&gt;
最严重的问题在于，它代码耦合程度太高，而且大部分工作都是一些 boilerplate
的初始化工作，以至于我真正开始写这个项目的时候感到有些力不从心。因此，我换成了
&lt;em&gt;Vulkan Guide&lt;/em&gt; 进行进一步的学习。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Guide&lt;/em&gt;
的一大优点在于，它会给读者一些抽象和封装上的建议，而且它使用的是 Vulkan
1.3 API，而不是 &lt;em&gt;Tutorial&lt;/em&gt; 的 1.0
API，极大地简化了代码编写。比如说，它使用 &lt;strong&gt;Sync2&lt;/strong&gt;
同步原语，使用 &lt;strong&gt;Bindless Descriptor Set&lt;/strong&gt; 来避免大量的
descriptor 结构描述代码编写。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;自然，我们可以引入 glslang 或者 SPIRV-Reflect 之类的玩意来反射 spv
文件从而自动化生成这些 descriptor。但是这样会引入过多的依赖，
&lt;em&gt;Guide&lt;/em&gt; 说到底就是个入门教程，不需要搞的这么复杂。&lt;/p&gt;
&lt;p&gt;另外，使用 Bindless Set 有一些潜在的性能收益，可以在一定程度上减少
descriptor binding
的开销。现在的工业级别渲染器在处理大量纹理资源的时候是要用到这个特性的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另外 &lt;em&gt;Guide&lt;/em&gt; 使用了 1.3 的 &lt;strong&gt;Dynamic Rendering&lt;/strong&gt;
特性，极大减少了 Render Pass 和 Frame Buffer 的
boilerplate，降低了编写难度。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当然论灵活性，Vulkan 还有我们 &lt;strong&gt;大开历史倒车的 Pipeline Library
和 Shader Object Extension&lt;/strong&gt;。哦牛批，还有这种图形 API
哦，绕了一大圈居然回到原地了！&lt;strong&gt;Steering Committee
在赣森魔呀？怎么能走改旗易帜的邪路呢！？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;遗憾的是，当时我还在用 Windows，我的 AMD
显卡驱动不支持这一套骚操作，没法体验它带来的返祖 OpenGL
般的性能损失了。不知道苏姿丰女士在忙什么。现在的 Mesa
驱动好像是支持了，但是没啥去体验的热情了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vulkan 在我心中的完美形象正在崩塌，现在我只想不遇到
regression……（笑）&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Guide&lt;/em&gt; 更贴近实际工程，因而使用了 &lt;strong&gt;Vulkan Bootstrap
（vkb）&lt;/strong&gt; 项目来简化 &lt;em&gt;Tutorial&lt;/em&gt; 的一开始的大量 boilerplate
的部分。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我不认为大部分用户需要关心 Physical Device 如何选择，Device
如何创建，如何启用 Extension/Feature，如何开 Validation Layer，Instance
如何初始化等等，&lt;strong&gt;这些不重要！&lt;/strong&gt;
很多人就是一上来就被这些东西劝退了，反而没有触及到 Vulkan
真正关键和真正需要投入精力认真学习体会的地方。初始化也就是调一下 vkb
的事情而已。&lt;/p&gt;
&lt;p&gt;不过 Bootstrap
确实让我在很后面栽了跟头。三个月以后进行答辩前测试运行的时候，在我室友的双显卡电脑上总是选择
Intel 的集显而不是英伟达的独显。然后这个 Iris 的驱动还有问题会
segfault，即使我 prefer 了独显它还是选集显。后面发现是 vkb 的选择逻辑有
bug，不过这个 bug
去年九月就被人修了，然而在那之前我都把依赖配置好了……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另外 &lt;em&gt;Tutorial&lt;/em&gt; 严重简化的一部分是内存（特别是 device
memory）管理。我们知道小而多的内存分配效率很低性能很差，而且可能导致碎片化（自然我们可以想到用
memory pool 或者 arena
之类的），但是它这教程遇不到这情况，毕竟太简单了，需要的资源很少，于是也就彻底摆烂完全不教了。它直接让读者去做
further reading 自学，然后也不给在哪里 further reading，这……相比而言
&lt;em&gt;Guide&lt;/em&gt; 就非常人性化了，它直接推荐去用 &lt;strong&gt;Vulkan Memory
Allocator（vma）&lt;/strong&gt;。vma 是个封装好的 device memory
allocator，性能算是工业级的（至少肯定比我手写一个好），提供 Vulkan
风格的 API 和一些基本的 C++ 抽象，非常好用和简便。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Guide&lt;/em&gt;
到底只是一个教程，要是能参考现有的项目就比较好了。我参考的是一个叫做
&lt;strong&gt;Elias Daler’s Bikeshed Renderer（edbr）&lt;/strong&gt; 的实现。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;原作者有博客文章 &lt;a href=&quot;https://edw.is/learning-vulkan/&quot;&gt;How I
learned Vulkan and wrote a small game engine with it&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我的目的不是一个通用
RHI，我需要的就是一个可以很好很简便地完成到时候的期末作业的框架。而 edbr
作为 Daler
的个人项目也是非常简单的（虽然功能孱弱但是对于简单任务绰绰有余），我完全可以参考其实现。像什么
bgfx 之类的架构就过于复杂了。&lt;/p&gt;
&lt;p&gt;Daler 氏的重要设计理念是：&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;避免过度设计，&lt;strong&gt;特别是在学习进行中就自作聪明地提前设计和解耦合。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;用不到的功能就不做。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关于第一点，我一开始就吃了这个亏。当时刚开始做的时候拆了一堆模块，最后根本用不了还要来回改，非常恶心，最后直接全部删了重新写。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;From Blinker to RISC-V&lt;/em&gt;
教程里面也讨论了类似的问题。因为软件工程讲究的就是一个问题拆分，模块化和解耦之类的，以至于我们把这个原则在任何情况下都奉为无可辩驳的圭臬了。但是作者提到，他一开始也把模块拆的很细碎（DEC，ALU
什么的），结果项目反而难以组织。最后还是选择耦合到一起了，代码反而变简洁。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后的结果是一个巨大的 &lt;code&gt;VulkanEngine&lt;/code&gt;
结构体，大量的功能（包括但不限于各种初始化，渲染管线组合，surface/swapchain
管理，内存资源管理，窗口管理，输入事件处理）耦合在其内部。这是走向另一个极端了，其实也不太合适。&lt;strong&gt;But
anyway, it just works.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;关于第二点，edbr 的设计里面体现为 &lt;strong&gt;它只有几个固定的
pipeline&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Vulkan 的基础 API 是 immutable
pipeline，这我们都知道。相比之下，现代渲染引擎通常会在底层 immutable
pipeline 之上提供动态或者半动态的 abstraction，再由 shader compiler
根据材质的各类特征生成一堆 permutation。具体实现里自然少不了各种
compile-time selection，根据各种材质特征排列组合出一堆 variant，再拿个
cache 把它们管理起来，UE 的材质系统就是典型例子。这些 permutation
得交给实习生管理，怎么交？和平中交不成就只有动荡中交，搞不好就
permutation explosion 了。&lt;/p&gt;
&lt;p&gt;但是 edbr 是真的就几种管线状态，包括一个基础的 PBR Forward
Pipeline，一个 CSM Pipeline 和一个用于骨骼动画的 GPU Skinning Compute
Pipeline。而且这些管线之间的同步就是直接手写了几个了事。&lt;/p&gt;
&lt;p&gt;说到同步问题了，比较搞笑的是 &lt;em&gt;Guide&lt;/em&gt; 为了简便把所有的 memory
barrier 都设置成了一个非常 coarse-grained 的
&lt;code&gt;ALL_COMMANDS&lt;/code&gt;，我的代码也沿用了这个设计，如下：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;#cb1-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt; transitionImage&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;VkCommandBuffer cmdBuffer&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; VkImage image&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; VkImageLayout srcLayout&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; VkImageLayout dstLayout&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;#cb1-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkImageMemoryBarrier2 barrier&lt;span class=&quot;op&quot;&gt;{};&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;#cb1-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;sType &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;#cb1-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;pNext &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;#cb1-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;#cb1-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;srcStageMask &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-7&quot;&gt;&lt;a href=&quot;#cb1-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;srcAccessMask &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_ACCESS_2_MEMORY_WRITE_BIT&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-8&quot;&gt;&lt;a href=&quot;#cb1-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id=&quot;cb1-9&quot;&gt;&lt;a href=&quot;#cb1-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;dstStageMask &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-10&quot;&gt;&lt;a href=&quot;#cb1-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    barrier&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;dstAccessMask &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_ACCESS_2_MEMORY_WRITE_BIT &lt;span class=&quot;op&quot;&gt;|&lt;/span&gt; VK_ACCESS_2_MEMORY_READ_BIT&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-11&quot;&gt;&lt;a href=&quot;#cb1-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-12&quot;&gt;&lt;a href=&quot;#cb1-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;co&quot;&gt;// ... snip ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-13&quot;&gt;&lt;a href=&quot;#cb1-13&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt; &lt;/span&gt;
&lt;span id=&quot;cb1-14&quot;&gt;&lt;a href=&quot;#cb1-14&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkDependencyInfo dependencyInfo&lt;span class=&quot;op&quot;&gt;{};&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-15&quot;&gt;&lt;a href=&quot;#cb1-15&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-16&quot;&gt;&lt;a href=&quot;#cb1-16&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;co&quot;&gt;// ... snip ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-17&quot;&gt;&lt;a href=&quot;#cb1-17&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id=&quot;cb1-18&quot;&gt;&lt;a href=&quot;#cb1-18&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    vkCmdPipelineBarrier2&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;cmdBuffer&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;dependencyInfo&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-19&quot;&gt;&lt;a href=&quot;#cb1-19&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这玩意写的确实有点简单粗暴了，dependency
的范围明显太大太保守了，可能产生一些不必要的
ordering。但是鉴于我的简陋使用场景就一个 queue，而且也没有一堆 Render
Target 来回互相
sample，没有什么并行化的余地，想必影响不大。这样写起来还简单。&lt;/p&gt;
&lt;p&gt;另一个同步的问题涉及到 Fence 和 Semaphore。Fence 主要是涉及 Host 和
Device 之间的同步，Semaphore 主要是涉及到 Device
内部的同步。这是一个比较粗略的 distinction。&lt;/p&gt;
&lt;p&gt;为了最大化利用硬件资源，不难想到，我们可以同时渲染多个
frame，然后一个一个 present 到 swapchain。我当时好像是选的 3 frames in
flight。我写了如下的数据结构管理一个 frame 的 sync primitives：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;struct&lt;/span&gt; FrameData &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;#cb2-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkCommandPool commandPool&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;#cb2-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkCommandBuffer mainCommandBuffer&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-4&quot;&gt;&lt;a href=&quot;#cb2-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-5&quot;&gt;&lt;a href=&quot;#cb2-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkSemaphore imageAvailableSemaphore&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-6&quot;&gt;&lt;a href=&quot;#cb2-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkSemaphore renderFinishedSemaphore&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-7&quot;&gt;&lt;a href=&quot;#cb2-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-8&quot;&gt;&lt;a href=&quot;#cb2-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkFence inFlightFence&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-9&quot;&gt;&lt;a href=&quot;#cb2-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-10&quot;&gt;&lt;a href=&quot;#cb2-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    DeletionQueue deletionQueue&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-11&quot;&gt;&lt;a href=&quot;#cb2-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VulkanDescriptorAllocatorDynamic descriptorAllocator&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-12&quot;&gt;&lt;a href=&quot;#cb2-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中涉及 syncing 的主要是两个 Semaphore 和一个 Fence。Fence
的作用是，同步上一次对一个 frame 的操作和当次操作。显然，如果我们在一个
frame 尚在使用的时候就开始下一次绘制，就可能导致 race
condition，进而导致数据完全混乱。因此在绘制的开始：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;#cb3-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;MOE_VK_CHECK_MSG&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;#cb3-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        vkWaitForFences&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-3&quot;&gt;&lt;a href=&quot;#cb3-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;va&quot;&gt;m_device&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-4&quot;&gt;&lt;a href=&quot;#cb3-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;inFlightFence&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-5&quot;&gt;&lt;a href=&quot;#cb3-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                VK_TRUE&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-6&quot;&gt;&lt;a href=&quot;#cb3-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                VkUtils&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;secsToNanoSecs&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;fl&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;bu&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-7&quot;&gt;&lt;a href=&quot;#cb3-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;st&quot;&gt;&amp;quot;Failed to wait for fence&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-8&quot;&gt;&lt;a href=&quot;#cb3-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-9&quot;&gt;&lt;a href=&quot;#cb3-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;MOE_VK_CHECK_MSG&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-10&quot;&gt;&lt;a href=&quot;#cb3-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        vkResetFences&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_device&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;inFlightFence&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-11&quot;&gt;&lt;a href=&quot;#cb3-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;st&quot;&gt;&amp;quot;Failed to reset fence&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;首先等待这个 Fence。如果这是第一次使用这个 FrameData，那么 Fence
可以在创建时就指定为 signaled 状态（这里我设置成了 VK_TRUE），这样第一次
vkWaitForFences 就会直接通过；之后则需要等上一轮使用这个 frame
的工作完成后才能继续。在当次绘制的末尾：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;#cb4-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;MOE_VK_CHECK&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;vkQueueSubmit2&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_graphicsQueue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;submitInfo2&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;inFlightFence&lt;span class=&quot;op&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;让 Command Queue 在执行完后去 signal 这个 Fence。这是 in-flight frame
之间的同步。不过，渲染和呈现之间也需要同步，这就需要 Semaphore 了。&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb5&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb5-1&quot;&gt;&lt;a href=&quot;#cb5-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkResult acquireResult &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; vkAcquireNextImageKHR&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-2&quot;&gt;&lt;a href=&quot;#cb5-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;va&quot;&gt;m_device&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-3&quot;&gt;&lt;a href=&quot;#cb5-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;va&quot;&gt;m_swapchain&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-4&quot;&gt;&lt;a href=&quot;#cb5-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VkUtils&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;secsToNanoSecs&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;fl&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;bu&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-5&quot;&gt;&lt;a href=&quot;#cb5-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;imageAvailableSemaphore&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-6&quot;&gt;&lt;a href=&quot;#cb5-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    VK_NULL_HANDLE&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-7&quot;&gt;&lt;a href=&quot;#cb5-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;swapchainImageIndex&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;首先通过这个函数获取下一张 swapchain
图像，获取到后我们才可以渲染，同时通知
&lt;code&gt;imageAvailableSemaphore&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb6&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb6-1&quot;&gt;&lt;a href=&quot;#cb6-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkCommandBufferSubmitInfo submitInfo &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VkInit&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;commandBufferSubmitInfo&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;commandBuffer&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-2&quot;&gt;&lt;a href=&quot;#cb6-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkSemaphoreSubmitInfo waitInfo &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-3&quot;&gt;&lt;a href=&quot;#cb6-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        VkInit&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;semaphoreSubmitInfo&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-4&quot;&gt;&lt;a href=&quot;#cb6-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;imageAvailableSemaphore&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-5&quot;&gt;&lt;a href=&quot;#cb6-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-6&quot;&gt;&lt;a href=&quot;#cb6-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkSemaphoreSubmitInfo signalInfo &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-7&quot;&gt;&lt;a href=&quot;#cb6-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        VkInit&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;semaphoreSubmitInfo&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-8&quot;&gt;&lt;a href=&quot;#cb6-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;renderFinishedSemaphore&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-9&quot;&gt;&lt;a href=&quot;#cb6-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-10&quot;&gt;&lt;a href=&quot;#cb6-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-11&quot;&gt;&lt;a href=&quot;#cb6-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkSubmitInfo2 submitInfo2 &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VkInit&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;submitInfo&lt;span class=&quot;op&quot;&gt;(&amp;amp;&lt;/span&gt;submitInfo&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;waitInfo&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;signalInfo&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-12&quot;&gt;&lt;a href=&quot;#cb6-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;MOE_VK_CHECK&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;vkQueueSubmit2&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_graphicsQueue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;submitInfo2&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;inFlightFence&lt;span class=&quot;op&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在提交绘制指令的时候，我们要求 Command Queue 去 wait
&lt;code&gt;imageAvailableSemaphore&lt;/code&gt;，接着才执行渲染操作；渲染完了通知
&lt;code&gt;renderFinishedSemaphore&lt;/code&gt;，然后：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb7&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb7-1&quot;&gt;&lt;a href=&quot;#cb7-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkPresentInfoKHR presentInfo&lt;span class=&quot;op&quot;&gt;{};&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-2&quot;&gt;&lt;a href=&quot;#cb7-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-3&quot;&gt;&lt;a href=&quot;#cb7-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// ... snip ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-4&quot;&gt;&lt;a href=&quot;#cb7-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-5&quot;&gt;&lt;a href=&quot;#cb7-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;presentInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;pWaitSemaphores &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;currentFrame&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;renderFinishedSemaphore&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-6&quot;&gt;&lt;a href=&quot;#cb7-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;presentInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;waitSemaphoreCount &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-7&quot;&gt;&lt;a href=&quot;#cb7-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-8&quot;&gt;&lt;a href=&quot;#cb7-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkResult presentResult &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; vkQueuePresentKHR&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_graphicsQueue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;presentInfo&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样，驱动在渲染完毕后就可以把 swapchain 图像的内容呈现到 surface
啦。&lt;/p&gt;
&lt;p&gt;看起来非常好！&lt;/p&gt;
&lt;p&gt;在我的电脑上（当时还是 AMD 的神秘 Windows
驱动），这些东西都正常工作；但是我室友之一的英伟达驱动的 validation
layer 疯狂报错。它说什么一个 Semaphore
正在使用呢另一个地方又去用它了，不建议我这么做，还推荐我用 Timeline
Semaphore Feature。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里的错误指的是
&lt;code&gt;VUID-vkQueueSubmit-pSignalSemaphores-00067&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是 Semaphore
是一个非常基础的特性，怎么可能会出现一个如此基本的使用场景却需要我专门启用一个
feature 的情况呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Timeline Semaphore 是可以被 Host 和 Device
两边等待的，可以在一定程度上简化同步代码编写。&lt;/p&gt;
&lt;p&gt;另外，我现在 Mesa 的 validation layer 也能正常抓这个 Error 了；以前的
proprietary driver 简直费拉不堪！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;排错以后我才意识到我的 swapchain image 的同步资源没有任何义务和
in-flight frame 绑定！首先，它们的数量就完全可以不是 1:1
的，也许我可以有 2 个 in-flight frame，但我的 swapchain image 完全可以有
3 个。但即使就是 1:1 的数量也可能出问题！假如说都是 2 个吧。渲染时候获取
in-flight frame 的序列可能是 &lt;code&gt;0, 1, 0, 1, ...&lt;/code&gt;，但是
swapchain image 在 present 完毕后就绪的序列完全可能是
&lt;code&gt;0 1 1 0 ...&lt;/code&gt;，实际情况如何是取决于驱动那边的情况的！在第三次渲染结束后试图使用的是
&lt;code&gt;FrameData[0]&lt;/code&gt; 的
&lt;code&gt;renderFinishedSemaphore&lt;/code&gt;，但是驱动那边这个 semaphore
仍然被第一次渲染结束后的 presentation 阶段占用，这时候再去
&lt;code&gt;vkQueuePresentKHR&lt;/code&gt; 就会复用一个被占用的
semaphore，导致报错。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参阅：&lt;a
href=&quot;https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html&quot;
class=&quot;uri&quot;&gt;https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;要修复我们把 &lt;code&gt;renderFinishedSemaphore&lt;/code&gt; 跟 swapchain image
绑定而非 in-flight frames 绑定，把它从 &lt;code&gt;FrameData&lt;/code&gt;
解耦出去，从而避免因为 in-flight frame 和 swapchain
两者循环周期或者单纯的就绪序列不同导致的 semaphore 冲突：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb8&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb8-1&quot;&gt;&lt;a href=&quot;#cb8-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkSemaphoreSubmitInfo signalInfo &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-2&quot;&gt;&lt;a href=&quot;#cb8-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        VkInit&lt;span class=&quot;op&quot;&gt;::&lt;/span&gt;semaphoreSubmitInfo&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-3&quot;&gt;&lt;a href=&quot;#cb8-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;va&quot;&gt;m_perSwapchainImageData&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;swapchainImageIndex&lt;span class=&quot;op&quot;&gt;].&lt;/span&gt;renderFinishedSemaphore&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-4&quot;&gt;&lt;a href=&quot;#cb8-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-5&quot;&gt;&lt;a href=&quot;#cb8-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-6&quot;&gt;&lt;a href=&quot;#cb8-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// ... snip ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-7&quot;&gt;&lt;a href=&quot;#cb8-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-8&quot;&gt;&lt;a href=&quot;#cb8-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;presentInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;pWaitSemaphores &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_perSwapchainImageData&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;swapchainImageIndex&lt;span class=&quot;op&quot;&gt;].&lt;/span&gt;renderFinishedSemaphore&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-9&quot;&gt;&lt;a href=&quot;#cb8-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;presentInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;waitSemaphoreCount &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-10&quot;&gt;&lt;a href=&quot;#cb8-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-11&quot;&gt;&lt;a href=&quot;#cb8-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;VkResult presentResult &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; vkQueuePresentKHR&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;m_graphicsQueue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;presentInfo&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;然后这个问题就修好了。&lt;/p&gt;
&lt;p&gt;然后就是 CSM Pipeline 的问题。我是用了个有好几个 array layer 的
device image 来实现级联效果，然后再在别的 Pipeline 去 sample：&lt;/p&gt;
&lt;pre class=&quot;slang&quot;&gt;&lt;code&gt;float4 sampleTextureArrayLinear(ImageId index, float2 uv, uint layer) {
    Texture2DArray t = u_textureArrays[NonUniformResourceIndex(index)];
    SamplerState s = u_samplers[SAMPLER_LINEAR_ID];
    return t.Sample(s, float3(uv, (float) layer));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是死活不对，后面调了半天发现是我创建的绑定到 Bindless Set 的
ImageView 没有指定为 &lt;code&gt;TYPE_2D_ARRAY&lt;/code&gt;，没法这样 sample
否则出错。Hack 了一下就好了：&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb10&quot;&gt;&lt;pre
class=&quot;sourceCode cpp&quot;&gt;&lt;code class=&quot;sourceCode cpp&quot;&gt;&lt;span id=&quot;cb10-1&quot;&gt;&lt;a href=&quot;#cb10-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;imageInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;arrayLayers &lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-2&quot;&gt;&lt;a href=&quot;#cb10-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    imageViewInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;subresourceRange&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;baseArrayLayer &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-3&quot;&gt;&lt;a href=&quot;#cb10-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    imageViewInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;subresourceRange&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;layerCount &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; imageInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;arrayLayers&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-4&quot;&gt;&lt;a href=&quot;#cb10-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    imageViewInfo&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;viewType &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; VK_IMAGE_VIEW_TYPE_2D_ARRAY&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-5&quot;&gt;&lt;a href=&quot;#cb10-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个问题折磨了我整整两天，我差点都要爆了。要说有成就感是假的，为什么？要是就这一个问题也就罢了。一个问题解决了又来一个问题，每个问题都要耗上一两天，这谁受得了。光就
CSM 一个模块，前后便出现了三四个怪异问题需要我去反复调试。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本来以为这就是 CSM
最后一次坑我了，结果没想到几个月后又坑我一次。最早的 shader 不是用 Slang
而是用 GLSL 写的，结果迁移到 Slang 以后就不对了。最后发现把一个 dynamic
indexing 改成一个 switch case 穷举就好了，我觉得是 Slang SPIR-V
后端的问题。这都是些什么鬼！！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;和 edbr 的 Forward Pipeline 不同的是，我采用的是 Deferred
Shading。我主要是参考 &lt;em&gt;Learn OpenGL&lt;/em&gt;
的原理进行的实现。我需要提供以下数据：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mesh object 的变换矩阵&lt;/li&gt;
&lt;li&gt;此变换矩阵的逆矩阵&lt;/li&gt;
&lt;li&gt;网格顶点缓冲区的 SSBO Device Address&lt;/li&gt;
&lt;li&gt;放全局渲染信息的 SSBO Device Address&lt;/li&gt;
&lt;li&gt;网格物体的材质信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我的数据都是通过 Push Constants（PCS）
传递的。但是，但是！整个管线中各阶段的 Push Constant 大小加起来不能超过
128B，光那两个 &lt;code&gt;float4x4&lt;/code&gt; 都 128B
了，怎么办呢？首先我不能把它放到全局渲染信息的 SSBO
去读取，它有自己的同步时机，乱动的话数据会 corrupt。我也不希望为了这个
Pipeline 开一个 binding，要不然那么多 bindless
代码就白写了。于是我开动脑筋，反正每个逆矩阵不一样的数据只有个
&lt;code&gt;float3x3&lt;/code&gt;，我直接把 PCS 里面 3x3 以外的地方全砍了，在 shader
里面再补齐不就行了？这下对了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其实不一定是 128B，主要是看支持的
&lt;code&gt;maxPushConstantsSize&lt;/code&gt;。但是在我的电脑上它就是 128B。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是屏幕上没有东西，怎么回事？闹了半天是 cull mode
的问题，直接把全屏三角形给 cull 了，关闭 culling
就好了……怎么到处出问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;还有 Vulkan 的 NDC 和 depth 也跟 OpenGL 不一样。GL 的 depth 是
&lt;code&gt;[-1, 1]&lt;/code&gt;，Vulkan 是
&lt;code&gt;[0, 1]&lt;/code&gt;，总之是各方面的坑爹。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后我的架构是一个从加载资源到绘制完全隐式的 API。只能读取一个
Mesh，指定变换，然后往绘制队列里面提交，最后绘制，整个流程只能用这套固定的管线。而且资源的
lifetime 也非常不明晰，Host 和 Device 资源到区分也混乱，比如说 mesh
读取以后所有资源都被发射到显存了，对于 Host
这边的程序完全不可见，而且资源何时释放也难以控制，部分原因是耦合度太高了。总之是牺牲了挺多灵活性的。&lt;/p&gt;
&lt;p&gt;但是，不管怎么说，至少理论上是可以做一个完整的游戏了。做不出来怎么办？凉拌，自负
GPA 盈亏，延毕吧！但是吾辈青年就是要和 Khronos Group
一争高下。我才不怕泼冷水，一听 Graphics API 我就高兴，非常的
excited。这是课程自动安排，不可能不写！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mein Fuhrer, I can walk!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们组最后做的是一个多人 FPS 游戏，大体上是按照 CS
爆破模式设计的玩法。&lt;/p&gt;
&lt;p&gt;然而在答辩前测试的时候发现同一队的角色的模型动作是一模一样的（注意每队模型都一样），比如操作上一个玩家走另一个静止，那么可能两个玩家都走，或者都静止。最搞的是，如果一个玩家死了躺在地上，那么可能马上整个队的玩家全趴地上了，但是他们还能来回走动进行攻击，很有节目效果。&lt;/p&gt;
&lt;p&gt;然而此时距离我概率论考试已经不到一周，只好紧急开始复习，等到考完试答辩前再来修复。&lt;/p&gt;
&lt;p&gt;考完试后再检查。设计上我是会先走一个 compute pipeline 去进行一个
skinning，但是这个 skinned vertex
的数据不能把原本的数据覆盖了，所以要给它一个 buffer
来放。然而最早我的动态数据，也就是 skinned vertex buffer 是和静态的 mesh
data 一起管理的，导致一个模型共用一个
buffer，最后的效果就是大家的动作都一模一样了。后面换成了一个动态扩容的独立
buffer 就修复了。&lt;/p&gt;
&lt;p&gt;我的队友还是觉得 UI 不好看。于是我又用我的低配 Render Graph
加了炫光的 VFX，这下他们终于满意了。&lt;/p&gt;
&lt;p&gt;然而答辩当天在场的学院的老师们对我们的游戏似乎兴致不高。其中一位老师让我们打开
codebase
展示一下，结果打开发现不是咖啡语言又勒令我们关掉，转头让我们展示文档，而且似乎对文档里面的错别字和标点运用过分关心了，背后原因暂且未知。&lt;/p&gt;
&lt;p&gt;我的队友之一觉得这多少有点傲慢了。他觉得他们答辩提问的问题和我们做出来的产品毫无关联，简直满嘴跑火车，因而感到十分气愤，特别是当他发现分数分配下来本组只有我刚好没法拿
3.9
的绩点时（虽然我对此并没有什么意见，这个数字对我来说意义不大，我不太高兴的点是答辩当天没有老师跟我进行任何具有建设性的技术讨论），于是他写了一大段文本，去找学院负责管理本课程的两位老师进行申诉。值得我们欣慰的是，学院老师重视了我们的申诉，并对我们的成绩进行了重新评估。这下，我的队友终于消停了。&lt;/p&gt;
&lt;p&gt;btw，因为我一直写代码，最后不得不三天速通了概率论，最后也是风风光光取得了超美丽的绩点。&lt;/p&gt;
&lt;p&gt;所有的工作都结束以后，我并没有变得很开心或者很有成就感；事实上，在到处夸耀和经历了一阵短暂的令人目眩的狂喜后，我陷入了某种更大的虚无，这虚无感比去年八月的时候来的还要剧烈。我想，或许只要我再写一个更大的项目，我便会有真正的成就感，我便不会这么麻了，只要再写一个项目……&lt;/p&gt;
&lt;p&gt;很遗憾的是，在那之后，我并未能找到这样的一个项目来写。我意识到我整天就光顾着对着那块显示屏敲打键盘了，对自己的生活过的如何或者身边的人怎么样完全是不闻不问不管不顾，简直不能更卑琐了，但说实话我也不太清楚该从何时以何种形式做出改变。祸不单行，在那之后我又在情感上遭遇了挫折，随后其他各种破事也接踵而至。&lt;/p&gt;
&lt;p&gt;自此我就没有一天是不难受的。&lt;/p&gt;
&lt;p&gt;不过嘛，现在想来 Vulkan
其实还是挺好玩的。那还能怎么样，继续用呗。&lt;/p&gt;</content></entry><entry><title>我们的国产2D动画……</title><link rel="alternate" href="https://lizi.moe/posts/The-Future-of-Chinese-Anime/"></link><id>https://lizi.moe/posts/The-Future-of-Chinese-Anime/</id><updated>2026-08-05</updated><category term="杂谈"></category><category term="动画"></category><summary>会变成，什么样子……</summary><content type="html">&lt;!-- digest: 3306d94b --&gt;
&lt;p&gt;前两天有人建议我去看B站新上的国产动画《记忆管理局》。&lt;/p&gt;
&lt;p&gt;其实这部的一些预告啊宣传片小短片什么的我之前倒是看过，不过看完了也没有太关注（因为觉得等到播出的时候大概率是会给我推的）。而且居然选了今年各路神仙打架（包括但不限于科学猴的《穹庐下的魔女》《攻壳机动队》和京都动画的
&lt;em&gt;被寄予厚望然而爆雷的&lt;/em&gt;
《二十世纪电气目录》）的第三季度上线，想必资方和制作组都对它期待很高。&lt;/p&gt;
&lt;p&gt;不过前两天看动画片的兴致约等于没有，于是一直拖着。结果不出几个月前所料，B站开屏广告果然给我推广了。我看还不行吗！？&lt;/p&gt;
&lt;p&gt;于是去看了一下。&lt;/p&gt;
&lt;h2 id=&quot;我看火药方糖摔跤社搞的不错&quot;&gt;我看火药方糖摔跤社搞的不错&lt;/h2&gt;
&lt;p&gt;作画非常精湛，我认为和大部分日本公司正面对比完全够了。动作张力和镜头设计和演出都很有想法，确实就是在炫技，而且我是真的觉得炫的挺成功的。&lt;/p&gt;
&lt;p&gt;个人感觉制作组受到 Trigger
的影响比较大。另外还有汤浅政明的风格和一种《FLCL》的既视感（不过考虑到
Trigger
的很多成员也参与了《FLCL》的制作也算正常？）。明快的色彩，夸张的运动表现，整体观感还是很有活力滴。&lt;/p&gt;
&lt;p&gt;顺带一提女主角之一的人设似乎有点春原晴子的既视感。&lt;/p&gt;
&lt;p&gt;不论是一般场景的精细程度还是战斗画面的表现力和张力我觉得都是完全够的。即使不考虑制作组是第一次做原创独立长篇动画这一点，他们在这部的制作里展现出的工业能力也已经非常优秀了。&lt;/p&gt;
&lt;h2
id=&quot;我不希望再出现剧情空洞言之无物的事情不然那么多原画师的原稿就白画了&quot;&gt;我不希望再出现剧情空洞言之无物的事情；不然，那么多原画师的原稿就白画了&lt;/h2&gt;
&lt;p&gt;首先要明确一件事情，画面也好音乐也好配音也好，只是为了做好一件事情服务的。是什么事情呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;很显然是讲故事。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;首先动画不是作画集或者作品集或者角色设定集，也不是什么概念设计的展览会。动画是要来讲故事的。如果连故事都立不起来，那么投入的资源其实都很难最大限度发挥作用。&lt;/p&gt;
&lt;p&gt;本作制作组犯的最大错误就是他们完全没有意识到这一点，以至于过分沉迷展示作画而完完全全忽略了故事和叙事本身的重要性，剧情也好，设定也好都是背景板罢了。真是成也炫技，败也炫技。&lt;/p&gt;
&lt;p&gt;剧情推进混乱，叙事节奏支离破碎，人物弧光塑造欠缺，东一榔头西一棒槌，这样我怎么看？看的时候我对接下来会发生什么完全不感兴趣，我连正在发生什么都闹不明白。有人说坚持几集就好了最后肯定会豁然开朗（暂且不知道在后面四集还没出来的时候，他们是如何
jump to this conclusion
的），不过有没有一种可能性，要欣赏你的作画有一个前设条件是，我得先看下去你的作品；要看下去你的作品，我得先浸入你的剧情，但是你的剧情现在属于是一个
&lt;code&gt;Option::None&lt;/code&gt; 的状况，那么我到底要沉浸到什么里面？
&lt;strong&gt;悲しみの海？&lt;/strong&gt;
结果就是，我完全看不下去，越来越想跳着看，专拣作画好的地方看，至于剧情啊设定啊什么的已经无所谓了。我是来看你们烧钱做作画
mad
的吗？为什么我不去看我的老学长，绵阳中学优秀破鞋毕业生腹疼爆公爵的作画
mad 视频？（Credit：&lt;a href=&quot;mailto:Bilibili@符腾堡公爵&quot;
class=&quot;email&quot;&gt;Bilibili@符腾堡公爵&lt;/a&gt;，永远缅怀）&lt;/p&gt;
&lt;p&gt;有人说本作的缺点在于，音乐和画面的配合不好，要是和《FLCL》一样有 The
Pillows
制作的无时无刻播放的牛逼的配乐托举情绪，整体表现力会提升一个层次云云。没什么问题，但是现在缺的是表现力吗？显然不是。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;是故事呀……&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更让我感到一言难尽的是世界观的处理方式。制作组一开场便引入了大量设定对着观众狂轰滥炸，这个组织那个组织，这个物件那个物件，就连作为核心设定的
Bug
人这一概念讲了半天，我还是不知道到底是要为了什么服务，最后的感觉也就是能负责一个打怪升级的非常
cliche
的效果。最后在我看来的效果就是，制作组造出一堆看起来名字很潮酷的设定，然后把这些东西一股脑塞给观众，再捏了一堆
OC 让他们打架斗殴。&lt;/p&gt;
&lt;p&gt;最后这些设定我一个都没有记住，还是一头雾水。&lt;strong&gt;而且这还是我看了制作组提供的设定表再看的情况下，&lt;/strong&gt;
遑论没有看过设定表的观众了。&lt;/p&gt;
&lt;p&gt;于是乎这整个作品呈现出的效果更像是制作组在展示
worldbuilding，孜孜不倦地介绍自己的 OC
世界观。介绍世界观无所谓，《罗小黑战记》的很多角色设定就是源于寒木春华的员工的
OC，《魁拔》的世界观更是不知道大到哪里去了，但是它们至少是通过故事一步步把世界观潜移默化地传达给观众的。但是这部的制作组似乎是希望观众先把他们过去短篇和设定和企划书全部学习一通，再去小红书上面找一堆他们的老粉的解读深化理解，然后他们才能开始讲故事。&lt;/p&gt;
&lt;p&gt;如果这部是一个 cult
片，那我对读各种前置材料没有任何意见。但是很遗憾，它是一个面向大众的作品，我不是他们的粉丝，我只是一介观众，我没有先去读冗长的设定再来观看动画的责任。&lt;strong&gt;我是来看动画的，你们要干什么！&lt;/strong&gt;
我不是来看设定集的，设定的花哨和世界观的完备不能替代叙事；恰恰相反，越是复杂的世界观，越需要更优秀的剧情来支撑。如此把世界观揉成一坨软广性质的东西，然后就武断地管它叫剧情叙事，是不是有点不合适呢？&lt;/p&gt;
&lt;p&gt;类似的，隔壁 Trigger 的《Kill la
Kill》（虽然有超展开而且前半段也有一种打怪升级的 deja
vu）的设定也是随着剧情发展逐渐铺陈出来的呀，甚至战斗纤维这么一个核心设定都是有了一定的铺垫，等到角色经历推动到合适的时候，才自然而然的揭晓的。观众是要在剧情推进中逐渐理解你构造的世界，而不是先站在上帝视角理解你的世界才能看剧情。不然乐趣还剩多少呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;……而且《Kill la
Kill》核心设定就这么一个也整出了二十几集的容量，他们整这么多核心设定是要做银河英雄传说一样的一百集大型电视剧吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2
id=&quot;意识流是怎么一回事搞了很久也没搞清楚&quot;&gt;意识流是怎么一回事，搞了很久也没搞清楚&lt;/h2&gt;
&lt;p&gt;有人声称本作的风格是意识流，是会排除掉不对电波的人的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;那我说鲨卷风也是意识流超级文艺逼小众逼作品，有没有懂的。要是剧情稀碎都能拿意识流来掩护了，那我说一堆烂片都是意识流了（不过本作离烂片范畴还非常远），无非大家品味低下，过去主义遗毒鉴赏不了未来主义潮酷大作，鉴定为吃意大利面吃太多导致的，赶紧去
Piazzale Loreto
倒挂金钩做几个卷腹锻炼核心力量大步迈进后现代。&lt;strong&gt;我寻思也不是人均乔伊斯普鲁斯特或者今敏啊？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实验性的表达当然是应该鼓励的，但是做实验不代表彻底的混乱，&lt;strong&gt;特别是在（如上文提到的）本作是一部面向大众播映的作品的情况下。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;汤浅政明监督的《四畳半》《Mind
Game》很意识流了；夏目真悟负责编剧的《Sonny
Boy》很电波了。但是它们都有一条观众可以抓住的主线。哪怕制作组没有事无巨细不厌其烦地解释一些细节，我们依然能知道角色为什么而前进，也能理解到艺术家通过作品最后希望传达什么样的思想。&lt;/p&gt;
&lt;p&gt;前文提到本作让我想起《FLCL》。《FLCL》它就一 6 集的
OVA，容量比《记忆管理局》还少，也充满了大量的隐喻和跳跃和象征性的意识流的表现。但是它作为一个非常典型的
coming-of-age 类型的故事，它的叙事是完整的。&lt;/p&gt;
&lt;p&gt;剧情一团浆糊的作品，只要拿出个意识流当免死金牌，就可以立体防御一切批评和质疑吗？那以前以后所有在叙事上一塌糊涂的作品恐怕都完全能如此滥用这个解释为自己脱罪了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我缺的叙事这块谁给我补呀？&lt;/strong&gt;
观众总得有个什么锚点可以抓住吧。&lt;/p&gt;
&lt;h2
id=&quot;以作画绚丽为纲再到以剧情建设为中心&quot;&gt;以作画绚丽为纲，再到以剧情建设为中心&lt;/h2&gt;
&lt;p&gt;还有 4
集可以抢救一下，虽然大概率都已经做完了（不过考虑到这整个动画的制作前后跨度达到了令人感到惊讶的六年，后续风格变成熟也是有可能的）。如果后续在宏观的剧情上可以（不知道通过什么超级雷霆手段）把所有坑悉数填上，并顺利收束剧情，而且微观的叙事上能够做到不像现在这样一片狼藉，那我觉得也不是没有挽回的余地。&lt;/p&gt;
&lt;p&gt;然而在我看来制作组在创作思路上出现了重大偏差。他们似乎相信，只要画面表现足够绚丽夺目，观众自然会被吸引。很遗憾，我做不到不带脑子看。作画可以让我点开这个作品（开屏广告真给我吸引到了），但是如果要想观众真正记住这部作品，还是先好好打磨了剧情再说吧。&lt;/p&gt;
&lt;p&gt;国内动画产业已经不再缺少能够完成高质量画面的团队。我认为这么多年的锻炼以及和日本公司的合作下来，国内的创作者应当已经具备了和日本本土动画从业者平齐甚至比日本更胜一筹的水平。而且后面还拉来了大股东（李旎）投资，好像东西已经齐活了？我们马上就可以做出又新又好的国产2D动画了！没有什么需要做的了！是这样吗！？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;……文艺工作者要讲好中国故事、传播好中国声音、阐发中国精神、展现中国风貌。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;拿到了钱有了技术，就请充分利用这么优渥的条件好好讲故事。&lt;/p&gt;
&lt;h2 id=&quot;不要总想搞个大新闻&quot;&gt;不要总想搞个大新闻&lt;/h2&gt;
&lt;p&gt;我比较担心的是在这之后会发生什么。原创动画本来就是一个高投入高风险长周期的事情。创作能力的积累，需要时间。问题在于，目前国产原创动画环境往往缺少允许失败的空间。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;日本本土倒是有制作委员会模式分摊风险，而且有成熟的业务流程，这种情况下稍微好点（尽管如此近年除了头部公司也鲜有人愿意冒险）。本国目前的制作和商业化模式相对还不太成熟，但我认为本国大可不必生搬硬套日本的模式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目前看来，资方是有把本作向国产神作方向炒作的意图。如果制作组这部作品不温不火褒贬不一，资方不愿意继续注资，那团队可能又得回到青黄不接，靠接外包过活的日子，以几年的周期缓慢积攒资源，等待下一次机会再去试错。叙事能力和作画的技术一样，都是需要在不断练习中积累的，如果每一次原创的尝试都因为失利而中断，那么创作者很难积累属于自己的创作经验。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人生能有几回搏……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以总体而言，我还是对这部作品抱持相对宽容的看法的，并不应该用要求大师的眼光去看待几位独立创作者，当然前提是制作组能够正确面对自己作品存在的问题，并在未来的作品中尽可能避免同类问题。本作虽然算不上优秀，但也绝对谈不上所谓的烂片。退一万步讲，我认为本作单凭作画一项，就足以证明国内2D动画已经有了相当可观甚至远远超乎某些人刻板印象的工业水平了。这一点我认为具有一定的里程碑意义。&lt;/p&gt;
&lt;p&gt;不过这说到底只是个开始。如前文所述，国产2D动画产业不缺技术人才，但是有再多技术人才，没有把这些技术人才组织起来讲一个完整故事的人，最后也只能成为一个先进的外包工厂，始终跟在日本动画后面亦步亦趋，而无法形成真正具有原创能力的动画产业链，我非常不希望看到这样的局面。&lt;/p&gt;
&lt;p&gt;然而比起技术和编剧人才，我们更缺乏的或许是一群愿意长期投入这个行业的人。&lt;strong&gt;不管怎么说，即使现在这种尝试还略显不成熟，至少我们已经知道，还有一群人愿意去做这种吃力未必讨好的事情，我觉得这种尝试本身就是有价值的。至少在这个国家还有人愿意去坐这个冷板凳，这种情怀实在是难能可贵的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那么，国产在地2D动画的出路在哪里呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;God knows…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;无论如何，饭要一口口吃，路要一步步走。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;期待以后更多国内艺术家创作出更佳优秀的作品。&lt;/p&gt;
&lt;p&gt;以上。&lt;/p&gt;</content></entry><entry><title>博客迁移说明</title><link rel="alternate" href="https://lizi.moe/posts/Notes-on-Blog-Migration/"></link><id>https://lizi.moe/posts/Notes-on-Blog-Migration/</id><updated>2026-08-01</updated><summary>乔迁新居，可喜可贺。</summary><content type="html">&lt;!-- digest: 8c4dcb5c --&gt;
&lt;p&gt;终于决定不用 Hexo 了，于是用又新又好的 dsv4-flash
拿蛤丝壳写了个自用的博客框架。毕竟我一直都是国模的坚定支持者呀。&lt;/p&gt;
&lt;p&gt;本来想要把之前写的东西往这里迁移一部分，结果发现当初刷系统的时候备份那些
Markdown 的硬盘放学校里了。没办法，改天吧。&lt;/p&gt;</content></entry></feed>