{"record":{"id":"ed43ff2fb0e810ea","repo":"unoplatform/uno","slug":"offscreen-fbo-is-not-complete","errorCode":null,"errorMessage":"Offscreen FBO is not complete","messagePattern":"Offscreen FBO is not complete","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PostProcessGlCanvasElement.cs","lineNumber":73,"sourceCode":"\t\t{\n\t\t\t_startTime = DateTime.UtcNow;\n\n\t\t\t// --- Offscreen color texture ---\n\t\t\t_offscreenColor = gl.GenTexture();\n\t\t\tgl.BindTexture(TextureTarget.Texture2D, _offscreenColor);\n\t\t\tgl.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba, OffscreenSize, OffscreenSize, 0, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);\n\t\t\tgl.TexParameter(TextureTarget.Texture2D, GLEnum.TextureMinFilter, (int)GLEnum.Linear);\n\t\t\tgl.TexParameter(TextureTarget.Texture2D, GLEnum.TextureMagFilter, (int)GLEnum.Linear);\n\t\t\tgl.TexParameter(TextureTarget.Texture2D, GLEnum.TextureWrapS, (int)GLEnum.ClampToEdge);\n\t\t\tgl.TexParameter(TextureTarget.Texture2D, GLEnum.TextureWrapT, (int)GLEnum.ClampToEdge);\n\n\t\t\t// --- Offscreen FBO ---\n\t\t\t_offscreenFbo = gl.GenFramebuffer();\n\t\t\tgl.BindFramebuffer(GLEnum.Framebuffer, _offscreenFbo);\n\t\t\tgl.FramebufferTexture2D(GLEnum.Framebuffer, FramebufferAttachment.ColorAttachment0, GLEnum.Texture2D, _offscreenColor, 0);\n\t\t\tif (gl.CheckFramebufferStatus(GLEnum.Framebuffer) != GLEnum.FramebufferComplete)\n\t\t\t{\n\t\t\t\tthrow new Exception(\"Offscreen FBO is not complete\");\n\t\t\t}\n\n\t\t\t// --- Scene VAO/VBO ---\n\t\t\t_sceneVao = gl.GenVertexArray();\n\t\t\tgl.BindVertexArray(_sceneVao);\n\t\t\t_sceneVbo = gl.GenBuffer();\n\t\t\tgl.BindBuffer(BufferTargetARB.ArrayBuffer, _sceneVbo);\n\t\t\tgl.BufferData(BufferTargetARB.ArrayBuffer, new ReadOnlySpan<float>(_triangleData), BufferUsageARB.StaticDraw);\n\t\t\tgl.VertexAttribPointer(0, 2, GLEnum.Float, false, 5 * sizeof(float), (void*)0);\n\t\t\tgl.EnableVertexAttribArray(0);\n\t\t\tgl.VertexAttribPointer(1, 3, GLEnum.Float, false, 5 * sizeof(float), (void*)(2 * sizeof(float)));\n\t\t\tgl.EnableVertexAttribArray(1);\n\n\t\t\t// --- Post-process VAO/VBO ---\n\t\t\t_postVao = gl.GenVertexArray();\n\t\t\tgl.BindVertexArray(_postVao);\n\t\t\t_postVbo = gl.GenBuffer();\n\t\t\tgl.BindBuffer(BufferTargetARB.ArrayBuffer, _postVbo);","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/unoplatform/uno/blob/04183404888ea21b4e5bfa3493e8d5f15e972c3a/src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/PostProcessGlCanvasElement.cs#L55-L91","documentation":"Thrown by GLCanvasElement_PostProcessElement.Init when the offscreen render target's framebuffer is not complete after attaching the 256x256 color texture (InternalFormat.Rgba, Linear filter). A non-complete offscreen FBO means the multi-pass render-to-texture pipeline cannot work — Pass 1 draws into this target, Pass 2 samples it back.","triggerScenarios":"Specifically after gl.FramebufferTexture2D(Framebuffer, ColorAttachment0, Texture2D, _offscreenColor, 0) and gl.CheckFramebufferStatus(GLEnum.Framebuffer) != GLEnum.FramebufferComplete (lines 70-73). The texture was created with InternalFormat.Rgba (unsized) and TexImage2D at OffscreenSize=256, Linear filtering (no mipmaps).","commonSituations":"Using the unsized InternalFormat.Rgba instead of a sized color-renderable format (Rgba8) — some drivers reject unsized formats as render targets; on GLES/WebGL the texture is color-renderable but the framebuffer needs GL_DEPTH_ATTACHMENT24 if depth is used (it is not here, but a stray depth test later would fail differently); llvmpipe/SwiftShader software contexts with limited render target configs; the texture storage was never allocated because TexImage2D's pixels pointer was 0/null and the driver deferred allocation until first use.","solutions":["Use the sized renderable format InternalFormat.Rgba8 instead of the unsized InternalFormat.Rgba — unsized formats are not guaranteed color-renderable across GLES/WebGL contexts.","Call gl.GetError() immediately before the status check to surface the underlying GL error.","Read the specific completeness code (Unsupported, IncompleteAttachment, IncompleteMissingAttachment) to distinguish missing-attachment from unsupported-format.","Confirm the texture was actually sized: ensure TexImage2D was called with OffscreenSize x OffscreenSize and a non-zero area, and that _offscreenColor is a valid name returned by GenTexture.","On WebGL2/GLES3, query the implementation's supported renderable formats via gl.GetInternalformativ before relying on a particular format."],"exampleFix":"// before\ngl.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba, OffscreenSize, OffscreenSize, 0, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);\n...\nif (gl.CheckFramebufferStatus(GLEnum.Framebuffer) != GLEnum.FramebufferComplete)\n    throw new Exception(\"Offscreen FBO is not complete\");\n\n// after — sized renderable format + explicit completeness code\ngl.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba8, OffscreenSize, OffscreenSize, 0, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);\n...\nvar status = gl.CheckFramebufferStatus(GLEnum.Framebuffer);\nif (status != GLEnum.FramebufferComplete)\n    throw new Exception($\"Offscreen FBO not complete: 0x{(int)status:x}, GL error=0x{(int)gl.GetError():x}\");","handlingStrategy":"validation","validationCode":"// Use a sized, color-renderable format and check it before FBO attach\ngl.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba8, OffscreenSize, OffscreenSize, 0, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)0);\n// Optionally query internalformat support on GLES3/WebGL2\n// gl.GetInternalformativ(...) to confirm RENDERABLE bit","typeGuard":null,"tryCatchPattern":"try { /* FBO setup + CheckFramebufferStatus */ }\ncatch (Exception ex) when (ex.Message.Contains(\"Offscreen FBO\"))\n{\n    App.MainWindow?.LogError($\"Offscreen FBO incomplete: {ex.Message}\");\n    // Skip multi-pass: render the scene directly to the default framebuffer\n}","preventionTips":["Prefer sized renderable formats (Rgba8) over unsized (Rgba) for color attachments.","Drain GetError before the status check to surface the underlying cause.","Read the specific completeness code, not just 'not complete'.","Query supported renderable formats on GLES/WebGL before relying on one."],"tags":["opengl","silk-net","fbo","framebuffer","render-to-texture","gl-es","webgl"],"backgroundTag":null,"analyzedSha":"04183404888ea21b4e5bfa3493e8d5f15e972c3a","analyzedAt":"2026-08-13T21:08:11.651Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}