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_BITS
MAX_TEXTURE_SIZE
MAX_CUBE_MAP_TEXTURE_SIZE
MAX_VIEWPORT_DIMS
ALIASED_POINT_SIZE_RANGE
ALIASED_LINE_WIDTH_RANGE
SAMPLE_BUFFERS
SAMPLES
COMPRESSED_TEXTURE_FORMATS
NUM_COMPRESSED_TEXTURE_FORMATS
SHADER_BINARY_FORMATS
NUM_SHADER_BINARY_FORMATS
SHADER_COMPILER
EXTENSIONS
RENDERER
SHADING_LANGUAGE_VERSION
VENDOR
VERSION
MAX_VERTEX_ATTRIBS
MAX_VERTEX_UNIFORM_VECTORS
MAX_VARYING_VECTORS
MAX_COMBINED_TEXTURE_IMAGE_UNITS
MAX_VERTEX_TEXTURE_IMAGE_UNITS
MAX_TEXTURE_IMAGE_UNITS
MAX_FRAGMENT_UNIFORM_VECTORS
MAX_RENDERBUFFER_SIZE
RED_BITS
GREEN_BITS
BLUE_BITS
ALPHA_BITS
DEPTH_BITS
STENCIL_BITS
IMPLEMENTATION_COLOR_READ_TYPE
IMPLEMENTATION_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_STATUS
VALIDATE_STATUS
ATTACHED_STATUS
ACTIVE_UNIFORMS
ACTIVE_ATTRIBUTES
Buffer
BUFFER_SIZE
BUFFER_USAGE
Renderbuffer
RENDERBUFFER_BINDING
RENDERBUFFER_WIDTH
RENDERBUFFER_HEIGHT
RENDERBUFFER_INTERNAL_FORMAT
RENDERBUFFER_RED_SIZE
RENDERBUFFER_GREEN_SIZE
RENDERBUFFER_BLUE_SIZE
RENDERBUFFER_ALPHA_SIZE
RENDERBUFFER_DEPTH_SIZE
RENDERBUFFER_STENCIL_SIZE
Framebuffer
FRAMEBUFFER_BINDING
FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL
FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
固定機能ステート
Spector.jsのステート欄から持ってきた。 https://gist.github.com/okuoku/4dbd629cb4dc5fa463a17f645fb5e591
Alignment State
PACK_ALIGNMENT
UNPACK_ALIGNMENT
UNPACK_COLORSPACE_CONVERSION_WEBGL
UNPACK_FLIP_Y_WEBGL
UNPACK_PREMULTIPLY_ALPHA_WEBGL
Blend State
BLEND
BLEND_COLOR
BLEND_DST_ALPHA
BLEND_DST_RGB
BLEND_EQUATION_ALPHA
BLEND_EQUATION_RGB
BLEND_SRC_ALPHA
BLEND_SRC_RGB
Clear State
COLOR_CLEAR_VALUE
DEPTH_CLEAR_VALUE
STENCIL_CLEAR_VALUE
Color State
COLOR_WRITEMASK
Coverage State
SAMPLE_COVERAGE_VALUE
SAMPLE_COVERAGE_INVERT
-
SAMPLE_COVERAGE
★ 追加? -
SAMPLE_ALPHA_TO_COVERAGE
★ 追加?
Cull State
CULL_FACE
CULL_FACE_MODE
Depth State
DEPTH_TEST
DEPTH_FUNC
DEPTH_RANGE
DEPTH_WRITEMASK
Draw State
DITHER
VIEWPORT
FRONT_FACE
FRAGMENT_SHADER_DERIVATIVE_HINT_OES
Mipmap Hint State
GENERATE_MIPMAP_HINT
Polygon Offset State
POLYGON_OFFSET_FILL
POLYGON_OFFSET_FACTOR
POLYGON_OFFSET_UNITS
Scissor State
SCISSOR_TEST
SCISSOR_BOX
Stencil State
STENCIL_TEST
STENCIL_BACK_FAIL
STENCIL_BACK_FUNC
STENCIL_BACK_PASS_DEPTH_FAIL
STENCIL_BACK_PASS_DEPTH_PASS
STENCIL_BACK_REF
STENCIL_BACK_VALUE_MASK
STENCIL_BACK_WRITEMASK
STENCIL_FAIL
STENCIL_FUNC
STENCIL_PASS_DEPTH_FAIL
STENCIL_PASS_DEPTH_PASS
STENCIL_REF
STENCIL_VALUE_MASK
STENCIL_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 negativeheight
allows 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も同様の制約を設定しているので問題ない。