(Fixed) webgl

This commit is contained in:
Zyko 2024-04-10 18:57:55 +02:00
parent fc3a6ed373
commit 55f1a5d32e
7 changed files with 106 additions and 54 deletions

View File

@ -102,6 +102,7 @@ type context struct {
locationCache *locationCache
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
mrtFramebuffer framebufferNative // The dynamic framebuffer used for MRT operations
lastFramebuffer framebufferNative
lastTexture textureNative
lastRenderbuffer renderbufferNative
@ -110,8 +111,6 @@ type context struct {
lastBlend graphicsdriver.Blend
maxTextureSize int
maxTextureSizeOnce sync.Once
highp bool
highpOnce sync.Once
initOnce sync.Once
}
@ -139,26 +138,25 @@ func (c *context) bindFramebuffer(f framebufferNative) {
c.lastFramebuffer = f
}
func (c *context) setViewport(f *framebuffer) {
c.bindFramebuffer(f.native)
if c.lastViewportWidth == f.width && c.lastViewportHeight == f.height {
func (c *context) setViewport(width, height int, screen bool) {
if c.lastViewportWidth == width && c.lastViewportHeight == height {
return
}
// On some environments, viewport size must be within the framebuffer size.
// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
// Use the same size of the framebuffer here.
c.ctx.Viewport(0, 0, int32(f.width), int32(f.height))
c.ctx.Viewport(0, 0, int32(width), int32(height))
// glViewport must be called at least at every frame on iOS.
// As the screen framebuffer is the last render target, next SetViewport should be
// the first call at a frame.
if f.native == c.screenFramebuffer {
if screen {
c.lastViewportWidth = 0
c.lastViewportHeight = 0
} else {
c.lastViewportWidth = f.width
c.lastViewportHeight = f.height
c.lastViewportWidth = width
c.lastViewportHeight = height
}
}
@ -264,16 +262,6 @@ func (c *context) framebufferPixels(buf []byte, f *framebuffer, region image.Rec
return nil
}
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
c.ctx.Flush()
c.bindFramebuffer(f.native)
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer))
c.ctx.ReadPixels(nil, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, 0)
}
func (c *context) deleteTexture(t textureNative) {
if c.lastTexture == t {
c.lastTexture = 0
@ -357,7 +345,7 @@ func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) e
c.ctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
return fmt.Errorf("opengl: glFramebufferRenderbuffer failed: %d", s)
}
return nil
}

View File

@ -52,6 +52,7 @@ const (
MIN = 0x8007
NEAREST = 0x2600
NO_ERROR = 0
NONE = 0
NOTEQUAL = 0x0205
ONE = 1
ONE_MINUS_DST_ALPHA = 0x0305

View File

@ -54,6 +54,7 @@ type defaultContext struct {
fnDeleteVertexArray js.Value
fnDisable js.Value
fnDisableVertexAttribArray js.Value
fnDrawBuffers js.Value
fnDrawElements js.Value
fnEnable js.Value
fnEnableVertexAttribArray js.Value
@ -184,6 +185,7 @@ func NewDefaultContext(v js.Value) (Context, error) {
fnDeleteVertexArray: v.Get("deleteVertexArray").Call("bind", v),
fnDisable: v.Get("disable").Call("bind", v),
fnDisableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v),
fnDrawBuffers: v.Get("drawBuffers").Call("bind", v),
fnDrawElements: v.Get("drawElements").Call("bind", v),
fnEnable: v.Get("enable").Call("bind", v),
fnEnableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v),
@ -384,6 +386,11 @@ func (c *defaultContext) DisableVertexAttribArray(index uint32) {
c.fnDisableVertexAttribArray.Invoke(index)
}
func (c *defaultContext) DrawBuffers(bufs []uint32) {
arr := jsutil.NewUint32Array(bufs)
c.fnDrawBuffers.Invoke(arr)
}
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
c.fnDrawElements.Invoke(mode, count, xtype, offset)
}

View File

