1
Fork 0

odin(sdl-opengl-rendering): tinker with instancing

This commit is contained in:
prescientmoon 2025-04-01 23:59:12 +02:00
parent f98d2a6baa
commit b3aad17877
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
4 changed files with 258 additions and 67 deletions
odin/sdl-opengl-rendering

View file

@ -46,11 +46,19 @@
system:
let
pkgs = inputs.nixpkgs.legacyPackages.${system}.extend inputs.self.overlays.default;
raylib = pkgs.raylib.override { platform = "SDL"; };
cross = import inputs.nixpkgs {
localSystem = system;
crossSystem.config = "x86_64-w64-mingw32";
};
inherit (pkgs) lib;
in
{
packages = { inherit (pkgs) odin; };
packages = {
inherit (pkgs) odin;
gcc = cross.callPackage (import ./gcc.nix) { };
libgl = cross.libGL;
};
# {{{ Shell
devShell = pkgs.mkShell rec {
@ -65,18 +73,19 @@
pkgs.gdb # Debugger
pkgs.seer # Debugger GUI
pkgs.valgrind # Detect memory leaks
pkgs.renderdoc # Graphics debugger
];
buildInputs = [
# pkgs.libGL
# pkgs.libxkbcommon
# pkgs.xorg.libXi
# pkgs.xorg.libX11
# pkgs.xorg.libXrandr
# pkgs.xorg.libXinerama
# pkgs.xorg.libXcursor
# pkgs.wayland
pkgs.sdl3
pkgs.xorg.libX11
pkgs.xorg.libXScrnSaver
pkgs.xorg.libXcursor
pkgs.xorg.libXext
pkgs.xorg.libXfixes
pkgs.xorg.libXi
pkgs.xorg.libXrandr
];
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath buildInputs;

View file

@ -4,17 +4,16 @@ import "base:runtime"
import "core:c"
import "core:fmt"
import "core:log"
import "core:math"
import "vendor:OpenGL"
import "vendor:sdl3"
State :: struct {
window: ^sdl3.Window,
program: u32,
vertex_pos_location: u32,
vao: u32,
vbo: u32,
ibo: u32,
wireframe: bool,
tick: u32,
window: ^sdl3.Window,
program: u32,
rect_vao: VAO,
wireframe: bool,
}
init :: proc() -> (state: State, ok: bool) {
@ -67,6 +66,11 @@ init :: proc() -> (state: State, ok: bool) {
)
// }}}
sdl3.SetAppMetadata(
"odin-rendering-experiments",
"<hash-here>",
"dev.moonythm.odin-rendering-experiments",
) or_return
sdl3.Init(sdl3.InitFlags{.VIDEO}) or_return
sdl3.GL_SetAttribute(.CONTEXT_MAJOR_VERSION, GL_MAJOR) or_return
sdl3.GL_SetAttribute(.CONTEXT_MINOR_VERSION, GL_MINOR) or_return
@ -87,60 +91,24 @@ init :: proc() -> (state: State, ok: bool) {
(gl_ctx != nil) or_return
OpenGL.load_up_to(GL_MAJOR, GL_MINOR, sdl3.gl_set_proc_address)
OpenGL.ClearColor(0, 0, 0, 1)
OpenGL.Enable(OpenGL.DEPTH_TEST)
state.program = OpenGL.load_shaders_source(
#load("./vert.glsl"),
#load("./frag.glsl"),
) or_return
// log.debug("Doing Opengl stuff")
vertex_pos_location := OpenGL.GetAttribLocation(state.program, "aPos")
(vertex_pos_location != -1) or_return
state.vertex_pos_location = u32(vertex_pos_location)
state.rect_vao = create_vao({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, {0, 1, 2, 3}) or_return
OpenGL.ClearColor(0, 0, 0, 1)
// VBO data
vertex_data := 2 * [?]f32{-0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5}
// IBO data
index_data := [?]u32{0, 1, 2, 3}
OpenGL.GenVertexArrays(1, &state.vao)
OpenGL.BindVertexArray(state.vao)
//Create VBO
OpenGL.GenBuffers(1, &state.vbo)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, state.vbo)
OpenGL.BufferData(OpenGL.ARRAY_BUFFER, 2 * 4 * size_of(f32), &vertex_data, OpenGL.STATIC_DRAW)
OpenGL.EnableVertexAttribArray(state.vertex_pos_location)
OpenGL.VertexAttribPointer(
state.vertex_pos_location,
2,
OpenGL.FLOAT,
false,
2 * size_of(f32),
0,
)
OpenGL.GenBuffers(1, auto_cast &state.ibo)
OpenGL.BindBuffer(OpenGL.ELEMENT_ARRAY_BUFFER, state.ibo)
OpenGL.BufferData(
OpenGL.ELEMENT_ARRAY_BUFFER,
4 * size_of(u32),
&index_data,
OpenGL.STATIC_DRAW,
)
OpenGL.BindVertexArray(0)
OpenGL.DisableVertexAttribArray(state.vertex_pos_location)
init_command_queue()
return state, true
}
render :: proc(state: State) {
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT)
render :: proc(state: ^State) {
state.tick += 1
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
OpenGL.UseProgram(state.program)
defer OpenGL.UseProgram(0)
@ -151,8 +119,23 @@ render :: proc(state: State) {
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL)
}
OpenGL.BindVertexArray(state.vao)
OpenGL.DrawElements(OpenGL.TRIANGLE_FAN, 4, OpenGL.UNSIGNED_INT, nil)
draw_rect({-0.5, 0}, {0.75, 0.5}, {1, 0, 0, 1}, z = 0.5)
draw_rect({0.5, 0.25}, {0.3, 0.5}, {0, 1, 0, 1}, z = -0.5)
count := (32)
for x in (0) ..< count {
for y in (0) ..< count {
i := x * count + y
pos :=
²{x + math.sin_f32(2 * math.π * ((state.tick) + i) / 60), y} *
2 /
(count) -
1
draw_rect(Rect{pos, 1 / (count), 0, {(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1}})
}
}
render_queue(state)
}
close :: proc(state: State) {
@ -194,11 +177,12 @@ main :: proc() {
case 'w':
state.wireframe = !state.wireframe
}
log.debug(event.text)
}
}
render(state)
render(&state)
sdl3.GL_SwapWindow(state.window)
free_all(context.temp_allocator)
}
}

