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 |