Documentation: engine/docs assembles one registry from the script
bindings, the schema, module docs and module-owned script paths; problems
are reported as warnings at build time. Module registries record doc
sources and script module owners; diag.Codes lists every diagnostic code.
Project bootstrap: project.defaultSpace ("2d"/"3d"), EnsureDirs creates
the default folders when the editor opens a project (reported in the
console), prefabs/ is a pipeline root. Native builds drop -trimpath so
binding locations resolve inside native/.
Editor files: create scene/prefab/script from menus, move/rename/duplicate
/delete with undo, script package retargeting on move, open scenes and
script panels follow renames, Save As / Rename Scene prompts, scene
picker, startup scene. Scoped shortcuts fall back to the global command
when disabled; Save As is Ctrl+Shift+S, Save All Ctrl+Alt+S.
Script editor: Go to Implementation (stub → native/ source), host
definitions always resolve to stubs, bracket match/mismatch states,
rainbow brackets (persisted), mono code font (ui-mono atlases, per-command
fonts in draw.List, PushFont/PopFont restored by Mark/Restore).
Tests: langsvc definition/implementation, bracket states, dispatch
fallback, script renames, font stack, editor file-op integration, golden
mono glyph and bracket references; depcheck rule for engine/docs.
Docs pipeline: engine, render2d and script/engine ship their own res/docs;
keyword hover text comes from the registry through manifest.Keywords;
project script symbols join the registry; aego docs export|check|build|
serve, --tool docs in native binaries, aego check --explain; app/docstool
and the tools/gendocs module (goldmark) build the static site.
Playground: app/playground + cmd/aego-playground (js/wasm, 3.3 MB gzip)
compile, highlight and run scripts in the browser; run blocks in the
language docs become examples. engine/builtin/headless is the module set
without render3d; workspace.Config.FS; StubFor/StubPaths; backends and the
script importer drop out of js builds.
VM: one frame pointer in the interpreter loop, direct host calls, panic
recovery once per run instead of per host call (Update2k -19%,
Schedule10k -24% against the previous head). bench-build/compare/profile
run pre-built, pinned, interleaved binaries.
Editor: structural bracket levels and current-block highlight, indent
guides, occurrence highlighting with write marks, multi-format clipboard
(copy/cut/paste assets, paste images as sprites or textures). gl33 probes
a renderable depth format and reports framebuffer failures by stage.
7 E:
- project templates empty/2d/2.5d/3d, app/projects as the single creation path (D148)
- launcher and recent projects (D149)
- orthographic Camera3D and Scene View (D150)
- reference states in the Inspector, ids.Short (D151)
- sceneio remaps node references when loading without KeepIDs (D152)
- exported ComponentRef<T> (D153)
- aego icons via module.Audit (D146)
133 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|