Open16

C-WebGL: Vulkan移植を計画する会

okuokuokuoku

最終的にはWebGPUに持っていきたいが、それまでの繋ぎとして素のVulkan + glslang + SPIRV-Toolsで実装できるかトライしてみることにする。

GoogleはANGLEの他にOpenGL ESの実装フレームワークとしてGlimpを持っている。

https://github.com/youtube/cobalt/tree/04cd6faca4dd5039a1b810e9ba7b7a79e51a781d/src/glimp

こちらはかなり構造がスッキリしていて参考になるかもしれない。

okuokuokuoku

(あとで埋める)

オブジェクトの実現方法

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

okuokuokuoku

無視

  • 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
okuokuokuoku

Alignment

WebGLでは pixelStorei で設定できるステート。つまり、GPUへのテクスチャアップロード/ダウンロードの挙動を決定する。

ストレージ

PACK_ALIGNMENTUNPACK_ALIGNMENT はそもそもVulkanに対応する機能が存在しない。

https://github.com/KhronosGroup/Vulkan-Docs/issues/776

非アライン領域にblitするとか正常な心があればやらないはず。。本当にマジで必要ならBuffer同士のコピーとしてデータを補完しながらコンピュートシェーダでコピーするしかないんでは。。

Y-Flip

UNPACK_FLIP_Y_WEBGL も無いはず。。 Vulkanはこれがセットされた状態が標準で 、通常のimageアクセスは反転の必要がある。

https://github.com/gpuweb/gpuweb/issues/416

WebGPUのissueにまとまっているように、VulkanとOpenGLではテクスチャ原点が逆になっているため、座標系の変換は様々なところで必要になる。VulkanがDirectXやMetalに合ってないのはかなり謎で前身のMantleとかの所為だったりするんですかね。。

色変換

UNPACK_PREMULTIPLY_ALPHA_WEBGL は主にCanvasとのデータのやりとりに使うものなのでC-WebGLではサポート不要。必要ならシェーダでエミュレートできる。

UNPACK_COLORSPACE_CONVERSION_WEBGL はImage要素とのやりとりで使うものなのでやっぱりC-WebGLでは不要。

okuokuokuoku

(あとでVulkan側を埋める)

Blend State

これ複雑だし良い図を探したいところだな。。

WebGL的には Framebuffer に対してピクセルを書き込む際に元のFramebuffer上のピクセルとどう合成するかというパラメタ。

Enable/Color/Func

BLEND はBlendFuncの有効無効を切り替える。無効の場合は単純な上書き。

Equation

EquationはBlend結果を加算するか減算するかを決める。減算は可換でないので2種類ある。 ...これDisableに挙がってないな。。BlendをDisableした場合こちらも無視されるはず。

okuokuokuoku

Clear

実際のGPUにはClear機能があったり無かったりした時期もあったが、近代ではメモリ帯域がかなり貴重かつ描画のセットアップ自体が重いので専用のClearコマンドはつきものになっている。

VulkanでもClearは1章割いている。

ステートはそのまま VkClearColorValueVkClearDepthStencilValue が対応し、これらを統合した VkClearValue をコマンド vkCmdClearAttachments に渡す。

更に、VulkanではOpenGLとは異なりアタッチしていないバッファに対してもクリアを行える。特にANGLEは最適化のために両者を使いわけている。

okuokuokuoku

Color Write Mask

Color maskはFramebufferへの最終的な書き込み可否を設定させる。

Vulkanでもそのままの機能が VkColorComponentFlags として存在する。

GLではMask機能として他のDepth/Stencil Maskが関連に挙がっているが、VulkanではBlend側に統合されている。

okuokuokuoku

Depth State

Vulkanではpipelineの生成時に設定する必要があるが、動的に再設定を可能にする拡張 vkCmdSetDepthTestEnableEXT 等がある。

全て VkPipelineDepthStencilStateCreateInfo で設定する必要があるということは、動的な変更が超クッソ重いということでもある。

あと、OpenGLのdepthは他のAPIと異なり通常 -1 〜 1 範囲となっている。Vulkanは 0 〜 1 なのでシェーダ内で変換が必要になる ...と思うんだけどANGLEは変換していないように見える。要調査だな。。

okuokuokuoku

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 negative height, 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 negative height 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_OESOES_standard_derivatives で追加されたhintで、一応WebGLとしては拡張にあたる。が、IE11も含めほぼ全ての実装でサポートしているため前提としても良いんじゃないかという気はする。

Vulkanでは仕様化されていて、精度ヒントはない:

okuokuokuoku

Mipmap Hint State

GLにはMipmapを生成する機能がドライバ側にあり、その挙動(Fastest、Nicest)を決めることができる。

Vulkanには、そもそもドライバ側のMipmap生成機能なんか無いので自前で描画を組まないといけない。VulkanではBlit機能が vkCmdBlitImageとして公開されているので、Imageを必要ぶん用意してBlitすれば良い。

okuokuokuoku

Polygon Offset

非常に近接したポリゴンを描画するときにZバッファ的な誤差をうちけすといった目的でワークアラウンド的に使用されるPolygon offset。

Vulkanでは(DirectX風に)これはDepth Biasと呼ばれている。

WebGL1には、 depthBiasClamp に相当する機能性はない。

okuokuokuoku

シザリング

シザリングは window 座標系の中での描画可能領域を設定する。機能的にはステンシルと被るが割と頻出の機能なので分けてあるんだろう多分。

VulkanではDepthRange同様ビューポート構成に含まれていて VkPipelineViewportStateCreateInfo に含まれる。

okuokuokuoku

ステンシル

ステート一覧では最大勢力に見えるが、表(Front) 裏(Back) x 通過(Depth fail) 棄却(Fail) ぶんの 設定値が必要なので。。

Vulkanも相当機能を取り込んでいて、 VkPipelineDepthStencilStateCreateInfo で設定可能となっている (FIXME: mask)

VulkanはDepthとStencilを(DirectX風に)常に1まとめにしているが、WebGLも同様の制約を設定しているので問題ない。