Files
aego/gpu/gl33/framebuffer.go
T

133 lines
3.9 KiB
Go

package gl33
import (
"aego/core/errs"
"aego/core/handle"
"aego/core/log"
"aego/gpu/gl33/gl"
)
type fboKey struct {
color handle.Handle[glTexture]
depth handle.Handle[glTexture]
}
func fbStatusName(s uint32) string {
switch s {
case gl.FRAMEBUFFER_UNDEFINED:
return "UNDEFINED"
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
return "INCOMPLETE_ATTACHMENT"
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
return "INCOMPLETE_MISSING_ATTACHMENT"
case gl.FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
return "INCOMPLETE_DRAW_BUFFER"
case gl.FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
return "INCOMPLETE_READ_BUFFER"
case gl.FRAMEBUFFER_UNSUPPORTED:
return "UNSUPPORTED"
case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
return "INCOMPLETE_MULTISAMPLE"
}
return "UNKNOWN"
}
func (d *Device) validDepth(colorDepth bool, cw, ch int, depth handle.Handle[glTexture]) handle.Handle[glTexture] {
if depth.IsZero() {
return depth
}
dt, ok := d.textures.Get(depth)
switch {
case !ok:
d.log.Warn("depth target handle is invalid, rendering without depth")
case colorDepth || !dt.info.depth:
d.log.Warn("depth target must be a depth texture paired with a color texture")
case dt.w != cw || dt.h != ch:
d.log.Warn("depth target size differs from the color target, rendering without depth",
log.F("color_w", cw), log.F("color_h", ch), log.F("depth_w", dt.w), log.F("depth_h", dt.h))
default:
return depth
}
return handle.Handle[glTexture]{}
}
func fbStatus() (uint32, uint32) {
glErr := gl.GetError()
return gl.CheckFramebufferStatus(gl.FRAMEBUFFER), glErr
}
func (d *Device) framebuffer(color, depth handle.Handle[glTexture]) (uint32, int, int, bool, error) {
ct, ok := d.textures.Get(color)
if !ok {
return 0, 0, 0, false, errs.New(errs.GPUInvalidHandle, "framebuffer: color texture handle is invalid")
}
colorID, cw, ch, colorInfo := ct.id, ct.w, ct.h, ct.info
colorDepth := colorInfo.depth
depth = d.validDepth(colorDepth, cw, ch, depth)
hasDepth := colorDepth || !depth.IsZero()
key := fboKey{color: color, depth: depth}
if fbo, ok := d.fbos[key]; ok {
return fbo, cw, ch, hasDepth, nil
}
depthInfo := texInfo{name: "none"}
var depthID uint32
if !depth.IsZero() {
dt, _ := d.textures.Get(depth)
depthID, depthInfo = dt.id, dt.info
}
for gl.GetError() != gl.NO_ERROR {
}
var fbo uint32
gl.GenFramebuffers(1, &fbo)
gl.BindFramebuffer(gl.FRAMEBUFFER, fbo)
d.cache.Invalidate()
stage := "color"
if colorDepth {
stage = "depth-only"
gl.FramebufferTexture2D(gl.FRAMEBUFFER, colorInfo.attach, gl.TEXTURE_2D, colorID, 0)
gl.DrawBuffer(gl.NONE)
gl.ReadBuffer(gl.NONE)
} else {
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorID, 0)
gl.DrawBuffer(gl.COLOR_ATTACHMENT0)
gl.ReadBuffer(gl.COLOR_ATTACHMENT0)
}
status, glErr := fbStatus()
if status == gl.FRAMEBUFFER_COMPLETE && glErr == gl.NO_ERROR && depthID != 0 {
stage = "depth"
gl.FramebufferTexture2D(gl.FRAMEBUFFER, depthInfo.attach, gl.TEXTURE_2D, depthID, 0)
status, glErr = fbStatus()
}
if status != gl.FRAMEBUFFER_COMPLETE || glErr != gl.NO_ERROR {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
gl.DeleteFramebuffers(1, &fbo)
d.cache.Invalidate()
return 0, 0, 0, false, errs.New(errs.GPUResource, "framebuffer is incomplete",
log.F("stage", stage), log.F("status", fbStatusName(status)), log.F("status_code", status),
log.F("gl", glErrorName(glErr)), log.F("color_format", colorInfo.name), log.F("depth_format", depthInfo.name),
log.F("w", cw), log.F("h", ch), log.F("renderer", d.caps.Renderer))
}
d.fbos[key] = fbo
return fbo, cw, ch, hasDepth, nil
}
func (d *Device) forgetFramebuffers(h handle.Handle[glTexture]) {
for k, fbo := range d.fbos {
if k.color != h && k.depth != h {
continue
}
id := fbo
d.cache.ForgetFramebuffer(id)
gl.DeleteFramebuffers(1, &id)
delete(d.fbos, k)
}
}
func (d *Device) deleteFramebuffers() {
for k, fbo := range d.fbos {
id := fbo
gl.DeleteFramebuffers(1, &id)
delete(d.fbos, k)
}
}