Rewrite on simdjson with a flat src and smack-style build
This commit is contained in:
1 parent
2d11cddc5e
commit
1a0db1abbd
178 files changed
+5935
-44757
No files matched your search
@@ -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();
|
||||
}
|
||||
Reference in new issue
Block a user