@ -45,8 +45,8 @@ type Graphics struct {
// drawCalled is true just after Draw is called. This holds true until WritePixels is called.
drawCalled bool
uniformVariableNameCache map[int]string
textureVariableNameCache map[int]string
uniformVariableNameCache map[int]string
textureVariableNameCache map[int]string
uniformVars []uniformVariable
@ -204,9 +204,9 @@ func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdr
}
g.drawCalled = true
g.context.ctx.BindTexture(gl.TEXTURE_2D, 0)
dstCount := 0
var destinations [graphics.ShaderDstImageCount]*Image
var dsts [graphics.ShaderDstImageCount]*Image
for i, dstID := range dstIDs {
if dstID == graphicsdriver.InvalidImageID {
continue
@ -215,27 +215,66 @@ func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdr
if dst == nil {
continue
}
destinations[i] = dst
dst.ensureFramebuffer()
dsts[i] = dst
dstCount++
}
if dstCount == 0 {
return nil
}
g.context.bindFramebuffer(0)
// Only necessary for the same shared framebuffer
if err := destinations[0].setViewport(); err != nil {
return err
f := uint32(dsts[0].framebuffer.native)
if dstCount > 1 {
if g.context.mrtFramebuffer == 0 {
f = g.context.ctx.CreateFramebuffer()
if f <= 0 {
return fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f)
}
g.context.mrtFramebuffer = framebufferNative(f)
} else {
f = uint32(g.context.mrtFramebuffer)
}
g.context.bindFramebuffer(framebufferNative(f))
//g.context.ctx.BindFramebuffer(gl.FRAMEBUFFER, f)
// Reset color attachments
if s := g.context.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s == gl.FRAMEBUFFER_COMPLETE {
g.context.ctx.Clear(16384 | gl.STENCIL_BUFFER_BIT)
}
for i, dst := range dsts {
if dst == nil {
continue
}
g.context.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+uint32(i), gl.TEXTURE_2D, uint32(dst.texture), 0)
}
if s := g.context.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
if s != 0 {
return fmt.Errorf("opengl: creating framebuffer failed: %v", s)
}
if e := g.context.ctx.GetError(); e != gl.NO_ERROR {
return fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
}
return fmt.Errorf("opengl: creating framebuffer failed: unknown error")
}
// Color attachments
var attached []uint32
for i, dst := range dsts {
if dst == nil {
attached = append(attached, gl.NONE)
continue
}
attached = append(attached, uint32(gl.COLOR_ATTACHMENT0+i))
}
g.context.ctx.DrawBuffers(attached)
} else {
g.context.bindFramebuffer(framebufferNative(f))
}
// Color attachments
var attached []uint32
for i, dst := range destinations {
if dst == nil {
continue
}
attached = append(attached, uint32(gl.COLOR_ATTACHMENT0+i))
g.context.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, uint32(gl.COLOR_ATTACHMENT0+i), gl.TEXTURE_2D, uint32(dst.texture), 0)
}
g.context.ctx.DrawBuffers(attached)
w, h := dsts[0].framebufferSize()
g.context.setViewport(w, h, dsts[0].screen)
g.context.blend(blend)
@ -259,7 +298,7 @@ func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdr
}
// In OpenGL, the NDC's Y direction is upward, so flip the Y direction for the final framebuffer.
if dstCount == 1 && destinations[0] != nil && destinations[0].screen {
if dstCount == 1 && dsts[0] != nil && dsts[0].screen {
const idx = graphics.ProjectionMatrixUniformVariableIndex
// Invert the sign bits as float32 values.
g.uniformVars[idx].value[1] ^= 1 << 31
@ -287,11 +326,11 @@ func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdr
g.uniformVars = g.uniformVars[:0]
if fillRule != graphicsdriver.FillAll {
for _, dst := range destinations {
for _, dst := range dsts {
if dst == nil {
continue
}
if err := dst.ensureStencilBuffer(); err != nil {
if err := dst.ensureStencilBuffer(framebufferNative(f)); err != nil {
return err
}
}
@ -336,6 +375,10 @@ func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdr
g.context.ctx.Disable(gl.STENCIL_TEST)
}
// Detach existing color attachments
//g.context.bindFramebuffer(fb)
//TODO:
return nil
}

View File

@ -37,7 +37,6 @@ type Image struct {
// framebuffer is a wrapper of OpenGL's framebuffer.
type framebuffer struct {
graphics *Graphics
native framebufferNative
width int
height int
@ -61,14 +60,6 @@ func (i *Image) Dispose() {
i.graphics.removeImage(i)
}
func (i *Image) setViewport() error {
if err := i.ensureFramebuffer(); err != nil {
return err
}
i.graphics.context.setViewport(i.framebuffer)
return nil
}
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
if err := i.ensureFramebuffer(); err != nil {
return err
@ -109,14 +100,14 @@ func (i *Image) ensureFramebuffer() error {
return nil
}
func (i *Image) ensureStencilBuffer() error {
func (i *Image) ensureStencilBuffer(f framebufferNative) error {
if i.stencil != 0 {
return nil
}
if err := i.ensureFramebuffer(); err != nil {
/*if err := i.ensureFramebuffer(); err != nil {
return err
}
}*/
r, err := i.graphics.context.newRenderbuffer(i.framebufferSize())
if err != nil {
@ -124,7 +115,7 @@ func (i *Image) ensureStencilBuffer() error {
}
i.stencil = r
if err := i.graphics.context.bindStencilBuffer(i.framebuffer.native, i.stencil); err != nil {
if err := i.graphics.context.bindStencilBuffer(f, i.stencil); err != nil {
return err
}
return nil

View File

@ -24,6 +24,7 @@ var (
uint8Array = js.Global().Get("Uint8Array")
float32Array = js.Global().Get("Float32Array")
int32Array = js.Global().Get("Int32Array")
uint32Array = js.Global().Get("Uint32Array")
)
var (
@ -40,8 +41,11 @@ var (
// temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
// temporaryInt32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
// temporaryInt32Array is a Int32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
// temporaryUint32Array is a Uint32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
temporaryUint32Array = uint32Array.New(temporaryArrayBuffer)
)
func ensureTemporaryArrayBufferSize(byteLength int) {
@ -54,6 +58,7 @@ func ensureTemporaryArrayBufferSize(byteLength int) {
temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
temporaryUint32Array = uint32Array.New(temporaryArrayBuffer)
}
}
@ -101,3 +106,11 @@ func TemporaryInt32Array(minLength int, data []int32) js.Value {
copySliceToTemporaryArrayBuffer(data)
return temporaryInt32Array
}
// NewUint32Array returns a Uint32Array whose length is equal to the length of data.
func NewUint32Array(data []uint32) js.Value {
ensureTemporaryArrayBufferSize(len(data) * 4)
copySliceToTemporaryArrayBuffer(data)
a := temporaryUint32Array.Call("slice", 0, len(data))
return a
}

View File

@ -229,6 +229,12 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
fslines = append(fslines, fmt.Sprintf("in %s;", c.varDecl(p, &t, fmt.Sprintf("V%d", i))))
}
}
// If ES300 out colors need to be defined explicitely
if version == GLSLVersionES300 {
for i := 0; i < p.ColorsOutCount; i++ {
fslines = append(fslines, fmt.Sprintf("layout(location = %d) out vec4 glFragColor%d;", i, i))
}
}
var funcs []*shaderir.Func
if p.VertexFunc.Block != nil {
@ -420,6 +426,9 @@ func (c *compileContext) localVariableName(p *shaderir.Program, topBlock *shader
case idx < nv+1:
return fmt.Sprintf("V%d", idx-1)
default:
if c.version == GLSLVersionES300 {
return fmt.Sprintf("glFragColor%d", idx-(nv+1))
}
return fmt.Sprintf("gl_FragData[%d]", idx-(nv+1))
}
default: