C-WebGL: Vulkan移植を計画する会
まじめにやりなおした:
最終的にはWebGPUに持っていきたいが、それまでの繋ぎとして素のVulkan + glslang + SPIRV-Toolsで実装できるかトライしてみることにする。
GoogleはANGLEの他にOpenGL ESの実装フレームワークとしてGlimpを持っている。
こちらはかなり構造がスッキリしていて参考になるかもしれない。
(あとで埋める)
オブジェクトの実現方法
C-WebGLが現状生成可能なオブジェクトは以下の7種がある https://github.com/okuoku/yuniframe/blob/90cf25036fdff52ad1835c61b39fa3c0b69e925d/include/cwgl.h#L88-L94
これらのVulkan的な表現方法、バインド方法を考える。例えば、WebGL1にはサンプラーオブジェクトの概念が無く、Texture側にサンプリング方法などのデータを設定するが、それをVulkanの流儀に適宜対応させる必要がある。
Buffer
Shader
Program
Texture
Framebuffer
Renderbuffer
VertexArrayObject
無視
LINE_WIDTH
実装クエリ
SUBPIXEL_BITSMAX_TEXTURE_SIZEMAX_CUBE_MAP_TEXTURE_SIZEMAX_VIEWPORT_DIMSALIASED_POINT_SIZE_RANGEALIASED_LINE_WIDTH_RANGESAMPLE_BUFFERSSAMPLESCOMPRESSED_TEXTURE_FORMATSNUM_COMPRESSED_TEXTURE_FORMATSSHADER_BINARY_FORMATSNUM_SHADER_BINARY_FORMATSSHADER_COMPILEREXTENSIONSRENDERERSHADING_LANGUAGE_VERSIONVENDORVERSIONMAX_VERTEX_ATTRIBSMAX_VERTEX_UNIFORM_VECTORSMAX_VARYING_VECTORSMAX_COMBINED_TEXTURE_IMAGE_UNITSMAX_VERTEX_TEXTURE_IMAGE_UNITSMAX_TEXTURE_IMAGE_UNITSMAX_FRAGMENT_UNIFORM_VECTORSMAX_RENDERBUFFER_SIZERED_BITSGREEN_BITSBLUE_BITSALPHA_BITSDEPTH_BITSSTENCIL_BITSIMPLEMENTATION_COLOR_READ_TYPEIMPLEMENTATION_COLOR_READ_FORMAT
機能ブロックステート
-
TEXTURE_BINDING_2D -
TEXTURE_BINDING_CUBE_MAP -
ACTIVE_TEXTURE -
CURRENT_PROGRAM -
TEXTURE_MIN_FILTER -
TEXTURE_MAG_FILTER -
TEXTURE_WRAP_S -
TEXTURE_WRAP_T
頂点シェーダ
-
ARRAY_BUFFER_BINDING -
ELEMENT_ARRAY_BUFFER_BINDING -
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING -
VERTEX_ATTRIB_ARRAY_ENABLED -
VERTEX_ATTRIB_ARRAY_SIZE -
VERTEX_ATTRIB_ARRAY_STRIDE -
VERTEX_ATTRIB_ARRAY_TYPE -
VERTEX_ATTRIB_ARRAY_NORMALIZED -
VERTEX_ATTRIB_POINTER -
CURRENT_VERTEX_ATTRIB
シェーダークエリ
SHADER_TYPE-
DELETE_STATUS★ Programにもある COMPILE_STATUS-
INFO_LOG_LENGTH★ Programにもある SHADER_SOURCE_LENGTH
プログラムクエリ
LINK_STATUSVALIDATE_STATUSATTACHED_STATUSACTIVE_UNIFORMSACTIVE_ATTRIBUTES
Buffer
BUFFER_SIZEBUFFER_USAGE
Renderbuffer
RENDERBUFFER_BINDINGRENDERBUFFER_WIDTHRENDERBUFFER_HEIGHTRENDERBUFFER_INTERNAL_FORMATRENDERBUFFER_RED_SIZERENDERBUFFER_GREEN_SIZERENDERBUFFER_BLUE_SIZERENDERBUFFER_ALPHA_SIZERENDERBUFFER_DEPTH_SIZERENDERBUFFER_STENCIL_SIZE
Framebuffer
FRAMEBUFFER_BINDINGFRAMEBUFFER_ATTACHMENT_OBJECT_TYPEFRAMEBUFFER_ATTACHMENT_OBJECT_NAMEFRAMEBUFFER_ATTACHMENT_TEXTURE_LEVELFRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
固定機能ステート
Spector.jsのステート欄から持ってきた。 https://gist.github.com/okuoku/4dbd629cb4dc5fa463a17f645fb5e591
Alignment State
PACK_ALIGNMENTUNPACK_ALIGNMENTUNPACK_COLORSPACE_CONVERSION_WEBGLUNPACK_FLIP_Y_WEBGLUNPACK_PREMULTIPLY_ALPHA_WEBGL
Blend State
BLENDBLEND_COLORBLEND_DST_ALPHABLEND_DST_RGBBLEND_EQUATION_ALPHABLEND_EQUATION_RGBBLEND_SRC_ALPHABLEND_SRC_RGB
Clear State
COLOR_CLEAR_VALUEDEPTH_CLEAR_VALUESTENCIL_CLEAR_VALUE
Color State
COLOR_WRITEMASK
Coverage State
SAMPLE_COVERAGE_VALUESAMPLE_COVERAGE_INVERT-
SAMPLE_COVERAGE★ 追加? -
SAMPLE_ALPHA_TO_COVERAGE★ 追加?
Cull State
CULL_FACECULL_FACE_MODE
Depth State
DEPTH_TESTDEPTH_FUNCDEPTH_RANGEDEPTH_WRITEMASK
Draw State
DITHERVIEWPORTFRONT_FACEFRAGMENT_SHADER_DERIVATIVE_HINT_OES
Mipmap Hint State
GENERATE_MIPMAP_HINT
Polygon Offset State
POLYGON_OFFSET_FILLPOLYGON_OFFSET_FACTORPOLYGON_OFFSET_UNITS
Scissor State
SCISSOR_TESTSCISSOR_BOX
Stencil State
STENCIL_TESTSTENCIL_BACK_FAILSTENCIL_BACK_FUNCSTENCIL_BACK_PASS_DEPTH_FAILSTENCIL_BACK_PASS_DEPTH_PASSSTENCIL_BACK_REFSTENCIL_BACK_VALUE_MASKSTENCIL_BACK_WRITEMASKSTENCIL_FAILSTENCIL_FUNCSTENCIL_PASS_DEPTH_FAILSTENCIL_PASS_DEPTH_PASSSTENCIL_REFSTENCIL_VALUE_MASKSTENCIL_WRITEMASK
Alignment
WebGLでは pixelStorei で設定できるステート。つまり、GPUへのテクスチャアップロード/ダウンロードの挙動を決定する。
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glPixelStorei.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/pixelStorei
ストレージ
PACK_ALIGNMENT と UNPACK_ALIGNMENT はそもそもVulkanに対応する機能が存在しない。
非アライン領域にblitするとか正常な心があればやらないはず。。本当にマジで必要ならBuffer同士のコピーとしてデータを補完しながらコンピュートシェーダでコピーするしかないんでは。。
Y-Flip
UNPACK_FLIP_Y_WEBGL も無いはず。。 Vulkanはこれがセットされた状態が標準で 、通常のimageアクセスは反転の必要がある。
WebGPUのissueにまとまっているように、VulkanとOpenGLではテクスチャ原点が逆になっているため、座標系の変換は様々なところで必要になる。VulkanがDirectXやMetalに合ってないのはかなり謎で前身のMantleとかの所為だったりするんですかね。。
色変換
UNPACK_PREMULTIPLY_ALPHA_WEBGL は主にCanvasとのデータのやりとりに使うものなのでC-WebGLではサポート不要。必要ならシェーダでエミュレートできる。
UNPACK_COLORSPACE_CONVERSION_WEBGL はImage要素とのやりとりで使うものなのでやっぱりC-WebGLでは不要。
(あとでVulkan側を埋める)
Blend State
これ複雑だし良い図を探したいところだな。。
WebGL的には Framebuffer に対してピクセルを書き込む際に元のFramebuffer上のピクセルとどう合成するかというパラメタ。
Enable/Color/Func
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glBlendColor.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendColor
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glBlendFuncSeparate.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFuncSeparate
BLEND はBlendFuncの有効無効を切り替える。無効の場合は単純な上書き。
Equation
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glBlendFuncSeparate.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquationSeparate
EquationはBlend結果を加算するか減算するかを決める。減算は可換でないので2種類ある。 ...これDisableに挙がってないな。。BlendをDisableした場合こちらも無視されるはず。
Clear
実際のGPUにはClear機能があったり無かったりした時期もあったが、近代ではメモリ帯域がかなり貴重かつ描画のセットアップ自体が重いので専用のClearコマンドはつきものになっている。
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glClearColor.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glClearDepthf.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glClearStencil.xml
VulkanでもClearは1章割いている。
ステートはそのまま VkClearColorValue と VkClearDepthStencilValue が対応し、これらを統合した VkClearValue をコマンド vkCmdClearAttachments に渡す。
更に、VulkanではOpenGLとは異なりアタッチしていないバッファに対してもクリアを行える。特にANGLEは最適化のために両者を使いわけている。
Color Write Mask
Color maskはFramebufferへの最終的な書き込み可否を設定させる。
Vulkanでもそのままの機能が VkColorComponentFlags として存在する。
GLではMask機能として他のDepth/Stencil Maskが関連に挙がっているが、VulkanではBlend側に統合されている。
(あとで埋める)
Sample Coverage
↑ の表に ALPHA_TO_COVERAGE が無いな。。
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glSampleCoverage.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/sampleCoverage
VulkanだとSample maskを直接設定するしか方法が無くない。。?調査中。
Cull State
FRONT_FACE はDraw stateの方に入れてるのか。。裏面を描画するかどうかフラグ。
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glCullFace.xml
- https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/cullFace
Vulkanでは VkCullModeFlags としてそのまま存在する。
Depth State
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glDepthFunc.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glDepthMask.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glDepthRangef.xml
Vulkanではpipelineの生成時に設定する必要があるが、動的に再設定を可能にする拡張 vkCmdSetDepthTestEnableEXT 等がある。
- https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap28.html#fragops-depth
- https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap28.html#VkPipelineDepthStencilStateCreateInfo
全て VkPipelineDepthStencilStateCreateInfo で設定する必要があるということは、動的な変更が超クッソ重いということでもある。
あと、OpenGLのdepthは他のAPIと異なり通常 -1 〜 1 範囲となっている。Vulkanは 0 〜 1 なのでシェーダ内で変換が必要になる ...と思うんだけどANGLEは変換していないように見える。要調査だな。。
Draw State
※ この辺 DynamicState でoverrideできるのが割と有り、これらに頼った方が良さそう。
ディザ
そんなもんはVulkanには無い(たぶん)。シェーダでエミュレーションするしかないと思うけど、そもそもVulkan界には中間バッファを16bppで済ませて帯域節約みたいな奴は無いんだろうか。。
GL_DITHER は唯一OpenGL ES2でenableを初期ステートとする。
ビューポート
ビューポートは NDC(Normalized Device Coordinates)からWindow座標系への変換を記述する。この NDCの定義自体がOpenGLとVulkanで異なっている(GL: +Y Up、Vulkan: +Y Down)。
Vulkanではpipeline生成時に指定する他、 vkCmdSetViewport で 事前定義したものに 設定可能で、DepthRangeと同時に指定する
OpenGL、VulkanともにminDepth > maxDepthを許容している。(Metalはこれを許していないらしく、ANGLEではシェーダでエミュレートしている。たぶんMoltenVkも?)
また、 Vulkan 1.1 ( VK_KHR_maintenance1 以降) から ビューポートの height には負値が設定可能で、 DirectXやMetalと同じ座標系が(ついに)使えるようになっている。
The application can specify a negative term for
height, which has the effect of negating the y coordinate in clip space before performing the transform. When using a negativeheight, the application should also adjust the y value to point to the lower left corner of the viewport instead of the upper left corner. Using the negativeheightallows the application to avoid having to negate the y component of the Position output from the last vertex processing stage in shaders that also target other graphics APIs.
Front face
三角形の表面を決定するために、描画順序の手順を指定するフラグ。時計周りモード(CW、ClockWise)と反時計周りモード(CCW、Counter ClockWise)がある。
Vulkanではパイライン生成時に VkFrontFace で決定する必要があり、拡張でコマンド vkCmdSetFrontFaceEXT 指定ができる。
OES_standard_derivatives
FRAGMENT_SHADER_DERIVATIVE_HINT_OES は OES_standard_derivatives で追加されたhintで、一応WebGLとしては拡張にあたる。が、IE11も含めほぼ全ての実装でサポートしているため前提としても良いんじゃないかという気はする。
- https://www.khronos.org/registry/OpenGL/extensions/OES/OES_standard_derivatives.txt
- https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
Vulkanでは仕様化されていて、精度ヒントはない:
Mipmap Hint State
GLにはMipmapを生成する機能がドライバ側にあり、その挙動(Fastest、Nicest)を決めることができる。
Vulkanには、そもそもドライバ側のMipmap生成機能なんか無いので自前で描画を組まないといけない。VulkanではBlit機能が vkCmdBlitImageとして公開されているので、Imageを必要ぶん用意してBlitすれば良い。
Polygon Offset
非常に近接したポリゴンを描画するときにZバッファ的な誤差をうちけすといった目的でワークアラウンド的に使用されるPolygon offset。
Vulkanでは(DirectX風に)これはDepth Biasと呼ばれている。
WebGL1には、 depthBiasClamp に相当する機能性はない。
シザリング
シザリングは window 座標系の中での描画可能領域を設定する。機能的にはステンシルと被るが割と頻出の機能なので分けてあるんだろう多分。
VulkanではDepthRange同様ビューポート構成に含まれていて VkPipelineViewportStateCreateInfo に含まれる。
ステンシル
ステート一覧では最大勢力に見えるが、表(Front) 裏(Back) x 通過(Depth fail) 棄却(Fail) ぶんの 設定値が必要なので。。
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glStencilFuncSeparate.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glStencilMaskSeparate.xml
- https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glStencilOpSeparate.xml
Vulkanも相当機能を取り込んでいて、 VkPipelineDepthStencilStateCreateInfo で設定可能となっている (FIXME: mask)
VulkanはDepthとStencilを(DirectX風に)常に1まとめにしているが、WebGLも同様の制約を設定しているので問題ない。