fast sprite render

This commit is contained in:
2025-01-13 20:12:59 -06:00
parent dab13a1f54
commit 9a2e897464
2 changed files with 129 additions and 105 deletions

View File

@@ -758,6 +758,7 @@ function insertion_sort(arr, cmp)
function sprites_to_queue(sprites, ysort = false) function sprites_to_queue(sprites, ysort = false)
{ {
return render._main.make_sprite_queue(allsprites, prosperon.camera, sprite_pipeline)
var sprites = allsprites; var sprites = allsprites;
for (var i = 0; i < sprites.length; i++) for (var i = 0; i < sprites.length; i++)
sprites[i].transform.clean(); sprites[i].transform.clean();
@@ -769,7 +770,6 @@ function sprites_to_queue(sprites, ysort = false)
var mesh = render._main.make_sprite_mesh(sprites); var mesh = render._main.make_sprite_mesh(sprites);
var queue = []; var queue = [];
var idx = 0;
var image; var image;
var first_index = 0; var first_index = 0;
var count = 0; var count = 0;

View File

@@ -1149,7 +1149,6 @@ int js_arrlen(JSContext *js,JSValue v) {
len = js_getnum_str(js,v,"length"); len = js_getnum_str(js,v,"length");
return len; return len;
} }
static inline int js_transform_dirty_chain(JSContext *js, JSValue v) static inline int js_transform_dirty_chain(JSContext *js, JSValue v)
{ {
transform *t = js2transform(js, v); transform *t = js2transform(js, v);
@@ -1753,6 +1752,64 @@ static const JSCFunctionListEntry js_spline_funcs[] = {
MIST_FUNC_DEF(spline, bezier, 2) MIST_FUNC_DEF(spline, bezier, 2)
}; };
shader_globals camera_globals(JSContext *js, JSValue camera)
{
shader_globals data = {0};
HMM_Vec2 size;
transform *transform;
double fov;
double aspect;
int ortho;
double near;
double far;
JS_GETPROP(js, size, camera, size, vec2)
JS_GETPROP(js, transform, camera, transform, transform)
JS_GETPROP(js, fov, camera, fov, number)
JS_GETPROP(js, aspect, camera, aspect, number)
JS_GETPROP(js, ortho, camera,ortho,bool)
JS_GETPROP(js,near,camera,near,number)
JS_GETPROP(js,far,camera,far,number)
HMM_Mat4 proj;
HMM_Mat4 view;
if (ortho) {
proj = HMM_Orthographic_RH_NO(
-size.x*0.5, 0.5*size.x,
-size.y*0.5, 0.5*size.y,
-1.0f, 1.0f
);
view = HMM_Translate((HMM_Vec3){ -transform->pos.x, -transform->pos.y, 0.0f });
}
else {
proj = HMM_Perspective_RH_NO(fov, aspect,near,far);
HMM_Mat4 camera_transform = HMM_Translate(transform->pos);
camera_transform = HMM_MulM4(camera_transform, HMM_QToM4(transform->rotation));
// camera_transform = HMM_MulM4(camera_transform, HMM_Scale(transform->scale)); // don't bother w/ scale
view = HMM_InvGeneralM4(camera_transform);
}
// Update your shader globals
data.world_to_projection = HMM_MulM4(proj, view);
data.projection_to_world = HMM_InvGeneralM4(data.world_to_projection);
data.camera_pos_world = transform->pos;
data.viewport_min_z = near;
data.viewport_max_z = far;
data.render_size = size;
data.world_to_view = view;
data.view_to_projection = proj;
data.camera_dir_world = HMM_NormV3(HMM_QVRot((HMM_Vec3){0,0,-1},transform->rotation));
data.viewport_size = (HMM_Vec2){0.5,0.5};
data.viewport_offset = (HMM_Vec2){0,0};
data.time = SDL_GetTicksNS() / 1000000000.0f;
return data;
}
JSValue js_vector_dot(JSContext *js, JSValue self, int argc, JSValue *argv) { JSValue js_vector_dot(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen; size_t alen, blen;
float *a = js2floats(js,argv[0], &alen); float *a = js2floats(js,argv[0], &alen);
@@ -3986,7 +4043,7 @@ int sort_sprite(const sprite *a, const sprite *b)
if (a->quad.vert[0].pos.y != b->quad.vert[0].pos.y) return b->quad.vert[0].pos.y - a->quad.vert[0].pos.y; if (a->quad.vert[0].pos.y != b->quad.vert[0].pos.y) return b->quad.vert[0].pos.y - a->quad.vert[0].pos.y;
// if (!JS_SameValue(a->js, a->image, b->image)) return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; if (!JS_SameValue(a->js, a->image, b->image)) return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
return 0; return 0;
} }
@@ -4014,33 +4071,58 @@ JSC_CCALL(gpu_sort_sprite,
return number2js(js,0); return number2js(js,0);
) )
inline int sprite_in_view(HMM_Mat3 sprite, HMM_Mat4 camera)
{
int outside = 0;
for (int j = 0; j < 4; j++) {
HMM_Vec3 corner = HMM_MulM3V3(sprite, (HMM_Vec3){base_quad[j].x, base_quad[j].y, 1.0});
HMM_Vec4 clip = HMM_MulM4V4(camera, (HMM_Vec4){corner.x,corner.y,corner.z,1.0});
if (clip.w <= 0.0) {
outside++;
continue;
}
float nx = clip.x/clip.w;
float ny = clip.y/clip.w;
float nz = clip.z/clip.w;
if (nx < -1 || nx > 1) outside++;
else if (ny < -1 || ny > 1) outside++;
else if (nz < -1 || nz > 1) outside++;
}
return outside != 4;
}
JSC_CCALL(gpu_make_sprite_queue, JSC_CCALL(gpu_make_sprite_queue,
size_t quads = js_arrlen(js, argv[0]); size_t quads = js_arrlen(js, argv[0]);
// Reserve an array of 'sprites' if needed, but watch for out-of-bounds shader_globals info = camera_globals(js,argv[1]);
sprite *sprites = NULL; sprite *sprites = NULL;
arrsetlen(sprites, quads); arrsetcap(sprites, quads);
for (int i = 0; i < quads; i++) { for (int i = 0; i < quads; i++) {
JSValue sub = JS_GetPropertyUint32(js, argv[0], i); JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
JSValue jstransform = JS_GetPropertyStr(js, sub, "transform");
HMM_Mat3 trmat = js2transform_mat3(js,jstransform);
if (!sprite_in_view(trmat,info.world_to_projection)) {
JS_FreeValue(js,jstransform);
JS_FreeValue(js,sub);
continue;
}
rect src; rect src;
HMM_Vec4 color; HMM_Vec4 color;
JS_GETATOM(js, src, sub, src_atom, rect) JS_GETATOM(js, src, sub, src_atom, rect)
JS_GETATOM(js, color, sub, color_atom, color) JS_GETATOM(js, color, sub, color_atom, color)
JSValue jstransform = JS_GetPropertyStr(js, sub, "transform");
quad sprite_quad; quad sprite_quad;
transform *t;
JS_GETATOM(js,t,sub,transform_atom,transform)
HMM_Mat3 trmat = t->gcache3;
// Transform the base_quad's 4 points into sprite_quad
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
sprite_quad.vert[j].pos = HMM_MulM3V3(trmat, base_quad[j]).xy; sprite_quad.vert[j].pos = HMM_MulM3V3(trmat, base_quad[j]).xy;
// Use [0..3] for uv and color
sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h }; sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h };
sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h }; sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h };
sprite_quad.vert[2].uv = (HMM_Vec2){ src.x, src.y }; sprite_quad.vert[2].uv = (HMM_Vec2){ src.x, src.y };
@@ -4051,31 +4133,55 @@ JSC_CCALL(gpu_make_sprite_queue,
sprite_quad.vert[2].color = color; sprite_quad.vert[2].color = color;
sprite_quad.vert[3].color = color; sprite_quad.vert[3].color = color;
// If you need to store into a bigger array:
// sprites[i].quad = sprite_quad;
// etc.
sprite sp; sprite sp;
sp.quad = sprite_quad; sp.quad = sprite_quad;
sp.image = JS_GetPropertyStr(js,sub,"image"); sp.image = JS_GetPropertyStr(js,sub,"image");
sp.js = js; sp.js = js;
JS_GETPROP(js,sp.layer,sub,layer,number) JS_GETPROP(js,sp.layer,sub,layer,number)
sprites[i] = sp; arrput(sprites,sp);
sprites[i].sprite = JS_DupValue(js,sub);
JS_FreeValue(js, sub); JS_FreeValue(js, sub);
JS_FreeValue(js, jstransform); JS_FreeValue(js, jstransform);
JS_FreeValue(js, sp.image);
} }
qsort(sprites, arrlen(sprites),sizeof(sprite),sort_sprite); qsort(sprites, arrlen(sprites),sizeof(sprite),sort_sprite);
text_vert *buffer = NULL;
for (int i = 0; i < quads; i++)
for (int j = 0; j < 4; j++)
arrpush(buffer, sprites[i].quad.vert[j]);
JSValue mesh = quads_to_mesh(js, buffer);
arrfree(buffer);
ret = JS_NewArray(js); ret = JS_NewArray(js);
int first_index = 0;
for (int i = 0; i < quads; i++) int count = 0;
JS_SetPropertyUint32(js,ret,i,sprites[i].sprite); int n = 0;
JSValue img = JS_UNDEFINED;
for (int i = 0; i < quads; i++) {
if (!JS_SameValue(js,sprites[i].image, img)) {
if (count > 0) {
JSValue q = JS_NewObject(js);
JS_SetPropertyStr(js,q,"type", JS_NewString(js,"geometry"));
JS_SetPropertyStr(js,q,"mesh", JS_DupValue(js,mesh));
JS_SetPropertyStr(js,q,"pipeline", JS_DupValue(js,argv[2]));
JS_SetPropertyStr(js,q,"image", JS_DupValue(js,img));
JS_SetPropertyStr(js,q,"first_index", number2js(js,first_index));
JS_SetPropertyStr(js,q,"num_indices",number2js(js,count*6));
JS_SetPropertyUint32(js,ret,n++,q);
}
first_index = i*6;
count = 1;
JS_FreeValue(js,img);
img = JS_DupValue(js,sprites[i].image);
} else count++;
JS_FreeValue(js,sprites[i].image);
}
arrfree(sprites); arrfree(sprites);
JS_FreeValue(js,mesh);
) )
JSC_CCALL(gpu_make_sprite_mesh, JSC_CCALL(gpu_make_sprite_mesh,
@@ -4789,7 +4895,7 @@ static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
MIST_FUNC_DEF(gpu, load_texture, 2), MIST_FUNC_DEF(gpu, load_texture, 2),
MIST_FUNC_DEF(gpu, texture, 1), MIST_FUNC_DEF(gpu, texture, 1),
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2), MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
MIST_FUNC_DEF(gpu, make_sprite_queue, 1), MIST_FUNC_DEF(gpu, make_sprite_queue, 3),
MIST_FUNC_DEF(gpu, make_quad, 0), MIST_FUNC_DEF(gpu, make_quad, 0),
MIST_FUNC_DEF(gpu, driver, 0), MIST_FUNC_DEF(gpu, driver, 0),
MIST_FUNC_DEF(gpu, make_shader, 1), MIST_FUNC_DEF(gpu, make_shader, 1),
@@ -5057,61 +5163,6 @@ JSC_CCALL(cmd_hud,
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data)); SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
) )
shader_globals camera_globals(JSContext *js, JSValue camera)
{
shader_globals data = {0};
HMM_Vec2 size;
transform *transform;
double fov;
double aspect;
int ortho;
double near;
double far;
JS_GETPROP(js, size, camera, size, vec2)
JS_GETPROP(js, transform, camera, transform, transform)
JS_GETPROP(js, fov, camera, fov, number)
JS_GETPROP(js, aspect, camera, aspect, number)
JS_GETPROP(js, ortho, camera,ortho,bool)
JS_GETPROP(js,near,camera,near,number)
JS_GETPROP(js,far,camera,far,number)
HMM_Mat4 proj;
HMM_Mat4 view;
if (ortho) {
proj = HMM_Orthographic_RH_NO(
-size.x*0.5, 0.5*size.x,
-size.y*0.5, 0.5*size.y,
-1.0f, 1.0f
);
view = HMM_Translate((HMM_Vec3){ -transform->pos.x, -transform->pos.y, 0.0f });
}
else {
proj = HMM_Perspective_RH_NO(fov, aspect,near,far);
HMM_Mat4 camera_transform = HMM_Translate(transform->pos);
camera_transform = HMM_MulM4(camera_transform, HMM_QToM4(transform->rotation));
// camera_transform = HMM_MulM4(camera_transform, HMM_Scale(transform->scale)); // don't bother w/ scale
view = HMM_InvGeneralM4(camera_transform);
}
// Update your shader globals
data.world_to_projection = HMM_MulM4(proj, view);
data.projection_to_world = HMM_InvGeneralM4(data.world_to_projection);
data.camera_pos_world = transform->pos;
data.viewport_min_z = near;
data.viewport_max_z = far;
data.render_size = size;
data.world_to_view = view;
data.view_to_projection = proj;
data.camera_dir_world = HMM_NormV3(HMM_QVRot((HMM_Vec3){0,0,-1},transform->rotation));
data.viewport_size = (HMM_Vec2){0.5,0.5};
data.viewport_offset = (HMM_Vec2){0,0};
data.time = SDL_GetTicksNS() / 1000000000.0f;
return data;
}
JSC_CCALL(cmd_camera, JSC_CCALL(cmd_camera,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
shader_globals data = camera_globals(js, argv[0]); shader_globals data = camera_globals(js, argv[0]);
@@ -6963,39 +7014,12 @@ JSC_CCALL(os_cull_sprites,
int len = js_arrlen(js,sprites); int len = js_arrlen(js,sprites);
HMM_Vec2 corners[4] = {
(HMM_Vec2){0,0},
(HMM_Vec2){1,0},
(HMM_Vec2){0,1},
(HMM_Vec2){1,1},
};
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
JSValue sub = JS_GetPropertyUint32(js,sprites, i); JSValue sub = JS_GetPropertyUint32(js,sprites, i);
transform *t; transform *t;
JS_GETATOM(js,t,sub,transform_atom,transform) JS_GETATOM(js,t,sub,transform_atom,transform)
HMM_Mat3 trmat = t->gcache3; HMM_Mat3 trmat = t->gcache3;
int outside = 0; if (sprite_in_view(trmat, info.world_to_projection)) {
for (int j = 0; j < 4; j++) {
Uint64 start = SDL_GetTicksNS();
HMM_Vec3 corner = HMM_MulM3V3(trmat, (HMM_Vec3){corners[j].x, corners[j].y, 1.0});
HMM_Vec4 clip = HMM_MulM4V4(info.world_to_projection, (HMM_Vec4){corner.x,corner.y,corner.z,1.0});
if (clip.w <= 0.0) {
outside++;
continue;
}
float nx = clip.x/clip.w;
float ny = clip.y/clip.w;
float nz = clip.z/clip.w;
if (nx < -1 || nx > 1) outside++;
else if (ny < -1 || ny > 1) outside++;
else if (nz < -1 || nz > 1) outside++;
}
if (outside != 4) {
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub)); JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
n++; n++;
} }