Rewrite on simdjson with a flat src and smack-style build

This commit is contained in:
shweet committed 2026-11-12 21:43:49 -05:00
1 parent 2d11cddc5e
commit 1a0db1abbd
178 files changed
+5935 -44757

No files matched your search

+261
View File
@@ -0,0 +1,261 @@
#include "renderer.h"
#include <SDL3/SDL.h>
#include <cstddef>
#define GL_GLEXT_PROTOTYPES
#include <SDL3/SDL_opengl.h>
constexpr int LINE_WIDTH = 2;
constexpr int QUAD_ORDER[] = {0, 1, 2, 0, 2, 3};
constexpr int WHITE_SIZE = 1;
constexpr Uint32 WHITE_PIXEL = 0xFFFFFFFF;
#ifdef SDL_PLATFORM_EMSCRIPTEN
constexpr const char* SHADER_VERSION = "#version 300 es\nprecision highp float;\n";
#else
constexpr const char* SHADER_VERSION = "#version 330 core\n";
#endif
// Maps pixels to clip space with view: x and y scales, then offsets.
constexpr const char* VERTEX_SHADER = R"(
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec2 inUv;
layout(location = 2) in vec4 inTint;
layout(location = 3) in vec4 inOffset;
uniform vec4 view;
out vec2 uv;
out vec4 tint;
out vec4 offset;
void main()
{
uv = inUv;
tint = inTint;
offset = inOffset;
gl_Position = vec4(inPosition * view.xy + view.zw, 0.0, 1.0);
}
)";
// Tints each texel, then adds the color offset.
constexpr const char* FRAGMENT_SHADER = R"(
in vec2 uv;
in vec4 tint;
in vec4 offset;
uniform sampler2D image;
out vec4 color;
void main()
{
color = clamp(texture(image, uv) * tint + offset, 0.0, 1.0);
}
)";
// Every GL entry point the renderer uses, looked up from the driver once a context exists.
#define GL_FUNCTIONS(X) \
X(AttachShader) \
X(BindBuffer) \
X(BindFramebuffer) \
X(BindTexture) \
X(BindVertexArray) \
X(BlendFuncSeparate) \
X(BufferData) \
X(Clear) \
X(ClearColor) \
X(CompileShader) \
X(CreateProgram) \
X(CreateShader) \
X(DeleteShader) \
X(DeleteTextures) \
X(Disable) \
X(DrawArrays) \
X(Enable) \
X(EnableVertexAttribArray) \
X(FramebufferTexture2D) \
X(GenBuffers) \
X(GenFramebuffers) \
X(GenTextures) \
X(GenVertexArrays) \
X(GetShaderInfoLog) \
X(GetUniformLocation) \
X(LinkProgram) \
X(ShaderSource) \
X(TexImage2D) \
X(TexParameteri) \
X(Uniform4f) \
X(UseProgram) \
X(VertexAttribPointer) \
X(Viewport)
struct Gl
{
#define GL_FUNCTION_DECLARE(name) decltype(&gl##name) name = nullptr;
GL_FUNCTIONS(GL_FUNCTION_DECLARE)
#undef GL_FUNCTION_DECLARE
};
static Gl gl;
static GLuint shader_compile(GLenum stage, const char* source)
{
const char* sources[] = {SHADER_VERSION, source};
GLuint shader = gl.CreateShader(stage);
gl.ShaderSource(shader, 2, sources, nullptr);
gl.CompileShader(shader);
char log[1024] = {};
gl.GetShaderInfoLog(shader, sizeof(log), nullptr, log);
if (*log)
SDL_Log("Shader: %s", log);
return shader;
}
static Texture texture_create(int width, int height, const void* pixels)
{
Texture texture = {0, height, width};
gl.GenTextures(1, &texture.id);
gl.BindTexture(GL_TEXTURE_2D, texture.id);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
return texture;
}
bool Renderer::init()
{
bool isLoaded = true;
#define GL_FUNCTION_LOAD(name) \
gl.name = (decltype(gl.name))SDL_GL_GetProcAddress("gl" #name); \
isLoaded &= gl.name != nullptr;
GL_FUNCTIONS(GL_FUNCTION_LOAD)
#undef GL_FUNCTION_LOAD
if (!isLoaded)
return false;
program = gl.CreateProgram();
GLuint vertexShader = shader_compile(GL_VERTEX_SHADER, VERTEX_SHADER);
GLuint fragmentShader = shader_compile(GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
gl.AttachShader(program, vertexShader);
gl.AttachShader(program, fragmentShader);
gl.LinkProgram(program);
gl.DeleteShader(vertexShader);
gl.DeleteShader(fragmentShader);
viewLocation = gl.GetUniformLocation(program, "view");
gl.GenVertexArrays(1, &vertexArray);
gl.GenBuffers(1, &vertexBuffer);
gl.BindVertexArray(vertexArray);
gl.BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
auto attribute = [](int index, int count, size_t offset) {
gl.EnableVertexAttribArray(index);
gl.VertexAttribPointer(index, count, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offset);
};
attribute(0, 2, offsetof(Vertex, x));
attribute(1, 2, offsetof(Vertex, u));
attribute(2, 4, offsetof(Vertex, tint));
attribute(3, 4, offsetof(Vertex, offset));
white = texture_create(WHITE_SIZE, WHITE_SIZE, &WHITE_PIXEL);
return true;
}
Texture* Renderer::texture_get(const std::string& path)
{
if (auto found = textures.find(path); found != textures.end())
return found->second.id ? &found->second : nullptr;
Texture& texture = textures[path];
SDL_Surface* loaded = SDL_LoadPNG(path.c_str());
SDL_Surface* surface = loaded ? SDL_ConvertSurface(loaded, SDL_PIXELFORMAT_RGBA32) : nullptr;
SDL_DestroySurface(loaded);
if (!surface)
{
SDL_Log("Failed to load %s: %s", path.c_str(), SDL_GetError());
return nullptr;
}
texture = texture_create(surface->w, surface->h, surface->pixels);
SDL_DestroySurface(surface);
return &texture;
}
// Draws to a canvas (sized to fit, y flipped so imgui shows it upright), or to the window when canvas is null.
void Renderer::canvas_set(Canvas* canvas, int width, int height, Color clear)
{
flush();
if (canvas)
{
if (!canvas->framebuffer)
gl.GenFramebuffers(1, &canvas->framebuffer);
if (canvas->texture.width != width || canvas->texture.height != height)
{
gl.DeleteTextures(1, &canvas->texture.id);
canvas->texture = texture_create(width, height, nullptr);
gl.BindFramebuffer(GL_FRAMEBUFFER, canvas->framebuffer);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas->texture.id, 0);
}
}
gl.BindFramebuffer(GL_FRAMEBUFFER, canvas ? canvas->framebuffer : 0);
gl.Viewport(0, 0, width, height);
gl.ClearColor(clear.r, clear.g, clear.b, clear.a);
gl.Clear(GL_COLOR_BUFFER_BIT);
float flip = canvas ? 1 : -1;
view[0] = 2.0f / width;
view[1] = flip * 2.0f / height;
view[2] = -1;
view[3] = -flip;
}
// Draws region (in the texture's pixels) as a quad: the transform takes the region's pixels, from its top left, to the
// target's.
void Renderer::quad_draw(const Texture& texture, Rect region, Affine transform, Color tint, Color offset)
{
if (!texture.id || !texture.width || !texture.height)
return;
if (texture.id != batchTexture)
flush();
batchTexture = texture.id;
Vec2 corners[] = {{0, 0}, {region.w, 0}, {region.w, region.h}, {0, region.h}};
for (int index : QUAD_ORDER)
{
Vec2 corner = corners[index];
Vec2 position = transform * corner;
float u = (region.x + corner.x) / texture.width;
float v = (region.y + corner.y) / texture.height;
vertices.push_back({offset, tint, u, v, position.x, position.y});
}
}
void Renderer::line_draw(Vec2 from, Vec2 to, Color color)
{
Vec2 delta = to - from;
float length = vec2_length(delta);
if (length <= 0)
return;
Vec2 normal = Vec2{-delta.y, delta.x} / length;
Affine transform = {delta.x / length, delta.y / length, normal.x, normal.y, from.x, from.y};
quad_draw(white, {0, 0, length, LINE_WIDTH}, transform * Affine{1, 0, 0, 1, 0, -LINE_WIDTH / 2.0f}, color);
}
void Renderer::outline_draw(Affine transform, float width, float height, Color color)
{
Vec2 corners[] = {transform * Vec2{0, 0}, transform * Vec2{width, 0}, transform * Vec2{width, height},
transform * Vec2{0, height}};
for (int i = 0; i < 4; i++)
line_draw(corners[i], corners[(i + 1) % 4], color);
}
void Renderer::flush()
{
if (vertices.empty())
return;
gl.Enable(GL_BLEND);
gl.Disable(GL_DEPTH_TEST);
gl.Disable(GL_SCISSOR_TEST);
gl.Disable(GL_CULL_FACE);
gl.BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
gl.UseProgram(program);
gl.Uniform4f(viewLocation, view[0], view[1], view[2], view[3]);
gl.BindVertexArray(vertexArray);
gl.BindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
gl.BufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_STREAM_DRAW);
gl.BindTexture(GL_TEXTURE_2D, batchTexture);
gl.DrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.size());
vertices.clear();
}