Open13

WebGL1のAPIレビュー(2周目)

okuokuokuoku

prev: https://zenn.dev/okuoku/scraps/a0a74834db15e6
next: https://zenn.dev/okuoku/scraps/9e2926081051f7

WebGL1のCバインディングのヘッダファイルを作る

目的 : 実装のためのCヘッダファイルを用意する。

今回のWebGL1 Cバインディング(CWGL)は3つの実装を用意したい。

  1. WebGL → CWGL以前DOOMを動かしたような、EmscriptenなしでWebGLを使うための実装。
  2. CWGL → WebGLNode.jsでEmscriptenアプリを動かす時 に画面を出す用の実装。
  3. CWGL → GLES2以前Qiitaに書いた ゲーム専用ブラウザで使う用の実装。ターゲットプラットフォームのネイティブアプリとして動作する。

今回のヘッダファイルは、この3パターン全てで使用される。

okuokuokuoku

元のWebGLには存在しない基本型。

Context

CWGLのコンテキストは、ヒープ、GPU、その他全ての状態を表現する。全てのAPIは基本的にコンテキストを引数に取り、異なるコンテキストから得られたオブジェクトは他のコンテキストでは使用できない。

struct cwgl_ctx_s;
typedef struct cwgl_ctx_s cwgl_ctx_t;
cwgl_ctx_t* cwgl_ctx_create(int height, int width, int reserved, int flags);
void cwgl_ctx_release(cwgl_ctx_t* ctx);
#define CWGL_CTX_FLAG_HAS_ALPHA (1<<0)
#define CWGL_CTX_FLAG_HAS_DEPTH (1<<1)
#define CWGL_CTX_FLAG_HAS_STENCIL (1<<2)
#define CWGL_CTX_FLAG_ANTIALIAS (1<<3)
#define CWGL_CTX_FLAG_PREMULTIPLIEDALPHA  (1<<4)
#define CWGL_CTX_FLAG_PRESERVEDRAWINGBUFFER (1<<5)

コンテキストにはいくつかフラグを供給できる。供給したフラグはあとからクエリも可能となっている。

Query result

ヒープアクセスを伴うAPIは、query result型の値を返却する。多くのクエリはリトライ可能であるため、返却された値を見て再度挑戦できる。

enum cwgl_query_result_e;
typedef enum cwgl_query_result_e cwgl_query_result_t;

Enum, String

デバッグの都合を考えて、GLのenumをC言語的な意味の enum にしてみる事にした。Khronosのヘッダでは全てのenumは GL_ でプレフィックスされているので、衝突することなく併用できる。

Stringについては前回の通り。size read release の各オペレーションを提供する。 readはゼロ終端する & size はゼロ終端を含めたサイズを返すstrlen と挙動が違うことに注意。

enum cwgl_enum_e;
typedef enum cwgl_enum_e cwgl_enum_t;

enumの中身は別のヘッダで与える。

struct cwgl_string_s;
typedef struct cwgl_string_s cwgl_string_t;
size_t cwgl_string_size(cwgl_ctx_t* ctx, cwgl_string_t* str);
cwgl_query_result_t cwgl_string_read(cwgl_ctx_t* ctx, cwgl_string_t* str, char* buf, size_t buflen);
void cwgl_string_release(cwgl_ctx_t* ctx, cwgl_string_t* str);
okuokuokuoku

ユーザオブジェクト (明示的)

ユーザが明示的に作成するオブジェクトは、WebGLでのAPIに加えて、明示的に削除を行う release APIが必要になる。本来のWebGLではGCに期待している。

WebGLBuffer? createBuffer(); // genBuffers
WebGLShader? createShader(GLenum type);
WebGLProgram? createProgram();
WebGLTexture? createTexture(); // GenTextures
WebGLFramebuffer? createFramebuffer(); // genFramebuffers
WebGLRenderbuffer? createRenderbuffer(); // genRenderBuffers

↓(create APIは別に宣言する)

struct cwgl_Buffer_s;
struct cwgl_Shader_s;
struct cwgl_Program_s;
struct cwgl_Texture_s;
struct cwgl_Framebuffer_s;
struct cwgl_Renderbuffer_s;

