Compare commits
10 Commits
47eb3ff907
...
10ee643a0c
Author | SHA1 | Date | |
---|---|---|---|
10ee643a0c | |||
00f5e71666 | |||
458c45d2b9 | |||
c4a3dd3a26 | |||
2014c67bfd | |||
177e52b0d0 | |||
3a0904023a | |||
c4c9716d61 | |||
6208711292 | |||
39bd7fb8bc |
8
.gitmodules
vendored
8
.gitmodules
vendored
@ -1,15 +1,7 @@
|
|||||||
[submodule "lib/raylib.c3l"]
|
|
||||||
path = lib/raylib.c3l
|
|
||||||
url = https://github.com/NexushasTaken/raylib.c3l
|
|
||||||
ignore = dirty
|
|
||||||
[submodule "lib/grapheme.c3l/thirdparty/libgrapheme"]
|
[submodule "lib/grapheme.c3l/thirdparty/libgrapheme"]
|
||||||
path = lib/grapheme.c3l/thirdparty/libgrapheme
|
path = lib/grapheme.c3l/thirdparty/libgrapheme
|
||||||
url = git://git.suckless.org/libgrapheme
|
url = git://git.suckless.org/libgrapheme
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "lib/mini-qoi.c3l/thirdparty/mini-qoi"]
|
|
||||||
path = lib/mini-qoi.c3l/thirdparty/mini-qoi
|
|
||||||
url = https://github.com/shraiwi/mini-qoi
|
|
||||||
ignore = dirty
|
|
||||||
[submodule "lib/schrift.c3l/thirdparty/libschrift"]
|
[submodule "lib/schrift.c3l/thirdparty/libschrift"]
|
||||||
path = lib/schrift.c3l/thirdparty/libschrift
|
path = lib/schrift.c3l/thirdparty/libschrift
|
||||||
url = https://github.com/tomolt/libschrift
|
url = https://github.com/tomolt/libschrift
|
||||||
|
2
Makefile
2
Makefile
@ -1,3 +1,3 @@
|
|||||||
test_renderer: test_renderer.c3 src/renderer.c3 resources/shaders/source/*
|
test_renderer: test_renderer.c3 src/renderer.c3 resources/shaders/source/*
|
||||||
scripts/compile_shaders.sh
|
scripts/compile_shaders.sh
|
||||||
c3c compile -g -O0 test_renderer.c3 src/renderer.c3 --libdir ../sdl3.c3l --lib sdl3
|
c3c compile -g -O0 test_renderer.c3 src/renderer.c3 --libdir lib --lib sdl3 --lib ugui
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
all: thirdparty/mini-qoi/mqoi.a
|
|
||||||
mkdir -p linux-x64
|
|
||||||
cp thirdparty/mini-qoi/mqoi.a linux-x64/libmqoi.a
|
|
||||||
|
|
||||||
thirdparty/mini-qoi/mqoi.a:
|
|
||||||
gcc -O2 -c -o thirdparty/mini-qoi/mqoi.o thirdparty/mini-qoi/src/mini_qoi.c
|
|
||||||
ar -rc thirdparty/mini-qoi/mqoi.a thirdparty/mini-qoi/mqoi.o
|
|
@ -1,122 +0,0 @@
|
|||||||
module mqoi;
|
|
||||||
|
|
||||||
macro rgb_hash(Rgb px) => ((px.r * 3 + px.g * 5 + px.b * 7) & 0b00111111);
|
|
||||||
macro mqoi_rgba_hash(Rgba px) => ((px.r * 3 + px.g * 5 + px.b * 7 + px.a * 11) & 0b00111111);
|
|
||||||
|
|
||||||
const uint MQOI_HEADER_SIZE = 14;
|
|
||||||
|
|
||||||
const char MQOI_MASK_OP_2B = 0b11000000;
|
|
||||||
const char MQOI_MASK_OP_8B = 0b11111111;
|
|
||||||
|
|
||||||
const char MQOI_MASK_OP_LUMA_DG = 0b00111111;
|
|
||||||
const char MQOI_MASK_OP_RUN = 0b00111111;
|
|
||||||
|
|
||||||
// basic types
|
|
||||||
|
|
||||||
enum DescErr : uint (uint value) {
|
|
||||||
MQOI_DESC_OK = 0, // The descriptor is valid
|
|
||||||
MQOI_DESC_INVALID_MAGIC = 1, // The magic value isn't correct
|
|
||||||
MQOI_DESC_INVALID_CHANNELS = 2, // The channel number isn't valid
|
|
||||||
MQOI_DESC_INVALID_COLORSPACE = 3, // The colorspace isn't valid
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Op : char (char value) {
|
|
||||||
MQOI_OP2_INDEX = (0b00 << 6),
|
|
||||||
MQOI_OP2_DIFF = (0b01 << 6),
|
|
||||||
MQOI_OP2_LUMA = (0b10 << 6),
|
|
||||||
MQOI_OP2_RUN = (0b11 << 6),
|
|
||||||
MQOI_OP8_RUN_RGB = (0b11111110),
|
|
||||||
MQOI_OP8_RUN_RGBA = (0b11111111),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Channels : char (char value) {
|
|
||||||
MQOI_CHANNELS_RGB = 3,
|
|
||||||
MQOI_CHANNELS_RGBA = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Colorspace : char (char value) {
|
|
||||||
MQOI_COLORSPACE_SRGB = 0,
|
|
||||||
MQOI_COLORSPACE_LINEAR = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
union Rgb {
|
|
||||||
struct { char r, g, b; }
|
|
||||||
char[3] value;
|
|
||||||
}
|
|
||||||
|
|
||||||
union Rgba{
|
|
||||||
struct { char r, g, b, a; }
|
|
||||||
char[4] value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Desc {
|
|
||||||
char head;
|
|
||||||
|
|
||||||
char[4] magic;
|
|
||||||
char[4] width; // big-endian width
|
|
||||||
char[4] height; // big-endian height
|
|
||||||
char channels;
|
|
||||||
char colorspace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== chunks ====
|
|
||||||
|
|
||||||
union Chunk {
|
|
||||||
struct {
|
|
||||||
char head;
|
|
||||||
union {
|
|
||||||
Rgb rgb;
|
|
||||||
Rgba rgba;
|
|
||||||
char drdb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char[5] value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== codecs ====
|
|
||||||
|
|
||||||
struct Encoding {
|
|
||||||
Rgba[64] hashtable;
|
|
||||||
Rgba prev_px;
|
|
||||||
Chunk working_chunk;
|
|
||||||
char working_chunk_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Dec {
|
|
||||||
Rgba[64] hashtable;
|
|
||||||
Rgba prev_px;
|
|
||||||
Chunk curr_chunk;
|
|
||||||
bitstruct : char {
|
|
||||||
char curr_chunk_head : 0..3;
|
|
||||||
char curr_chunk_size : 4..7;
|
|
||||||
}
|
|
||||||
uint pix_left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== utilities ====
|
|
||||||
|
|
||||||
fn void u32_write(uint * n, char * dest) @extern("mqoi_u32_write");
|
|
||||||
fn void u32_read(char * src, uint * n) @extern("mqoi_u32_read");
|
|
||||||
|
|
||||||
// ==== Desc ====
|
|
||||||
|
|
||||||
fn void desc_init(Desc* desc) @extern("mqoi_desc_init");
|
|
||||||
fn void desc_push(Desc* desc, char byte) @extern("mqoi_desc_push");
|
|
||||||
fn char* desc_pop(Desc* desc) @extern("mqoi_desc_pop");
|
|
||||||
fn char desc_verify(Desc* desc, uint* w, uint* h) @extern("mqoi_desc_verify");
|
|
||||||
fn bool desc_done(Desc* desc) @extern("mqoi_desc_done");
|
|
||||||
|
|
||||||
/* the encoder is still WIP
|
|
||||||
void mqoi_enc_init(mqoi_enc_t * enc);
|
|
||||||
void mqoi_enc_push(mqoi_enc_t * enc, Rgba * pix)
|
|
||||||
Chunk * mqoi_enc_pop(mqoi_enc_t * enc, char * size);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ==== Dec ====
|
|
||||||
|
|
||||||
fn void dec_init(Dec* dec, uint n_pix) @extern("mqoi_dec_init");
|
|
||||||
fn void dec_push(Dec* dec, char byte) @extern("mqoi_dec_push");
|
|
||||||
fn char dec_take(Dec* dec, char* bytes) @extern("mqoi_dec_take");
|
|
||||||
fn Rgba* dec_pop(Dec* dec) @extern("mqoi_dec_pop");
|
|
||||||
fn bool dec_done(Dec* dec) @extern("mqoi_dec_done");
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"provides" : "mqoi",
|
|
||||||
"targets" : {
|
|
||||||
"linux-x64" : {
|
|
||||||
"dependencies" : [],
|
|
||||||
"linked-libraries" : ["mqoi", "c"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"langrev": "1",
|
|
||||||
"warnings": [ "no-unused" ],
|
|
||||||
"dependency-search-paths": [ ".." ],
|
|
||||||
"dependencies": [ "mqoi" ],
|
|
||||||
"authors": [ "Alessandro Mauri <alemauri001@gmail.com>", "Shranav Palakurthi"],
|
|
||||||
"version": "0.1.0",
|
|
||||||
"sources": [ ],
|
|
||||||
"output": "build",
|
|
||||||
"target": "linux-x64",
|
|
||||||
"features": [],
|
|
||||||
"cpu": "generic",
|
|
||||||
"opt": "O0",
|
|
||||||
}
|
|
1
lib/mini-qoi.c3l/thirdparty/mini-qoi
vendored
1
lib/mini-qoi.c3l/thirdparty/mini-qoi
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit f8b5a8a4d2eeee68b52e143060b7412ba78b5077
|
|
@ -4,7 +4,7 @@
|
|||||||
"targets" : {
|
"targets" : {
|
||||||
"linux-x64" : {
|
"linux-x64" : {
|
||||||
"link-args" : [],
|
"link-args" : [],
|
||||||
"dependencies" : ["schrift", "grapheme", "mqoi"],
|
"dependencies" : ["schrift", "grapheme"],
|
||||||
"linked-libraries" : []
|
"linked-libraries" : []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS};
|
|||||||
|
|
||||||
alias CmdQueue = fifo::Fifo{Cmd};
|
alias CmdQueue = fifo::Fifo{Cmd};
|
||||||
|
|
||||||
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, UNEXPECTED_ELEMENT, WRONG_ELEMENT_TYPE;
|
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, UNEXPECTED_ELEMENT, WRONG_ELEMENT_TYPE, WRONG_ID;
|
||||||
|
|
||||||
const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 };
|
const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 };
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ fn void? Ctx.init(&ctx)
|
|||||||
ctx.style.margin = {2, 2, 2, 2};
|
ctx.style.margin = {2, 2, 2, 2};
|
||||||
ctx.style.border = {2, 2, 2, 2};
|
ctx.style.border = {2, 2, 2, 2};
|
||||||
ctx.style.padding = {1, 1, 1, 1};
|
ctx.style.padding = {1, 1, 1, 1};
|
||||||
ctx.style.radius = 5;
|
ctx.style.radius = 12;
|
||||||
ctx.style.bgcolor = 0x282828ffu.to_rgba();
|
ctx.style.bgcolor = 0x282828ffu.to_rgba();
|
||||||
ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba();
|
ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba();
|
||||||
ctx.style.brcolor = 0xd79921ffu.to_rgba();
|
ctx.style.brcolor = 0xd79921ffu.to_rgba();
|
||||||
@ -303,18 +303,10 @@ $if DEBUG == 1:
|
|||||||
.rect.color = 0xff00ffffu.to_rgba()
|
.rect.color = 0xff00ffffu.to_rgba()
|
||||||
};
|
};
|
||||||
ctx.cmd_queue.enqueue(&cmd)!;
|
ctx.cmd_queue.enqueue(&cmd)!;
|
||||||
|
|
||||||
// dump the command buffer
|
|
||||||
// io::printn("Command Buffer Dump:");
|
|
||||||
// foreach(idx, c: ctx.cmd_queue) {
|
|
||||||
// io::printfn("\t [%d] = {%s}", idx, c);
|
|
||||||
// }
|
|
||||||
ctx.cmd_queue.sort()!;
|
|
||||||
// io::printn("Sorted Command Buffer Dump:");
|
|
||||||
// foreach(idx, c: ctx.cmd_queue) {
|
|
||||||
// io::printfn("\t [%d] = {%s}", idx, c);
|
|
||||||
// }
|
|
||||||
$endif
|
$endif
|
||||||
|
|
||||||
|
// sort the command buffer by the z-index
|
||||||
|
ctx.cmd_queue.sort()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
@ -224,3 +224,13 @@ fn Id Ctx.get_font_id(&ctx, String label)
|
|||||||
{
|
{
|
||||||
return (Id)label.hash();
|
return (Id)label.hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn Atlas*? Ctx.get_font_atlas(&ctx, String name)
|
||||||
|
{
|
||||||
|
// TODO: use the font name, for now there is only one font
|
||||||
|
if (name.hash() != ctx.font.id) {
|
||||||
|
return WRONG_ID?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctx.font.atlas;
|
||||||
|
}
|
||||||
|
@ -193,7 +193,7 @@ struct Color{
|
|||||||
char r, g, b, a;
|
char r, g, b, a;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Color uint.to_rgba(uint u)
|
macro Color uint.to_rgba(u)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
.r = (char)((u >> 24) & 0xff),
|
.r = (char)((u >> 24) & 0xff),
|
||||||
@ -202,3 +202,9 @@ macro Color uint.to_rgba(uint u)
|
|||||||
.a = (char)((u >> 0) & 0xff)
|
.a = (char)((u >> 0) & 0xff)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro uint Color.to_uint(c)
|
||||||
|
{
|
||||||
|
uint u = c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ module ugui;
|
|||||||
import std::core::mem::allocator;
|
import std::core::mem::allocator;
|
||||||
import std::collections::map;
|
import std::collections::map;
|
||||||
import std::io;
|
import std::io;
|
||||||
import mqoi;
|
import std::compression::qoi;
|
||||||
|
|
||||||
const usz SRITES_PER_ATLAS = 64;
|
const usz SRITES_PER_ATLAS = 64;
|
||||||
|
|
||||||
@ -102,37 +102,12 @@ fn void? Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, us
|
|||||||
|
|
||||||
fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL)
|
fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL)
|
||||||
{
|
{
|
||||||
mqoi::Desc image_desc;
|
QOIDesc desc;
|
||||||
uint w, h;
|
|
||||||
|
|
||||||
File file = file::open(path, "rb")!;
|
char[] pixels = qoi::read(allocator::heap(), path, &desc, QOIChannels.RGBA)!;
|
||||||
defer (void) file.close();
|
|
||||||
|
|
||||||
while (!mqoi::desc_done(&image_desc)) {
|
|
||||||
mqoi::desc_push(&image_desc, file.read_byte()!);
|
|
||||||
}
|
|
||||||
if (mqoi::desc_verify(&image_desc, &w, &h) != 0) {
|
|
||||||
return io::FILE_NOT_VALID?;
|
|
||||||
}
|
|
||||||
|
|
||||||
mqoi::Dec dec;
|
|
||||||
mqoi::Rgba* px;
|
|
||||||
|
|
||||||
usz idx;
|
|
||||||
char[] pixels = mem::new_array(char, (usz)w*h*4);
|
|
||||||
defer mem::free(pixels);
|
defer mem::free(pixels);
|
||||||
|
|
||||||
mqoi::dec_init(&dec, w*h);
|
ctx.sprite_atlas.insert(name, type, pixels, (ushort)desc.width, (ushort)desc.height, (ushort)desc.width)!;
|
||||||
while (!mqoi::dec_done(&dec)) {
|
|
||||||
mqoi::dec_push(&dec, file.read_byte()!);
|
|
||||||
|
|
||||||
while ((px = mqoi::dec_pop(&dec)) != null) {
|
|
||||||
pixels[idx..idx+3] = px.value[..];
|
|
||||||
idx += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.sprite_atlas.insert(name, type, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
fn void? Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"langrev": "1",
|
"langrev": "1",
|
||||||
"warnings": ["no-unused"],
|
"warnings": ["no-unused"],
|
||||||
"dependency-search-paths": ["lib", "lib/vendor/libraries"],
|
"dependency-search-paths": ["lib", "lib/vendor/libraries"],
|
||||||
"dependencies": ["raylib55", "sdl3", "ugui"],
|
"dependencies": ["sdl3", "ugui"],
|
||||||
"features": [],
|
"features": [],
|
||||||
"authors": ["Alessandro Mauri <ale@shitposting.expert>"],
|
"authors": ["Alessandro Mauri <ale@shitposting.expert>"],
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
21
resources/shaders/source/font.frag.glsl
Normal file
21
resources/shaders/source/font.frag.glsl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(set = 3, binding = 0) uniform Viewport {
|
||||||
|
ivec2 view;
|
||||||
|
};
|
||||||
|
layout(set = 2, binding = 0) uniform sampler2D tx;
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
layout(location = 1) in vec4 color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
ivec2 ts = textureSize(tx, 0);
|
||||||
|
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||||
|
vec2 real_uv = uv / fts;
|
||||||
|
|
||||||
|
vec4 opacity = texture(tx, real_uv);
|
||||||
|
fragColor = vec4(color.rgb, color.a*opacity.r);
|
||||||
|
}
|
35
resources/shaders/source/msdf.frag.glsl
Normal file
35
resources/shaders/source/msdf.frag.glsl
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(set = 3, binding = 0) uniform Viewport {
|
||||||
|
ivec2 view;
|
||||||
|
};
|
||||||
|
layout(set = 2, binding = 0) uniform sampler2D tx;
|
||||||
|
|
||||||
|
const float PX_RANGE = 4.0f;
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
layout(location = 1) in vec4 color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
float screen_px_range(vec2 uv) {
|
||||||
|
vec2 unit_range = vec2(PX_RANGE)/vec2(textureSize(tx, 0));
|
||||||
|
vec2 texel_size = vec2(1.0)/fwidth(uv);
|
||||||
|
return max(0.5*dot(unit_range, texel_size), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float median(float r, float g, float b) {
|
||||||
|
return max(min(r, g), min(max(r, g), b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
ivec2 ts = textureSize(tx, 0);
|
||||||
|
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||||
|
vec2 real_uv = uv / fts;
|
||||||
|
|
||||||
|
vec3 msd = texture(tx, real_uv).rgb;
|
||||||
|
float sd = median(msd.r, msd.g, msd.b);
|
||||||
|
float distance = screen_px_range(real_uv)*(sd - 0.5);
|
||||||
|
float opacity = clamp(distance + 0.5, 0.0, 1.0);
|
||||||
|
fragColor = color * opacity;
|
||||||
|
}
|
@ -1,9 +1,59 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec4 col;
|
layout(set = 3, binding = 0) uniform Viewport {
|
||||||
|
ivec2 view;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 color;
|
||||||
|
layout(location = 1) in vec2 local_position;
|
||||||
|
layout(location = 2) in vec2 global_position;
|
||||||
|
layout(location = 3) in float radius;
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
// SDF for a rounded rectangle given the centerpoint, half size and radius, all in pixels
|
||||||
|
float sdf_rr(vec2 p, vec2 center, vec2 half_size, float radius) {
|
||||||
|
// Translate fragment position to rectangle's coordinate system
|
||||||
|
p -= center;
|
||||||
|
// Adjust for rounded corners: shrink the rectangle by the radius
|
||||||
|
vec2 q = abs(p) - half_size + radius;
|
||||||
|
// Combine distance components:
|
||||||
|
// - max(q, 0.0) handles regions outside the rounded corners
|
||||||
|
// - min(max(q.x, q.y), 0.0) handles regions inside the rectangle
|
||||||
|
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
fragColor = col;
|
// local_position are normalized coordinates in the rectangle, passed from the
|
||||||
|
// vertex shader
|
||||||
|
/*
|
||||||
|
* Window
|
||||||
|
* +-----------------------+
|
||||||
|
* | (1,1) |
|
||||||
|
* | +----------x |
|
||||||
|
* | | | |
|
||||||
|
* | | | |
|
||||||
|
* | | Rect | |
|
||||||
|
* | | | |
|
||||||
|
* | | | |
|
||||||
|
* | x----------+ |
|
||||||
|
* | (-1,-1) |
|
||||||
|
* | |
|
||||||
|
* +-----------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
vec2 dx = dFdx(local_position);
|
||||||
|
vec2 dy = dFdy(local_position);
|
||||||
|
// Conversion from normalized coordinates to pixels
|
||||||
|
vec2 norm_to_px = 1.0 / vec2(length(dx), length(dy));
|
||||||
|
|
||||||
|
vec2 centerpoint = global_position - local_position * norm_to_px;
|
||||||
|
// the half size of the rectangle is also norm_to_px
|
||||||
|
vec2 half_size = 1.0 * norm_to_px;
|
||||||
|
|
||||||
|
float distance = sdf_rr(global_position, centerpoint, half_size, radius);
|
||||||
|
float alpha = 1.0 - smoothstep(0.0, 1.0, max(distance, 0.0));
|
||||||
|
|
||||||
|
fragColor = vec4(color.rgb, color.a * alpha);
|
||||||
}
|
}
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
layout(set = 1, binding = 0) uniform Viewport {
|
layout(set = 1, binding = 0) uniform Viewport {
|
||||||
ivec2 view;
|
ivec2 view;
|
||||||
ivec2 off;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(location = 0) in ivec2 position;
|
layout(location = 0) in ivec2 position;
|
||||||
@ -10,19 +9,22 @@ layout(location = 1) in ivec2 uv;
|
|||||||
layout(location = 2) in ivec4 color;
|
layout(location = 2) in ivec4 color;
|
||||||
|
|
||||||
layout(location = 0) out vec4 col;
|
layout(location = 0) out vec4 col;
|
||||||
|
layout(location = 1) out vec2 local_position;
|
||||||
|
layout(location = 2) out vec2 global_position;
|
||||||
|
layout(location = 3) out float radius;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
// per vertex shift
|
|
||||||
vec2 shift;
|
|
||||||
shift.x = float(off.x) / view.x;
|
|
||||||
shift.y = -(float(off.y) / view.y);
|
|
||||||
|
|
||||||
// vertex position
|
// vertex position
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
pos.x = float(position.x)*2.0 / view.x - 1.0;
|
pos.x = float(position.x)*2.0 / view.x - 1.0;
|
||||||
pos.y = -(float(position.y)*2.0 / view.y - 1.0);
|
pos.y = -(float(position.y)*2.0 / view.y - 1.0);
|
||||||
|
|
||||||
gl_Position = vec4(pos+shift, 0.0, 1.0);
|
gl_Position = vec4(pos, 0.0, 1.0);
|
||||||
|
|
||||||
|
local_position = vec2(sign(uv));
|
||||||
|
global_position = gl_Position.xy;
|
||||||
|
radius = abs(float(uv.x));
|
||||||
|
|
||||||
col = vec4(color) / 255.0;
|
col = vec4(color) / 255.0;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
layout(set = 3, binding = 0) uniform Viewport {
|
||||||
|
ivec2 view;
|
||||||
|
};
|
||||||
|
|
||||||
layout(location = 0) in vec2 uv;
|
layout(location = 0) in vec2 uv;
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
layout(set = 1, binding = 0) uniform Viewport {
|
layout(set = 1, binding = 0) uniform Viewport {
|
||||||
ivec2 view;
|
ivec2 view;
|
||||||
ivec2 not_needed;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(location = 0) in ivec2 position;
|
layout(location = 0) in ivec2 position;
|
||||||
@ -10,6 +9,7 @@ layout(location = 1) in ivec2 in_uv;
|
|||||||
layout(location = 2) in ivec4 color;
|
layout(location = 2) in ivec4 color;
|
||||||
|
|
||||||
layout(location = 0) out vec2 out_uv;
|
layout(location = 0) out vec2 out_uv;
|
||||||
|
layout(location = 1) out vec4 out_color;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
@ -19,4 +19,5 @@ void main()
|
|||||||
gl_Position = vec4(pos, 0.0, 1.0);
|
gl_Position = vec4(pos, 0.0, 1.0);
|
||||||
|
|
||||||
out_uv = vec2(float(in_uv.x), float(in_uv.y));
|
out_uv = vec2(float(in_uv.x), float(in_uv.y));
|
||||||
|
out_color = vec4(color) / 255.0;
|
||||||
}
|
}
|
||||||
|
BIN
resources/tux_inv.qoi
Normal file
BIN
resources/tux_inv.qoi
Normal file
Binary file not shown.
301
src/main.c3
301
src/main.c3
@ -2,10 +2,11 @@ import std::io;
|
|||||||
import vtree;
|
import vtree;
|
||||||
import cache;
|
import cache;
|
||||||
import ugui;
|
import ugui;
|
||||||
import raylib5::rl;
|
|
||||||
import std::time;
|
import std::time;
|
||||||
import std::collections::ringbuffer;
|
import std::collections::ringbuffer;
|
||||||
import std::core::string;
|
import std::core::string;
|
||||||
|
import sdlrenderer::ren;
|
||||||
|
import sdl3::sdl;
|
||||||
|
|
||||||
alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]};
|
alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]};
|
||||||
|
|
||||||
@ -44,84 +45,14 @@ fn TimeStats Times.get_stats(×)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const ZString MSDF_FS = `
|
const char[*] MSDF_FS_PATH = "resources/shaders/compiled/msdf.frag.spv";
|
||||||
#version 330
|
const char[*] SPRITE_FS_PATH = "resources/shaders/compiled/sprite.frag.spv";
|
||||||
|
const char[*] FONT_FS_PATH = "resources/shaders/compiled/font.frag.spv";
|
||||||
|
const char[*] RECT_FS_PATH = "resources/shaders/compiled/rect.frag.spv";
|
||||||
|
|
||||||
in vec2 fragTexCoord;
|
const char[*] SPRITE_VS_PATH = "resources/shaders/compiled/sprite.vert.spv";
|
||||||
out vec4 fragColor;
|
const char[*] RECT_VS_PATH = "resources/shaders/compiled/rect.vert.spv";
|
||||||
uniform sampler2D texture0;
|
|
||||||
uniform vec4 colDiffuse;
|
|
||||||
|
|
||||||
const float pxRange = 4.0f;
|
|
||||||
|
|
||||||
float screenPxRange() {
|
|
||||||
vec2 unitRange = vec2(pxRange)/vec2(textureSize(texture0, 0));
|
|
||||||
vec2 screenTexSize = vec2(1.0)/fwidth(fragTexCoord);
|
|
||||||
return max(0.5*dot(unitRange, screenTexSize), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float median(float r, float g, float b) {
|
|
||||||
return max(min(r, g), min(max(r, g), b));
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 msd = texture(texture0, fragTexCoord).rgb;
|
|
||||||
float sd = median(msd.r, msd.g, msd.b);
|
|
||||||
float screenPxDistance = screenPxRange()*(sd - 0.5);
|
|
||||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
|
||||||
fragColor = colDiffuse * opacity;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ZString FONT_FS = `
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
// Input vertex attributes (from vertex shader)
|
|
||||||
in vec2 fragTexCoord;
|
|
||||||
in vec4 fragColor;
|
|
||||||
|
|
||||||
// Input uniform values
|
|
||||||
uniform sampler2D texture0;
|
|
||||||
uniform vec4 colDiffuse;
|
|
||||||
|
|
||||||
// Output fragment color
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec4 alpha = texture(texture0, fragTexCoord);
|
|
||||||
finalColor = colDiffuse*fragColor;
|
|
||||||
finalColor.a *= alpha.r;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// SDF based rect rendering
|
|
||||||
const ZString RECT_FS = `
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
in vec2 fragTexCoord;
|
|
||||||
in vec4 fragColor;
|
|
||||||
out vec4 finalColor;
|
|
||||||
|
|
||||||
uniform float radius;
|
|
||||||
uniform float border;
|
|
||||||
uniform vec2 size;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
macro rl::Color ugui::Color.conv(color)
|
|
||||||
{
|
|
||||||
return {.r = color.r, .g = color.g, .b = color.b, .a = color.a};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro rl::Rectangle Rect.conv(rect)
|
|
||||||
{
|
|
||||||
return {.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn int main(String[] args)
|
fn int main(String[] args)
|
||||||
{
|
{
|
||||||
@ -129,18 +60,57 @@ fn int main(String[] args)
|
|||||||
ui.init()!!;
|
ui.init()!!;
|
||||||
defer ui.free();
|
defer ui.free();
|
||||||
|
|
||||||
|
ren::Renderer ren;
|
||||||
|
ren.init("Ugui Test", 640, 480);
|
||||||
|
defer ren.free();
|
||||||
|
ui.input_window_size(640, 480)!!;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FONT LOADING
|
||||||
|
//
|
||||||
|
{
|
||||||
|
// import font in the ui context
|
||||||
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
|
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
|
||||||
|
|
||||||
|
// create the rendering pipeline
|
||||||
|
ren.font_atlas_id = ui.get_font_id("font1");
|
||||||
|
ren.load_spirv_shader_from_file("UGUI_PIPELINE_FONT", SPRITE_VS_PATH, FONT_FS_PATH, 1, 0);
|
||||||
|
ren.create_pipeline("UGUI_PIPELINE_FONT", SPRITE);
|
||||||
|
|
||||||
|
// send the atlas to the gpu
|
||||||
|
Atlas* font_atlas = ui.get_font_atlas("font1")!!;
|
||||||
|
ren.new_texture("font1", JUST_ALPHA, font_atlas.buffer, font_atlas.width, font_atlas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ICON LOADING
|
||||||
|
//
|
||||||
|
{
|
||||||
|
// create the atlas and upload some icons
|
||||||
ui.sprite_atlas_create("icons", AtlasType.ATLAS_R8G8B8A8, 512, 512)!!;
|
ui.sprite_atlas_create("icons", AtlasType.ATLAS_R8G8B8A8, 512, 512)!!;
|
||||||
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
|
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
|
||||||
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
|
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
|
||||||
|
|
||||||
short width = 800;
|
// create the rendering pipelines
|
||||||
short height = 450;
|
ren.sprite_atlas_id = ui.get_sprite_atlas_id("icons");
|
||||||
rl::setConfigFlags(rl::FLAG_WINDOW_RESIZABLE);
|
// normal sprite pipeline
|
||||||
rl::initWindow(width, height, "Ugui Test");
|
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE", SPRITE_VS_PATH, SPRITE_FS_PATH, 1, 0);
|
||||||
ui.input_window_size(width, height)!!;
|
ren.create_pipeline("UGUI_PIPELINE_SPRITE", SPRITE);
|
||||||
rl::setTargetFPS(60);
|
// msdf sprite pipeline
|
||||||
rl::enableEventWaiting();
|
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE_MSDF", SPRITE_VS_PATH, MSDF_FS_PATH, 1, 0);
|
||||||
|
ren.create_pipeline("UGUI_PIPELINE_SPRITE_MSDF", SPRITE);
|
||||||
|
|
||||||
|
// upload the atlas to the gpu
|
||||||
|
Atlas atlas = ui.sprite_atlas.atlas;
|
||||||
|
ren.new_texture("icons", FULL_COLOR, atlas.buffer, atlas.width, atlas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// RECT PIPELINE
|
||||||
|
//
|
||||||
|
ren.load_spirv_shader_from_file("UGUI_PIPELINE_RECT", RECT_VS_PATH, RECT_FS_PATH, 0, 0);
|
||||||
|
ren.create_pipeline("UGUI_PIPELINE_RECT", RECT);
|
||||||
|
|
||||||
|
|
||||||
isz frame;
|
isz frame;
|
||||||
bool toggle = true;
|
bool toggle = true;
|
||||||
@ -148,25 +118,49 @@ fn int main(String[] args)
|
|||||||
Times ui_times;
|
Times ui_times;
|
||||||
Times draw_times;
|
Times draw_times;
|
||||||
|
|
||||||
// font stuff
|
|
||||||
rl::Shader font_shader = rl::loadShaderFromMemory(null, FONT_FS);
|
|
||||||
rl::Image font_atlas;
|
|
||||||
rl::Texture2D font_texture;
|
|
||||||
ugui::Id font_id = ui.get_font_id("font1");
|
|
||||||
|
|
||||||
// sprite stuff
|
//
|
||||||
rl::Shader msdf_shader = rl::loadShaderFromMemory(null, MSDF_FS);
|
// MAIN LOOP
|
||||||
rl::Image sprite_atlas;
|
//
|
||||||
rl::Texture2D sprite_texture;
|
sdl::Event e;
|
||||||
ugui::Id sprite_id = ui.get_sprite_atlas_id("icons");
|
bool quit = false;
|
||||||
|
while (!quit) {
|
||||||
// Main loop
|
|
||||||
while (!rl::windowShouldClose()) {
|
|
||||||
clock.mark();
|
clock.mark();
|
||||||
|
|
||||||
|
// FIXME: modkeys input is broken
|
||||||
ugui::ModKeys mod;
|
ugui::ModKeys mod;
|
||||||
mod.rctrl = rl::isKeyDown(rl::KEY_RIGHT_CONTROL);
|
while (sdl::poll_event(&e)) {
|
||||||
mod.lctrl = rl::isKeyDown(rl::KEY_LEFT_CONTROL);
|
switch (e.type) {
|
||||||
|
case EVENT_QUIT:
|
||||||
|
quit = true;
|
||||||
|
case EVENT_KEY_DOWN:
|
||||||
|
mod.rctrl = !!(e.key.key == K_RCTRL);
|
||||||
|
mod.lctrl = !!(e.key.key == K_LCTRL);
|
||||||
|
case EVENT_WINDOW_RESIZED:
|
||||||
|
ui.input_window_size((short)e.window.data1, (short)e.window.data2)!!;
|
||||||
|
case EVENT_WINDOW_FOCUS_GAINED:
|
||||||
|
ui.input_changefocus(true);
|
||||||
|
case EVENT_WINDOW_FOCUS_LOST:
|
||||||
|
ui.input_changefocus(false);
|
||||||
|
case EVENT_MOUSE_MOTION:
|
||||||
|
ui.input_mouse_abs((short)e.motion.x, (short)e.motion.y);
|
||||||
|
case EVENT_MOUSE_WHEEL:
|
||||||
|
ui.input_mouse_wheel((short)e.wheel.integer_x, (short)e.wheel.integer_y);
|
||||||
|
case EVENT_MOUSE_BUTTON_DOWN: nextcase;
|
||||||
|
case EVENT_MOUSE_BUTTON_UP:
|
||||||
|
sdl::MouseButtonFlags mb = sdl::get_mouse_state(null, null);
|
||||||
|
ugui::MouseButtons b = {
|
||||||
|
.btn_left = !!(mb & BUTTON_LMASK),
|
||||||
|
.btn_right = !!(mb & BUTTON_RMASK),
|
||||||
|
.btn_middle = !!(mb & BUTTON_MMASK),
|
||||||
|
.btn_4 = !!(mb & BUTTON_X1MASK),
|
||||||
|
.btn_5 = !!(mb & BUTTON_X2MASK),
|
||||||
|
};
|
||||||
|
ui.input_mouse_button(b);
|
||||||
|
default:
|
||||||
|
io::eprintfn("unhandled event: %s", e.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
ui.input_mod_keys(mod);
|
ui.input_mod_keys(mod);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -177,33 +171,20 @@ fn int main(String[] args)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* Start Input Handling */
|
|
||||||
if (rl::isWindowResized()) {
|
|
||||||
width = (short)rl::getScreenWidth();
|
|
||||||
height = (short)rl::getScreenHeight();
|
|
||||||
ui.input_window_size(width, height)!!;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.input_changefocus(rl::isWindowFocused());
|
|
||||||
|
|
||||||
rl::Vector2 mpos = rl::getMousePosition();
|
|
||||||
ui.input_mouse_abs((short)mpos.x, (short)mpos.y);
|
|
||||||
rl::Vector2 mwheel = rl::getMouseWheelMoveV();
|
|
||||||
ui.input_mouse_wheel((short)mwheel.x, (short)mwheel.y);
|
|
||||||
|
|
||||||
ugui::MouseButtons buttons = {
|
|
||||||
.btn_left = rl::isMouseButtonDown(rl::MouseButton.LEFT),
|
|
||||||
.btn_right = rl::isMouseButtonDown(rl::MouseButton.RIGHT),
|
|
||||||
.btn_middle = rl::isMouseButtonDown(rl::MouseButton.MIDDLE),
|
|
||||||
};
|
|
||||||
ui.input_mouse_button(buttons);
|
|
||||||
/* End Input Handling */
|
/* End Input Handling */
|
||||||
|
|
||||||
/* Start UI Handling */
|
/* Start UI Handling */
|
||||||
ui.frame_begin()!!;
|
ui.frame_begin()!!;
|
||||||
|
|
||||||
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) break;
|
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
ui.div_begin("main", ugui::DIV_FILL)!!;
|
||||||
|
ui.button("button0", {0,0,30,30})!!;
|
||||||
|
ui.draw_sprite("sprite1", "tux")!!;
|
||||||
|
ui.text_unbounded("text1", "Ciao")!!;
|
||||||
|
ui.div_end()!!;
|
||||||
|
*/
|
||||||
|
|
||||||
ui.div_begin("main", {.w=-100})!!;
|
ui.div_begin("main", {.w=-100})!!;
|
||||||
{
|
{
|
||||||
@ -292,86 +273,24 @@ fn int main(String[] args)
|
|||||||
};
|
};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
|
|
||||||
ui.frame_end()!!;
|
ui.frame_end()!!;
|
||||||
/* End UI Handling */
|
/* End UI Handling */
|
||||||
ui_times.push(clock.mark());
|
ui_times.push(clock.mark());
|
||||||
//ui_times.print_stats();
|
//ui_times.print_stats();
|
||||||
|
|
||||||
/* Start UI Drawing */
|
|
||||||
rl::beginDrawing();
|
|
||||||
// ClearBackground(BLACK);
|
|
||||||
|
|
||||||
for (Cmd* cmd; (cmd = ui.cmd_queue.dequeue() ?? null) != null;) {
|
/* Start UI Drawing */
|
||||||
switch (cmd.type) {
|
ren.begin_render(true);
|
||||||
case CmdType.CMD_RECT:
|
|
||||||
Rect r = cmd.rect.rect;
|
ren.render_ugui(&ui.cmd_queue);
|
||||||
float rad = cmd.rect.radius;
|
|
||||||
// for some weird-ass reason the straight forward inverse formula does not work
|
ren.end_render();
|
||||||
float roundness = r.w > r.h ? (2.1f*rad)/(float)r.h : (2.1f*rad)/(float)r.w;
|
|
||||||
rl::drawRectangleRounded(cmd.rect.rect.conv(), roundness, 0, cmd.rect.color.conv());
|
|
||||||
case CmdType.CMD_UPDATE_ATLAS:
|
|
||||||
if (cmd.update_atlas.id == font_id) {
|
|
||||||
//rl::unload_image(font_atlas);
|
|
||||||
font_atlas.data = cmd.update_atlas.raw_buffer;
|
|
||||||
font_atlas.width = cmd.update_atlas.width;
|
|
||||||
font_atlas.height = cmd.update_atlas.height;
|
|
||||||
font_atlas.mipmaps = 1;
|
|
||||||
font_atlas.format = rl::PixelFormat.UNCOMPRESSED_GRAYSCALE;
|
|
||||||
if (rl::isTextureValid(font_texture)) {
|
|
||||||
rl::unloadTexture(font_texture);
|
|
||||||
}
|
|
||||||
font_texture = rl::loadTextureFromImage(font_atlas);
|
|
||||||
} else if (cmd.update_atlas.id == sprite_id) {
|
|
||||||
sprite_atlas.data = cmd.update_atlas.raw_buffer;
|
|
||||||
sprite_atlas.width = cmd.update_atlas.width;
|
|
||||||
sprite_atlas.height = cmd.update_atlas.height;
|
|
||||||
sprite_atlas.mipmaps = 1;
|
|
||||||
sprite_atlas.format = rl::PixelFormat.UNCOMPRESSED_R8G8B8A8;
|
|
||||||
if (rl::isTextureValid(sprite_texture)) {
|
|
||||||
rl::unloadTexture(sprite_texture);
|
|
||||||
}
|
|
||||||
sprite_texture = rl::loadTextureFromImage(sprite_atlas);
|
|
||||||
}
|
|
||||||
case CmdType.CMD_SPRITE:
|
|
||||||
if (cmd.sprite.texture_id == font_id) {
|
|
||||||
rl::Vector2 position = {
|
|
||||||
.x = cmd.sprite.rect.x,
|
|
||||||
.y = cmd.sprite.rect.y,
|
|
||||||
};
|
|
||||||
rl::beginShaderMode(font_shader);
|
|
||||||
rl::drawTextureRec(font_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
|
|
||||||
rl::endShaderMode();
|
|
||||||
} else if (cmd.sprite.texture_id == sprite_id) {
|
|
||||||
// FIXME: THIS CODE IS SHIT, REAL DOO DOO
|
|
||||||
{
|
|
||||||
if (cmd.sprite.type == SpriteType.SPRITE_MSDF) {
|
|
||||||
rl::beginShaderMode(msdf_shader);
|
|
||||||
defer rl::endShaderMode();
|
|
||||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
|
||||||
} else {
|
|
||||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
|
|
||||||
}
|
|
||||||
case CmdType.CMD_SCISSOR:
|
|
||||||
if (cmd.scissor.rect.w == 0 && cmd.scissor.rect.h == 0) {
|
|
||||||
rl::endScissorMode();
|
|
||||||
} else {
|
|
||||||
rl::beginScissorMode(cmd.scissor.rect.x, cmd.scissor.rect.y, cmd.scissor.rect.w, cmd.scissor.rect.h);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
io::printfn("Unknown cmd type: %s", cmd.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
draw_times.push(clock.mark());
|
draw_times.push(clock.mark());
|
||||||
//draw_times.print_stats();
|
//draw_times.print_stats();
|
||||||
rl::endDrawing();
|
|
||||||
/* End Drawing */
|
/* End Drawing */
|
||||||
}
|
}
|
||||||
|
|
||||||
rl::closeWindow();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
498
src/renderer.c3
498
src/renderer.c3
@ -1,54 +1,84 @@
|
|||||||
|
module idlist{Type};
|
||||||
|
|
||||||
|
// extends the List type to search elements that have a type.id property
|
||||||
|
// TODO: check that type has an id
|
||||||
|
|
||||||
|
import std::collections::list;
|
||||||
|
|
||||||
|
alias IdList = List{Type};
|
||||||
|
|
||||||
|
macro Type* IdList.get_from_name(&self, String name)
|
||||||
|
{
|
||||||
|
return self.get_from_id(name.hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
macro Type* IdList.get_from_id(&self, id)
|
||||||
|
{
|
||||||
|
foreach(&s: self) {
|
||||||
|
if (s.id == id) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
module sdlrenderer::ren;
|
module sdlrenderer::ren;
|
||||||
|
|
||||||
// 2D renderer for ugui, based on SDL3 using the new GPU API
|
// 2D renderer for ugui, based on SDL3 using the new GPU API
|
||||||
// TODO: use unreachable() instead of the combo eprintfn();exit(1); since it does the same thing
|
|
||||||
// but also adds a helpful stack trace
|
|
||||||
|
|
||||||
import std::io;
|
import std::io;
|
||||||
import std::core::mem;
|
import std::core::mem;
|
||||||
import sdl3::sdl;
|
import sdl3::sdl;
|
||||||
import libc;
|
import idlist;
|
||||||
import std::collections::list;
|
import ugui;
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
sdl::GPUShader* frag;
|
sdl::GPUShader* frag;
|
||||||
sdl::GPUShader* vert;
|
sdl::GPUShader* vert;
|
||||||
uint id;
|
ugui::Id id;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Pipeline {
|
struct Pipeline {
|
||||||
sdl::GPUGraphicsPipeline* pipeline;
|
sdl::GPUGraphicsPipeline* pipeline;
|
||||||
uint id;
|
ugui::Id id;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
sdl::GPUTexture* texture;
|
sdl::GPUTexture* texture;
|
||||||
sdl::GPUSampler* sampler;
|
sdl::GPUSampler* sampler;
|
||||||
ushort width, height;
|
ushort width, height;
|
||||||
uint id;
|
ugui::Id id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The GPU buffers that contain quad info, the size is determined by MAX_QUAD_BATCH
|
// The GPU buffers that contain quad info, the size is determined by MAX_QUAD_BATCH
|
||||||
const int MAX_QUAD_BATCH = 128;
|
const int MAX_QUAD_BATCH = 256;
|
||||||
struct QuadBuffer {
|
struct QuadBuffer {
|
||||||
sdl::GPUBuffer* vert_buf;
|
sdl::GPUBuffer* vert_buf;
|
||||||
sdl::GPUBuffer* idx_buf;
|
sdl::GPUBuffer* idx_buf;
|
||||||
|
sdl::GPUTransferBuffer* transfer_buffer;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
int count;
|
int count;
|
||||||
int off; // the offset to draw from
|
int off; // the offset to draw from
|
||||||
}
|
}
|
||||||
|
|
||||||
alias ShaderList = List{Shader};
|
alias ShaderList = IdList{Shader};
|
||||||
alias PipelineList = List{Pipeline};
|
alias PipelineList = IdList{Pipeline};
|
||||||
alias TextureList = List{Texture};
|
alias TextureList = IdList{Texture};
|
||||||
|
|
||||||
struct Renderer {
|
struct Renderer {
|
||||||
sdl::Window* win;
|
sdl::Window* win;
|
||||||
sdl::GPUDevice* gpu;
|
sdl::GPUDevice* gpu;
|
||||||
|
sdl::GPURenderPass* render_pass;
|
||||||
|
sdl::GPUTexture* swapchain_texture;
|
||||||
|
sdl::GPUCommandBuffer* render_cmdbuf;
|
||||||
|
|
||||||
QuadBuffer quad_buffer;
|
QuadBuffer quad_buffer;
|
||||||
ShaderList shaders;
|
ShaderList shaders;
|
||||||
PipelineList pipelines;
|
PipelineList pipelines;
|
||||||
TextureList textures;
|
TextureList textures;
|
||||||
|
|
||||||
|
Id sprite_atlas_id;
|
||||||
|
Id font_atlas_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// how each vertex is represented in the gpu
|
// how each vertex is represented in the gpu
|
||||||
@ -77,9 +107,14 @@ struct Quad @packed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int DEBUG = 1;
|
struct ViewsizeUniform @align(16) {
|
||||||
|
int w, h;
|
||||||
|
}
|
||||||
|
|
||||||
fn void Renderer.init(&self, ZString title)
|
const int DEBUG = 1;
|
||||||
|
const bool CYCLE = true;
|
||||||
|
|
||||||
|
fn void Renderer.init(&self, ZString title, uint width, uint height)
|
||||||
{
|
{
|
||||||
// set wayland hint automagically
|
// set wayland hint automagically
|
||||||
$if DEBUG == 0:
|
$if DEBUG == 0:
|
||||||
@ -96,13 +131,11 @@ $if DEBUG == 0:
|
|||||||
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "wayland");
|
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "wayland");
|
||||||
}
|
}
|
||||||
$else
|
$else
|
||||||
// in debug mode set the video driver to X11 because renderdoc/
|
// in debug mode set the video driver to X11 because renderdoc
|
||||||
// doesn't support debugging in wayland yet.
|
// doesn't support debugging in wayland yet.
|
||||||
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "x11");
|
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "x11");
|
||||||
$endif
|
|
||||||
|
|
||||||
|
|
||||||
sdl::set_hint(sdl::HINT_RENDER_GPU_DEBUG, "1");
|
sdl::set_hint(sdl::HINT_RENDER_GPU_DEBUG, "1");
|
||||||
|
$endif
|
||||||
|
|
||||||
// init subsystems
|
// init subsystems
|
||||||
if (!sdl::init(INIT_VIDEO)) {
|
if (!sdl::init(INIT_VIDEO)) {
|
||||||
@ -110,7 +143,7 @@ $endif
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the window
|
// create the window
|
||||||
self.win = sdl::create_window(title, 640, 480, WINDOW_RESIZABLE|WINDOW_VULKAN);
|
self.win = sdl::create_window(title, width, height, WINDOW_RESIZABLE|WINDOW_VULKAN);
|
||||||
if (self.win == null) {
|
if (self.win == null) {
|
||||||
unreachable("sdl error: %s", sdl::get_error());
|
unreachable("sdl error: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
@ -125,6 +158,9 @@ $endif
|
|||||||
unreachable("failed to claim window for use with gpu: %s", sdl::get_error());
|
unreachable("failed to claim window for use with gpu: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set swapchain parameters, like vsync
|
||||||
|
sdl::set_gpu_swapchain_parameters(self.gpu, self.win, GPU_SWAPCHAINCOMPOSITION_SDR, GPU_PRESENTMODE_IMMEDIATE);
|
||||||
|
|
||||||
//
|
//
|
||||||
// initialize the quad buffer
|
// initialize the quad buffer
|
||||||
// ==========================
|
// ==========================
|
||||||
@ -142,6 +178,13 @@ $endif
|
|||||||
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.quad_buffer.transfer_buffer = sdl::create_gpu_transfer_buffer(self.gpu,
|
||||||
|
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
|
||||||
|
);
|
||||||
|
if (self.quad_buffer.transfer_buffer == null) {
|
||||||
|
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
||||||
|
}
|
||||||
|
|
||||||
self.quad_buffer.initialized = true;
|
self.quad_buffer.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,12 +201,27 @@ fn void Renderer.free(&self)
|
|||||||
}
|
}
|
||||||
self.pipelines.free();
|
self.pipelines.free();
|
||||||
|
|
||||||
|
// FIXME: release the quad buffer
|
||||||
|
|
||||||
sdl::release_window_from_gpu_device(self.gpu, self.win);
|
sdl::release_window_from_gpu_device(self.gpu, self.win);
|
||||||
sdl::destroy_gpu_device(self.gpu);
|
sdl::destroy_gpu_device(self.gpu);
|
||||||
sdl::destroy_window(self.win);
|
sdl::destroy_window(self.win);
|
||||||
sdl::quit();
|
sdl::quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn void Renderer.resize_window(&self, uint width, uint height)
|
||||||
|
{
|
||||||
|
sdl::set_window_size(self.win, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.get_window_size(&self, int* width, int* height)
|
||||||
|
{
|
||||||
|
sdl::get_window_size_in_pixels(self.win, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Both the vertex shader and fragment shader have an implicit uniform buffer at binding 0 that
|
||||||
|
// contains the viewport size. It is populated automatically at every begin_render() call
|
||||||
fn void Renderer.load_spirv_shader_from_mem(&self, String name, char[] vert_code, char[] frag_code, uint textures, uint uniforms)
|
fn void Renderer.load_spirv_shader_from_mem(&self, String name, char[] vert_code, char[] frag_code, uint textures, uint uniforms)
|
||||||
{
|
{
|
||||||
Shader s;
|
Shader s;
|
||||||
@ -183,7 +241,7 @@ fn void Renderer.load_spirv_shader_from_mem(&self, String name, char[] vert_code
|
|||||||
.format = GPU_SHADERFORMAT_SPIRV,
|
.format = GPU_SHADERFORMAT_SPIRV,
|
||||||
.stage = GPU_SHADERSTAGE_VERTEX,
|
.stage = GPU_SHADERSTAGE_VERTEX,
|
||||||
.num_samplers = 0,
|
.num_samplers = 0,
|
||||||
.num_uniform_buffers = uniforms,
|
.num_uniform_buffers = 1+uniforms,
|
||||||
.num_storage_buffers = 0,
|
.num_storage_buffers = 0,
|
||||||
.num_storage_textures = 0
|
.num_storage_textures = 0
|
||||||
};
|
};
|
||||||
@ -204,7 +262,7 @@ fn void Renderer.load_spirv_shader_from_mem(&self, String name, char[] vert_code
|
|||||||
.format = GPU_SHADERFORMAT_SPIRV,
|
.format = GPU_SHADERFORMAT_SPIRV,
|
||||||
.stage = GPU_SHADERSTAGE_FRAGMENT,
|
.stage = GPU_SHADERSTAGE_FRAGMENT,
|
||||||
.num_samplers = textures,
|
.num_samplers = textures,
|
||||||
.num_uniform_buffers = 0,
|
.num_uniform_buffers = 1,
|
||||||
.num_storage_buffers = 0,
|
.num_storage_buffers = 0,
|
||||||
.num_storage_textures = 0
|
.num_storage_textures = 0
|
||||||
};
|
};
|
||||||
@ -241,40 +299,6 @@ fn void Renderer.load_spirv_shader_from_file(&self, String name, String vert_pat
|
|||||||
self.load_spirv_shader_from_mem(name, vert_code, frag_code, textures, uniforms);
|
self.load_spirv_shader_from_mem(name, vert_code, frag_code, textures, uniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Shader* ShaderList.get_from_name(&self, String name)
|
|
||||||
{
|
|
||||||
uint id = name.hash();
|
|
||||||
foreach(&s: self) {
|
|
||||||
if (s.id == id) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn Pipeline* PipelineList.get_from_name(&self, String name)
|
|
||||||
{
|
|
||||||
uint id = name.hash();
|
|
||||||
foreach(&p: self) {
|
|
||||||
if (p.id == id) {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn Texture* TextureList.get_from_name(&self, String name)
|
|
||||||
{
|
|
||||||
uint id = name.hash();
|
|
||||||
foreach(&t: self) {
|
|
||||||
if (t.id == id) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// this describes what we want to draw, since for drawing different things we have to change
|
// this describes what we want to draw, since for drawing different things we have to change
|
||||||
// the GPUPrimitiveType and GPURasterizerState for the pipeline.
|
// the GPUPrimitiveType and GPURasterizerState for the pipeline.
|
||||||
enum PipelineType : (GPUPrimitiveType primitive_type, GPURasterizerState raster_state) {
|
enum PipelineType : (GPUPrimitiveType primitive_type, GPURasterizerState raster_state) {
|
||||||
@ -380,12 +404,29 @@ enum TextureType : (GPUTextureFormat format) {
|
|||||||
JUST_ALPHA = GPU_TEXTUREFORMAT_R8_UNORM
|
JUST_ALPHA = GPU_TEXTUREFORMAT_R8_UNORM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro void Renderer.new_texture(&self, name_or_id, TextureType type, char[] pixels, uint width, uint height)
|
||||||
|
{
|
||||||
|
$switch $typeof(name_or_id):
|
||||||
|
$case usz: return self.new_texture_by_id(id, type, pixels, width, height);
|
||||||
|
$case String: return self.new_texture_by_id(name_or_id.hash(), type, pixels, width, height);
|
||||||
|
$default: unreachable("texture must have a name (String) or an id (usz)");
|
||||||
|
$endswitch
|
||||||
|
}
|
||||||
|
|
||||||
|
macro void Renderer.update_texture(&self, name_or_id, char[] pixels, uint width, uint height, uint x = 0, uint y = 0)
|
||||||
|
{
|
||||||
|
$switch $typeof(name_or_id):
|
||||||
|
$case usz: return self.update_texture_by_id(name_or_id, pixels, width, height, x, y);
|
||||||
|
$case String: return self.update_texture_by_id(name_or_id.hash(), pixels, width, height, x, y);
|
||||||
|
$default: unreachable("texture must have a name (String) or an id (usz)");
|
||||||
|
$endswitch
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// create a new gpu texture from a pixel buffer, the format has to be specified
|
// create a new gpu texture from a pixel buffer, the format has to be specified
|
||||||
// the new texture s given an id and pushed into a texture list
|
// the new texture s given an id and pushed into a texture list
|
||||||
fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels, uint width, uint height)
|
fn void Renderer.new_texture_by_id(&self, Id id, TextureType type, char[] pixels, uint width, uint height)
|
||||||
{
|
{
|
||||||
uint id = name.hash();
|
|
||||||
|
|
||||||
// the texture description
|
// the texture description
|
||||||
GPUTextureCreateInfo tci = {
|
GPUTextureCreateInfo tci = {
|
||||||
.type = GPU_TEXTURETYPE_2D,
|
.type = GPU_TEXTURETYPE_2D,
|
||||||
@ -401,7 +442,7 @@ fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels
|
|||||||
|
|
||||||
GPUTexture* texture = sdl::create_gpu_texture(self.gpu, &tci);
|
GPUTexture* texture = sdl::create_gpu_texture(self.gpu, &tci);
|
||||||
if (texture == null) {
|
if (texture == null) {
|
||||||
unreachable("failed to create texture (name: %s, type: %s): %s", name, type.nameof, sdl::get_error());
|
unreachable("failed to create texture (id: %s, type: %s): %s", id, type.nameof, sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
// the sampler description, how the texture should be sampled
|
// the sampler description, how the texture should be sampled
|
||||||
@ -417,7 +458,7 @@ fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels
|
|||||||
|
|
||||||
GPUSampler* sampler = sdl::create_gpu_sampler(self.gpu, &sci);
|
GPUSampler* sampler = sdl::create_gpu_sampler(self.gpu, &sci);
|
||||||
if (sampler == null) {
|
if (sampler == null) {
|
||||||
unreachable("failed to create sampler (texture name: %s, type: %s): %s", name, type.nameof, sdl::get_error());
|
unreachable("failed to create sampler (texture id: %s, type: %s): %s", id, type.nameof, sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture t = {
|
Texture t = {
|
||||||
@ -428,20 +469,20 @@ fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels
|
|||||||
self.textures.push(t);
|
self.textures.push(t);
|
||||||
|
|
||||||
// upload the texture data
|
// upload the texture data
|
||||||
self.update_texture(name, pixels, width, height);
|
self.update_texture_by_id(id, pixels, width, height, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Renderer.update_texture(&self, String name, char[] pixels, uint width, uint height, uint x = 0, uint y = 0)
|
fn void Renderer.update_texture_by_id(&self, Id id, char[] pixels, uint width, uint height, uint x, uint y)
|
||||||
{
|
{
|
||||||
Texture* t = self.textures.get_from_name(name);
|
Texture* t = self.textures.get_from_id(id);
|
||||||
if (t == null || t.texture == null) {
|
if (t == null || t.texture == null) {
|
||||||
unreachable("failed updating texture: no texture named %s", name);
|
unreachable("failed updating texture: no texture with id %s", id);
|
||||||
}
|
}
|
||||||
GPUTexture* texture = t.texture;
|
GPUTexture* texture = t.texture;
|
||||||
|
|
||||||
// FIXME: do a better job at validating the copy
|
// FIXME: do a better job at validating the copy
|
||||||
if (x > t.width || y > t.height) {
|
if (x > t.width || y > t.height) {
|
||||||
unreachable("failed updating texture: attempting to copy outside of the texture region", name);
|
unreachable("failed updating texture: attempting to copy outside of the texture region");
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload image data
|
// upload image data
|
||||||
@ -461,7 +502,7 @@ fn void Renderer.update_texture(&self, String name, char[] pixels, uint width, u
|
|||||||
unreachable("failed to upload texture data at creating the transfer buffer: %s", sdl::get_error());
|
unreachable("failed to upload texture data at creating the transfer buffer: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
char* gpu_mem = (char*)sdl::map_gpu_transfer_buffer(self.gpu, buf, false);
|
char* gpu_mem = (char*)sdl::map_gpu_transfer_buffer(self.gpu, buf, CYCLE);
|
||||||
if (gpu_mem == null) {
|
if (gpu_mem == null) {
|
||||||
unreachable("failed to upload texture data at mapping the transfer buffer: %s", sdl::get_error());
|
unreachable("failed to upload texture data at mapping the transfer buffer: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
@ -484,28 +525,9 @@ fn void Renderer.update_texture(&self, String name, char[] pixels, uint width, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u, short v) {
|
fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u, short v, uint color = 0xffffffff)
|
||||||
if (self.quad_buffer.count >= MAX_QUAD_BATCH) {
|
{
|
||||||
return false;
|
Quad quad;
|
||||||
}
|
|
||||||
|
|
||||||
// upload the quad data to the gpu
|
|
||||||
if (self.quad_buffer.initialized == false) {
|
|
||||||
unreachable("quad buffer not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUTransferBuffer* buf = sdl::create_gpu_transfer_buffer(self.gpu,
|
|
||||||
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
|
|
||||||
);
|
|
||||||
if (buf == null) {
|
|
||||||
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, buf, false);
|
|
||||||
if (quad == null) {
|
|
||||||
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* v1 v4
|
/* v1 v4
|
||||||
* +-------------+
|
* +-------------+
|
||||||
* | _/|
|
* | _/|
|
||||||
@ -518,10 +540,10 @@ fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u,
|
|||||||
* +-------------+
|
* +-------------+
|
||||||
* v2 v3
|
* v2 v3
|
||||||
*/
|
*/
|
||||||
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = u, .v = v}};
|
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = u, .v = v}, .col.u = color};
|
||||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = u, .v = v+h}};
|
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = u, .v = v+h}, .col.u = color};
|
||||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = u+w, .v = v+h}};
|
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = u+w, .v = v+h}, .col.u = color};
|
||||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = u+w, .v = v}};
|
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = u+w, .v = v}, .col.u = color};
|
||||||
// triangle 1 indices
|
// triangle 1 indices
|
||||||
quad.indices.i1 = 0; // v1
|
quad.indices.i1 = 0; // v1
|
||||||
quad.indices.i2 = 1; // v2
|
quad.indices.i2 = 1; // v2
|
||||||
@ -531,64 +553,13 @@ fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u,
|
|||||||
quad.indices.i5 = 2; // v3
|
quad.indices.i5 = 2; // v3
|
||||||
quad.indices.i6 = 3; // v4
|
quad.indices.i6 = 3; // v4
|
||||||
|
|
||||||
sdl::unmap_gpu_transfer_buffer(self.gpu, buf);
|
return self.upload_quad(&quad);
|
||||||
|
|
||||||
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
|
||||||
if (cmd == null) {
|
|
||||||
unreachable("failed to upload quad at acquiring command buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
|
||||||
|
|
||||||
// upload vertices
|
|
||||||
QuadBuffer* qb = &self.quad_buffer;
|
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
|
|
||||||
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
// upload indices
|
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.indices.offsetof},
|
|
||||||
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
sdl::end_gpu_copy_pass(cpy);
|
|
||||||
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
|
||||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
|
||||||
//sdl::wait_for_gpu_idle(self.gpu);
|
|
||||||
|
|
||||||
qb.count++;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push a quad into the quad buffer, return true on success and false on failure
|
// Push a quad into the quad buffer, return true on success and false on failure
|
||||||
fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color)
|
fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color, ushort radius = 0)
|
||||||
{
|
{
|
||||||
if (self.quad_buffer.count >= MAX_QUAD_BATCH) {
|
Quad quad;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// upload the quad data to the gpu
|
|
||||||
if (self.quad_buffer.initialized == false) {
|
|
||||||
unreachable("quad buffer not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
GPUTransferBuffer* buf = sdl::create_gpu_transfer_buffer(self.gpu,
|
|
||||||
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
|
|
||||||
);
|
|
||||||
if (buf == null) {
|
|
||||||
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, buf, false);
|
|
||||||
if (quad == null) {
|
|
||||||
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* v1 v4
|
/* v1 v4
|
||||||
* +-------------+
|
* +-------------+
|
||||||
* | _/|
|
* | _/|
|
||||||
@ -601,10 +572,13 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
|||||||
* +-------------+
|
* +-------------+
|
||||||
* v2 v3
|
* v2 v3
|
||||||
*/
|
*/
|
||||||
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .col.u = color};
|
// the wanted radius is pushed into the uv coordinates, the vertex shader then extracts the absolute value
|
||||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .col.u = color};
|
// and passes it to the fragment shader, then it uses the sign to give the fragment shader local coordinates
|
||||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .col.u = color};
|
// into the quad.
|
||||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .col.u = color};
|
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = -radius, .v = +radius}, .col.u = color};
|
||||||
|
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = -radius, .v = -radius}, .col.u = color};
|
||||||
|
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = +radius, .v = -radius}, .col.u = color};
|
||||||
|
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = +radius, .v = +radius}, .col.u = color};
|
||||||
// triangle 1 indices
|
// triangle 1 indices
|
||||||
quad.indices.i1 = 0; // v1
|
quad.indices.i1 = 0; // v1
|
||||||
quad.indices.i2 = 1; // v2
|
quad.indices.i2 = 1; // v2
|
||||||
@ -614,7 +588,29 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
|||||||
quad.indices.i5 = 2; // v3
|
quad.indices.i5 = 2; // v3
|
||||||
quad.indices.i6 = 3; // v4
|
quad.indices.i6 = 3; // v4
|
||||||
|
|
||||||
sdl::unmap_gpu_transfer_buffer(self.gpu, buf);
|
return self.upload_quad(&quad);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool Renderer.upload_quad(&self, Quad* source_quad)
|
||||||
|
{
|
||||||
|
if (self.quad_buffer.count >= MAX_QUAD_BATCH || source_quad == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QuadBuffer* qb = &self.quad_buffer;
|
||||||
|
|
||||||
|
// upload the quad data to the gpu
|
||||||
|
if (qb.initialized == false) {
|
||||||
|
unreachable("quad buffer not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, qb.transfer_buffer, CYCLE);
|
||||||
|
if (quad == null) {
|
||||||
|
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
*quad = *source_quad;
|
||||||
|
|
||||||
|
sdl::unmap_gpu_transfer_buffer(self.gpu, qb.transfer_buffer);
|
||||||
|
|
||||||
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
||||||
if (cmd == null) {
|
if (cmd == null) {
|
||||||
@ -623,15 +619,14 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
|||||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
||||||
|
|
||||||
// upload vertices
|
// upload vertices
|
||||||
QuadBuffer* qb = &self.quad_buffer;
|
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
sdl::upload_to_gpu_buffer(cpy,
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
|
&&(GPUTransferBufferLocation){.transfer_buffer = qb.transfer_buffer, .offset = Quad.vertices.offsetof},
|
||||||
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// upload indices
|
// upload indices
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
sdl::upload_to_gpu_buffer(cpy,
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.indices.offsetof},
|
&&(GPUTransferBufferLocation){.transfer_buffer = qb.transfer_buffer, .offset = Quad.indices.offsetof},
|
||||||
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -640,8 +635,6 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
|||||||
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
||||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
|
||||||
//sdl::wait_for_gpu_idle(self.gpu);
|
|
||||||
|
|
||||||
qb.count++;
|
qb.count++;
|
||||||
|
|
||||||
@ -650,18 +643,18 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
|||||||
|
|
||||||
// draw all quads in the quad buffer, since uniforms are per-drawcall it makes no sense
|
// draw all quads in the quad buffer, since uniforms are per-drawcall it makes no sense
|
||||||
// to draw them one a the time
|
// to draw them one a the time
|
||||||
fn void Renderer.draw_quads(&self, GPURenderPass* pass)
|
fn void Renderer.draw_quads(&self)
|
||||||
{
|
{
|
||||||
QuadBuffer* qb = &self.quad_buffer;
|
QuadBuffer* qb = &self.quad_buffer;
|
||||||
|
|
||||||
if (qb.off == qb.count) return;
|
if (qb.off == qb.count) return;
|
||||||
|
|
||||||
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = qb.off*Quad.vertices.sizeof}}, 1);
|
sdl::bind_gpu_vertex_buffers(self.render_pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = qb.off*Quad.vertices.sizeof}}, 1);
|
||||||
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = qb.off*Quad.indices.sizeof}, GPU_INDEXELEMENTSIZE_16BIT);
|
sdl::bind_gpu_index_buffer(self.render_pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = qb.off*Quad.indices.sizeof}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||||
|
|
||||||
// we need instancing to not do this
|
// we need instancing to not do this
|
||||||
for (int i = 0; i < qb.count - qb.off; i++) {
|
for (int i = 0; i < qb.count - qb.off; i++) {
|
||||||
sdl::draw_gpu_indexed_primitives(pass, 6, 1, i*6, i*4, 0);
|
sdl::draw_gpu_indexed_primitives(self.render_pass, 6, 1, i*6, i*4, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
qb.off = qb.count;
|
qb.off = qb.count;
|
||||||
@ -674,9 +667,107 @@ fn void Renderer.reset_quads(&self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: fn Renderer.draw_sprite, same as draw_quad but also bind the texture
|
fn void Renderer.begin_render(&self, bool clear_screen)
|
||||||
// TODO: fn Renderer.begin_render
|
{
|
||||||
// TODO: fn Renderer.end_render
|
self.render_cmdbuf = sdl::acquire_gpu_command_buffer(self.gpu);
|
||||||
|
sdl::wait_and_acquire_gpu_swapchain_texture(self.render_cmdbuf, self.win, &self.swapchain_texture, null, null);
|
||||||
|
|
||||||
|
// push the window size as a uniform
|
||||||
|
// TODO: maybe make this configurable and/or add more things
|
||||||
|
ViewsizeUniform v;
|
||||||
|
self.get_window_size(&v.w, &v.h);
|
||||||
|
sdl::push_gpu_vertex_uniform_data(self.render_cmdbuf, 0, &v, ViewsizeUniform.sizeof);
|
||||||
|
sdl::push_gpu_fragment_uniform_data(self.render_cmdbuf, 0, &v, ViewsizeUniform.sizeof);
|
||||||
|
|
||||||
|
if (clear_screen) {
|
||||||
|
GPURenderPass* pass = sdl::begin_gpu_render_pass(self.render_cmdbuf,
|
||||||
|
&&(GPUColorTargetInfo){
|
||||||
|
.texture = self.swapchain_texture,
|
||||||
|
.mip_level = 0,
|
||||||
|
.layer_or_depth_plane = 0,
|
||||||
|
.clear_color = {.r = 1.0, .g = 0.0, .b = 1.0, .a = 1.0},
|
||||||
|
.load_op = GPU_LOADOP_CLEAR, // clear the screen at the start of the render pass
|
||||||
|
.store_op = GPU_STOREOP_STORE,
|
||||||
|
.resolve_texture = null,
|
||||||
|
.resolve_mip_level = 0,
|
||||||
|
.resolve_layer = 0,
|
||||||
|
.cycle = false,
|
||||||
|
.cycle_resolve_texture = false
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
null // huh
|
||||||
|
);
|
||||||
|
if (pass == null) {
|
||||||
|
unreachable("render pass creation went wrong: %s", sdl::get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl::end_gpu_render_pass(pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.end_render(&self)
|
||||||
|
{
|
||||||
|
sdl::submit_gpu_command_buffer(self.render_cmdbuf);
|
||||||
|
self.reset_quads();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.start_render_pass(&self, String pipeline_name)
|
||||||
|
{
|
||||||
|
self.render_pass = sdl::begin_gpu_render_pass(self.render_cmdbuf,
|
||||||
|
&&(GPUColorTargetInfo){
|
||||||
|
.texture = self.swapchain_texture,
|
||||||
|
.mip_level = 0,
|
||||||
|
.layer_or_depth_plane = 0,
|
||||||
|
.clear_color = {.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0},
|
||||||
|
.load_op = GPU_LOADOP_DONT_CARE,
|
||||||
|
.store_op = GPU_STOREOP_STORE,
|
||||||
|
.resolve_texture = null,
|
||||||
|
.resolve_mip_level = 0,
|
||||||
|
.resolve_layer = 0,
|
||||||
|
.cycle = false,
|
||||||
|
.cycle_resolve_texture = false
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
null // huh
|
||||||
|
);
|
||||||
|
|
||||||
|
if (self.render_pass == null) {
|
||||||
|
unreachable("render pass creation went wrong: %s", sdl::get_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl::GPUGraphicsPipeline* p;
|
||||||
|
p = self.pipelines.get_from_name(pipeline_name).pipeline;
|
||||||
|
if (p == null) {
|
||||||
|
unreachable("no pipeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl::bind_gpu_graphics_pipeline(self.render_pass, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.end_render_pass(&self)
|
||||||
|
{
|
||||||
|
sdl::end_gpu_render_pass(self.render_pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.bind_texture(&self, String texture_name)
|
||||||
|
{
|
||||||
|
ren::Texture* tx = self.textures.get_from_name(texture_name);
|
||||||
|
sdl::bind_gpu_fragment_samplers(self.render_pass, 0,
|
||||||
|
(GPUTextureSamplerBinding[]){{.texture = tx.texture, .sampler = tx.sampler}}, 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.set_scissor(&self, uint x, uint y, uint w, uint h)
|
||||||
|
{
|
||||||
|
sdl::set_gpu_scissor(self.render_pass, &&(sdl::Rect){x,y,w,h});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.reset_scissor(&self)
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
sdl::get_window_size(self.win, &w, &h);
|
||||||
|
self.set_scissor(0, 0, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
/// === NOTES ===
|
/// === NOTES ===
|
||||||
/* 1. The uniform data is per-render pass. So you can do:
|
/* 1. The uniform data is per-render pass. So you can do:
|
||||||
@ -697,3 +788,86 @@ fn void Renderer.reset_quads(&self)
|
|||||||
* render pass. So I cannot override an element in the buffer
|
* render pass. So I cannot override an element in the buffer
|
||||||
* before submitting the command buffer.
|
* before submitting the command buffer.
|
||||||
*/
|
*/
|
||||||
|
/// === END NOTES ===
|
||||||
|
|
||||||
|
|
||||||
|
fn void Renderer.render_ugui(&self, CmdQueue* queue)
|
||||||
|
{
|
||||||
|
Cmd* last_command;
|
||||||
|
for (Cmd* cmd; (cmd = queue.dequeue() ?? null) != null;) {
|
||||||
|
if (last_command == null || last_command.type != cmd.type) {
|
||||||
|
self.end_command(last_command);
|
||||||
|
self.begin_command(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd.type) {
|
||||||
|
case CMD_RECT:
|
||||||
|
CmdRect r = cmd.rect;
|
||||||
|
self.push_quad(r.rect.x, r.rect.y, r.rect.w, r.rect.h, r.color.to_uint(), r.radius);
|
||||||
|
case CMD_SPRITE:
|
||||||
|
// TODO: support hue in sprite
|
||||||
|
CmdSprite s = cmd.sprite;
|
||||||
|
self.push_sprite(s.rect.x, s.rect.y, s.texture_rect.w, s.texture_rect.h, s.texture_rect.x, s.texture_rect.y);
|
||||||
|
case CMD_UPDATE_ATLAS:
|
||||||
|
// TODO: verify the correct type
|
||||||
|
CmdUpdateAtlas u = cmd.update_atlas;
|
||||||
|
char[] pixels = u.raw_buffer[..u.width*u.height*u.bpp];
|
||||||
|
self.update_texture(u.id, pixels, u.width, u.height);
|
||||||
|
case CMD_SCISSOR: break; // FIXME: ugui sends a scissor event before any rect event, this cannot be done and needs different handling
|
||||||
|
ugui::Rect s = cmd.scissor.rect;
|
||||||
|
self.set_scissor(s.x, s.y, s.w, s.h);
|
||||||
|
default: unreachable("unknown command: %s", cmd.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_command = cmd;
|
||||||
|
}
|
||||||
|
self.end_command(last_command);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.begin_command(&self, Cmd* cmd)
|
||||||
|
{
|
||||||
|
if (cmd == null) return;
|
||||||
|
|
||||||
|
switch (cmd.type) {
|
||||||
|
case CMD_RECT:
|
||||||
|
self.start_render_pass("UGUI_PIPELINE_RECT");
|
||||||
|
case CMD_SPRITE:
|
||||||
|
// TODO: support multiple sprite and font atlases
|
||||||
|
CmdSprite s = cmd.sprite;
|
||||||
|
String pipeline;
|
||||||
|
String texture;
|
||||||
|
if (s.texture_id == self.sprite_atlas_id) {
|
||||||
|
switch (s.type) {
|
||||||
|
case SPRITE_NORMAL: pipeline = "UGUI_PIPELINE_SPRITE";
|
||||||
|
case SPRITE_SDF: pipeline = "UGUI_PIPELINE_SPRITE_SDF";
|
||||||
|
case SPRITE_MSDF: pipeline = "UGUI_PIPELINE_SPRITE_MSDF";
|
||||||
|
case SPRITE_ANIMATED: unreachable("animated sprtes are unsupported for now");
|
||||||
|
default: unreachable("unknown sprite type %s", s.type);
|
||||||
|
}
|
||||||
|
texture = "icons";
|
||||||
|
} else if (s.texture_id == self.font_atlas_id) {
|
||||||
|
pipeline = "UGUI_PIPELINE_FONT";
|
||||||
|
texture = "font1";
|
||||||
|
}
|
||||||
|
self.start_render_pass(pipeline);
|
||||||
|
self.bind_texture(texture);
|
||||||
|
case CMD_UPDATE_ATLAS: break;
|
||||||
|
case CMD_SCISSOR: break;
|
||||||
|
default: unreachable("unknown command: %s", cmd.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Renderer.end_command(&self, Cmd* cmd)
|
||||||
|
{
|
||||||
|
if (cmd == null) return;
|
||||||
|
|
||||||
|
switch (cmd.type) {
|
||||||
|
case CMD_RECT: nextcase;
|
||||||
|
case CMD_SPRITE:
|
||||||
|
self.draw_quads();
|
||||||
|
self.end_render_pass();
|
||||||
|
case CMD_UPDATE_ATLAS: break;
|
||||||
|
case CMD_SCISSOR: break;
|
||||||
|
default: unreachable("unknown command: %s", cmd.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
132
test_renderer.c3
132
test_renderer.c3
@ -5,18 +5,23 @@ import sdl3::sdl;
|
|||||||
import std::compression::qoi;
|
import std::compression::qoi;
|
||||||
import std::core::mem::allocator;
|
import std::core::mem::allocator;
|
||||||
|
|
||||||
struct Viewsize @align(16) {
|
char[*] shader_rect_vert = $embed("resources/shaders/compiled/rect.vert.spv");
|
||||||
int w, h;
|
char[*] shader_rect_frag = $embed("resources/shaders/compiled/rect.frag.spv");
|
||||||
int ox, oy;
|
|
||||||
}
|
char[*] shader_sprite_vert = $embed("resources/shaders/compiled/sprite.vert.spv");
|
||||||
|
char[*] shader_sprite_frag = $embed("resources/shaders/compiled/sprite.frag.spv");
|
||||||
|
|
||||||
|
const uint WINDOW_WIDTH = 640;
|
||||||
|
const uint WINDOW_HEIGHT = 480;
|
||||||
|
|
||||||
|
|
||||||
fn int main()
|
fn int main()
|
||||||
{
|
{
|
||||||
ren::Renderer ren;
|
ren::Renderer ren;
|
||||||
ren.init("test window");
|
ren.init("test window", WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
|
||||||
// TODO: these could be the same function
|
// TODO: these could be the same function
|
||||||
ren.load_spirv_shader_from_file("rect shader", "resources/shaders/compiled/rect.vert.spv", "resources/shaders/compiled/rect.frag.spv", 0, 1);
|
ren.load_spirv_shader_from_mem("rect shader", &shader_rect_vert, &shader_rect_frag, 0, 0);
|
||||||
ren.create_pipeline("rect shader", RECT);
|
ren.create_pipeline("rect shader", RECT);
|
||||||
|
|
||||||
// load the tux qoi image
|
// load the tux qoi image
|
||||||
@ -25,118 +30,63 @@ fn int main()
|
|||||||
// and put it in a texture
|
// and put it in a texture
|
||||||
ren.new_texture("tux", FULL_COLOR, img_pixels, img_desc.width, img_desc.height);
|
ren.new_texture("tux", FULL_COLOR, img_pixels, img_desc.width, img_desc.height);
|
||||||
// create a new pipeline to use the texture
|
// create a new pipeline to use the texture
|
||||||
ren.load_spirv_shader_from_file("sprite shader", "resources/shaders/compiled/sprite.vert.spv", "resources/shaders/compiled/sprite.frag.spv", 1, 1);
|
ren.load_spirv_shader_from_mem("sprite shader", &shader_sprite_vert, &shader_sprite_frag, 1, 0);
|
||||||
ren.create_pipeline("sprite shader", SPRITE);
|
ren.create_pipeline("sprite shader", SPRITE);
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
GPUCommandBuffer* cmdbuf = sdl::acquire_gpu_command_buffer(ren.gpu);
|
|
||||||
GPUTexture* swapchain_texture;
|
|
||||||
sdl::wait_and_acquire_gpu_swapchain_texture(cmdbuf, ren.win, &swapchain_texture, null, null);
|
|
||||||
|
|
||||||
GPURenderPass* pass;
|
sdl::Event e;
|
||||||
GPUGraphicsPipeline* p;
|
bool quit = false;
|
||||||
Viewsize v = {.w = 640, .h = 480};
|
for (usz i = 0; !quit; i++) {
|
||||||
|
|
||||||
// Colored Rectangles Render Pass
|
if (sdl::poll_event(&e)) {
|
||||||
// FIXME: if doing damage tracking DO NOT clear the screen
|
if (e.type == EVENT_QUIT) {
|
||||||
pass = sdl::begin_gpu_render_pass(cmdbuf,
|
quit = true;
|
||||||
&&(GPUColorTargetInfo){
|
}
|
||||||
.texture = swapchain_texture,
|
|
||||||
.mip_level = 0,
|
|
||||||
.layer_or_depth_plane = 0,
|
|
||||||
.clear_color = {.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0},
|
|
||||||
.load_op = GPU_LOADOP_CLEAR, // clear the screen at the start of the render pass
|
|
||||||
.store_op = GPU_STOREOP_STORE,
|
|
||||||
.resolve_texture = null,
|
|
||||||
.resolve_mip_level = 0,
|
|
||||||
.resolve_layer = 0,
|
|
||||||
.cycle = false,
|
|
||||||
.cycle_resolve_texture = false
|
|
||||||
},
|
|
||||||
1,
|
|
||||||
null // huh
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pass == null) {
|
|
||||||
unreachable("render pass creation went wrong: %s", sdl::get_error());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i == 300) {
|
||||||
|
io::printn("ciao!");
|
||||||
|
img_pixels = qoi::read(allocator::temp(), "resources/tux_inv.qoi", &img_desc)!!;
|
||||||
|
ren.update_texture("tux", img_pixels, img_desc.width, img_desc.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ren.begin_render(true);
|
||||||
|
|
||||||
|
// Colored Rectangles Render Pass
|
||||||
|
ren.start_render_pass("rect shader");
|
||||||
|
|
||||||
// rect 1
|
// rect 1
|
||||||
ren.push_quad(100,100,100,100,0xff00ff00);
|
ren.push_quad(100,100,100,100,0xff00ff00, 20);
|
||||||
// rect 2
|
// rect 2
|
||||||
ren.push_quad(0,0,20,20,0xff0000ff);
|
ren.push_quad(0,0,20,20,0xff0000ff);
|
||||||
// rect 3
|
// rect 3
|
||||||
ren.push_quad(200,300,50,50,0xffff0000);
|
ren.push_quad(200,300,50,50,0xffff0000);
|
||||||
|
|
||||||
p = ren.pipelines.get_from_name("rect shader").pipeline;
|
//ren.set_scissor(0,50,200,300);
|
||||||
if (p == null) {
|
|
||||||
unreachable("no pipeline");
|
|
||||||
}
|
|
||||||
|
|
||||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
ren.draw_quads();
|
||||||
|
|
||||||
v.ox = 10*i;
|
ren.end_render_pass();
|
||||||
v.oy = 10*i;
|
|
||||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
|
||||||
|
|
||||||
ren.draw_quads(pass);
|
|
||||||
|
|
||||||
sdl::end_gpu_render_pass(pass);
|
|
||||||
// End Rectangle Render Pass
|
// End Rectangle Render Pass
|
||||||
|
|
||||||
|
|
||||||
// Textured Rectangles Render Pass
|
// Textured Rectangles Render Pass
|
||||||
pass = sdl::begin_gpu_render_pass(cmdbuf,
|
ren.start_render_pass("sprite shader");
|
||||||
&&(GPUColorTargetInfo){
|
|
||||||
.texture = swapchain_texture,
|
|
||||||
.mip_level = 0,
|
|
||||||
.layer_or_depth_plane = 0,
|
|
||||||
.clear_color = {.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0},
|
|
||||||
.load_op = GPU_LOADOP_DONT_CARE, // clear the screen at the start of the render pass
|
|
||||||
.store_op = GPU_STOREOP_STORE,
|
|
||||||
.resolve_texture = null,
|
|
||||||
.resolve_mip_level = 0,
|
|
||||||
.resolve_layer = 0,
|
|
||||||
.cycle = false,
|
|
||||||
.cycle_resolve_texture = false
|
|
||||||
},
|
|
||||||
1,
|
|
||||||
null // huh
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pass == null) {
|
|
||||||
unreachable("render pass creation went wrong: %s", sdl::get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
p = ren.pipelines.get_from_name("sprite shader").pipeline;
|
|
||||||
if (p == null) {
|
|
||||||
unreachable("no pipeline");
|
|
||||||
}
|
|
||||||
|
|
||||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
|
||||||
// in this case it is not an offset but the texture size in pixels
|
|
||||||
v.ox = 54;
|
|
||||||
v.oy = 64;
|
|
||||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
|
||||||
|
|
||||||
// bind the pipeline's sampler
|
// bind the pipeline's sampler
|
||||||
ren::Texture* tx = ren.textures.get_from_name("tux");
|
ren.bind_texture("tux");
|
||||||
sdl::bind_gpu_fragment_samplers(pass, 0,
|
|
||||||
(GPUTextureSamplerBinding[]){{.texture = tx.texture, .sampler = tx.sampler}}, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// tux
|
// tux
|
||||||
ren.push_sprite(300, 0, 54, 64, 0, 0);
|
ren.push_sprite(300, 0, 54, 64, 0, 0);
|
||||||
|
|
||||||
ren.draw_quads(pass);
|
ren.reset_scissor();
|
||||||
|
|
||||||
sdl::end_gpu_render_pass(pass);
|
ren.draw_quads();
|
||||||
|
|
||||||
|
ren.end_render_pass();
|
||||||
// End Textured Rectangle Render Pass
|
// End Textured Rectangle Render Pass
|
||||||
|
|
||||||
sdl::submit_gpu_command_buffer(cmdbuf);
|
ren.end_render();
|
||||||
|
|
||||||
ren.reset_quads();
|
|
||||||
thread::sleep_ms(250);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ren.free();
|
ren.free();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user