vkoskiv / c-ray

c-ray is a small, simple path tracer written in C
MIT License
797 stars 44 forks source link

SDL implementation is really inefficient #66

Open vkoskiv opened 4 years ago

vkoskiv commented 4 years ago

Ideally we wouldn't SDL_RenderCopy the entire image buffers for every frame, but instead update only the currently active tile rectangles. Profiling reveals that a good portion of CPU time is indeed spent on the main thread where it copies the image buffers 60 times a second.

Another option would of course be to just reduce the update loop timing to 20 or 30fps, but I really prefer the smooth 60fps updating, and I think it can be manageable if we just implement this correctly. Another thing is, we're not using SDL_LockTexture at all yet.

I've been prototyping it like this recently, but it's still not working quite right:

void drawWindow(struct renderer *r, struct texture *t) {
    if (aborted) {
        r->state.renderAborted = true;
    }
#ifdef UI_ENABLED
    //Render frames
    updateFrames(r);
    //Update image data

    for (int i = 0; i < r->prefs.threadCount; ++i) {
        SDL_Rect rect = fromTile(r->state.renderTiles[r->state.threads[i].currentTileNum]);
        //int offset = rect.x + r->prefs.imageHeight * r->prefs.imageWidth; // (pitch * rect.y) + rect.x
        //int offset = (rect.y * r->prefs.imageWidth) + rect.x * t->channels;
        int offset = (rect.x + (rect.y) * t->width) * t->channels;
        SDL_UpdateTexture(r->mainDisplay->texture, &rect, t->byte_data + offset, t->width * 3);
        SDL_UpdateTexture(r->mainDisplay->overlayTexture, &rect, r->state.uiBuffer->byte_data, t->width * 4);
    }

    //SDL_UpdateTexture(r->mainDisplay->texture, NULL, t->byte_data, t->width * 3);
    //SDL_UpdateTexture(r->mainDisplay->overlayTexture, NULL, r->state.uiBuffer->byte_data, t->width * 4);
    SDL_RenderCopy(r->mainDisplay->renderer, r->mainDisplay->texture, NULL, NULL);
    SDL_RenderCopy(r->mainDisplay->renderer, r->mainDisplay->overlayTexture, NULL, NULL);
    SDL_RenderPresent(r->mainDisplay->renderer);
#endif
}

Here it gathers all the actively rendered tiles, generates an SDL_Rect based on them and then tries to update only those regions, but I keep getting the offset wrong for some reason.