Issue description
With the software renderer (GRAPHICS_API_OPENGL_SOFTWARE), LoadRenderTexture() produces an incomplete framebuffer whenever rlsw is compiled with a depth type other than the default D32 (i.e. SW_FRAMEBUFFER_DEPTH_TYPE = D8 or D16). rlFramebufferComplete() logs WARNING: FBO: [ID 1] Framebuffer has incomplete attachment and any BeginTextureMode()/EndTextureMode() then renders into nothing (the render texture stays empty). The default D32 build works, so the bug is latent and only appears once you pick a smaller depth buffer (e.g. to save memory on a constrained target).
Root cause
The rlsw rasterizer is bound at compile time to exactly one depth format, SW_FRAMEBUFFER_DEPTH_FORMAT (from SW_FRAMEBUFFER_DEPTH_TYPE); depth is read/written via the compile-time SW_FRAMEBUFFER_DEPTH_GET/SET macros with a fixed stride. So every depth attachment must have that format, and swCheckFramebufferStatus() enforces it:
// external/rlsw.h - swCheckFramebufferStatus()
if (RLSW.depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMAT)
return SW_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
But the FBO depth attachment is created with a fixed format that ignores the compiled type, at two spots:
1. src/rlgl.h, rlLoadTextureDepth() software branch hard-codes GL_DEPTH_COMPONENT32 (its own comment even warns about it):
#elif defined(GRAPHICS_API_OPENGL_SOFTWARE)
// NOTE: Renderbuffers are the same type of object as textures in rlsw
// WARNING: Ensure that the depth format is the one specified at rlsw compilation
glGenRenderbuffers(1, &id);
glBindRenderbuffer(GL_RENDERBUFFER, id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, width, height); // hard-coded
glBindRenderbuffer(GL_RENDERBUFFER, 0);
#endif
2. src/external/rlsw.h, swTexStorage2D() maps every depth internal format to D16/D32 regardless of the compiled type (there is no GL_DEPTH_COMPONENT8 enum):
case SW_DEPTH_COMPONENT16: pixelFormat = SW_PIXELFORMAT_DEPTH_D16; break;
case SW_DEPTH_COMPONENT24: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break;
case SW_DEPTH_COMPONENT32: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break;
case SW_DEPTH_COMPONENT32F: pixelFormat = SW_PIXELFORMAT_DEPTH_D32; break;
On a D8 build the FBO depth attachment ends up D32, so D32 != D8 -> INCOMPLETE_ATTACHMENT. On the default D32 build the two happen to agree, which is why the default build works.
Steps to reproduce
Build rlsw with a non-default depth type and create a render texture:
// rlsw compiled with -DSW_FRAMEBUFFER_DEPTH_TYPE=D8 (or D16)
InitWindow(800, 450, "rt");
RenderTexture2D t = LoadRenderTexture(160, 90);
// console: "WARNING: FBO: [ID 1] Framebuffer has incomplete attachment"
The stock examples/core/core_smooth_pixelperfect then shows an empty canvas (text overlays draw fine; the up-scaled render-texture region stays blank).
Minimal engine-free reproduction (runs on any host). rlsw is pure software, so the completeness path can be driven without a window/GL: #define RLSW_IMPLEMENTATION, swInit(), then create a color texture + a depth renderbuffer via glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, ...), attach both, and print glCheckFramebufferStatus() - i.e. exactly the gl* -> sw* calls rlgl makes. Built with native x86_64 mingw-gcc against the unmodified upstream rlsw.h, changing only SW_FRAMEBUFFER_DEPTH_TYPE:
| SW_FRAMEBUFFER_DEPTH_TYPE |
framebuffer status |
D8 |
0x8cd6 INCOMPLETE_ATTACHMENT |
D16 |
0x8cd6 INCOMPLETE_ATTACHMENT |
D32 (default) |
0x8cd5 COMPLETE |
Proposed fix
Any depth attachment used by the rasterizer must have the one compile-time format (SW_FRAMEBUFFER_DEPTH_FORMAT) - depth uses the fixed-stride SW_FRAMEBUFFER_DEPTH_GET/SET macros, so a different-format depth buffer would also be misread, not merely incomplete. Two ways:
Option A - minimal (rlsw only). Coerce any depth request in swTexStorage2D() to the compiled format:
case SW_DEPTH_COMPONENT16:
case SW_DEPTH_COMPONENT24:
case SW_DEPTH_COMPONENT32:
case SW_DEPTH_COMPONENT32F: pixelFormat = SW_FRAMEBUFFER_DEPTH_FORMAT; break;
One line, one file, covers D8 (no GL enum exists for it); default D32 unchanged.
Option B - explicit format (rlsw + rlgl). If you prefer the format named rather than silently coerced: add a (non-standard) SW_DEPTH_COMPONENT8 to SWinternalformat (the internal SW_PIXELFORMAT_DEPTH_D8 already exists) and map it in swTexStorage2D(); then in rlgl.h rlLoadTextureDepth() (software) replace the hard-coded GL_DEPTH_COMPONENT32 with the enum matching the compiled SW_FRAMEBUFFER_DEPTH_TYPE. More faithful, but touches two files, needs a non-standard enum, and adds no capability (the rasterizer is single-format, so rlLoadTextureDepth must request exactly the compiled format anyway).
Either way, keep the depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMAT check in swCheckFramebufferStatus() as the backstop. (I used Option A in my port and verified it - table above.)
Environment
- raylib 6.1-dev @
f00317be
GRAPHICS_API_OPENGL_SOFTWARE (rlsw), SW_FRAMEBUFFER_DEPTH_TYPE = D8
- Found while porting raylib to AmigaOS 3.2 (68k, software renderer); the minimal repro above was run and confirmed on Windows x86_64 (mingw-gcc 15.2) against the unmodified upstream
rlsw.h.
Issue description
With the software renderer (
GRAPHICS_API_OPENGL_SOFTWARE),LoadRenderTexture()produces an incomplete framebuffer wheneverrlswis compiled with a depth type other than the defaultD32(i.e.SW_FRAMEBUFFER_DEPTH_TYPE = D8orD16).rlFramebufferComplete()logsWARNING: FBO: [ID 1] Framebuffer has incomplete attachmentand anyBeginTextureMode()/EndTextureMode()then renders into nothing (the render texture stays empty). The defaultD32build works, so the bug is latent and only appears once you pick a smaller depth buffer (e.g. to save memory on a constrained target).Root cause
The
rlswrasterizer is bound at compile time to exactly one depth format,SW_FRAMEBUFFER_DEPTH_FORMAT(fromSW_FRAMEBUFFER_DEPTH_TYPE); depth is read/written via the compile-timeSW_FRAMEBUFFER_DEPTH_GET/SETmacros with a fixed stride. So every depth attachment must have that format, andswCheckFramebufferStatus()enforces it:But the FBO depth attachment is created with a fixed format that ignores the compiled type, at two spots:
1.
src/rlgl.h,rlLoadTextureDepth()software branch hard-codesGL_DEPTH_COMPONENT32(its own comment even warns about it):2.
src/external/rlsw.h,swTexStorage2D()maps every depth internal format toD16/D32regardless of the compiled type (there is noGL_DEPTH_COMPONENT8enum):On a
D8build the FBO depth attachment ends upD32, soD32 != D8->INCOMPLETE_ATTACHMENT. On the defaultD32build the two happen to agree, which is why the default build works.Steps to reproduce
Build
rlswwith a non-default depth type and create a render texture:The stock
examples/core/core_smooth_pixelperfectthen shows an empty canvas (text overlays draw fine; the up-scaled render-texture region stays blank).Minimal engine-free reproduction (runs on any host).
rlswis pure software, so the completeness path can be driven without a window/GL:#define RLSW_IMPLEMENTATION,swInit(), then create a color texture + a depth renderbuffer viaglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, ...), attach both, and printglCheckFramebufferStatus()- i.e. exactly thegl* -> sw*callsrlglmakes. Built with native x86_64 mingw-gcc against the unmodified upstreamrlsw.h, changing onlySW_FRAMEBUFFER_DEPTH_TYPE:D80x8cd6INCOMPLETE_ATTACHMENTD160x8cd6INCOMPLETE_ATTACHMENTD32(default)0x8cd5COMPLETEProposed fix
Any depth attachment used by the rasterizer must have the one compile-time format (
SW_FRAMEBUFFER_DEPTH_FORMAT) - depth uses the fixed-strideSW_FRAMEBUFFER_DEPTH_GET/SETmacros, so a different-format depth buffer would also be misread, not merely incomplete. Two ways:Option A - minimal (rlsw only). Coerce any depth request in
swTexStorage2D()to the compiled format:One line, one file, covers
D8(no GL enum exists for it); defaultD32unchanged.Option B - explicit format (rlsw + rlgl). If you prefer the format named rather than silently coerced: add a (non-standard)
SW_DEPTH_COMPONENT8toSWinternalformat(the internalSW_PIXELFORMAT_DEPTH_D8already exists) and map it inswTexStorage2D(); then inrlgl.hrlLoadTextureDepth()(software) replace the hard-codedGL_DEPTH_COMPONENT32with the enum matching the compiledSW_FRAMEBUFFER_DEPTH_TYPE. More faithful, but touches two files, needs a non-standard enum, and adds no capability (the rasterizer is single-format, sorlLoadTextureDepthmust request exactly the compiled format anyway).Either way, keep the
depthBuffer->format != SW_FRAMEBUFFER_DEPTH_FORMATcheck inswCheckFramebufferStatus()as the backstop. (I used Option A in my port and verified it - table above.)Environment
f00317beGRAPHICS_API_OPENGL_SOFTWARE(rlsw),SW_FRAMEBUFFER_DEPTH_TYPE = D8rlsw.h.