typedef struct cwgl_Buffer_s cwgl_Buffer_t;
typedef struct cwgl_Shader_s cwgl_Shader_t;
typedef struct cwgl_Program_s cwgl_Program_t;
typedef struct cwgl_Texture_s cwgl_Texture_t;
typedef struct cwgl_Framebuffer_s cwgl_Framebuffer_t;
typedef struct cwgl_Renderbuffer_s cwgl_Renderbuffer_t;

void cwgl_Buffer_release(cwgl_ctx_t* ctx, cwgl_Buffer_t* buffer);
void cwgl_Shader_release(cwgl_ctx_t* ctx, cwgl_Shader_t* shader);
void cwgl_Program_release(cwgl_ctx_t* ctx, cwgl_Program_t* program);
void cwgl_Texture_release(cwgl_ctx_t* ctx, cwgl_Texture_t* texture);
void cwgl_Framebuffer_release(cwgl_ctx_t* ctx, cwgl_Framebuffer_t* framebuffer);
void cwgl_Renderbuffer_release(cwgl_ctx_t* ctx, cwgl_Renderbuffer_t* renderbuffer);
okuokuokuoku

ユーザオブジェクト (暗黙的)

WebGLContextAttributes? getContextAttributes();
WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index);
WebGLUniformLocation? getUniformLocation(WebGLProgram? program, DOMString name);
WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index);
WebGLShaderPrecisionFormat? getShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);

これらはそれぞれ異なるAPI様式となる。

WebGLContextAttributes

これはちょっと良いマッピングが思いうかばなかったので、とりあえずintegerのフラグにした。

WebGLActiveInfo, WebGLShaderPrecisionFormat

ActiveInfoは数値と文字列の組合せであるため、 get APIの方で分解する。ActiveInfoを取るAPIは存在しない。ShaderPrecisionFormatも同様。

[Exposed=(Window,Worker)]
interface WebGLActiveInfo {
    readonly attribute GLint size;
    readonly attribute GLenum type;
    readonly attribute DOMString name;
};

[Exposed=(Window,Worker)]
interface WebGLShaderPrecisionFormat {
    readonly attribute GLint rangeMin;
    readonly attribute GLint rangeMax;
    readonly attribute GLint precision;
};

WebGLUniformLocation

これは明示的なユーザオブジェクトと同様に、 release APIが必要になる。

okuokuokuoku

(GLES規格書にないもの)

https://gist.github.com/okuoku/7b9f11f7ad236eaba05437e9a9dbb564/134c6c2ff3b3a53b295da8cd4258347cdc1710cf#file-cwgl-h-L1-L62

ちょっと悩んだけど、

  • APIで規格上の整数値はenumか int32_t 等のstdint
  • メモリサイズなど実行環境に拠るものは size_t
  • boolは int

という使い分けにしてみた ■ 。

getExtension は存在しない。ActivateするためのAPIを別途用意した方が良いのかは何とも。。

okuokuokuoku

6章 (GLES2規格書順)

https://gist.github.com/okuoku/7b9f11f7ad236eaba05437e9a9dbb564/ac3dc1b3bc0883f142389543be108bf4b83a006c#file-cwgl-h-L197-L248

これはキツい。。WebGLやOpenGLでは getUniform は1つか2つのAPIなのに、CWGLでは型毎に展開する必要があるため、

cwgl_query_result_t cwgl_getUniform_i1(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, int32_t* x);
cwgl_query_result_t cwgl_getUniform_i2(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, int32_t* x, int32_t* y);
cwgl_query_result_t cwgl_getUniform_i3(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, int32_t* x, int32_t* y, int32_t* z);
cwgl_query_result_t cwgl_getUniform_i4(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, int32_t* x, int32_t* y, int32_t* z, int32_t* w);
cwgl_query_result_t cwgl_getUniform_f1(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x);
cwgl_query_result_t cwgl_getUniform_f2(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x, float* y);
cwgl_query_result_t cwgl_getUniform_f3(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x, float* y, float* z);
cwgl_query_result_t cwgl_getUniform_f4(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x, float* y, float* z, float* w);
cwgl_query_result_t cwgl_getUniform_m2(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x);
cwgl_query_result_t cwgl_getUniform_m3(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x);
cwgl_query_result_t cwgl_getUniform_m4(cwgl_ctx_t* ctx, cwgl_Program_t* program, cwgl_UniformLocation_t* location, float* x);

のように長大になってしまう。常識的なプログラムは自分がセットしたuniformをクエリすることは無いので、実装自体サボってしまいたい。。