C-WebGL: Vulkanオブジェクトをキャッシュする
遅い
というわけでSwiftShaderで安定して絵が出るようになったが、めっちゃ遅い。1フレームシティ。これはその筈で今はVulkanのオブジェクトを1描画毎に生成→破棄を繰り返しているため。SwiftShaderはオブジェクトの生成時にJITCもするのでその負荷が重い。
GPUではハードウェアを設定するだけで終わったりするので、この辺の負荷は見えづらい。
オブジェクトのキャッシュを実装することでこの辺を常識的な速度にしていきたい。
Sampler
90fpsくらいになった。90倍の高速化!もうこれで良いんじゃないかな。。
シザーテストを修正
なんか左のWindowの内容が出ていないのが気になるので修正した。Vulkan 1.1ではNegative viewport機能があり、負のサイズのviewportを設定することで座標系をDirectXやMetalと同じにできる。が、viewport座標を調節したためscissorの座標も調節の必要がある。
... いや冷静に考えるとなんかおかしい気がするな。。ScissorはViewport座標なんだから同じように動かないといけない気もする。。
RenderPassとFramebuffer
オブジェクトは64bitのモノトニックカウンタをコンテキストに用意して、Vulkan側のオブジェクトを取得する度にユニークな番号が振られるようにした。
例えば、RenderPassはVulkan側のImageViewオブジェクトに依存するので、
という処理でキャッシュを実現する。
モノトニックなカウンタを使うのは、ポインタ値は再利用されてしまう可能性があるため。
ScissorとViewportのDynamic state化
Vulkan的なPipeline stateをキャッシュする前に、ScissorとViewportは頻繁に変更されてもキャッシュに影響が無いようにDynamic stateに逃がしておく。というかDirectXもMetalもこれらはDynamicしか無いので何故Vulkanがこれらをpipeline stateに含めたのか。。まぁシェーダでやる方向性も無くは無い気もするけど。。
どのAPIでもシザーテストを無効化する方法は提供されない。とりあえず無効な場合はViewport全領域がpassするように設定しておく。
Pipelineのキャッシュ
本命のPipelineのキャッシュ ...といっても、SwiftShaderの場合何もしなくてもそれなりに賢くキャッシュしてくれるためこれ単体ではあんまり効果が出ない。
Pipelineの構成要素を構造体に引き出し、それを単に memcmp
で比較してキャッシュにヒットしたかどうか判定している。 ...たぶんハッシュとか取って効率化した方が良いんじゃないかという気はする。
struct cwgl_backend_pipeline_identity_s {
/* Cache data for global context */
uint64_t framebuffer_ident;
uint64_t program_ident;
float blend_color[4]; // FIXME: Dynamic?
VkPipelineRasterizationStateCreateInfo rsi;
VkPipelineMultisampleStateCreateInfo msi;
VkPipelineDepthStencilStateCreateInfo dsi;
VkPipelineInputAssemblyStateCreateInfo iai;
VkPipelineColorBlendAttachmentState cbas_color; /* for color attachment */
/* Cache data for vertex attr fetching */
int bind_count;
VkVertexInputAttributeDescription attrs[CWGL_MAX_VAO_SIZE];
VkVertexInputBindingDescription binds[CWGL_MAX_VAO_SIZE];
};
Pipelineキャッシュの対象には、ラスタライザの構成情報と頂点アトリビュートのバッファフォーマットが含まれる。後者がpipelineに入っているのは、Vulkanが頂点アトリビュートのフェッチ自体もシェーダに逃がせるように配慮しているためと見られる。
CWGL_MAX_VAO_SIZE
は簡単のために固定サイズにしていて、これが頂点アトリビュートの最大数になる。
Blend colorはDynamic stateに追い出してPipelineからは外しても良いかな。。