View file

@ -0,0 +1,195 @@
package visuals
import "core:log"
import "vendor:OpenGL"
// {{{ Math constants
:: f32
² :: [2]
³ :: [3]
:: uint
Mat3 :: matrix[3, 3]
Color :: [4]
// }}}
// {{{ Command queues
Rect :: struct {
top_left: ²,
dimensions: ²,
z: ,
fill: Color,
}
Circle :: struct {
center: ²,
radius: ,
}
Shape :: union {
Rect,
Circle,
}
Command_Queue :: struct {
rects: [dynamic]Rect,
}
queue: ^Command_Queue
init_command_queue :: proc() {
queue = new(Command_Queue)
queue^ = Command_Queue {
rects = make([dynamic]Rect),
}
}
draw_rect_args :: proc(top_left: ², dimensions: ², color: Color, z: = 0) {
draw_rect_struct(Rect{top_left, dimensions, z, color})
}
draw_rect_struct :: proc(rect: Rect) {
append(&queue.rects, rect)
}
draw_rect :: proc {
draw_rect_args,
draw_rect_struct,
}
// }}}
// {{{ VAO & consts
VAO :: struct {
vao: u32,
ibo: u32,
vertex_pos_buffer: u32,
instance_fill_buffer: u32,
instance_mat_buffer: u32,
index_count: ,
}
INSTANCES :: 1024 // The number of instances to allocate space in the buffer for
VERTEX_POS_LOCATION :: 0
INSTANCE_FILL_LOCATION :: 1
INSTANCE_MAT_LOCATION :: 2
// }}}
// {{{ Create VAO
create_vao :: proc(vertices: []², indices: []u32) -> (out: VAO, ok: bool) {
out.index_count = len(indices)
// Create VAO
OpenGL.GenVertexArrays(1, &out.vao)
OpenGL.BindVertexArray(out.vao)
// Create instance fill VBO
OpenGL.GenBuffers(1, auto_cast &out.instance_fill_buffer)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_fill_buffer)
OpenGL.EnableVertexAttribArray(INSTANCE_FILL_LOCATION)
OpenGL.VertexAttribPointer(INSTANCE_FILL_LOCATION, 4, OpenGL.FLOAT, false, size_of(Color), 0)
OpenGL.VertexAttribDivisor(INSTANCE_FILL_LOCATION, 1)
// Create instance mat VBO
OpenGL.GenBuffers(1, auto_cast &out.instance_mat_buffer)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_mat_buffer)
for i in u32(0) ..< 3 {
OpenGL.EnableVertexAttribArray(INSTANCE_MAT_LOCATION + i)
vec3_size :: size_of(³)
OpenGL.VertexAttribPointer(
INSTANCE_MAT_LOCATION + i,
3,
OpenGL.FLOAT,
false,
3 * vec3_size,
uintptr(i * vec3_size),
)
OpenGL.VertexAttribDivisor(INSTANCE_MAT_LOCATION + i, 1)
}
//Create position VBO
OpenGL.GenBuffers(1, &out.vertex_pos_buffer)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.vertex_pos_buffer)
OpenGL.BufferData(
OpenGL.ARRAY_BUFFER,
len(vertices) * 2 * size_of(),
raw_data(vertices),
OpenGL.STATIC_DRAW,
)
// Put the data into the VBO
OpenGL.EnableVertexAttribArray(VERTEX_POS_LOCATION)
OpenGL.VertexAttribPointer(VERTEX_POS_LOCATION, 2, OpenGL.FLOAT, false, 2 * size_of(), 0)
// Create the IBO
OpenGL.GenBuffers(1, auto_cast &out.ibo)
OpenGL.BindBuffer(OpenGL.ELEMENT_ARRAY_BUFFER, out.ibo)
OpenGL.BufferData(
OpenGL.ELEMENT_ARRAY_BUFFER,
len(indices) * size_of(u32),
raw_data(indices),
OpenGL.STATIC_DRAW,
)
OpenGL.BindVertexArray(0)
return out, true
}
// }}}
// {{{ Set rect transforms
set_rect_transforms :: proc(vao: ^VAO, rects: []Rect) -> {
log.assert(len(rects) <= INSTANCES, "Attempting to send too many rects to the GPU")
matrices := new([INSTANCES]Mat3, context.temp_allocator)
fills := new([INSTANCES]Color, context.temp_allocator)
for rect, i in rects {
// This matrix must transform the rect [-1, 1]² into the desired rect
mat: Mat3
center := rect.top_left + rect.dimensions / 2
mat[0, 0] = rect.dimensions.x / 2
mat[1, 1] = rect.dimensions.y / 2
mat[2].xy = center.xy
mat[2].z = rect.z
matrices[i] = mat
fills[i] = rect.fill
}
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_mat_buffer)
OpenGL.BufferData(
OpenGL.ARRAY_BUFFER,
INSTANCES * size_of(Mat3),
matrices,
OpenGL.DYNAMIC_DRAW,
)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_fill_buffer)
OpenGL.BufferData(OpenGL.ARRAY_BUFFER, INSTANCES * size_of(Color), fills, OpenGL.DYNAMIC_DRAW)
return len(rects)
}
// }}}
// {{{ Render the entire queue
draw_instances :: proc(vao: VAO, instances: ) {
OpenGL.BindVertexArray(vao.vao)
OpenGL.DrawElementsInstanced(
OpenGL.TRIANGLE_FAN,
i32(vao.index_count),
OpenGL.UNSIGNED_INT,
nil,
i32(instances),
)
}
render_queue :: proc(state: ^State) {
rect_steps := len(queue.rects) / INSTANCES
for i := 0; i < len(queue.rects); i += INSTANCES {
slice := queue.rects[i:]
if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
instances := set_rect_transforms(&state.rect_vao, slice)
draw_instances(state.rect_vao, instances)
}
clear(&queue.rects)
}
// }}}

View file

@ -1,10 +1,13 @@
#version 330
in vec2 aPos;
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec4 instanceFill;
layout (location = 2) in mat3 instanceMatrix;
out vec4 vertexColor;
void main() {
gl_Position = vec4(aPos.x, aPos.y, 0, 1);
vertexColor = vec4((aPos.x + 1) / 2, (aPos.y + 1) / 2, 1, 1);
vec3 pos = instanceMatrix * vec3(aPos.xy, 1);
gl_Position = vec4(pos.xyz, 1);
vertexColor = instanceFill;
}