diff options
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | build.ninja | 9 | ||||
-rw-r--r-- | c2.cpp | 175 | ||||
-rw-r--r-- | configure.lua | 1 | ||||
-rw-r--r-- | convmodel.c | 3 | ||||
-rw-r--r-- | convtexture.c | 128 | ||||
-rw-r--r-- | entity.hpp | 8 | ||||
-rw-r--r-- | intermediate/forward.h | 12 | ||||
-rw-r--r-- | intermediate/surface.glsl | 182 | ||||
-rw-r--r-- | intermediate/ts.glsl | 148 | ||||
-rw-r--r-- | lighting.cpp | 82 | ||||
-rw-r--r-- | lighting.hpp | 12 | ||||
-rw-r--r-- | model.cpp | 33 | ||||
-rw-r--r-- | model.hpp | 4 | ||||
-rw-r--r-- | pipeline.cpp | 24 | ||||
-rw-r--r-- | qstd/memory.c | 2 | ||||
-rw-r--r-- | renderer.cpp | 210 | ||||
-rw-r--r-- | renderer.hpp | 38 | ||||
-rw-r--r-- | sc/sc.cpp | 223 | ||||
-rw-r--r-- | sc/sh_enums.h | 3 | ||||
-rw-r--r-- | sc/sh_helpers.h | 3 | ||||
-rw-r--r-- | todo.txt | 4 | ||||
-rw-r--r-- | video.cpp | 355 | ||||
-rw-r--r-- | video.hpp | 32 | ||||
-rw-r--r-- | world.cpp | 132 | ||||
-rw-r--r-- | world.hpp | 63 |
26 files changed, 1199 insertions, 704 deletions
@@ -118,18 +118,15 @@ data/tonemap.csh: intermediate/tonemap.glsl shadercompiler | data data/triangle.csh: intermediate/triangle.glsl shadercompiler | data cpp -MMD -MF data/triangle.d -MT data/triangle.csh -Iintermediate intermediate/triangle.glsl > /dev/null ./shadercompiler intermediate/triangle.glsl data/triangle.csh -data/ts.csh: intermediate/ts.glsl shadercompiler | data - cpp -MMD -MF data/ts.d -MT data/ts.csh -Iintermediate intermediate/ts.glsl > /dev/null - ./shadercompiler intermediate/ts.glsl data/ts.csh data/ui.csh: intermediate/ui.glsl shadercompiler | data cpp -MMD -MF data/ui.d -MT data/ui.csh -Iintermediate intermediate/ui.glsl > /dev/null ./shadercompiler intermediate/ui.glsl data/ui.csh -data/monkey.mdl: convmodel intermediate/monkey.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh | data +data/monkey.mdl: convmodel intermediate/monkey.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh | data ./convmodel data intermediate/monkey.glb data/monkey.mdl -data/cube.mdl: convmodel intermediate/cube.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh | data +data/cube.mdl: convmodel intermediate/cube.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh | data ./convmodel data intermediate/cube.glb data/cube.mdl -data/scene.mdl: convmodel intermediate/scene.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh | data +data/scene.mdl: convmodel intermediate/scene.glb data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh | data ./convmodel data intermediate/scene.glb data/scene.mdl data/22.tex: convtexture intermediate/22.bmp | data @@ -152,16 +149,16 @@ data/plastic.mat: convmaterial intermediate/plastic.mat | data data/greybox.mat: convmaterial intermediate/greybox.mat | data ./convmaterial intermediate/greybox.mat data/greybox.mat -pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat data/greybox.mat - ./packer pack data debug.csh mip_spec.csh sky.csh surface.csh surface_depthonly.csh tonemap.csh triangle.csh ts.csh ui.csh monkey.mdl cube.mdl scene.mdl 22.tex kita.tex brick_albedo.tex brick_ao.tex brick_normal.tex sky.tex bricks.mat plastic.mat greybox.mat +pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat data/greybox.mat + ./packer pack data debug.csh mip_spec.csh sky.csh surface.csh surface_depthonly.csh tonemap.csh triangle.csh ui.csh monkey.mdl cube.mdl scene.mdl 22.tex kita.tex brick_albedo.tex brick_ao.tex brick_normal.tex sky.tex bricks.mat plastic.mat greybox.mat data: mkdir -p data --include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d lighting.d maths.d model.d physics.d pipeline.d renderer.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.d data/mip_spec.d data/sky.d data/surface.d data/surface_depthonly.d data/tonemap.d data/triangle.d data/ts.d data/ui.d +-include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d lighting.d maths.d model.d physics.d pipeline.d renderer.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.d data/mip_spec.d data/sky.d data/surface.d data/surface_depthonly.d data/tonemap.d data/triangle.d data/ui.d clean: - rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o lighting.o maths.o model.o physics.o pipeline.o renderer.o scene.o ui.o video.o world.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d lighting.d maths.d model.d physics.d pipeline.d renderer.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.d data/mip_spec.d data/sky.d data/surface.d data/surface_depthonly.d data/tonemap.d data/triangle.d data/ts.d data/ui.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat data/greybox.mat + rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o lighting.o maths.o model.o physics.o pipeline.o renderer.o scene.o ui.o video.o world.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d lighting.d maths.d model.d physics.d pipeline.d renderer.d scene.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.d data/mip_spec.d data/sky.d data/surface.d data/surface_depthonly.d data/tonemap.d data/triangle.d data/ui.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat data/greybox.mat rm -f shadercompiler rmdir data rm -f c2 diff --git a/build.ninja b/build.ninja index 8635232..0d15a5e 100644 --- a/build.ninja +++ b/build.ninja @@ -83,12 +83,11 @@ build data/surface.csh: shadercompiler intermediate/surface.glsl | shadercompile build data/surface_depthonly.csh: shadercompiler intermediate/surface_depthonly.glsl | shadercompiler.exe build data/tonemap.csh: shadercompiler intermediate/tonemap.glsl | shadercompiler.exe build data/triangle.csh: shadercompiler intermediate/triangle.glsl | shadercompiler.exe -build data/ts.csh: shadercompiler intermediate/ts.glsl | shadercompiler.exe build data/ui.csh: shadercompiler intermediate/ui.glsl | shadercompiler.exe -build data/monkey.mdl: convmodel intermediate/monkey.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh -build data/cube.mdl: convmodel intermediate/cube.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh -build data/scene.mdl: convmodel intermediate/scene.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh +build data/monkey.mdl: convmodel intermediate/monkey.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh +build data/cube.mdl: convmodel intermediate/cube.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh +build data/scene.mdl: convmodel intermediate/scene.glb | convmodel.exe data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh build data/bricks.mat: convmaterial intermediate/bricks.mat | convmaterial.exe build data/plastic.mat: convmaterial intermediate/plastic.mat | convmaterial.exe @@ -107,5 +106,5 @@ build data/brick_normal.tex: convtexture intermediate/brick_normal.bmp | convtex build data/sky.tex: convtexture intermediate/sky.hdr | convtexture.exe format = rgba16f -build pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ts.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/bricks.mat data/plastic.mat data/greybox.mat data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex | packer.exe +build pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/tonemap.csh data/triangle.csh data/ui.csh data/monkey.mdl data/cube.mdl data/scene.mdl data/bricks.mat data/plastic.mat data/greybox.mat data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex | packer.exe @@ -21,9 +21,11 @@ extern "C" { #define video_arena_size (1024 * 1024 * 16) #define asset_arena_size (1024 * 1024 * 4) #define ui_arena_size (1024 * 1024) -#define scene_arena_size (1024 * 4) +#define scene_arena_size (1024 * 8) #define per_frame_memory_size (1024 * 1024) +#define MSAA_SAMPLES 4 + static float verts[] = { 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, @@ -58,33 +60,6 @@ static Buffer_Id upload_verts(Device* dev) { return vbo; } -Texture_Id make_default_texture(Device* dev) { - unsigned* mem; - Texture_Id tex; - Buffer_Id buf = dev->create_buffer( - "default texture stage", - 4, - Buffer_Flags::copy_src | - Buffer_Flags::cpu_readwrite - ); - mem = (unsigned*)dev->map_buffer(buf, 0, 4); - mem[0] = 0xffffffff; - dev->unmap_buffer(buf); - tex = dev->create_texture( - "default PBR texture", - texture_format_rgba8i, - Texture_Flags::sampleable | Texture_Flags::copy_dst, - 1, - 1, - 1, - 1, - 1, - buf - ); - dev->destroy_bufferi(buf); - return tex; -} - static Sampler_Id create_clamped_linear(Device* dev) { Sampler_State s{}; s.min = Filter_Mode::linear; @@ -162,6 +137,61 @@ struct Orbit_Cam { } }; +struct Fullscreen_Quad { + Buffer_Id vb; + + void init(Device* d) { + float verts[] = { + -1.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 3.0f, 0.0f, 2.0f, + 3.0f, -1.0f, 2.0f, 0.0f + }; + Buffer_Id stage; + void* mem; + stage = d->create_buffer( + "sky vb stage", + sizeof verts, + Buffer_Flags::cpu_readwrite | + Buffer_Flags::copy_src + ); + mem = d->map_buffer(stage, 0, sizeof verts); + memcpy(mem, verts, sizeof verts); + d->unmap_buffer(stage); + vb = d->create_buffer( + "fullscreen quad", + sizeof verts, + Buffer_Flags::copy_dst | + Buffer_Flags::vertex_buffer + ); + Context& ctx = d->acquire(); + ctx.copy(vb, stage); + d->submit(ctx); + d->destroy_bufferi(stage); + } + + void destroy(Device* d) { + d->destroy_buffer(vb); + } + + void render( + Context& ctx, + Pipeline& pip, + Render_Pass& pass, + int bind + ) { + Vertex_Buffer_Binding vbb[] = {{ + .id = vb, + .offset = 0, + .target = bind + }, {}}; + Draw draw{}; + draw.verts = vbb; + draw.vertex_count = 3; + draw.instance_count = 1; + ctx.submit(draw, pip, pass); + } +}; + struct Sky { Staged_Buffer config; Texture_Id texture; @@ -585,19 +615,19 @@ struct C2 : public App { Collider* box_col, * floor_col; Texture* texture; Texture* texture2; - Texture_Id default_texture; Entity_Id monkey, monkey2, box, floor; Model_Scene scene; Renderer renderer; Lighting lighting; Camera_Id camera; Orbit_Cam orbit_cam; + Fullscreen_Quad quad; Sky sky; Env_Probe eprobe; Tonemap tonemap; Buffer_Id vbo, cbuf; Sampler_Id clamped_linear; - Texture_Id hdr_target; + Texture_Id hdr_target, hdr_resolved, ms_depth; Texture_Id ui_texture; Buffer_Id ui_buffer; Line_Renderer lr; @@ -636,17 +666,17 @@ struct C2 : public App { ); assets.init(&asset_arena, "pack", 128); dev = Device::create(&video_arena, this); - default_texture = make_default_texture(dev); make_hdr_target(); make_ui_texture(); model_loader.init(dev, &assets); - mat_loader.init(&assets, default_texture); + mat_loader.init(&assets); register_asset_loader("MODL", &model_loader); register_asset_loader("MTRL", &mat_loader); shader = (Shader*)assets.load("triangle.csh"); ui_shader = (Shader*)assets.load("ui.csh"); texture = (Texture*)assets.load("22.tex"); texture2 = (Texture*)assets.load("kita.tex"); + quad.init(dev); cbuf = dev->create_buffer( "config buffer", sizeof(Config_Buffer), @@ -658,7 +688,7 @@ struct C2 : public App { per_frame_memory_size ); clamped_linear = create_clamped_linear(dev); - renderer.init(arena, dev, assets); + renderer.init(arena, dev); world = (World*)arena_alloc(arena, sizeof *world); world->init(arena); ui = UI::create(this, &ui_arena); @@ -669,7 +699,7 @@ struct C2 : public App { ui->layout(w, h); fps_label = ui->create_element<UI::Label>(ui->root, ""); scene.init(&scene_arena, 32, clamped_linear); - lighting.init(dev, w, h); + lighting.init(dev); sky.init(dev, &assets); eprobe.init(dev, &assets, 256); tonemap.init(dev, &assets); @@ -702,6 +732,24 @@ struct C2 : public App { light.caster = true; } { + auto l = world->create_entity(); + auto [t, light] = world->add<Transform, Point_Light>(l); + t.mat = m4f::translate(m4f::identity(), v3f(-3.0f, 1.0f, 0.0f)); + light.colour = v3f(1.0f, 0.0f, 0.0f); + light.brightness = 5.0f; + light.caster = true; + light.range = 5.0f; + } + { + auto l = world->create_entity(); + auto [t, light] = world->add<Transform, Point_Light>(l); + t.mat = m4f::translate(m4f::identity(), v3f(3.0f, 2.0f, 0.0f)); + light.colour = v3f(0.0f, 1.0f, 0.0f); + light.brightness = 5.0f; + light.caster = true; + light.range = 3.0f; + } + { box_col = make_box(&asset_arena, v3f(1.0f, 1.0f, 1.0f)); floor_col = make_box(&asset_arena, v3f(10.0f, 0.1f, 10.0f)); box = world->create_entity(); @@ -819,7 +867,7 @@ struct C2 : public App { pb.begin_rp(); pb.rp_target(hdr_target, Clear_Mode::restore); - pb.rp_depth_target(dev->get_depth_target(), Clear_Mode::restore); + pb.rp_depth_target(ms_depth, Clear_Mode::restore); Render_Pass& sky_pass = pb.build_rp(); pb.begin_rp(); @@ -827,7 +875,7 @@ struct C2 : public App { Render_Pass& tonemap_pass = pb.build_rp(); ctx.debug_push("environment cube"); - eprobe.render(dev, &frame_arena, renderer.quad, sky); + eprobe.render(dev, &frame_arena, quad, sky); ctx.debug_pop(); renderer.env_cubemap = eprobe.get_cubemap(); @@ -846,6 +894,7 @@ struct C2 : public App { dev, &frame_arena, hdr_target, + ms_depth, &lighting ); ctx.debug_pop(); @@ -854,7 +903,7 @@ struct C2 : public App { sky.render( dev, &frame_arena, - renderer.quad, + quad, sky_pass, clamped_linear, pcam, @@ -862,15 +911,17 @@ struct C2 : public App { ); ctx.debug_pop(); + ctx.resolve(hdr_resolved, hdr_target); + tonemap.update(dev, 0.2f); ctx.debug_push("TONEMAP"); tonemap.render( dev, &frame_arena, - renderer.quad, + quad, tonemap_pass, - hdr_target, + hdr_resolved, clamped_linear ); ctx.debug_pop(); @@ -902,7 +953,7 @@ struct C2 : public App { clamped_linear ); Pipeline& ui_pip = pb.build(); - renderer.quad.render( + quad.render( ctx, ui_pip, ui_pass, @@ -944,6 +995,7 @@ struct C2 : public App { } void on_destroy() override { + quad.destroy(dev); scene.destroy(dev); sky.destroy(dev); lighting.destroy(dev, renderer); @@ -954,8 +1006,9 @@ struct C2 : public App { ui->destroy(); deinit_editor(); assets.destroy(); - dev->destroy_texture(default_texture); dev->destroy_texture(hdr_target); + dev->destroy_texture(hdr_resolved); + dev->destroy_texture(ms_depth); dev->destroy_texture(ui_texture); dev->destroy_buffer(ui_buffer); dev->destroy_sampler(clamped_linear); @@ -967,25 +1020,53 @@ struct C2 : public App { void on_resize() override { ui->layout(w, h); dev->on_resize(); + dev->destroy_texture(ms_depth); dev->destroy_texture(hdr_target); + dev->destroy_texture(hdr_resolved); dev->destroy_texture(ui_texture); dev->destroy_buffer(ui_buffer); make_hdr_target(); make_ui_texture(); - lighting.recreate(dev, w, h); } void make_hdr_target() { + int sw = dev->swap_w(); + int sh = dev->swap_h(); hdr_target = dev->create_texture( - "HDR target", + "MS HDR target", texture_format_rgba16f, - Texture_Flags::sampleable | Texture_Flags::colour_target, - dev->swap_w(), - dev->swap_h(), + Texture_Flags::copy_src | Texture_Flags::colour_target, + sw, + sh, 1, 1, 1, - Buffer_Id(0) + Buffer_Id(0), + MSAA_SAMPLES + ); + hdr_resolved = dev->create_texture( + "Resolved HDR", + texture_format_rgba16f, + Texture_Flags::sampleable | Texture_Flags::copy_dst, + sw, + sh, + 1, + 1, + 1, + Buffer_Id(0), + 1 + ); + ms_depth = dev->create_texture( + "MS depth", + texture_format_d24s8, + Texture_Flags::sampleable | Texture_Flags::depth_stencil_target, + sw, + sh, + 1, + 1, + 1, + 0, + MSAA_SAMPLES ); } diff --git a/configure.lua b/configure.lua index d944308..9bfe342 100644 --- a/configure.lua +++ b/configure.lua @@ -50,7 +50,6 @@ config = { "surface_depthonly", "tonemap", "triangle", - "ts", "ui", }, materials = { diff --git a/convmodel.c b/convmodel.c index 31a062d..89f65c8 100644 --- a/convmodel.c +++ b/convmodel.c @@ -332,7 +332,7 @@ Shader_Attrib* parse_shader_attribs(const char* fname) { FILE* f; char magic[4]; int type, i; - int binding_count, target_count, desc_count; + int binding_count, target_count, desc_count, opt_count; char* fpath = arena_alloc( &arena, string_len(fname) + @@ -362,6 +362,7 @@ Shader_Attrib* parse_shader_attribs(const char* fname) { fread(&binding_count, 4, 1, f); fread(&target_count, 4, 1, f); fread(&desc_count, 4, 1, f); + fread(&opt_count, 4, 1, f); assert(binding_count); for (i = 0; i < binding_count; i++) { char name[24]; diff --git a/convtexture.c b/convtexture.c index e5ec996..5ad4735 100644 --- a/convtexture.c +++ b/convtexture.c @@ -35,6 +35,73 @@ const char* format_names[] = { #undef x }; +void gen_mip(Image* src, Image* dst, int w, int h) { + const float weight = 1.0f / 4.0f; + int i, x, y, sw = src->w; + dst->pixels = malloc(w * h * sizeof *dst->pixels); + dst->w = w; + dst->h = h; + assert(w == src->w / 2); + assert(h == src->h / 2); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + int sx = x << 1; + int sy = y << 1; + int ind[] = { + sx + (sy) * sw, + sx + 1 + (sy) * sw, + sx + (sy + 1) * sw, + sx + 1 + (sy + 1) * sw + }; + float avg[4] = { 0 }; + Colour* d = &dst->pixels[x + y * w]; + for (i = 0; i < 4; i++) { + int j; + for (j = 0; j < 4; j++) + avg[j] += + (float)((uint8_t*)&src->pixels[ind[i]])[j] * + weight; + } + d->r = (uint8_t)avg[0]; + d->g = (uint8_t)avg[1]; + d->b = (uint8_t)avg[2]; + d->a = (uint8_t)avg[3]; + } + } +} + +int auto_count(int w, int h) { + int i; + for (i = 0; w > 1 || h > 1; i++, w >>= 1, h >>= 1); + return i; +} + +int po2(int x) { + return x && (!(x & (x - 1))); +} + +Image* gen_mips(Image* src, int count, int* gen_count) { + int i, w, h; + Image* imgs; + if (!count) + count = auto_count(src->w, src->h); + imgs = malloc(count * sizeof *imgs); + imgs[0] = *src; + w = src->w >> 1; + h = src->h >> 1; + if (count && (!po2(src->w) || !po2(src->h))) { + print_war("Image size not a power of 2 (no mips for you >:(\n"); + count = 1; + } + for (i = 1; i < count; i++) { + gen_mip(&imgs[i - 1], &imgs[i], w, h); + w >>= 1; + h >>= 1; + } + *gen_count = i; + return imgs; +} + unsigned encode_endpoint(const vec3* v) { return ((unsigned)(v->r * 31.9999f) << 11) | @@ -264,28 +331,33 @@ void compress_bc5(Colour* pixels, int w, int h, FILE* f) { } } -void write_header(FILE* f, int w, int h, Texture_Format fmt) { +void write_header(FILE* f, int w, int h, int mips, Texture_Format fmt) { fwrite("TXTR", 1, 4, f); fwrite(&w, 1, 4, f); fwrite(&h, 1, 4, f); - fwrite(&fmt, 1, 4, f); + fwrite(&fmt, 1, 2, f); + fwrite(&mips, 1, 2, f); } -void convert(Image* image, Texture_Format target, FILE* f) { - write_header(f, image->w, image->h, target); - switch (target) { - case texture_format_bc1: - compress_bc1(image->pixels, image->w, image->h, f); - break; - case texture_format_bc4: - compress_bc4(image->pixels, image->w, image->h, f); - break; - case texture_format_bc5: - compress_bc5(image->pixels, image->w, image->h, f); - break; - default: - print_err("Unsupported target format.\n"); - pbreak(40); +void convert(Image* images, int count, Texture_Format target, FILE* f) { + int i; + write_header(f, images->w, images->h, count, target); + for (i = 0; i < count; i++) { + Image* image = &images[i]; + switch (target) { + case texture_format_bc1: + compress_bc1(image->pixels, image->w, image->h, f); + break; + case texture_format_bc4: + compress_bc4(image->pixels, image->w, image->h, f); + break; + case texture_format_bc5: + compress_bc5(image->pixels, image->w, image->h, f); + break; + default: + print_err("Unsupported target format.\n"); + pbreak(40); + } } } @@ -299,12 +371,17 @@ Texture_Format texture_format_from_string(const char* s) { return (Texture_Format)0; } -int proc_bitmap(FILE* infile, FILE* outfile, Texture_Format target) { +int proc_bitmap( + FILE* infile, + FILE* outfile, + Texture_Format target, + int mc +) { unsigned bmp_offset; int bmp_w, bmp_h, s, x, y; unsigned short bmp_bits; Colour* buffer; - Image img; + Image img, * imgs; Colour pixel; fseek(infile, 10, SEEK_SET); @@ -347,7 +424,8 @@ int proc_bitmap(FILE* infile, FILE* outfile, Texture_Format target) { img.pixels = buffer; img.w = bmp_w; img.h = bmp_h; - convert(&img, target, outfile); + imgs = gen_mips(&img, mc, &mc); + convert(imgs, mc, target, outfile); fclose(outfile); return 0; } @@ -519,12 +597,13 @@ int proc_hdr(FILE* infile, FILE* outfile, int half) { } } if (half) { - write_header(outfile, w, h, texture_format_rgba16f); + write_header(outfile, w, h, 1, texture_format_rgba16f); write_halves(outfile, w * h * 4, (float*)pixels); } else { - write_header(outfile, w, h, texture_format_rgba32f); + write_header(outfile, w, h, 1, texture_format_rgba32f); fwrite(pixels, sizeof *pixels, w * h, outfile); } + /* todo mipgen for hdr */ fclose(outfile); return 0; } @@ -532,6 +611,7 @@ int proc_hdr(FILE* infile, FILE* outfile, int half) { int main(int argc, const char** argv) { FILE* outfile, * infile; char magic[11]; + int mips = 0; Texture_Format target; if (argc < 4) { @@ -545,6 +625,8 @@ int main(int argc, const char** argv) { return 2; } target = texture_format_from_string(argv[3]); + if (argc > 4) + mips = (int)strtol(argv[4], 0, 10); outfile = fopen(argv[2], "wb"); if (!outfile) { @@ -554,7 +636,7 @@ int main(int argc, const char** argv) { fread(magic, 1, 10, infile); magic[10] = 0; if (magic[0] == 'B' && magic[1] == 'M') { - return proc_bitmap(infile, outfile, target); + return proc_bitmap(infile, outfile, target, mips); } if (string_equal(magic, "#?RADIANCE")) { int half = target == texture_format_rgba16f; diff --git a/entity.hpp b/entity.hpp new file mode 100644 index 0000000..34cc84d --- /dev/null +++ b/entity.hpp @@ -0,0 +1,8 @@ +#ifndef entity_hpp +#define entity_hpp + +#include <stdint.h> + +using Entity_Id = uint32_t; + +#endif diff --git a/intermediate/forward.h b/intermediate/forward.h index 7c4c9d3..3bc2d50 100644 --- a/intermediate/forward.h +++ b/intermediate/forward.h @@ -5,7 +5,7 @@ [struct] name: Light [variable] -name: dir +name: pos type: vec3 [variable] name: brightness @@ -16,6 +16,9 @@ type: vec3 [variable] name: caster_id type: int +[variable] +name: range +type: float [struct] name: Caster @@ -34,6 +37,12 @@ type: int [variable] name: frame type: int +[variable] +name: sun_irange +type: ivec2 +[variable] +name: point_irange +type: ivec2 [cbuffer] name: globals @@ -41,4 +50,5 @@ type: Globals stage: fragment #endif + #endif diff --git a/intermediate/surface.glsl b/intermediate/surface.glsl index 82bf2f6..cc287e4 100644 --- a/intermediate/surface.glsl +++ b/intermediate/surface.glsl @@ -9,6 +9,22 @@ fragment: main #ifdef DESC +[option] +name: albedomap +stage: fragment +[option] +name: aomap +stage: fragment +[option] +name: metalmap +stage: fragment +[option] +name: roughmap +stage: fragment +[option] +name: normalmap +stage: fragment + [binding] name: mesh rate: vertex @@ -29,9 +45,6 @@ type: vec2 name: uv type: vec2 [interpolator] -name: screen -type: vec4 -[interpolator] name: position type: vec4 [interpolator] @@ -117,7 +130,7 @@ dimension: cube [texture] name: shadowmaps stage: fragment -dimension: array +dimension: shadowArray [target] name: colour @@ -136,9 +149,7 @@ void main() { interpolator.uv = uv; interpolator.position = pos; interpolator.tbn = mat3(t, b, n); - pos = c_vp.view_projection * pos; - interpolator.screen = pos; - gl_Position = pos; + gl_Position = c_vp.view_projection * pos; } #endif @@ -147,8 +158,8 @@ void main() { #define pi 3.14159265358979323846 -vec3 diffuse_brdf(vec2 uv) { - vec3 a = material.albedo * texture(albedo, uv).rgb; +vec3 diffuse_brdf(vec2 uv, vec3 base) { + vec3 a = base; return a / pi; } @@ -162,7 +173,11 @@ float specular_G1(float a, vec3 v, vec3 n) { float specular_brdf(vec2 uv, vec3 ref, vec3 l, vec3 v, vec3 n) { float ndl = max(dot(n, l), 0.0); float ndv = max(dot(n, v), 0.0); +#if OPT_roughmap float a = texture(rough, uv).r * material.roughness; +#else + float a = material.roughness; +#endif float a2 = a * a; float ndr = max(dot(n, ref), 0.0); float b = ((ndr * ndr) * (a2 - 1) + 1); @@ -175,45 +190,160 @@ float specular_brdf(vec2 uv, vec3 ref, vec3 l, vec3 v, vec3 n) { return (D * F * G) / (4.0 * ndl * ndv + 0.001); } -void main() { +vec2 poissonDisk[16] = vec2[]( + vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790) +); + +float random(vec3 seed, int i){ + vec4 seed4 = vec4(seed,i); + float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); + return fract(sin(dot_product) * 43758.5453); +} + +float get_shadow(Light l, vec3 wpos) { + const int taps = 4; + const float w = 1.0 / float(taps); + const float m = 1.0 / 1000.0; int i; + float d = 0.0f; + Caster caster = casters[l.caster_id]; + vec4 surf = caster.projection * vec4(wpos, 1.0); + surf /= surf.w; + surf.xy = surf.xy * 0.5 + 0.5; + surf.z -= 0.005; + for (i = 0; i < taps; i++) { + int index = int(16.0 * random(floor(wpos.xyz * 1000.0), i)) % 16; + vec2 coord = surf.xy + poissonDisk[index] * m; + vec4 vec = vec4(coord, float(l.caster_id), surf.z); + d += texture(shadowmaps, vec).r * w; + } + return d; +} + +vec3 apply_light( + Light l, + vec3 p, + vec2 uv, + vec3 base_diffuse, + vec3 spec_col, + vec3 ref, + vec3 light_dir, + vec3 view_dir, + vec3 nrm, + float cos_theta_i, + float atten +) { + vec3 diffuse = base_diffuse * cos_theta_i; + vec3 spec = + spec_col * + specular_brdf(uv, ref, light_dir, view_dir, nrm) * + cos_theta_i; + float shadow = 1.0f; + if (l.caster_id >= 0) + shadow = get_shadow(l, p); + return (diffuse + spec) * atten * l.brightness * l.colour * shadow; +} + +void main() { + int i, e; vec2 uv = interpolator.uv; - vec2 suv = (interpolator.screen.xy / interpolator.screen.w) * 0.5 + 0.5; vec3 p = interpolator.position.xyz; +#if OPT_normalmap vec3 nrmsample = texture(normal, uv).rgb; vec2 nrmxy = nrmsample.xy * 2.0 - 1.0; vec3 nrm = normalize(vec3(nrmxy, 1.0)); - if (nrmsample.b == 1.0) /* default texture */ - nrm = normalize(interpolator.tbn[2]); - else - nrm = normalize(interpolator.tbn * nrm); + nrm = normalize(interpolator.tbn * nrm); +#else + vec3 nrm = normalize(interpolator.tbn[2]); +#endif +#if OPT_albedomap vec3 col = texture(albedo, uv).rgb * material.albedo; - vec3 view_dir = normalize(globals.camera_pos - p); +#else + vec3 col = material.albedo; +#endif + +#if OPT_metalmap float met = texture(metal, uv).r * material.metalness; +#else + float met = material.metalness; +#endif + + vec3 view_dir = normalize(globals.camera_pos - p); vec3 ref = reflect(-view_dir, nrm); vec3 ref_col = texture(env_cube, ref, material.roughness * 8.0).rgb; vec3 spec_col = mix(ref_col, ref_col * col, met); vec3 amb_col = min(textureLod(env_cube, nrm, 8.0).rgb, 0.05); +#if OPT_aomap vec3 ambient = amb_col * texture(ao, uv).r * material.ao; +#else + vec3 ambient = amb_col * material.ao; +#endif - vec3 base_diffuse = diffuse_brdf(uv) * (1.0 - met); + vec3 base_diffuse = diffuse_brdf(uv, col) * (1.0 - met); vec3 light = 0.0.xxx; - for (i = 0; i < globals.light_count; i++) { + e = globals.sun_irange.y; + for (i = globals.sun_irange.x; i < e; i++) { Light l = lights[i]; - vec3 light_dir = l.dir; + vec3 light_dir = l.pos; float cos_theta_i = max(dot(nrm, light_dir), 0.0); - vec3 diffuse = base_diffuse * cos_theta_i; - vec3 spec = - spec_col * - specular_brdf(uv, ref, light_dir, view_dir, nrm) * - cos_theta_i; - float shadow = texture(shadowmaps, vec3(suv, float(l.caster_id))).r; - light += (diffuse + spec) * l.brightness * l.colour * shadow; + float atten = 1.0; + light += apply_light( + l, + p, + uv, + base_diffuse, + spec_col, + ref, + light_dir, + view_dir, + nrm, + cos_theta_i, + atten + ); + } + e = globals.point_irange.y; + for (i = globals.point_irange.x; i < e; i++) { + Light l = lights[i]; + float cos_theta_i; + vec3 light_dir = l.pos - p; + float d = length(light_dir); + float atten = max(d, 0.01); + atten = 1.0 / (atten * atten); + light_dir /= d; + cos_theta_i = max(dot(nrm, light_dir), 0.0); + light += apply_light( + l, + p, + uv, + base_diffuse, + spec_col, + ref, + light_dir, + view_dir, + nrm, + cos_theta_i, + atten + ); } colour = vec4(ambient + light, 1.0); diff --git a/intermediate/ts.glsl b/intermediate/ts.glsl deleted file mode 100644 index 23195f6..0000000 --- a/intermediate/ts.glsl +++ /dev/null @@ -1,148 +0,0 @@ -#ifdef DESC -[program] -type: graphics -vertex: main -fragment: main -#endif - -#include "forward.h" - -#ifdef DESC - -[binding] -name: verts -rate: vertex -[attribute] -name: position -type: vec2 -[attribute] -name: uv -type: vec2 - -[interpolator] -name: uv -type: vec2 - -[texture] -name: shadowmaps -stage: fragment -dimension: shadowArray -[texture] -name: previous -stage: fragment -dimension: 2 -[texture] -name: depthmap -stage: fragment -dimension: 2 - -[struct] -name: Config -[variable] -name: inv_view -type: mat4 -[variable] -name: inv_proj -type: mat4 -[variable] -name: prev_vp -type: mat4 - -[struct] -name: Caster_Config -[variable] -name: index -type: int - -[cbuffer] -name: config -type: Config -stage: fragment - -[cbuffer] -name: caster_config -type: Caster_Config -stage: fragment - -[sbuffer] -name: casters -type: Caster -stage: fragment - -[target] -name: shadow_amount -type: float - -#endif - -#ifdef VERTEX_SHADER -void main() { - interpolator.uv = uv; - gl_Position = vec4(position, 1.0, 1.0); -} -#endif - -#ifdef FRAGMENT_SHADER - -vec2 poissonDisk[16] = vec2[]( - vec2(-0.94201624, -0.39906216), - vec2(0.94558609, -0.76890725), - vec2(-0.094184101, -0.92938870), - vec2(0.34495938, 0.29387760), - vec2(-0.91588581, 0.45771432), - vec2(-0.81544232, -0.87912464), - vec2(-0.38277543, 0.27676845), - vec2(0.97484398, 0.75648379), - vec2(0.44323325, -0.97511554), - vec2(0.53742981, -0.47373420), - vec2(-0.26496911, -0.41893023), - vec2(0.79197514, 0.19090188), - vec2(-0.24188840, 0.99706507), - vec2(-0.81409955, 0.91437590), - vec2(0.19984126, 0.78641367), - vec2(0.14383161, -0.14100790) -); - -float random(vec3 seed, int i){ - vec4 seed4 = vec4(seed,i); - float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); - return fract(sin(dot_product) * 43758.5453); -} - -float get_shadow(int id, vec3 wpos) { - const int taps = 4; - const float w = 1.0 / float(taps); - const float m = 1.0 / 1000.0; - int i; - float d = 0.0f; - Caster caster = casters[id]; - vec4 surf = caster.projection * vec4(wpos, 1.0); - surf /= surf.w; - surf.xy = surf.xy * 0.5 + 0.5; - surf.z -= 0.005; - for (i = 0; i < taps; i++) { - int index = int(16.0 * random(floor(wpos.xyz * 1000.0), i + globals.frame)) % 16; - vec2 coord = surf.xy + poissonDisk[index] * m; - vec4 vec = vec4(coord, float(id), surf.z); - d += texture(shadowmaps, vec).r * w; - } - return d; -} - -vec3 get_world_pos(float depth, vec2 uv) { - float z = depth; - vec4 clip = vec4(uv * 2.0 - 1.0, z, 1.0); - vec4 view = config.inv_proj * clip; - view /= view.w; - return (config.inv_view * view).xyz; -} - -void main() { - vec2 uv = interpolator.uv; - float d = texture(depthmap, uv).r; - vec3 wpos = get_world_pos(d, uv); - float prev = texture(previous, uv).r; - float current = get_shadow(caster_config.index, wpos); - shadow_amount = (prev + current) * 0.5; -} -#endif diff --git a/lighting.cpp b/lighting.cpp index 4f1fdfb..b140611 100644 --- a/lighting.cpp +++ b/lighting.cpp @@ -1,6 +1,7 @@ #include "lighting.hpp" #include "model.hpp" #include "renderer.hpp" +#include "scene.hpp" #include "world.hpp" extern "C" { @@ -10,17 +11,19 @@ extern "C" { /* needs to match surface shader */ struct GPU_Light { - v3f dir; + v3f pos; float brightness; v3f colour; int caster_id; + float range; + int pad[3]; }; struct GPU_Caster { m4f projection; }; -void Lighting::init(Device* dev, int w, int h) { +void Lighting::init(Device* dev) { int i; Sampler_State ss{}; lights.init( @@ -46,9 +49,6 @@ void Lighting::init(Device* dev, int w, int h) { max_shadows, 0 ); - zero(ss_shadows, sizeof ss_shadows); - zero(ss_shadow_slices, sizeof ss_shadow_slices); - recreate(dev, w, h); for (i = 0; i < max_shadows; i++) { cameras[i] = 0; shadow_slices[i] = dev->alias_texture( @@ -88,50 +88,6 @@ void Lighting::destroy(Device* dev, Renderer& r) { r.destroy_camera(cameras[i]); } dev->destroy_texture(shadows); - destroy_ss(dev); -} - -void Lighting::destroy_ss(Device* dev) { - int i, j; - for (i = 0; i < 2; i++) { - for (j = 0; j < max_shadows; j++) - if (ss_shadow_slices[i][j]) - dev->destroy_texture(ss_shadow_slices[i][j]); - if (ss_shadows[i]) - dev->destroy_texture(ss_shadows[i]); - } -} - -void Lighting::recreate(Device* dev, int w, int h) { - int i, j; - destroy_ss(dev); - for (i = 0; i < 2; i++) { - ss_shadows[i] = dev->create_texture( - "Shadow accumulation buffer", - texture_format_r8i, - Texture_Flags::sampleable | Texture_Flags::colour_target, - w, - h, - 1, - 1, - max_shadows, - 0 - ); - for (j = 0; j < max_shadows; j++) - ss_shadow_slices[i][j] = dev->alias_texture( - ss_shadows[i], - "Shadow accumulation buffer slice", - texture_format_r8i, - Texture_Flags::colour_target, - shadow_res, - shadow_res, - 1, - 1, - 1, - 0, - j - ); - } } void Lighting::write_bufs( @@ -144,16 +100,17 @@ void Lighting::write_bufs( GPU_Light* ldst = (GPU_Light*)lptr; GPU_Caster* cdst = (GPU_Caster*)cptr; int count = 0, ccount = 0; + sun_range[0] = count; for (auto v : w.view<Sun_Light>()) { GPU_Light gl; Sun_Light& l = v.get<Sun_Light>(); if (count >= max_lights) { print_war("Over light limit.\n"); - return; + goto cancel; } gl.brightness = l.brightness; gl.colour = l.colour; - gl.dir = l.dir; + gl.pos = l.dir; if (l.caster && ccount < max_shadows) { int cid = ccount++; GPU_Caster& c = cdst[cid]; @@ -169,6 +126,29 @@ void Lighting::write_bufs( gl.caster_id = -1; ldst[count++] = gl; } + sun_range[1] = count; + point_range[0] = count; + for (auto v : w.view<Transform, Point_Light>()) { + GPU_Light gl; + Transform& t = v.get<Transform>(); + Point_Light& l = v.get<Point_Light>(); + if (count >= max_lights) { + print_war("Over light limit.\n"); + goto cancel; + } + gl.brightness = l.brightness; + gl.colour = l.colour; + gl.pos = v3f( + t.mat.m[3][0], + t.mat.m[3][1], + t.mat.m[3][2] + ); + gl.caster_id = -1; + gl.range = l.range; + ldst[count++] = gl; + } + point_range[1] = count; +cancel: light_count = count; caster_count = ccount; } diff --git a/lighting.hpp b/lighting.hpp index 0d331d7..7c57bab 100644 --- a/lighting.hpp +++ b/lighting.hpp @@ -19,14 +19,12 @@ struct Lighting { Staged_Buffer casters; Texture_Id shadows; Texture_Id shadow_slices[max_shadows]; - Texture_Id ss_shadows[2]; - Texture_Id ss_shadow_slices[2][max_shadows]; Sampler_Id shadow_sampler; Camera_Id cameras[max_shadows]; + int sun_range[2]; + int point_range[2]; int light_count, caster_count; - void init(Device* dev, int w, int h); - void destroy_ss(Device* dev); - void recreate(Device* dev, int w, int h); + void init(Device* dev); void destroy(Device* dev, Renderer& r); void update( Device* dev, @@ -54,4 +52,8 @@ struct Sun_Light : Light { v3f dir; }; +struct Point_Light : Light { + float range; +}; + #endif @@ -32,20 +32,26 @@ void Material::use( Sampler_Id sampler, Shader& shader ) { - auto bind = [&shader, &pb, sampler]( + int opt = 0; + auto bind = [&shader, &pb, &opt, sampler]( const char* name, + const char* optname, Texture_Id t ) { int loc = shader.descriptor_binding(name); if (loc >= 0) { - pb.texture(loc, t, sampler); + if (t) { + pb.texture(loc, t, sampler); + opt |= shader.opt_mask(shader_type_fragment, optname); + } } }; - bind("albedo", tex.albedo); - bind("ao", tex.ao); - bind("metal", tex.metal); - bind("rough", tex.rough); - bind("normal", tex.normal); + bind("albedo", "albedomap", tex.albedo); + bind("ao", "aomap", tex.ao); + bind("metal", "metalmap", tex.metal); + bind("rough", "roughmap", tex.rough); + bind("normal", "normalmap", tex.normal); + pb.option(shader_type_fragment, opt); } void Model_Loader::init(Device* device, Asset_Arena* shader_arena) { @@ -214,10 +220,8 @@ void Model::update_transforms() { } void Material_Loader::init( - Asset_Arena* texture_arena, - Texture_Id dt + Asset_Arena* texture_arena ) { - default_tex = dt; textures = texture_arena; } @@ -235,11 +239,11 @@ Asset* Material_Loader::load( return r; }; auto read_tex = [&](int len) { - if (!len) return default_tex; + if (!len) return Texture_Id(0); const char* name = read_name(len); auto t = (Texture*)textures->load(name); if (!t) - return default_tex; + return Texture_Id(0); return t->id; }; (void)filename; @@ -308,6 +312,7 @@ void Model_Instance::update() { ); bound.encompass(bounds[i]); } + centre = bound.min + bound.max * 0.5f; } void Model_Instance::update_cbuffers(Device* dev) { @@ -384,11 +389,11 @@ void Model_Instance::render( res.vp ); } else { - pb.depth(true, false, Depth_Mode::equal); + pb.depth(true, true, Depth_Mode::less); pb.shader(mesh.shader); pb.sbuffer(mesh.light_binding, lighting->lights.gpuonly); pb.sbuffer(mesh.casters_binding, lighting->casters.gpuonly); - pb.texture(mesh.shadowmaps_binding, res.shadows, res.sampler); + pb.texture(mesh.shadowmaps_binding, lighting->shadows, lighting->shadow_sampler); mesh.material->use(pb, res.sampler, dev->get_shader(mesh.shader)); pb.cbuffer( mesh.mat_binding, @@ -85,7 +85,7 @@ struct Model_Loader : public Asset_Loader { struct Material_Loader : public Asset_Loader { Asset_Arena* textures; Texture_Id default_tex; - void init(Asset_Arena* texture_arena, Texture_Id dt); + void init(Asset_Arena* texture_arena); Asset* load( Arena* a, Arena* s, @@ -97,7 +97,6 @@ struct Material_Loader : public Asset_Loader { struct Model_Resources { Texture_Id env_cubemap; - Texture_Id shadows; Sampler_Id sampler; Buffer_Id vp; Buffer_Id globals; @@ -111,6 +110,7 @@ struct Model_Instance { Model* m; AABB* bounds; AABB bound; + v3f centre; void init(Device* dev, Heap* h, Model* model); void destroy(Device* dev, Heap* h); diff --git a/pipeline.cpp b/pipeline.cpp index 0636a1c..4d34ebd 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -23,6 +23,7 @@ Pipeline_Builder& Pipeline_Builder::rp_target(Texture_Id id, Colour clear) { Render_Pass::Target t{ .id = id, .fmt = texture.fmt, + .samples = texture.samples, .mode = Clear_Mode::clear, .clear = { .colour = clear } }; @@ -37,6 +38,7 @@ Pipeline_Builder& Pipeline_Builder::rp_target(Texture_Id id, Clear_Mode clear) { Render_Pass::Target t{ .id = id, .fmt = texture.fmt, + .samples = texture.samples, .mode = clear, .clear = { .depth = 0.0f } }; @@ -51,6 +53,7 @@ Pipeline_Builder& Pipeline_Builder::rp_depth_target(Texture_Id id, float clear) Render_Pass::Target t{ .id = id, .fmt = texture.fmt, + .samples = texture.samples, .mode = Clear_Mode::clear, .clear = { .depth = clear } }; @@ -63,6 +66,7 @@ Pipeline_Builder& Pipeline_Builder::rp_depth_target(Texture_Id id, Clear_Mode mo Render_Pass::Target t{ .id = id, .fmt = texture.fmt, + .samples = texture.samples, .mode = mode, .clear = { .depth = 0.0f } }; @@ -73,7 +77,7 @@ Pipeline_Builder& Pipeline_Builder::rp_depth_target(Texture_Id id, Clear_Mode mo void Pipeline_Builder::validate_rp() { int i, c = pass->colour_count; - int w, h; + int w, h, s; assert(c || pass->depth.id); if (c) { Texture& tex = dev->get_texture(pass->colours[0].id); @@ -81,7 +85,8 @@ void Pipeline_Builder::validate_rp() { assert(pass->colours[0].fmt == tex.fmt); w = tex.w; h = tex.h; - assert(w && h); + s = tex.samples; + assert(w && h && s); } for (i = 1; i < c; i++) { Texture& tex = dev->get_texture(pass->colours[i].id); @@ -89,6 +94,7 @@ void Pipeline_Builder::validate_rp() { assert(pass->colours[i].fmt == tex.fmt); assert(tex.w == w); assert(tex.h == h); + assert(tex.samples == s); } if (pass->depth.id) { Texture& d = dev->get_texture(pass->depth.id); @@ -101,6 +107,7 @@ void Pipeline_Builder::validate_rp() { if (c) { assert(d.w == w); assert(d.h == h); + assert(d.samples == s); } } } @@ -247,6 +254,14 @@ Pipeline_Builder& Pipeline_Builder::shader(Shader_Id s) { return *this; } +Pipeline_Builder& Pipeline_Builder::option( + Shader_Type type, + int mask +) { + pip->shader_masks[type] = mask; + return *this; +} + Pipeline_Builder& Pipeline_Builder::texture( int binding, Texture_Id t, @@ -314,6 +329,7 @@ Pipeline& Pipeline_Builder::build() { } void Pipeline::hash() { + int i; #define h(n, v) \ n = fnv1a64_2(n, (uint8_t*)&v, sizeof v) pipeline_hash = fnv1a64(0, 0); @@ -340,8 +356,10 @@ void Pipeline::hash() { h(pipeline_hash, blend_src_alpha); h(pipeline_hash, blend_dst_alpha); h(pipeline_hash, cull_mode); + for (i = 0; i < shader_type_count; i++) + h(pipeline_hash, shader_masks[i]); { - int i, e = descriptor_count; + int e = descriptor_count; descriptor_resource_hash = fnv1a64(0, 0); for (i = 0; i < e; i++) { Descriptor* d = &descriptors[i]; diff --git a/qstd/memory.c b/qstd/memory.c index 87618f9..6db9ad9 100644 --- a/qstd/memory.c +++ b/qstd/memory.c @@ -86,7 +86,7 @@ void arena_push(Arena* a) { void arena_pop(Arena* a) { assert(a->last_push); - a->ptr -= a->last_push; + a->ptr = a->last_push; a->last_push = *(int*)&a->buf[a->ptr]; } diff --git a/renderer.cpp b/renderer.cpp index ffc0a3c..6208d3c 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -5,27 +5,19 @@ extern "C" { #include "memory.h" } -#include <string.h> +#include <algorithm> struct VP_Cbuffer { m4f view_projection; }; -struct TS_Cbuffer { - m4f inv_view; - m4f inv_proj; - m4f prev_vp; -}; - -struct TS_Caster_Config { - int index; - char pad[60]; -}; - struct Global_Cbuffer { v3f camera_pos; int light_count; int frame; + int pad; + int sun_irange[2]; + int point_irange[2]; }; void init_drawlist( @@ -49,22 +41,7 @@ void init_drawlist( ); } -void Renderer::make_ts_sampler(Device* d) { - Sampler_State s{}; - s.min = Filter_Mode::point; - s.mag = Filter_Mode::point; - s.mip = Filter_Mode::point; - s.address_u = Address_Mode::border; - s.address_v = Address_Mode::border; - s.border[0] = 0.0f; - ts_sampler = d->create_sampler("temporal shadow sampler", s); -} - -void Renderer::init( - Arena* arena, - Device* d, - Asset_Arena& assets -) { +void Renderer::init(Arena* arena, Device* d) { int i; auto id = [&](int did, int cap) { init_drawlist(&drawlists[did], d, arena, cap); @@ -80,37 +57,6 @@ void Renderer::init( sizeof(Global_Cbuffer), Buffer_Flags::constant_buffer ); - ts_config.init( - d, - "Temporal shadow cbuffer", - sizeof(TS_Cbuffer), - Buffer_Flags::constant_buffer - ); - ts_config2.init( - d, - "Temporal shadow casters", - sizeof(TS_Caster_Config) * Lighting::max_shadows, - Buffer_Flags::constant_buffer - ); - quad.init(d); - ts_shader = (Shader*)assets.load("ts.csh"); - ts_shadowmap_binding = ts_shader->descriptor_binding("shadowmaps"); - ts_depthmap_binding = ts_shader->descriptor_binding("depthmap"); - ts_prev_binding = ts_shader->descriptor_binding("previous"); - ts_vert_binding = ts_shader->binding_index("verts"); - ts_config_binding = ts_shader->descriptor_binding("config"); - ts_caster_config_binding = ts_shader->descriptor_binding("caster_config"); - ts_casters_binding = ts_shader->descriptor_binding("casters"); - ts_globals_binding = ts_shader->descriptor_binding("globals"); - assert(ts_shadowmap_binding >= 0); - assert(ts_depthmap_binding >= 0); - assert(ts_prev_binding >= 0); - assert(ts_vert_binding >= 0); - assert(ts_config_binding >= 0); - assert(ts_caster_config_binding >= 0); - assert(ts_casters_binding >= 0); - assert(ts_globals_binding >= 0); - make_ts_sampler(d); frame = 0; } @@ -119,10 +65,6 @@ void Renderer::destroy(Device* d) { for (i = 0; i < drawlist_count; i++) drawlists[i].vp.destroy(d); globals.destroy(d); - ts_config.destroy(d); - ts_config2.destroy(d); - quad.destroy(d); - d->destroy_sampler(ts_sampler); } void Renderer::set_camera(Camera_Id cam, int drawlist) { @@ -144,7 +86,6 @@ void Drawlist::render( vpc->view_projection = cam.get_proj() * cam.get_view(); vp.unmap(dev); vp.update(dev->get_ctx()); - res.shadows = l->ss_shadows[r.frame & 1]; res.sampler = r.clamped_linear; res.env_cubemap = r.env_cubemap; res.vp = vp.gpuonly; @@ -155,6 +96,21 @@ void Drawlist::render( } } +void Drawlist::sort(const Renderer& r) { + const Camera& cam = r.get_camera(camera); + v3f far = cam.position + cam.far * cam.forward; + /* ideally want to sort the meshes within each model as + * well but as long as the models are small it wont matter + * too much... */ + std::sort( + models, + models + count, + [&far](const Model_Instance* a, const Model_Instance* b) { + return v3f::dot(a->centre, far) < v3f::dot(b->centre, far); + } + ); +} + void Renderer::update_globals( const Lighting* l, Device* d, @@ -166,74 +122,19 @@ void Renderer::update_globals( cb->camera_pos = cp; cb->frame = frame; cb->light_count = l->light_count; + cb->sun_irange[0] = l->sun_range[0]; + cb->sun_irange[1] = l->sun_range[1]; + cb->point_irange[0] = l->point_range[0]; + cb->point_irange[1] = l->point_range[1]; globals.unmap(d); globals.update(ctx); } -void Renderer::temporal_shadows( - Device* dev, - Context& ctx, - const Lighting* l, - Pipeline_Builder& pb -) { - int i, c = l->caster_count; - Camera& cam = get_camera(drawlists[FORWARD].camera); - TS_Cbuffer* cbuf = (TS_Cbuffer*)ts_config.map(dev); - TS_Caster_Config* casters = (TS_Caster_Config*)ts_config.map(dev); - for (i = 0; i < c; i++) { - casters[i].index = i; - } - cbuf->inv_view = cam.get_view().inverse(); - cbuf->inv_proj = cam.get_proj().inverse(); - ts_config.unmap(dev); - ts_config2.unmap(dev); - ctx.debug_push("temporal shadows"); - ts_config.update(ctx); - ts_config2.update(ctx); - for (i = 0; i < c; i++) { - int ind = frame & 1; - auto& pass = pb - .begin_rp() - .rp_target(l->ss_shadow_slices[ind][i], Clear_Mode::discard) - .build_rp(); - auto& pip = pb - .begin() - .shader(ts_shader->id) - .vertex_format(ts_shader->vf) - .texture( - ts_shadowmap_binding, - l->shadows, - l->shadow_sampler) - .texture( - ts_prev_binding, - l->ss_shadow_slices[!ind][i], - ts_sampler) - .texture( - ts_depthmap_binding, - dev->get_depth_target(), - ts_sampler) - .cbuffer(ts_config_binding, ts_config.gpuonly) - .cbuffer( - ts_caster_config_binding, - ts_config2.gpuonly, - i * sizeof(TS_Caster_Config), - sizeof(TS_Caster_Config)) - .cbuffer(ts_globals_binding, globals.gpuonly) - .sbuffer(ts_casters_binding, l->casters.gpuonly) - .build(); - quad.render( - ctx, - pip, - pass, - ts_vert_binding - ); - } - ctx.debug_pop(); -} - void Renderer::render( Device* dev, - Arena* a, Texture_Id hdr_target, + Arena* a, + Texture_Id hdr_target, + Texture_Id depth_target, const Lighting* l ) { int i, j; @@ -249,7 +150,7 @@ void Renderer::render( Render_Pass& forward_pass = pb .begin_rp() .rp_target(hdr_target, Clear_Mode::restore) - .rp_depth_target(dev->get_depth_target(), Clear_Mode::restore) + .rp_depth_target(depth_target, 1.0f) .build_rp(); Render_Pass* shadow_passes[Lighting::max_shadows]; for (i = 0; i < Lighting::max_shadows; i++) { @@ -259,6 +160,8 @@ void Renderer::render( .build_rp(); } + drawlists[FORWARD].sort(*this); + ctx.debug_push("depth prepass"); drawlists[FORWARD].render(*this, dev, a, l, depth_prepass, 0); ctx.debug_pop(); @@ -279,8 +182,6 @@ void Renderer::render( } ctx.debug_pop(); - temporal_shadows(dev, ctx, l, pb); - ctx.debug_push("forward"); drawlists[FORWARD].render(*this, dev, a, l, forward_pass, 0); ctx.debug_pop(); @@ -337,54 +238,3 @@ void Renderer::setcam(int did, Camera_Id cam) { assert(cam.index); drawlists[did].camera = cam; } - -void Fullscreen_Quad::init(Device* d) { - float verts[] = { - -1.0f, -1.0f, 0.0f, 0.0f, - -1.0f, 3.0f, 0.0f, 2.0f, - 3.0f, -1.0f, 2.0f, 0.0f - }; - Buffer_Id stage; - void* mem; - stage = d->create_buffer( - "sky vb stage", - sizeof verts, - Buffer_Flags::cpu_readwrite | - Buffer_Flags::copy_src - ); - mem = d->map_buffer(stage, 0, sizeof verts); - memcpy(mem, verts, sizeof verts); - d->unmap_buffer(stage); - vb = d->create_buffer( - "fullscreen quad", - sizeof verts, - Buffer_Flags::copy_dst | - Buffer_Flags::vertex_buffer - ); - Context& ctx = d->acquire(); - ctx.copy(vb, stage); - d->submit(ctx); - d->destroy_bufferi(stage); -} - -void Fullscreen_Quad::destroy(Device* d) { - d->destroy_buffer(vb); -} - -void Fullscreen_Quad::render( - Context& ctx, - Pipeline& pip, - Render_Pass& pass, - int bind -) { - Vertex_Buffer_Binding vbb[] = {{ - .id = vb, - .offset = 0, - .target = bind - }, {}}; - Draw draw{}; - draw.verts = vbb; - draw.vertex_count = 3; - draw.instance_count = 1; - ctx.submit(draw, pip, pass); -} diff --git a/renderer.hpp b/renderer.hpp index 46f5331..a76afa9 100644 --- a/renderer.hpp +++ b/renderer.hpp @@ -12,7 +12,6 @@ enum { SHADOW_MAP_END = SHADOW_MAP_START + Lighting::max_shadows, drawlist_count = SHADOW_MAP_END }; -struct Asset_Arena; struct Model_Instance; struct Renderer; @@ -31,19 +30,7 @@ struct Drawlist { Render_Pass& pass, void (*overrider)(Pipeline_Builder&) ); -}; - -struct Fullscreen_Quad { - Buffer_Id vb; - - void init(Device* d); - void destroy(Device* d); - void render( - Context& ctx, - Pipeline& pip, - Render_Pass& pass, - int bind - ); + void sort(const Renderer& r); }; struct Renderer { @@ -51,41 +38,22 @@ struct Renderer { Hash_Map<Camera_Id, Camera, max_cameras> cameras; Drawlist drawlists[drawlist_count]; Staged_Buffer globals; - Fullscreen_Quad quad; int camera_count; int frame; - Shader* ts_shader; - int ts_shadowmap_binding; - int ts_depthmap_binding; - int ts_prev_binding; - int ts_vert_binding; - int ts_config_binding; - int ts_caster_config_binding; - int ts_casters_binding; - int ts_globals_binding; - Sampler_Id ts_sampler; - Staged_Buffer ts_config, ts_config2; - Sampler_Id clamped_linear; Texture_Id env_cubemap; - void init(Arena* arena, Device* d, Asset_Arena& assets); + void init(Arena* arena, Device* d); void destroy(Device* d); void set_camera(Camera_Id cam, int drawlist); - void make_ts_sampler(Device* d); void render( Device* dev, Arena* a, Texture_Id hdr_target, + Texture_Id depth_target, const Lighting* l ); - void temporal_shadows( - Device* dev, - Context& ctx, - const Lighting* l, - Pipeline_Builder& pb - ); void add_model(int drawlist, Model_Instance* m); void rem_model(int drawlist, Model_Instance* m); @@ -168,14 +168,16 @@ struct Desc { std::pair<int, int> size() const { switch (type) { case svariable_type_float: - return { 4, 4 }; case svariable_type_int: return { 4, 4 }; case svariable_type_vec2: + case svariable_type_ivec2: return { 8, 8 }; case svariable_type_vec3: + case svariable_type_ivec3: return { 12, 16 }; case svariable_type_vec4: + case svariable_type_ivec4: return { 16, 16 }; case svariable_type_mat2: return { 16, 16 }; @@ -223,6 +225,10 @@ struct Desc { int stage; std::string strct; }; + struct Option { + int stage; + int mask; + }; int type; std::vector<Binding> bindings; std::vector<Variable> trgts; @@ -231,8 +237,10 @@ struct Desc { std::unordered_map<std::string, Texture> textures; std::unordered_map<std::string, CBuffer> cbuffers; std::unordered_map<std::string, SBuffer> sbuffers; + std::unordered_map<std::string, Option> options; std::vector<Descriptor> descriptors; std::string entrypoints[shader_type_count]; + int copts[shader_type_count]; void read_var(Variable& d, cfg_Object* desc) { const char* sname = find_string_default(desc, "name", 0); if (!sname) { @@ -395,10 +403,40 @@ struct Desc { buf.stage |= 1 << stage_from_string(sstage); } + void read_opt(cfg_Object* desc) { + const char* sname = find_string_default(desc, "name", 0); + if (!sname) { + print_err("%s must have a name.\n", desc->name); + pbreak(1001); + } + const char* sstage = find_string_default(desc, "stage", 0); + if (!sstage) { + print_err("%s must define a stage.\n", sname); + pbreak(1002); + } + std::string n(sname); + if (n.size() > 23) { + print_err("Option name %s is too long (max 23 chars).\n", sname); + pbreak(1003); + } + if (!options.contains(n)) { + Option& o = options[n]; + o.mask = 0; + o.stage = 0; + } + int stage = stage_from_string(sstage); + Option& opt = options[n]; + opt.stage |= 1 << stage; + opt.mask = copts[stage]; + copts[stage] <<= 1; + } + void build(cfg_Object* desc) { int i; Binding* cur_binding = 0; type = get_program_type(desc); + for (i = 0; i < shader_type_count; i++) + copts[i] = 1; if (type != sprogram_type_graphics) { assert(0); /* todo */ return; @@ -448,6 +486,8 @@ struct Desc { } else if (!strcmp(desc->name, "struct")) { desc = read_struct(desc); continue; + } else if (!strcmp(desc->name, "option")) { + read_opt(desc); } desc = desc->next; } @@ -626,22 +666,35 @@ std::vector<uint32_t> compile_shader( const char* src, const char* define, int stage, + int opt, EShLanguage lang ) { std::string vars = d.build_vs(); - const char* srcs[] = { + std::vector<const char*> srcs = { glsl_version_s, "\n", "#define ", define, "\n", builtin_src, - presrc, - src + presrc }; - const char* src_names[] = { + std::vector<const char*> src_names = { sname, sname, sname, sname, sname, - sname, sname, sname + sname, sname }; - static_assert(sizeof srcs == sizeof src_names); + for (const auto& p : d.options) { + const auto& o = p.second; + std::string def = "#define OPT_" + p.first + " "; + if (opt & o.mask) + def += "1\n"; + else + def += "0\n"; + /* memory leak lol */ + srcs.push_back(strdup(def.c_str())); + src_names.push_back(sname); + } + srcs.push_back(src); + src_names.push_back(sname); + assert(src_names.size() == srcs.size()); glslang::TShader shader(lang); glslang::TProgram program; glslang::TIntermediate* ir; @@ -661,10 +714,10 @@ std::vector<uint32_t> compile_shader( #endif EShMessages msg = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); shader.setStringsWithLengthsAndNames( - srcs, + &srcs[0], 0, - src_names, - sizeof srcs / sizeof *srcs + &src_names[0], + src_names.size() ); shader.setEnvClient(glslang::EShClientVulkan, client_version); shader.setEnvTarget(glslang::EShTargetSpv, target_version); @@ -734,10 +787,23 @@ void configure( } } +struct H_Variant { + int o = -1, s; + int mask; + int pad = 0; +}; +struct H_Option { + char name[24] = { 0 }; + int mask; + int stage; +}; +static_assert(sizeof(H_Variant) == 16); +using Variant_Map = std::unordered_map<int, std::vector<uint32_t>>; + void compile_shaders( const char* sname, const char* fname, - std::vector<uint32_t>* spv, + Variant_Map* spv, const char* src, Desc& d ) { @@ -745,19 +811,40 @@ void compile_shaders( EShLanguage lang; int i; for (i = 0; i < shader_type_count; i++) { + int sm = 1 << i; if (!d.entrypoints[i].empty()) { std::string ps; + int opt = 0, j; configure(d, i, define, lang, ps); - spv[i] = compile_shader( - d, - sname, - fname, - ps.c_str(), - src, - define, - i, - lang - ); + for (const auto& p : d.options) { + if (~p.second.stage & sm) continue; + opt |= p.second.mask; + } + for (j = 0; j <= opt; j++) { + spv[i][j] = compile_shader( + d, + sname, + fname, + ps.c_str(), + src, + define, + i, + j, + lang + ); + } + if (!opt) + spv[i][0] = compile_shader( + d, + sname, + fname, + ps.c_str(), + src, + define, + i, + 0, + lang + ); } } } @@ -765,10 +852,14 @@ void compile_shaders( void write_csh( const char* fname, const Desc& d, - const std::vector<uint32_t>* stages + Variant_Map* stages ) { - int hsize = 20, i, coff; + int hsize = 24, i, coff; FILE* f = fopen(fname, "wb"); + H_Variant* variants[shader_type_count]; + H_Option* hopts = 0; + int vc[shader_type_count]; + std::vector<const std::vector<uint32_t>*> order; if (!f) { print_err("Failed to open %s\n", fname); pbreak(500); @@ -779,6 +870,7 @@ void write_csh( c = d.bindings.size(); fwrite(&c, 4, 1, f); c = d.trgts.size(); fwrite(&c, 4, 1, f); c = d.descriptors.size(); fwrite(&c, 4, 1, f); + c = d.options.size(); fwrite(&c, 4, 1, f); for (const auto& b : d.bindings) { char buf[24]; int count = b.attrs.size(); @@ -814,36 +906,79 @@ void write_csh( fwrite(&d.stage, 4, 1, f); hsize += 32; } - hsize += shader_type_count * 32; - for (i = 0, coff = 0; i < shader_type_count; i++) { - int o = 0; - char buf[24]; - memset(buf, 0, sizeof buf); - if (d.entrypoints[i].empty()) { - fwrite(&o, 4, 1, f); - fwrite(&o, 4, 1, f); - } else { - int size = stages[i].size() * sizeof(uint32_t); - strcpy(buf, d.entrypoints[i].c_str()); - o = hsize + coff; - fwrite(&o, 4, 1, f); - fwrite(&size, 4, 1, f); - coff += size; + if (d.options.size()) + hopts = new H_Option[d.options.size()]; + for (const auto& p : d.options) { + const auto& name = p.first; + const auto& o = p.second; + int count = d.options.size(); + int j, bucket = (int)( + hash_string(name.c_str()) % + count + ); + for (j = 0; j < count; j++) { + auto& ho = hopts[j]; + if (!ho.name[0]) { + strcpy(ho.name, name.c_str()); + ho.mask = o.mask; + ho.stage = o.stage; + goto oklmao; + } + bucket = (bucket + 1) % count; } - fwrite(buf, 1, sizeof buf, f); + assert(0); + oklmao: + hsize += 32; } - for (i = 0; i < shader_type_count; i++) - if (!d.entrypoints[i].empty()) { - auto& stage = stages[i]; - fwrite(&stage[0], sizeof(uint32_t), stage.size(), f); + fwrite(hopts, sizeof *hopts, d.options.size(), f); + delete[] hopts; + for (i = 0; i < shader_type_count; i++) { + vc[i] = stages[i].size(); + hsize += vc[i] * 16 + 4; + variants[i] = new H_Variant[vc[i]]; + } + for (i = 0, coff = 0; i < shader_type_count; i++) { + fwrite(&vc[i], 4, 1, f); + for (const auto& p : stages[i]) { + int mask = p.first, j; + int bucket = (int)( + fnv1a64((uint8_t*)&mask, sizeof mask) % + vc[i] + ); + for (j = 0; j < vc[i]; j++) { + H_Variant& v = variants[i][bucket]; + if (v.o == -1) { + auto& arr = p.second; + v.o = coff + hsize; + v.s = arr.size() * sizeof(uint32_t); + v.mask = mask; + coff += v.s; + order.push_back(&arr); + goto done; + } + bucket = (bucket + 1) % vc[i]; + } + assert(0); + done:; } + fwrite(variants[i], sizeof(H_Variant), vc[i], f); + delete[] variants[i]; + } + for (auto bytecode : order) { + fwrite( + &(*bytecode)[0], + sizeof(uint32_t), + bytecode->size(), + f + ); + } } int main(int argc, const char** argv) { char* src; size_t src_size; std::string desc_src; - std::vector<uint32_t> spv[shader_type_count]; + Variant_Map spv[shader_type_count]; cfg_Object* cdesc; void* dp_mem; Desc desc; diff --git a/sc/sh_enums.h b/sc/sh_enums.h index e9a33b8..6cf9495 100644 --- a/sc/sh_enums.h +++ b/sc/sh_enums.h @@ -28,6 +28,9 @@ sprogram_type_xmacro() x(vec2) \ x(vec3) \ x(vec4) \ + x(ivec2) \ + x(ivec3) \ + x(ivec4) \ x(mat2) \ x(mat3) \ x(mat4) diff --git a/sc/sh_helpers.h b/sc/sh_helpers.h index fa503dc..1b81bd6 100644 --- a/sc/sh_helpers.h +++ b/sc/sh_helpers.h @@ -5,6 +5,9 @@ int svariable_type_size(SVariable_Type type) { case svariable_type_vec2: return 8; case svariable_type_vec3: return 12; case svariable_type_vec4: return 16; + case svariable_type_ivec2: return 8; + case svariable_type_ivec3: return 12; + case svariable_type_ivec4: return 16; case svariable_type_mat2: return 16; case svariable_type_mat3: return 36; case svariable_type_mat4: return 64; @@ -19,8 +19,8 @@ todo list - [x] render a sky box - [x] PBR + IBL - [x] Tonemapping - - [ ] shadows - - [ ] MSAA + - [x] shadows + - [x] MSAA - [ ] GI - [ ] clustering - [ ] bloom @@ -430,6 +430,12 @@ enum { context_state_init = 1 << 1 }; + +struct Shader_Module { + int mask; + VkShaderModule mod; +}; + struct Shader_Vk : public Shader, public Late_Terminated { struct Attribute { char name[28]; @@ -464,23 +470,31 @@ struct Shader_Vk : public Shader, public Late_Terminated { int stage; }; + struct Option { + char name[24]; + int mask; + int stage; + }; + SProgram_Type type; - VkShaderModule modules[shader_type_count]; - char entrypoints[shader_type_count][24]; Vertex_Format vfd; Desc* descs; - int desc_count; + Shader_Module* modules[shader_type_count]; + Option* options; + int desc_count, opt_count; + int module_count[shader_type_count]; - bool init(Device_Vk* dev, Pack_File* f); - bool init_module( + bool init(Device_Vk* dev, Arena* a, Pack_File* f); + VkShaderModule make_module( Device_Vk* dev, - int stage, char* buf, int size ); void destroy(Device_Vk* dev) override; int find_descriptor(const char* name); + int find_module(Shader_Type type, int mask); + int find_opt(Shader_Type, const char* name); static VkShaderStageFlagBits stage(Shader_Type type) { switch (type) { @@ -561,7 +575,8 @@ struct Texture_Vk : public Texture, public Late_Terminated { int array_size, int start_mip, int start_array, - bool alias + bool alias, + int samples ); void destroy(Device_Vk*) override; void set_name(Device_Vk* dev, const char* name); @@ -732,7 +747,8 @@ struct Pipeline_Vk { Arena& scope, Device_Vk* dev, VkGraphicsPipelineCreateInfo& info, - const Pipeline& desc + const Pipeline& desc, + const Render_Pass& rpo ); void init_depthstencil( Arena& scope, @@ -934,6 +950,7 @@ struct Device_Vk : public Device { VkPhysicalDevice phys_dev; VkSurfaceKHR surf; uint32_t backbuffer_index; + VkSampleCountFlagBits max_samples; Texture_Id backbuffer_id; Swap_Cap swap_cap; VkPhysicalDeviceMemoryProperties mem_props; @@ -1021,6 +1038,9 @@ struct Device_Vk : public Device { void create_terminators(); void create_depth(int w, int h); + VkSampleCountFlagBits get_max_samples(); + VkSampleCountFlagBits get_samples(int); + int find_memory_type( uint32_t filter, VkMemoryPropertyFlags flags @@ -1114,6 +1134,37 @@ void Device_Vk::init_validation() { #endif +VkSampleCountFlagBits Device_Vk::get_max_samples() { + VkPhysicalDeviceProperties p; + VkSampleCountFlagBits + i = VK_SAMPLE_COUNT_64_BIT, + e = VK_SAMPLE_COUNT_1_BIT; + VkSampleCountFlags c; + vkGetPhysicalDeviceProperties(phys_dev, &p); + c = + p.limits.framebufferColorSampleCounts & + p.limits.framebufferDepthSampleCounts; + for (; i >= e; i = (VkSampleCountFlagBits)(i >> 1)) + if (c & i) return i; + return VK_SAMPLE_COUNT_1_BIT; +} + +VkSampleCountFlagBits Device_Vk::get_samples( + int c +) { + VkSampleCountFlagBits b = VK_SAMPLE_COUNT_1_BIT; + switch (c) { + case 1: b = VK_SAMPLE_COUNT_1_BIT; break; + case 2: b = VK_SAMPLE_COUNT_2_BIT; break; + case 4: b = VK_SAMPLE_COUNT_4_BIT; break; + case 8: b = VK_SAMPLE_COUNT_8_BIT; break; + case 16: b = VK_SAMPLE_COUNT_16_BIT; break; + case 32: b = VK_SAMPLE_COUNT_32_BIT; break; + case 64: b = VK_SAMPLE_COUNT_64_BIT; break; + default: break; + } + return std::min(max_samples, b); +} bool Device_Vk::has_validation() { unsigned count, i; @@ -1352,6 +1403,7 @@ void Device_Vk::init_internal() { #endif surf = app_create_vk_surface(app, inst); create_dev(&swap_cap); + max_samples = get_max_samples(); vrama.init(this); gladLoaderLoadVulkan(inst, phys_dev, dev); vkGetDeviceQueue(dev, (uint32_t)queue_index, 0, &queue); @@ -1386,7 +1438,7 @@ void Device_Vk::create_depth(int w, int h) { destroy_texture(depth); depth = create_texture( "default depth", - texture_format_d32, + texture_format_d24s8, Texture_Flags::sampleable | Texture_Flags::depth_stencil_target, w, h, @@ -1642,7 +1694,7 @@ void Renderpass_Vk::init( auto& colour = rp.colours[i]; auto& ad = ads[index]; ad.format = get_vk_format(colour.fmt); - ad.samples = VK_SAMPLE_COUNT_1_BIT; + ad.samples = dev->get_samples(colour.samples); ad.loadOp = load_op_from_mode(colour.mode); ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; @@ -1659,9 +1711,10 @@ void Renderpass_Vk::init( if (has_depth) { int i = count++; auto& ad = ads[i]; - ad.format = get_vk_format(dev->get_texture(rp.depth.id).fmt); - ad.samples = VK_SAMPLE_COUNT_1_BIT; - ad.loadOp = load_op_from_mode(rp.depth.mode); + auto& depth = rp.depth; + ad.format = get_vk_format(dev->get_texture(depth.id).fmt); + ad.samples = dev->get_samples(depth.samples); + ad.loadOp = load_op_from_mode(depth.mode); ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; ad.stencilLoadOp = ad.loadOp; ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -1887,7 +1940,8 @@ Texture_Id Swapchain::create_image( 1, 0, 0, - true + true, + 1 ); return id; } @@ -2016,6 +2070,8 @@ void Device::present() { VkSubmitInfo si{}; VkPipelineStageFlags stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + heap_defrag(dev->heap); + // ^ this makes it >4x the speed ctx->check_end_rp(); ctx->transition( dev->get_backbuffer(), @@ -2252,6 +2308,71 @@ void Context::copy(Texture_Id dst, Buffer_Id src) { ); } +void Context::copy( + Texture_Id dst, + Buffer_Id src, + int mip, + int x, + int y, + int w, + int h +) { + Context_Vk* ctx = (Context_Vk*)this; + Device_Vk* dev = ctx->dev; + Texture_Vk& a = *(Texture_Vk*)&dev->get_texture(dst); + Buffer_Vk& b = *(Buffer_Vk*)&dev->get_buffer(src); + VkBufferImageCopy c{}; + transition(dst, Resource_State::copy_dst); + c.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + c.imageSubresource.layerCount = 1; + c.imageSubresource.mipLevel = mip; + c.imageExtent.width = w; + c.imageExtent.height = h; + c.imageExtent.depth = 1; + c.imageOffset.x = x; + c.imageOffset.y = y; + ctx->check_end_rp(); + vkCmdCopyBufferToImage( + ctx->cb, + b.buf, + a.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &c + ); +} + +void Context::resolve(Texture_Id dst, Texture_Id src) { + Context_Vk* ctx = (Context_Vk*)this; + Device_Vk* dev = ctx->dev; + Texture_Vk& d = *(Texture_Vk*)&dev->get_texture(dst); + Texture_Vk& s = *(Texture_Vk*)&dev->get_texture(src); + VkImageResolve r{}; + r.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + r.srcSubresource.layerCount = 1; + r.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + r.dstSubresource.layerCount = 1; + r.extent.width = d.w; + r.extent.height = d.h; + r.extent.depth = 1; + assert(d.w == s.w); + assert(d.h == s.h); + assert(d.d == 1 && s.d == 1); + assert(d.samples == 1 && s.samples > 1); + ctx->check_end_rp(); + transition(src, Resource_State::copy_src); + transition(dst, Resource_State::copy_dst); + vkCmdResolveImage( + ctx->cb, + s.image, + state_to_image_layout(s.state), + d.image, + state_to_image_layout(d.state), + 1, + &r + ); +} + void Context::transition(Texture_Id id, Resource_State state) { Context_Vk* ctx = (Context_Vk*)this; Device_Vk* dev = ctx->dev; @@ -2400,6 +2521,14 @@ void Context::transition(Texture_Id id, Resource_State state) { b.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dst_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } else if ( + src_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && + dst_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + ) { + b.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + b.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dst_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else { print_err("Bad resource transition.\n"); assert(0); @@ -2698,12 +2827,24 @@ void Pipeline_Vk::init_stages( zero(sis, sizeof *sis * count); for (i = 0, count = 0; i < shader_type_count; i++) { if (shader.modules[i]) { + int idx = shader.find_module( + (Shader_Type)i, + desc.shader_masks[i] + ); + VkShaderModule mod; + if (idx < 0) { + mod = shader.modules[i][0].mod; + print_war("Shader variant not found; using the default >~<\n"); + print(" ^ mask was 0x%x\n", desc.shader_masks[i]); + } else + mod = shader.modules[i][idx].mod; + assert(idx >= 0); auto& si = sis[i]; si.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; si.flags = 0; si.stage = Shader_Vk::stage((Shader_Type)i); - si.module = shader.modules[i]; - si.pName = shader.entrypoints[i]; + si.module = mod; + si.pName = "main"; count++; } } @@ -2823,7 +2964,8 @@ void Pipeline_Vk::init_msaa( Arena& scope, Device_Vk* dev, VkGraphicsPipelineCreateInfo& info, - const Pipeline& desc + const Pipeline& desc, + const Render_Pass& rpo ) { VkPipelineMultisampleStateCreateInfo& mi = *(VkPipelineMultisampleStateCreateInfo*)arena_alloc( @@ -2835,7 +2977,7 @@ void Pipeline_Vk::init_msaa( zero(&mi, sizeof mi); mi.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; mi.sampleShadingEnable = VK_FALSE; - mi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + mi.rasterizationSamples = dev->get_samples(rpo.get_samples()); info.pMultisampleState = &mi; } @@ -3030,7 +3172,7 @@ void Pipeline_Vk::init(Device_Vk* dev, const Pso_Key& key) { init_input_assembly(scope, dev, info, desc); init_viewport(scope, info, desc); init_rasterisation(scope, dev, info, desc); - init_msaa(scope, dev, info, desc); + init_msaa(scope, dev, info, desc, key.rpo.rpo); init_depthstencil(scope, dev, info, desc); init_blending(scope, dev, info, key.rpo.rpo, desc); info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; @@ -3282,9 +3424,8 @@ void Vertex_Format_Vk::optimise(const Vertex_Format_Vk* shadervf) { } } -bool Shader_Vk::init_module( +VkShaderModule Shader_Vk::make_module( Device_Vk* dev, - int stage, char* buf, int size ) { @@ -3295,8 +3436,9 @@ bool Shader_Vk::init_module( mi.codeSize = size; mi.pCode = (uint32_t*)buf; r = vkCreateShaderModule(dev->dev, &mi, &dev->ac, &m); - modules[stage] = m; - return r == VK_SUCCESS; + if (r == VK_SUCCESS) + return m; + return 0; } int Shader_Vk::Vertex_Format::find_binding(const char* name) { @@ -3408,8 +3550,11 @@ void Shader_Vk::Vertex_Format::destroy(Device_Vk* dev) { void Shader_Vk::destroy(Device_Vk* dev) { int i; for (i = 0; i < shader_type_count; i++) - if (modules[i]) - vkDestroyShaderModule(dev->dev, modules[i], &dev->ac); + if (modules[i]) { + int j, e = module_count[i]; + for (j = 0; j < e; j++) + vkDestroyShaderModule(dev->dev, modules[i][j].mod, &dev->ac); + } vfd.destroy(dev); heap_free(dev->heap, descs); dev->destroy_vertex_format(vf); @@ -3440,6 +3585,11 @@ int Shader::descriptor_binding(const char* name) { return sh->descs[idx].slot; } +int Shader::opt_mask(Shader_Type type, const char* name) { + Shader_Vk* sh = (Shader_Vk*)this; + return sh->find_opt(type, name); +} + int Shader::descriptor_stage(int slot) { Shader_Vk* sh = (Shader_Vk*)this; int i; @@ -3560,7 +3710,8 @@ Texture_Id Device::create_texture( int d, int mip_count, int array_size, - Buffer_Id init + Buffer_Id init, + int samples ) { VkImageCreateInfo ii{}; VkResult r; @@ -3592,7 +3743,7 @@ Texture_Id Device::create_texture( ii.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ii.usage = get_texture_usage(flags); ii.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - ii.samples = VK_SAMPLE_COUNT_1_BIT; + ii.samples = dev->get_samples(samples); ii.flags = image_flags; r = vkCreateImage(dev->dev, &ii, &dev->ac, &image); if (r != VK_SUCCESS) { @@ -3650,7 +3801,8 @@ Texture_Id Device::create_texture( array_size, 0, 0, - false + false, + samples ); if (init) { Context& ctx = dev->acquire(); @@ -3713,7 +3865,8 @@ Texture_Id Device::alias_texture( array_size, start_mip, start_array, - true + true, + texture.samples ); nt.set_name(dev, name); return ntid; @@ -3792,11 +3945,10 @@ Asset* Shader_Loader::load( Shader_Vk* shader; Shader_Id id; (void)s; - (void)a; (void)filename; id = dev->alloc_shader(); shader = (Shader_Vk*)&dev->get_shader(id); - if (!shader->init(dev, f)) { + if (!shader->init(dev, a, f)) { dev->shaders.remove(id); return 0; } @@ -3822,7 +3974,46 @@ int Shader_Vk::find_descriptor(const char* name) { return -1; } -bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) { +int Shader_Vk::find_module( + Shader_Type type, + int mask +) { + int i; + int count = module_count[type]; + Shader_Module* arr = modules[type]; + int bucket = (int)( + fnv1a64((uint8_t*)&mask, sizeof mask) % + count + ); + for (i = 0; i < count; i++) { + Shader_Module& mod = arr[bucket]; + if (mod.mask == mask) + return bucket; + bucket = (bucket + 1) % count; + } + return -1; +} + +int Shader_Vk::find_opt( + Shader_Type type, + const char* name +) { + int count = opt_count, i; + int bucket = (int)( + hash_string(name) % + count + ); + int stage = 1 << type; + for (i = 0; i < count; i++) { + Option& o = options[bucket]; + if (string_equal(name, o.name) && (o.stage & stage)) + return o.mask; + bucket = (bucket + 1) % count; + } + return 0; +} + +bool Shader_Vk::init(Device_Vk* dev, Arena* a, Pack_File* f) { char magic[4]; int binding_count, target_count, i; pack_read(f, magic, 4); @@ -3836,6 +4027,7 @@ bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) { pack_read(f, &binding_count, 4); pack_read(f, &target_count, 4); pack_read(f, &desc_count, 4); + pack_read(f, &opt_count, 4); assert(binding_count); vfd.binding_count = binding_count; if (!vfd.init(dev, f)) @@ -3883,24 +4075,60 @@ bool Shader_Vk::init(Device_Vk* dev, Pack_File* f) { desc_count * sizeof *descs ); pack_read(f, descs, desc_count * sizeof *descs); + options = (Option*)arena_alloc( + a, + opt_count * sizeof *options + ); + pack_read(f, options, opt_count * sizeof *options); for (i = 0; i < shader_type_count; i++) { - int o, s; - pack_read(f, &o, 4); - pack_read(f, &s, 4); - if (o) { - bool r; - int before = pack_tell(f); - char* buf = (char*)heap_alloc(dev->heap, s); - pack_seek(f, o, seek_rel_start); - pack_read(f, buf, s); - r = init_module(dev, i, buf, s); - heap_free(dev->heap, buf); - pack_seek(f, before, seek_rel_start); - if (!r) return false; + int c; + pack_read(f, &c, 4); + module_count[i] = c; + if (c) { + int o, s, mask; + int bucket, j; + Shader_Module* m = (Shader_Module*)arena_alloc( + a, + c * sizeof *m + ); + for (j = 0; j < c; j++) { + m[j].mask = -1; + } + for (j = 0; j < c; j++) { + int k; + pack_read(f, &o, 4); /* H_Variant */ + pack_read(f, &s, 4); + pack_read(f, &mask, 4); + pack_seek(f, 4, seek_rel_cur); + bucket = (int)( + fnv1a64((uint8_t*)&m, sizeof m) % + c + ); + for (k = 0; k < c; k++) { + Shader_Module& mod = m[bucket]; + if (mod.mask == -1) + goto found; + bucket = (bucket + 1) % c; + } + assert(0); + { + found: + char* buf = (char*)heap_alloc(dev->heap, s); + VkShaderModule r; + int before = pack_tell(f); + pack_seek(f, o, seek_rel_start); + pack_read(f, buf, s); + r = make_module(dev, buf, s); + heap_free(dev->heap, buf); + pack_seek(f, before, seek_rel_start); + if (!r) return false; + m[bucket] = Shader_Module { mask, r }; + } + } + modules[i] = m; } else { - modules[i] = VK_NULL_HANDLE; + modules[i] = 0; } - pack_read(f, entrypoints[i], 24); } return true; } @@ -3946,13 +4174,15 @@ Asset* Texture_Loader::load( char magic[4]; int w, h; size_t size; - Texture_Format fmt; + Texture_Format fmt = texture_format_r8i; + int mips = 0; (void)a; (void)s; pack_read(f, magic, 4); pack_read(f, &w, 4); pack_read(f, &h, 4); - pack_read(f, &fmt, 4); + pack_read(f, &fmt, 2); + pack_read(f, &mips, 2); size = calc_size(fmt, w, h); { Buffer_Id buf = dev->create_buffer( @@ -3961,9 +4191,6 @@ Asset* Texture_Loader::load( Buffer_Flags::copy_src | Buffer_Flags::cpu_readwrite ); - void* mem = dev->map_buffer(buf, 0, size); - pack_read(f, mem, size); - dev->unmap_buffer(buf); Texture_Id tex = dev->create_texture( filename, fmt, @@ -3971,11 +4198,25 @@ Asset* Texture_Loader::load( w, h, 1, + mips, 1, - 1, - buf + 0 ); - dev->destroy_buffer(buf); + { + int i; + for (i = 0; i < mips; i++) { + size = calc_size(fmt, w, h); + void* mem = dev->map_buffer(buf, 0, size); + pack_read(f, mem, size); + dev->unmap_buffer(buf); + auto& ctx = dev->acquire(); + ctx.copy(tex, buf, i, 0, 0, w, h); + dev->submit(ctx); + w >>= 1; + h >>= 1; + } + } + dev->destroy_bufferi(buf); return &dev->get_texture(tex); } } @@ -4002,7 +4243,8 @@ void Texture_Vk::init( int array_size, int start_mip, int start_array, - bool alias + bool alias, + int samples ) { t->id = id; t->parent = parent; @@ -4022,6 +4264,7 @@ void Texture_Vk::init( t->start_mip = start_mip; t->start_array = start_array; t->alias = alias; + t->samples = samples; } void Texture_Vk::destroy(Device_Vk* dev) { @@ -133,12 +133,19 @@ struct Pipeline { Cull_Mode cull_mode; Vertex_Format_Id vertex_format; Shader_Id shader; + int samples; + int shader_masks[shader_type_count]; Descriptor descriptors[pipeline_max_descriptors]; int descriptor_count; void hash(); bool pipeline_eq(const Pipeline& other) const { + int i; + for (i = 0; i < shader_type_count; i++) { + if (other.shader_masks[i] != shader_masks[i]) + return false; + } return shader == other.shader && vertex_format == other.vertex_format && @@ -161,7 +168,8 @@ struct Pipeline { blend_dst_alpha == other.blend_dst_alpha && blend_mode == other.blend_mode && blend_mode_alpha == other.blend_mode_alpha && - cull_mode == other.cull_mode; + cull_mode == other.cull_mode && + samples == other.samples; } bool desc_layout_eq(const Pipeline& other) const { @@ -228,6 +236,7 @@ struct Render_Pass { struct Target { Texture_Id id; Texture_Format fmt; + int samples; Clear_Mode mode; union { Colour colour; @@ -266,6 +275,11 @@ struct Render_Pass { } return true; } + + int get_samples() const { + if (colour_count) return colours[0].samples; + return depth.samples; + } }; struct Vertex_Buffer_Binding { @@ -325,6 +339,7 @@ struct Pipeline_Builder { ); PB& cull(Cull_Mode mode); PB& shader(Shader_Id s); + PB& option(Shader_Type type, int mask); PB& vertex_format(Vertex_Format_Id vf); PB& texture(int binding, Texture_Id t, Sampler_Id s); PB& cbuffer( @@ -359,6 +374,7 @@ struct Texture : public Asset { int w, h, d; int mip_count, array_size; int start_mip, start_array; + int samples; bool alias; }; @@ -486,7 +502,8 @@ struct Device { int d, int mip_count, int array_size, - Buffer_Id init + Buffer_Id init, + int samples = 1 ); Texture_Id alias_texture( Texture_Id o, @@ -553,6 +570,16 @@ struct Context { void submit(const Render_Pass& rp); void copy(Buffer_Id dst, Buffer_Id src); void copy(Texture_Id dst, Buffer_Id src); + void copy( + Texture_Id dst, + Buffer_Id src, + int mip, + int x, + int y, + int w, + int h + ); + void resolve(Texture_Id dst, Texture_Id src); void transition(Texture_Id id, Resource_State state); void debug_push(const char* name); void debug_pop(); @@ -568,6 +595,7 @@ struct Shader : public Asset { int attribute_index(const char* name); int target_index(const char* name); int descriptor_binding(const char* name); + int opt_mask(Shader_Type type, const char* name); int descriptor_stage(int slot); }; @@ -8,12 +8,33 @@ extern "C" { static int component_sizes[max_components]; +void Slinky::init(Arena* a, int size) { + data = arena_alloc(a, size * slinky_size); + entities = (Entity_Id*)arena_alloc( + a, + sizeof *entities * slinky_size + ); + count = 0; +} + +void* Slinky::add(Entity_Id eid, int size, int& mapping) { + int idx = count++; + assert(count <= slinky_size); + entities[idx] = eid; + mapping = idx; + return &((char*)data)[idx * size]; +} + +void* Slinky::get(Entity_Id eid, int mapping, int size) { + char* e = &((char*)data)[mapping * size]; + assert(eid == entities[mapping]); + (void)eid; + return e; +} + void Pool::init(Arena* a, Component_Mask m) { int i, coff; - next = 0; - size = 0; mask = m; - count = 0; for (i = 0, coff = 0; i < max_components; i++) { if (m & ((Component_Mask)1 << i)) { offsets[i] = coff; @@ -21,50 +42,76 @@ void Pool::init(Arena* a, Component_Mask m) { } } size = coff; - data = arena_alloc(a, size * pool_cap); + slinkies[0].init(a, size); + slinky_count = 1; + count = 0; } -void* Pool::add(Arena* a, Entity_Id eid) { - int idx; - if (next) - return next->add(a, eid); - if (count >= pool_cap) { - next = (Pool*)arena_alloc(a, sizeof *next); - next->init(a, mask); - return next->add(a, eid); +Slinky& Pool::get_slinky(Arena* a) { + int i, e = slinky_count; + for (i = 0; i < e; i++) { + Slinky& s = slinkies[i]; + if (s.count < slinky_size) + return s; } - idx = count++; - entities[idx] = eid; - mapping[entity_index(eid)] = idx; - return &((char*)data)[idx * size]; + assert(slinky_count < max_slinkies); + Slinky& s = slinkies[slinky_count++]; + s.init(a, size); + return s; +} + +void* Pool::add(Arena* a, Entity_Id eid) { + Slinky& slinky = get_slinky(a); + Mapping& m = mapping[entity_index(eid)]; + m.slinky = &slinky - slinkies; + count++; + return slinky.add(eid, size, m.mapping); } void Pool::remove(Entity_Id eid) { - int eind = entity_index(eid); - int end = count - 1; - assert(eid == entities[mapping[eind]]); - memmove( - (char*)data + eind * size, - (char*)data + end * size, - size - ); - entities[eind] = entities[count]; - mapping[end] = eind; - count = end; + Mapping& m = mapping[entity_index(eid)]; + Slinky& s = slinkies[m.slinky]; + assert(m.slinky >= 0); + if (count > 1) { + int idx = slinky_count - 1; + Entity_Id le; + void* last; +#ifdef DEBUG + last = 0; +#endif + for (; idx >= 0; idx--) { + Slinky& s = slinkies[idx]; + int& c = s.count; + if (c) { + c--; + last = (char*)s.data + c * size; + le = s.entities[c]; + s.entities[c] = 0; + break; + } + } + assert(last != 0); + memcpy((char*)s.data + m.mapping * size, last, size); + s.entities[m.mapping] = le; + mapping[entity_index(le)] = m; + m = Mapping { -1, -1 }; + } else + s.count--; + count--; } void* Pool::get(Entity_Id eid, int cid) { - int eind = entity_index(eid); - char* e = &((char*)data)[mapping[eind] * size]; - assert(eid == entities[mapping[eind]]); - return &e[offsets[cid]]; + Mapping m = mapping[entity_index(eid)]; + Slinky& s = slinkies[m.slinky]; + char* ptr = (char*)s.get(eid, m.mapping, size); + return ptr + offsets[cid]; } void* Pool::get(Entity_Id eid) { - int eind = entity_index(eid); - void* e = &((char*)data)[mapping[eind] * size]; - assert(eid == entities[mapping[eind]]); - return e; + Mapping m = mapping[entity_index(eid)]; + Slinky& s = slinkies[m.slinky]; + char* ptr = (char*)s.get(eid, m.mapping, size); + return ptr; } int get_new_component_id(int size) { @@ -91,6 +138,7 @@ void World::init(Arena* a) { arena = a; count = 0; free_count = 0; + dq_count = 0; for (i = 0; i < max_entities; i++) versions[i] = 1; for (i = 0; i < max_pools; i++) @@ -166,7 +214,6 @@ void* World::get(Entity_Id eid, int cid) { int eind = entity_index(eid); Pool& p = get_pool(masks[eind]); assert(versions[eind] == entity_version(eid)); - assert(p.entities[p.mapping[eind]] == eid); return p.get(eid, cid); } @@ -202,3 +249,16 @@ void World::destroy(Entity_Id e) { versions[entity_index(e)]++; } +void World::qdestroy(Entity_Id e) { + assert(e); + assert(dq_count < max_entities); + dq[dq_count++] = e; +} + +void World::update() { + int i, e = dq_count; + Entity_Id* d = dq; + for (i = 0; i < e; i++, d++) + destroy(*d); + dq_count = 0; +} @@ -3,28 +3,59 @@ struct Arena; +#include "entity.hpp" + #include <stdint.h> #include <new> +#include <tuple> #include <utility> - +extern "C" { +#include "plat.h" +} using Component_Mask = uint64_t; -using Entity_Id = uint32_t; #define max_entities 1024 -#define pool_cap 128 +#define slinky_size 1 #define max_components 64 +#define max_slinkies (max_entities / slinky_size) + +static_assert((!(slinky_size & (slinky_size - 1)))); +/* slinky_size needs to be a power of 2 */ +static consteval int get_slinky_size_bit() { + int i; + int m = slinky_size; + for (i = 0; m; m >>= 1, i++); + if (i == 0) + throw "wtf"; + return i - 1; +} +static constexpr int slinky_size_bit = get_slinky_size_bit(); + +struct Slinky { + Entity_Id* entities; + void* data; + int count; + + void init(Arena* a, int size); + void* add(Entity_Id eid, int size, int& mapping); + void* get(Entity_Id eid, int mapping, int size); +}; struct Pool { + struct Mapping { + int slinky; + int mapping; + }; + Component_Mask mask; - Pool* next; - void* data; - Entity_Id entities[pool_cap]; - int mapping[max_entities]; + Slinky slinkies[max_slinkies]; + Mapping mapping[max_entities]; int offsets[max_components]; - int size, count; + int size, count, slinky_count; void init(Arena* a, Component_Mask m); + Slinky& get_slinky(Arena* a); void* add(Arena* a, Entity_Id eid); void* get(Entity_Id eid, int cid); void* get(Entity_Id eid); @@ -43,8 +74,9 @@ struct World { uint8_t versions[max_entities]; Entity_Id entities[max_entities]; Entity_Id freelist[max_entities]; + Entity_Id dq[max_entities]; Arena* arena; - int count, free_count; + int count, dq_count, free_count; void init(Arena* a); uint64_t hash_mask(Component_Mask m); @@ -94,6 +126,8 @@ struct World { void* get(Entity_Id eid, int cid); void remove(Entity_Id eid, int cid); void destroy(Entity_Id e); + void qdestroy(Entity_Id e); + void update(); struct View { World* w; @@ -126,8 +160,11 @@ struct World { template <typename C> C& get() { Pool* p = v->pools[ind]; + Slinky& s = p->slinkies[ptr >> slinky_size_bit]; + int si = &s - p->slinkies; + int off = ptr - (si << slinky_size_bit); return *(C*)( - (char*)p->data + ptr * p->size + + (char*)s.data + off * p->size + p->offsets[v->w->get_component_id<C>()] ); } @@ -138,7 +175,11 @@ struct World { Entity_Id entity() { Pool* p = v->pools[ind]; - return p->entities[ptr]; + Slinky& s = p->slinkies[ptr >> slinky_size_bit]; + int si = &s - p->slinkies; + int off = ptr - (si << slinky_size_bit); + assert(s.entities[off]); + return s.entities[off]; } }; |