github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/internal/gl/gl_js.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package gl
     4  
     5  import (
     6  	"errors"
     7  	"strings"
     8  	"syscall/js"
     9  )
    10  
    11  type Functions struct {
    12  	Ctx                             js.Value
    13  	EXT_disjoint_timer_query        js.Value
    14  	EXT_disjoint_timer_query_webgl2 js.Value
    15  
    16  	// Cached reference to the Uint8Array JS type.
    17  	uint8Array js.Value
    18  
    19  	// Cached JS arrays.
    20  	arrayBuf js.Value
    21  	int32Buf js.Value
    22  
    23  	isWebGL2 bool
    24  }
    25  
    26  type Context js.Value
    27  
    28  func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
    29  	f := &Functions{
    30  		Ctx:        js.Value(ctx),
    31  		uint8Array: js.Global().Get("Uint8Array"),
    32  	}
    33  	if err := f.Init(); err != nil {
    34  		return nil, err
    35  	}
    36  	return f, nil
    37  }
    38  
    39  func (f *Functions) Init() error {
    40  	webgl2Class := js.Global().Get("WebGL2RenderingContext")
    41  	f.isWebGL2 = !webgl2Class.IsUndefined() && f.Ctx.InstanceOf(webgl2Class)
    42  	if !f.isWebGL2 {
    43  		f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query")
    44  		if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() {
    45  			return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float")
    46  		}
    47  		if f.getExtension("EXT_sRGB").IsNull() {
    48  			return errors.New("gl: EXT_sRGB not supported")
    49  		}
    50  	} else {
    51  		// WebGL2 extensions.
    52  		f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2")
    53  		if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() {
    54  			return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float")
    55  		}
    56  	}
    57  	return nil
    58  }
    59  
    60  func (f *Functions) getExtension(name string) js.Value {
    61  	return f.Ctx.Call("getExtension", name)
    62  }
    63  
    64  func (f *Functions) ActiveTexture(t Enum) {
    65  	f.Ctx.Call("activeTexture", int(t))
    66  }
    67  func (f *Functions) AttachShader(p Program, s Shader) {
    68  	f.Ctx.Call("attachShader", js.Value(p), js.Value(s))
    69  }
    70  func (f *Functions) BeginQuery(target Enum, query Query) {
    71  	if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
    72  		f.Ctx.Call("beginQuery", int(target), js.Value(query))
    73  	} else {
    74  		f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
    75  	}
    76  }
    77  func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
    78  	f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name)
    79  }
    80  func (f *Functions) BindBuffer(target Enum, b Buffer) {
    81  	f.Ctx.Call("bindBuffer", int(target), js.Value(b))
    82  }
    83  func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
    84  	f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b))
    85  }
    86  func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
    87  	f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb))
    88  }
    89  func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
    90  	f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb))
    91  }
    92  func (f *Functions) BindTexture(target Enum, t Texture) {
    93  	f.Ctx.Call("bindTexture", int(target), js.Value(t))
    94  }
    95  func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
    96  	panic("not implemented")
    97  }
    98  func (f *Functions) BindVertexArray(a VertexArray) {
    99  	panic("not supported")
   100  }
   101  func (f *Functions) BlendEquation(mode Enum) {
   102  	f.Ctx.Call("blendEquation", int(mode))
   103  }
   104  func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
   105  	f.Ctx.Call("blendFunc", int(srcRGB), int(dstRGB), int(srcA), int(dstA))
   106  }
   107  func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) {
   108  	panic("not implemented")
   109  }
   110  func (f *Functions) BufferData(target Enum, size int, usage Enum) {
   111  	f.Ctx.Call("bufferData", int(target), size, int(usage))
   112  }
   113  func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
   114  	f.Ctx.Call("bufferSubData", int(target), offset, f.byteArrayOf(src))
   115  }
   116  func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
   117  	return Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int())
   118  }
   119  func (f *Functions) Clear(mask Enum) {
   120  	f.Ctx.Call("clear", int(mask))
   121  }
   122  func (f *Functions) ClearColor(red, green, blue, alpha float32) {
   123  	f.Ctx.Call("clearColor", red, green, blue, alpha)
   124  }
   125  func (f *Functions) ClearDepthf(d float32) {
   126  	f.Ctx.Call("clearDepth", d)
   127  }
   128  func (f *Functions) CompileShader(s Shader) {
   129  	f.Ctx.Call("compileShader", js.Value(s))
   130  }
   131  func (f *Functions) CreateBuffer() Buffer {
   132  	return Buffer(f.Ctx.Call("createBuffer"))
   133  }
   134  func (f *Functions) CreateFramebuffer() Framebuffer {
   135  	return Framebuffer(f.Ctx.Call("createFramebuffer"))
   136  }
   137  func (f *Functions) CreateProgram() Program {
   138  	return Program(f.Ctx.Call("createProgram"))
   139  }
   140  func (f *Functions) CreateQuery() Query {
   141  	return Query(f.Ctx.Call("createQuery"))
   142  }
   143  func (f *Functions) CreateRenderbuffer() Renderbuffer {
   144  	return Renderbuffer(f.Ctx.Call("createRenderbuffer"))
   145  }
   146  func (f *Functions) CreateShader(ty Enum) Shader {
   147  	return Shader(f.Ctx.Call("createShader", int(ty)))
   148  }
   149  func (f *Functions) CreateTexture() Texture {
   150  	return Texture(f.Ctx.Call("createTexture"))
   151  }
   152  func (f *Functions) CreateVertexArray() VertexArray {
   153  	panic("not supported")
   154  }
   155  func (f *Functions) DeleteBuffer(v Buffer) {
   156  	f.Ctx.Call("deleteBuffer", js.Value(v))
   157  }
   158  func (f *Functions) DeleteFramebuffer(v Framebuffer) {
   159  	f.Ctx.Call("deleteFramebuffer", js.Value(v))
   160  }
   161  func (f *Functions) DeleteProgram(p Program) {
   162  	f.Ctx.Call("deleteProgram", js.Value(p))
   163  }
   164  func (f *Functions) DeleteQuery(query Query) {
   165  	if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
   166  		f.Ctx.Call("deleteQuery", js.Value(query))
   167  	} else {
   168  		f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
   169  	}
   170  }
   171  func (f *Functions) DeleteShader(s Shader) {
   172  	f.Ctx.Call("deleteShader", js.Value(s))
   173  }
   174  func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
   175  	f.Ctx.Call("deleteRenderbuffer", js.Value(v))
   176  }
   177  func (f *Functions) DeleteTexture(v Texture) {
   178  	f.Ctx.Call("deleteTexture", js.Value(v))
   179  }
   180  func (f *Functions) DeleteVertexArray(a VertexArray) {
   181  	panic("not implemented")
   182  }
   183  func (f *Functions) DepthFunc(fn Enum) {
   184  	f.Ctx.Call("depthFunc", int(fn))
   185  }
   186  func (f *Functions) DepthMask(mask bool) {
   187  	f.Ctx.Call("depthMask", mask)
   188  }
   189  func (f *Functions) DisableVertexAttribArray(a Attrib) {
   190  	f.Ctx.Call("disableVertexAttribArray", int(a))
   191  }
   192  func (f *Functions) Disable(cap Enum) {
   193  	f.Ctx.Call("disable", int(cap))
   194  }
   195  func (f *Functions) DrawArrays(mode Enum, first, count int) {
   196  	f.Ctx.Call("drawArrays", int(mode), first, count)
   197  }
   198  func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
   199  	f.Ctx.Call("drawElements", int(mode), count, int(ty), offset)
   200  }
   201  func (f *Functions) DispatchCompute(x, y, z int) {
   202  	panic("not implemented")
   203  }
   204  func (f *Functions) Enable(cap Enum) {
   205  	f.Ctx.Call("enable", int(cap))
   206  }
   207  func (f *Functions) EnableVertexAttribArray(a Attrib) {
   208  	f.Ctx.Call("enableVertexAttribArray", int(a))
   209  }
   210  func (f *Functions) EndQuery(target Enum) {
   211  	if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
   212  		f.Ctx.Call("endQuery", int(target))
   213  	} else {
   214  		f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
   215  	}
   216  }
   217  func (f *Functions) Finish() {
   218  	f.Ctx.Call("finish")
   219  }
   220  func (f *Functions) Flush() {
   221  	f.Ctx.Call("flush")
   222  }
   223  func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
   224  	f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
   225  }
   226  func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
   227  	f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level)
   228  }
   229  func (f *Functions) GetError() Enum {
   230  	// Avoid slow getError calls. See gio#179.
   231  	return 0
   232  }
   233  func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
   234  	return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname)))
   235  }
   236  func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
   237  	if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING {
   238  		// FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2
   239  		return LINEAR
   240  	}
   241  	return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname)))
   242  }
   243  func (f *Functions) GetBinding(pname Enum) Object {
   244  	obj := f.Ctx.Call("getParameter", int(pname))
   245  	if !obj.Truthy() {
   246  		return Object{}
   247  	}
   248  	return Object(obj)
   249  }
   250  func (f *Functions) GetBindingi(pname Enum, idx int) Object {
   251  	obj := f.Ctx.Call("getIndexedParameter", int(pname), idx)
   252  	if !obj.Truthy() {
   253  		return Object{}
   254  	}
   255  	return Object(obj)
   256  }
   257  func (f *Functions) GetInteger(pname Enum) int {
   258  	return paramVal(f.Ctx.Call("getParameter", int(pname)))
   259  }
   260  func (f *Functions) GetFloat(pname Enum) float32 {
   261  	return float32(f.Ctx.Call("getParameter", int(pname)).Float())
   262  }
   263  func (f *Functions) GetInteger4(pname Enum) [4]int {
   264  	arr := f.Ctx.Call("getParameter", int(pname))
   265  	var res [4]int
   266  	for i := range res {
   267  		res[i] = arr.Index(i).Int()
   268  	}
   269  	return res
   270  }
   271  func (f *Functions) GetFloat4(pname Enum) [4]float32 {
   272  	arr := f.Ctx.Call("getParameter", int(pname))
   273  	var res [4]float32
   274  	for i := range res {
   275  		res[i] = float32(arr.Index(i).Float())
   276  	}
   277  	return res
   278  }
   279  func (f *Functions) GetProgrami(p Program, pname Enum) int {
   280  	return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname)))
   281  }
   282  func (f *Functions) GetProgramInfoLog(p Program) string {
   283  	return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String()
   284  }
   285  func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
   286  	if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
   287  		return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname))))
   288  	} else {
   289  		return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
   290  	}
   291  }
   292  func (f *Functions) GetShaderi(s Shader, pname Enum) int {
   293  	return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname)))
   294  }
   295  func (f *Functions) GetShaderInfoLog(s Shader) string {
   296  	return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String()
   297  }
   298  func (f *Functions) GetString(pname Enum) string {
   299  	switch pname {
   300  	case EXTENSIONS:
   301  		extsjs := f.Ctx.Call("getSupportedExtensions")
   302  		var exts []string
   303  		for i := 0; i < extsjs.Length(); i++ {
   304  			exts = append(exts, "GL_"+extsjs.Index(i).String())
   305  		}
   306  		return strings.Join(exts, " ")
   307  	default:
   308  		return f.Ctx.Call("getParameter", int(pname)).String()
   309  	}
   310  }
   311  func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
   312  	return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name)))
   313  }
   314  func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
   315  	return Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name))
   316  }
   317  func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
   318  	return paramVal(f.Ctx.Call("getVertexAttrib", index, int(pname)))
   319  }
   320  func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
   321  	obj := f.Ctx.Call("getVertexAttrib", index, int(pname))
   322  	if !obj.Truthy() {
   323  		return Object{}
   324  	}
   325  	return Object(obj)
   326  }
   327  func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
   328  	return uintptr(f.Ctx.Call("getVertexAttribOffset", index, int(pname)).Int())
   329  }
   330  func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
   331  	fn := f.Ctx.Get("invalidateFramebuffer")
   332  	if !fn.IsUndefined() {
   333  		if f.int32Buf.IsUndefined() {
   334  			f.int32Buf = js.Global().Get("Int32Array").New(1)
   335  		}
   336  		f.int32Buf.SetIndex(0, int32(attachment))
   337  		f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf)
   338  	}
   339  }
   340  func (f *Functions) IsEnabled(cap Enum) bool {
   341  	return f.Ctx.Call("isEnabled", int(cap)).Truthy()
   342  }
   343  func (f *Functions) LinkProgram(p Program) {
   344  	f.Ctx.Call("linkProgram", js.Value(p))
   345  }
   346  func (f *Functions) PixelStorei(pname Enum, param int32) {
   347  	f.Ctx.Call("pixelStorei", int(pname), param)
   348  }
   349  func (f *Functions) MemoryBarrier(barriers Enum) {
   350  	panic("not implemented")
   351  }
   352  func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
   353  	panic("not implemented")
   354  }
   355  func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
   356  	f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height)
   357  }
   358  func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
   359  	ba := f.byteArrayOf(data)
   360  	f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), ba)
   361  	js.CopyBytesToGo(data, ba)
   362  }
   363  func (f *Functions) Scissor(x, y, width, height int32) {
   364  	f.Ctx.Call("scissor", x, y, width, height)
   365  }
   366  func (f *Functions) ShaderSource(s Shader, src string) {
   367  	f.Ctx.Call("shaderSource", js.Value(s), src)
   368  }
   369  func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
   370  	f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil)
   371  }
   372  func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
   373  	f.Ctx.Call("texStorage2D", int(target), levels, int(internalFormat), width, height)
   374  }
   375  func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
   376  	f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
   377  }
   378  func (f *Functions) TexParameteri(target, pname Enum, param int) {
   379  	f.Ctx.Call("texParameteri", int(target), int(pname), int(param))
   380  }
   381  func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
   382  	f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
   383  }
   384  func (f *Functions) Uniform1f(dst Uniform, v float32) {
   385  	f.Ctx.Call("uniform1f", js.Value(dst), v)
   386  }
   387  func (f *Functions) Uniform1i(dst Uniform, v int) {
   388  	f.Ctx.Call("uniform1i", js.Value(dst), v)
   389  }
   390  func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
   391  	f.Ctx.Call("uniform2f", js.Value(dst), v0, v1)
   392  }
   393  func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
   394  	f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2)
   395  }
   396  func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
   397  	f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3)
   398  }
   399  func (f *Functions) UseProgram(p Program) {
   400  	f.Ctx.Call("useProgram", js.Value(p))
   401  }
   402  func (f *Functions) UnmapBuffer(target Enum) bool {
   403  	panic("not implemented")
   404  }
   405  func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
   406  	f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset)
   407  }
   408  func (f *Functions) Viewport(x, y, width, height int) {
   409  	f.Ctx.Call("viewport", x, y, width, height)
   410  }
   411  
   412  func (f *Functions) byteArrayOf(data []byte) js.Value {
   413  	if len(data) == 0 {
   414  		return js.Null()
   415  	}
   416  	f.resizeByteBuffer(len(data))
   417  	ba := f.uint8Array.New(f.arrayBuf, int(0), int(len(data)))
   418  	js.CopyBytesToJS(ba, data)
   419  	return ba
   420  }
   421  
   422  func (f *Functions) resizeByteBuffer(n int) {
   423  	if n == 0 {
   424  		return
   425  	}
   426  	if !f.arrayBuf.IsUndefined() && f.arrayBuf.Length() >= n {
   427  		return
   428  	}
   429  	f.arrayBuf = js.Global().Get("ArrayBuffer").New(n)
   430  }
   431  
   432  func paramVal(v js.Value) int {
   433  	switch v.Type() {
   434  	case js.TypeBoolean:
   435  		if b := v.Bool(); b {
   436  			return 1
   437  		} else {
   438  			return 0
   439  		}
   440  	case js.TypeNumber:
   441  		return v.Int()
   442  	default:
   443  		panic("unknown parameter type")
   444  	}
   445  }