C-WebGL: Vulkanバックエンドの計画
OpenGLES2でWebGLを実装することはできたので、次にVulkanにバックエンド部分を移植していく。WebGL1には大量のコマンドが存在するが、バックエンドで直接的に実装しなければならないのはわずか 20 コマンド。それに同期機構やらシェーダーの管理やらを付け加えれば絵が出る。。。はず。。。
描画系コマンド
| WebGL | Vulkan |
|---|---|
| cwgl_drawArrays, cwgl_drawElements | vkCmdDraw |
| cwgl_clear | vkCmdClearColorImage, vkCmdClearDepthStencilImage |
| cwgl_readPixels | vkCmdBlitImage または vkCmdCopyImage してから vkMapMemoryしてコピー |
| cwgl_renderbufferStorage , cwgl_checkFramebufferStatus | vkCreateRenderPass 、 vkCreateFramebuffer と vkCreateImageView |
| cwgl_finish | vkWaitForFences |
| cwgl_flush | なし(明示的に vkQueueSubmit ) |
実際には、当然 これら以外の drawstate 設定 も移植する必要がある。
オブジェクト
Vulkan-GuideのDecoder ring や https://www.khronos.org/assets/uploads/developers/library/2016-cedec/Khronos-and-Vulkan-CEDEC-Aug16.pdf にマッピングがあるが、正確な対応はしない。例えば、RenderbufferとTextureは同じ VkImage のフラグ、ビュー違いになる。
| WebGL | Vulkan |
|---|---|
| Buffer | VkBuffer |
| Shader | VkShaderModule |
| Program | pipline内のShader stageにModuleを入れる |
| Texture | Samplerつきの VkImage |
| Renderbuffer | VkImage |
| VertexArrayObject | (エミュレートする) |
Draw
基本的には Dynamic state を可能な限り使用する。ただ、それでも color write enable等拡張に逃がされてしまっているものがあり、モバイルを考えると原則的にコア側でdynamic stateになっていないものは使用できない。
Dynamic stateで表現できないステートが変更された場合は、pipelineを作りなおす必要がある。まぁOpenGLネイティブでもたまにそういう構造を持っているGPUは存在する。
Dynamic state
| WebGL | Vulkan |
|---|---|
| glViewport | VK_DYNAMIC_STATE_VIEWPORT, vkCmdSetViewport |
| glLineWidth | VK_DYNAMIC_STATE_LINE_WIDTH, vkCmdSetLineWidth |
| glDepthRangef ★ | VK_DYNAMIC_STATE_DEPTH_BOUNDS, vkCmdSetDepthBounds |
| glPolygonOffset ★ | VK_DYNAMIC_STATE_DEPTH_BIAS, vkCmdSetDepthBias |
| glBlendColor ★ | VK_DYNAMIC_STATE_BLEND_CONSTANTS, vkCmdSetBlendConstants |
| glStencilFuncSeparate ▲ | VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, vkCmdSetStencilCompareMask, vkCmdSetStencilReference |
| GL_SCISSOR_TEST, glScissor ● | VK_DYNAMIC_STATE_SCISSOR, vkCmdSetScissor |
| GL_DITHER | 無い |
| glStencilMaskSeparate | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, vkCmdSetStencilWriteMask |
★: フラグメントの書き込みを制御(test)する要素は基本的にpipelineの生成時に有効/無効を設定する。このため、dynamic stateではそのパラメタだけを変更できる。
●: ただし Scissor は例外的にそもそもDisableが無いためdynamic stateで完結する。
▲: StencilFuncSeparateではCompare操作も設定できるが、これはpipeline stateなのでdynamicには変更できない。残りのMaskとRefは標準のVulkanで変更できる。
Pipeline state
| WebGL | Vulkan |
|---|---|
| プリミティブタイプ | VkPipelineInputAssemblyStateCreateInfo + VkPrimitiveTopology |
| GL_CULL_FACE, glCullFace, glFrontFace | VkPipelineRasterizationStateCreateInfo |
| GL_BLEND, glBlendFuncSeparate, glBlendEquationSeparate | VkPipelineColorBlendStateCreateInfo + VkPipelineColorBlendAttachmentState |
| GL_DEPTH_TEST, glDepthFunc | VkPipelineDepthStencilStateCreateInfo |
| GL_STENCIL_TEST, glStencilOpSeparate, glStencilFuncSeparate | VkPipelineDepthStencilStateCreateInfo + VkStencilOpState |
| GL_POLYGON_OFFSET_FILL | VkPipelineRasterizationStateCreateInfo |
| glColorMask | VkPipelineColorBlendAttachmentState |
| glDepthMask | VkPipelineDepthStencilStateCreateInfo |
| GL_SAMPLE_ALPHA_TO_COVERAGE, glSampleCoverage | ? |
| GL_SAMPLE_COVERAGE, glSampleCoverage | ? |
そもそもVulkanではプリミティブタイプをdynamicに指定できない( VK_EXT_extended_dynamic_state 拡張)
Pipelineには、更にバッファとRenderPassを接続する必要がある。バッファについてはTextureとShaderで後述。
RenderPass
Render pass は出力先であるframebufferとPipelineを接続するために使用する。
Textureで後述。RenderbufferとTextureそれぞれでRenderPassに設定すべき内容が異なる。
描画のキック
| WebGL | Vulkan |
|---|---|
| GL_ARRAY_BUFFER, glVertexAttribPointer | vkCmdBindVertexBuffers |
| glDrawArrays | vkCmdDraw |
| GL_ELEMENT_ARRAY_BUFFER, glDrawElements | vkCmdBindIndexBuffer, vkCmdDrawIndexed |
インデックスを使用した描画には追加のバッファをbindする必要がある。このBindはWebGLでもVulkanでも同様に必要となる。
strideの動的な変更には VK_EXT_extended_dynamic_state が必要。つまり、頂点属性のレイアウト自体はPipeline stateとして事前に VkPipelineVertexInputStateCreateInfo で設定しておく必要がある。
Clear
VulkanのClear操作にはRenderPass外部/内部の2種類がある。WebGL1ではClearは描画の一種になるのでRenderPass内部で処理することになる。
更に、現状のC-WebGLではサポートしていないが preserveDrawingBuffer をFalseに設定することに相当する機能がRenderPass自体に存在し多くのVulkan実装ではそちらが推奨されている。
| WebGL | Vulkan |
|---|---|
| glClear | vkCmdClearAttachments |
| glClearColor | VkClearColorValue |
| glClearDepthf, glClearStencil | VkClearDepthStencilValue |