Search code examples
chtml5-canvassdlwebassemblyemscripten

Wasm with emscripten and SDL - How to clear only part of the renderer


I've created a Wasm animation using emscripten and SDL. I am now trying to create a button where the user can start and stop/pause the animation. The problem I have is that the whole canvas is cleared on every iteration. As I see it, there are 3 options here:

  1. Redraw the button on every frame.
  2. Only clear the part of the canvas that the animation uses on each iteration.
  3. Use a HTML form for the input buttons.

(1) seems inefficient. (3) would require me to start interoperating between JS and Wasm. So far I've been able to build the whole thing without writing a single line of JS, and would prefer to keep it that way. So probably (2) is best.

I know that with JS it is possible to specify the dimensions of a clearRect call. I can't find this functionality in the SDL API however.

Below is the mainLoop function (which gets called about 60 times a second) (This is written in C if it's unclear. Not C++). My question is, how can I adapt the "SDL_RenderClear" so that it only clears one half of the canvas? (The half with the animation. I.e. don't clear the half where I want to make buttons.

main.c

#include <stdio.h>
#include <SDL2/SDL.h>
#include <emscripten.h>
#include <stdlib.h>
#include <stdbool.h>

<....>

/**
 * The loop handler, will be called repeatedly
 */
void mainLoop(void *arg) {
    struct Context *ctx = arg;
    printf("iteration: %d\n", ctx->iteration);

    SDL_SetRenderDrawColor(ctx->renderer, 255, 0, 100, 255);
    SDL_RenderClear(ctx->renderer);

    bool running = true;

    if (running) {
        for (int j=1; j<8; j++) {
            for (int i=1; i<130; i++) {
                // drawRect is another app function that calls the SDL to draw a rectangles.
                drawRectangle(ctx, i, j);
            }
        }
    }
    SDL_RenderPresent(ctx->renderer);
    ctx->iteration++;
}

<....>

Solution

  • You can just paint over parts you don't want with SDL_RenderFillRect. However, unless you do a lot of trickery, it isn't very helpful.

    Formal reason - https://wiki.libsdl.org/SDL_RenderPresent says "The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames."

    There are many cases when your buffer will be (partially) invalidated, e.g. moving window out of screen bounds. I don't know about webasm, you probably need to check webgl documentation if anything about buffer contents is being promised.

    Even if you can rely on contents to be kept, but e.g. you have double buffered setup. You've rendered to first buffer, performed buffer swap - now you have second buffer, which contents isn't initialised yet. After second swap it could be more-or-less similar, but any change will be lagging behind 1 frame.

    Some 3d software (e.g. blender) did what you described but even there it wasn't 100% stable and usually there was an option to disable that behabiour. Performing full redraw is much cheaper than you think, and if it becomes too costly - you can always render part of your screen into render texture and use it as single static image, updating it when necessary.