Skip to content

Optimize code rendering by adjusting viewport handling - #2974

Open
Wang-Yue wants to merge 1 commit into
nesbox:mainfrom
Wang-Yue:pr/optimize-code-rendering
Open

Optimize code rendering by adjusting viewport handling#2974
Wang-Yue wants to merge 1 commit into
nesbox:mainfrom
Wang-Yue:pr/optimize-code-rendering

Conversation

@Wang-Yue

Copy link
Copy Markdown
Contributor

and skipping on-visible lines.

we simply don't render anything above or below the view port. This gives significant speed up with large source code files,

@joshgoebel

joshgoebel commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

I like this. Here is what AI is suggesting and it makes sense to me. I think we can ignore the syntaxPointer thought for now... I think it's per character... I assume you'd have seen some very ugly syntax highligting with this patch if that were not the case.


Nice perf improvement idea 👍 — I have two correctness concerns around highlight/selection:

  • In the “skip lines above viewport” loop, pointer and syntaxPointer are both advanced per character. This is correct only if code->state is strictly char-aligned with code->src. If state is token/line-derived, this can desync syntax/highlight state after skipping, especially on the first visible line(s).

  • The early-exit condition uses if (y >= TIC80_HEIGHT) break;. Since rendering is scoped by rect, shouldn’t this be bounded by the editor viewport (e.g. rect.y + rect.h) instead of the full screen height? In non-fullscreen/editor-offset layouts this can clip visible selection/highlight unexpectedly.

Could we validate with cases where selection starts off-screen and ends on-screen, and with editor rect not filling the full screen?

@Wang-Yue

Copy link
Copy Markdown
Contributor Author

I ran your AI concerns with my AI. Seems the first concern is not valid, but the second is. It is also pretty trivial to fix the second issue.

I am not very confident on AI analysis though --- better to let human knowledgable about this take a look what is actually needed.


1. Character/Byte Alignment of CodeState

In the viewport optimization loop, both pointer (referencing code->src) and syntaxPointer (referencing code->state) are advanced together per character:

// Skip lines above the viewport
s32 skippedLines = 0;
while (*pointer && skippedLines < code->scroll.y)
{
    if (*pointer == '\n')
    {
        skippedLines++;
    }
    pointer++;
    syntaxPointer++;
}

Analysis & Verification

This logic assumes that code->state is strictly char-aligned (byte-for-byte mapped) with the source string code->src. If code->state were token- or line-derived, skipping characters like this would desynchronize the highlights.

Looking at src/studio/editors/code.c, we find the mapping implementation in the getState helper:

static inline CodeState* getState(Code* code, const char* pos)
{
    return code->state + (pos - code->src);
}

Because code->state is an array of CodeState structs where index $i$ corresponds directly to the character offset at code->src[i], the per-character advancement of both pointers remains perfectly synchronized and correct.


2. Early-Exit Bounding Check

Commit originally introduced the following early-exit condition:

if (y >= TIC80_HEIGHT)
{
    // break early if we are below the visible screen viewport
    break;
}

The Problem

Using the screen height boundary (TIC80_HEIGHT):

  • Works under the default layout because the editor viewport bottom rect.y + rect.h equals TIC80_HEIGHT - STUDIO_TEXT_HEIGHT (exactly 1 line-height above the screen edge, which is covered/overwritten by drawStatus).
  • Breaks under custom/offset layouts: If the code editor is rendered within a windowed or offset layout where the bottom viewport boundary is less than the screen height, using TIC80_HEIGHT leads to drawing text and selections outside of the viewport area, leaking into other elements.

The Fix

The viewport is logically bounded by rect.y + rect.h. Changing the condition to check against rect.y + rect.h bounds the loop precisely to the editor viewport:

-            if (y >= TIC80_HEIGHT)
+            if (y >= rect.y + rect.h)
             {
                 // break early if we are below the visible screen viewport
                 break;
             }

This ensures that lines beginning below the viewport bounds are skipped early, avoiding redundant iterations and rendering leaks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants