This repository has been archived on 2023-01-29. You can view files and clone it, but cannot push or open issues or pull requests.
1weekendraytracer/code/sdl_platform.cpp

289 lines
7.4 KiB
C++

#include "rt_weekend.h"
#include <SDL.h>
#include <stdio.h>
#include <sys/mman.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <errno.h>
#include <dlfcn.h>
#include "sdl_platform.h"
const char *WindowTitle = "Ray Tracing in a Weekend";
const uint32_t TARGET_FRAME_RATE = 15;
const uint32_t TICKS_PER_FRAME = 1000 / TARGET_FRAME_RATE;
const uint32_t WINDOW_WIDTH = 500;
const uint32_t WINDOW_HEIGHT = 250;
global_variable bool Running = true;
global_variable sdl_offscreen_buffer GlobalBackbuffer;
void PluginUpdateAndRenderStub(plugin_offscreen_buffer *Buffer)
{
}
internal sdl_window_dimension
SDLGetWindowDimension(SDL_Window *Window)
{
sdl_window_dimension Result;
SDL_GetWindowSize(Window, &Result.Width, &Result.Height);
return(Result);
}
internal void
SDLResizeTexture(sdl_offscreen_buffer *Buffer,
SDL_Renderer *Renderer,
int Width, int Height)
{
const int BytesPerPixel = 4;
if(Buffer->Memory)
{
munmap(Buffer->Memory,
Buffer->Height * Buffer->Pitch);
}
if(Buffer->Texture)
{
SDL_DestroyTexture(Buffer->Texture);
}
Buffer->Texture = SDL_CreateTexture(Renderer,
SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STREAMING,
Width, Height);
Buffer->Width = Width;
Buffer->Height = Height;
Buffer->Pitch = Width * BytesPerPixel;
Buffer->Memory = mmap(0, Width * Height * BytesPerPixel,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
}
internal void
SDLUpdateWindow(SDL_Renderer *Renderer,
sdl_offscreen_buffer Buffer)
{
SDL_UpdateTexture(Buffer.Texture,
0,
Buffer.Memory,
Buffer.Pitch);
SDL_RenderCopy(Renderer, Buffer.Texture, 0, 0);
SDL_RenderPresent(Renderer);
}
internal void
HandleEvent(SDL_Event *Event)
{
switch(Event->type)
{
case SDL_QUIT:
{
Running = false;
} break;
case SDL_WINDOWEVENT:
{
switch (Event->window.event)
{
case SDL_WINDOWEVENT_EXPOSED:
{
SDL_Window *Window = SDL_GetWindowFromID(Event->window.windowID);
SDL_Renderer *Renderer = SDL_GetRenderer(Window);
SDLUpdateWindow(Renderer, GlobalBackbuffer);
} break;
}
}
}
}
inline time_t
SDLGetLastModificationTime(const char *Filename)
{
time_t LastModificationTime = 0;
// Find out plugin library's last modification time
struct stat LibraryFileStat;
if (stat(Filename, &LibraryFileStat) == 0)
{
LastModificationTime = LibraryFileStat.st_ctime;
}
else
{
printf("Could not read plugin library: %s\n", strerror(errno));
}
return(LastModificationTime);
}
internal sdl_plugin_code
SDLLoadPluginCode(const char *Filename)
{
sdl_plugin_code Result = {};
Result.UpdateAndRender = PluginUpdateAndRenderStub;
Result.LastModificationTime = SDLGetLastModificationTime(Filename);
const char *error;
/* Load library */
Result.CodeLibrary = dlopen(Filename, RTLD_LAZY);
if (Result.CodeLibrary)
{
dlerror();
Result.UpdateAndRender =
(plugin_update_and_render *)dlsym(Result.CodeLibrary,
"PluginUpdateAndRender");
if ((error = dlerror()))
{
fprintf(stderr, "Couldn't find PluginUpdateAndRender: %s\n", error);
}
}
else
{
fprintf(stderr, "Couldn't open plugin library: %s\n",
dlerror());
}
return(Result);
}
internal void
SDLUnloadPluginCode(sdl_plugin_code *Plugin)
{
if(Plugin->CodeLibrary)
{
dlclose(Plugin->CodeLibrary);
}
Plugin->UpdateAndRender = PluginUpdateAndRenderStub;
}
int main()
{
// Variables required to calculate framerate
#define FPS_INTERVAL 1.0
uint32_t LastFrameEndTime = SDL_GetTicks();
uint32_t CurrentFPS = 0;
uint32_t FramesElapsed = 0;
uint32_t FramesMissed = 0;
// Initialize SDL
SDL_Window *Window = NULL;
SDL_Renderer *Renderer = NULL;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
// Create window
Window = SDL_CreateWindow(WindowTitle,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
SDL_WINDOW_OPENGL);
if (Window == NULL)
{
printf("Could not create window: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
// Create renderer
Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED);
if (Renderer == NULL)
{
printf("Could not create renderer: %s\n", SDL_GetError());
SDL_DestroyWindow(Window);
SDL_Quit();
return 1;
}
// Create texture
sdl_window_dimension Dimension = SDLGetWindowDimension(Window);
SDLResizeTexture(&GlobalBackbuffer, Renderer, Dimension.Width, Dimension.Height);
// Load plugin code
const char *PluginLibraryFilename = "librt_weekend.so";
sdl_plugin_code Plugin = SDLLoadPluginCode(PluginLibraryFilename);
// Main loop
while (Running)
{
uint32_t FrameStartTime = SDL_GetTicks();
// Live reloading
time_t NewLastModificationTime = SDLGetLastModificationTime(PluginLibraryFilename);
if(NewLastModificationTime > Plugin.LastModificationTime)
{
SDLUnloadPluginCode(&Plugin);
Plugin = SDLLoadPluginCode(PluginLibraryFilename);
}
SDL_Event Event;
while (SDL_PollEvent(&Event))
{
HandleEvent(&Event);
}
// Display rendering information in Window title
char WindowTitleBuffer[512];
sprintf(WindowTitleBuffer,
"%s - %d x %d - %u fps - %d frames missed",
WindowTitle,
WINDOW_WIDTH,
WINDOW_HEIGHT,
CurrentFPS,
FramesMissed);
SDL_SetWindowTitle(Window, WindowTitleBuffer);
plugin_offscreen_buffer Buffer = {};
Buffer.Memory = GlobalBackbuffer.Memory;
Buffer.Width = GlobalBackbuffer.Width;
Buffer.Height = GlobalBackbuffer.Height;
Buffer.Pitch = GlobalBackbuffer.Pitch;
Plugin.UpdateAndRender(&Buffer);
SDLUpdateWindow(Renderer, GlobalBackbuffer);
uint32_t FrameEndTime = SDL_GetTicks();
// Calculate framerate
if (LastFrameEndTime < FrameEndTime - FPS_INTERVAL * 1000)
{
LastFrameEndTime = FrameEndTime;
CurrentFPS = FramesElapsed;
FramesElapsed = 0;
}
FramesElapsed++;
// Cap framerate
uint32_t FrameTicks = FrameEndTime - FrameStartTime;
if (FrameTicks < TICKS_PER_FRAME)
{
// Wait remaining time
SDL_Delay(TICKS_PER_FRAME - FrameTicks);
}
else
{
FramesMissed++;
}
}
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
return 0;
}