Skip to content

[Software Renderer/Windows] Severe Rendering Bugs, Freezes, and Performance Regressions #158

Description

@ChristianG1984

[Software Renderer/Windows] Severe Rendering Bugs, Freezes, and Performance Regressions

Note: This issue was analyzed and compiled by an AI coding agent assisting a human developer. We performed a deep dive into the software renderer fallback path and identified several critical issues affecting gogpu and wgpu.

Environment

  • OS: Microsoft Windows 10 Pro (10.0.19045)
  • Hardware GPU: Intel(R) HD Graphics 4600 (Integrated GPU)
  • Go Version: 1.26.4
  • Framework Versions:
    • github.com/gogpu/ui v0.1.40
    • github.com/gogpu/gg v0.49.5
    • github.com/gogpu/gogpu v0.43.0
    • github.com/gogpu/wgpu v0.30.7

Description

When running gogpu/ui applications (like the hello or gallery examples) on Windows without hardware acceleration (falling back to the Software Renderer), the application exhibits severe bugs ranging from complete freezes to corrupted visuals and overlapping layouts.

While the framework's development has done a fantastic job implementing modern GPU features (MSAA, Compositor Clipping) for hardware backends, these features heavily penalize the pure-Go CPU fallback. Additionally, several core WebGPU features are simply stubbed out or implemented incorrectly in the hal/software backend.

Here is a detailed breakdown of the 6 root causes we identified during our debugging session:


1. Massive Performance Regression / "Freeze" (ADR-021 & MSAA)

Symptom: The application appears to completely freeze immediately upon startup.
Root Cause: A goroutine dump reveals the application isn't deadlocked; it is spinning at 100% CPU in hal/software/raster.Rasterize executing SPIR-V fragment shaders.
In gg v0.49.5, ADR-021 enforces full-screen renders (encodeSubmitSurfaceGrouped) and ignores DamageRects when MSAA or grouped rendering is active. For a pure CPU software renderer, calculating complex SDF shaders for an entire 800x900 window (potentially at 4x MSAA) every frame creates an insurmountable fill-rate bottleneck. It takes minutes per frame, masquerading as a hard freeze.
Proposed Fix: The engine should detect the Software adapter and explicitly disable MSAA and full-screen grouped rendering to force the blitOnly path with strict DamageRects.

2. Missing OS Paint Event Propagation (WM_PAINT)

Symptom: Even when rendering is fast enough, the window often remains completely black until a pointer event (mouse move/click) forces a wake-up.
Root Cause: gogpu v0.43.0 defaults to event-driven rendering (WithContinuousRender(false)). The Windows message loop in internal/platform/platform_windows.go intercepts WM_PAINT and validates the region, but never queues a redraw event. WaitEvents() remains blocked.
Proposed Fix: Introduce an EventPaint to the platform interface and trigger app.RequestRedraw() when handling WM_PAINT.

3. Missing DrawIndexed Support

Symptom: Text rendering is either completely missing or severely corrupted.
Root Cause: Modern text pipelines (like MSDF) use indexed quads. However, DrawIndexed in hal/software/command.go is an empty no-op. All text draw calls are silently dropped.
Proposed Fix: Implement executeDrawIndexed and index-aware vertex fetchers (fetchTrianglesWithIndices) to properly resolve attributes.

4. Ignored Dynamic Uniform Buffer Offsets

Symptom: Elements within lists (e.g., 1000 ListView items) fail to layout correctly and instead stack on top of each other at (0,0).
Root Cause: The software backend's SetBindGroup ignores dynamicOffsets. The rasterizer always reads uniform data starting at offset 0.
Proposed Fix: Update bufferBinding to retain base offsets, and add dynamicOffsets logic inside buildExecutionContext to slice the buffer correctly before executing the shader.

5. Red/Blue (R/B) Channel Swap on Existing Pixels

Symptom: Backgrounds and borders turn orange instead of blue; colors degrade continuously on every redraw.
Root Cause: Windows offscreen DIBs use BGRA8Unorm. When draw.go reads existing pixels for alpha blending, it blindly assumes RGBA. The R and B channels are swapped when reading, and swapped again when writing, permanently corrupting the existing background pixels.
Proposed Fix: Check if the target format is BGRA and apply the correct swizzle when feeding existingData into pipe.SetPixel.

6. Missing GDI Synchronization

Symptom: Random black blocks or tearing during partial blits.
Root Cause: blitDamageRectsToWindow uses BitBlt directly after CPU writes to the DIB section. Without calling GdiFlush(), GDI commands and CPU memory writes race, resulting in stale data being presented.
Proposed Fix: Call GdiFlush() before and after GDI blit calls in blit_windows.go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions