diff --git a/sapp/CMakeLists.txt b/sapp/CMakeLists.txt index 4fd493a0..d0a7d784 100644 --- a/sapp/CMakeLists.txt +++ b/sapp/CMakeLists.txt @@ -126,6 +126,20 @@ fips_begin_app(texcube-sapp-ui windowed) target_compile_definitions(texcube-sapp-ui PRIVATE USE_DBG_UI) fips_end_app() +fips_ide_group(Samples) +fips_begin_app(sbuftex-sapp windowed) + fips_files(sbuftex-sapp.c) + sokol_shader_debuggable(sbuftex-sapp.glsl ${slang}) + fips_deps(sokol) +fips_end_app() +fips_ide_group(SamplesWithDebugUI) +fips_begin_app(sbuftex-sapp-ui windowed) + fips_files(sbuftex-sapp.c) + sokol_shader(sbuftex-sapp.glsl ${slang}) + fips_deps(sokol dbgui) + target_compile_definitions(sbuftex-sapp-ui PRIVATE USE_DBG_UI) +fips_end_app() + fips_ide_group(Samples) fips_begin_app(offscreen-sapp windowed) fips_files(offscreen-sapp.c) diff --git a/sapp/sbuftex-sapp.c b/sapp/sbuftex-sapp.c new file mode 100644 index 00000000..3c21b554 --- /dev/null +++ b/sapp/sbuftex-sapp.c @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// sbuftex-sapp.c +// +// Test whether binding texture and storage buffers to the same shader stage +// works. +// +// The vertex shader pulls vertices and a color palette index from a storage +// buffer. The color palette index is passed to the fragment shader. +// +// In the fragment shader, a color palette lookup is done via a storage buffer, +// and the resulting color is modulated by a texture. +//------------------------------------------------------------------------------ +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_log.h" +#include "sokol_glue.h" +#define HANDMADE_MATH_IMPLEMENTATION +#define HANDMADE_MATH_NO_SSE +#include "HandmadeMath.h" +#include "dbgui/dbgui.h" +#include "sbuftex-sapp.glsl.h" + +static struct { + float rx, ry; + sg_pass_action pass_action; + sg_pipeline pip; + sg_bindings bind; +} state; + +static vs_params_t make_vs_params(void); + +static void init(void) { + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + .logger.func = slog_func, + }); + __dbgui_setup(sapp_sample_count()); + + // if no storage buffers supported, just clear the screen to red + if (!sg_query_features().storage_buffer) { + state.pass_action = (sg_pass_action){ + .colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 1.0f, 0.0f, 0.0f, 1.0f } }, + }; + return; + } + // otherwise clear to a regular color + state.pass_action = (sg_pass_action){ + .colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.5f, 0.75f, 0.25f } }, + }; + + // a storage buffer with cube vertices + sb_vertex_t vertices[] = { + { .pos = { -1.0f, -1.0f, -1.0f }, .idx = 0, .uv = { 0.0f, 0.0f } }, + { .pos = { 1.0f, -1.0f, -1.0f }, .idx = 0, .uv = { 1.0f, 0.0f } }, + { .pos = { 1.0f, 1.0f, -1.0f }, .idx = 0, .uv = { 1.0f, 1.0f } }, + { .pos = { -1.0f, 1.0f, -1.0f }, .idx = 0, .uv = { 0.0f, 1.0f } }, + + { .pos = { -1.0f, -1.0f, 1.0f }, .idx = 1, .uv = { 0.0f, 0.0f } }, + { .pos = { 1.0f, -1.0f, 1.0f }, .idx = 1, .uv = { 1.0f, 0.0f } }, + { .pos = { 1.0f, 1.0f, 1.0f }, .idx = 1, .uv = { 1.0f, 1.0f } }, + { .pos = { -1.0f, 1.0f, 1.0f }, .idx = 1, .uv = { 0.0f, 1.0f } }, + + { .pos = { -1.0f, -1.0f, -1.0f }, .idx = 2, .uv = { 0.0f, 0.0f } }, + { .pos = { -1.0f, 1.0f, -1.0f }, .idx = 2, .uv = { 1.0f, 0.0f } }, + { .pos = { -1.0f, 1.0f, 1.0f }, .idx = 2, .uv = { 1.0f, 1.0f } }, + { .pos = { -1.0f, -1.0f, 1.0f }, .idx = 2, .uv = { 0.0f, 1.0f } }, + + { .pos = { 1.0f, -1.0f, -1.0f }, .idx = 3, .uv = { 0.0f, 0.0f } }, + { .pos = { 1.0f, 1.0f, -1.0f }, .idx = 3, .uv = { 1.0f, 0.0f } }, + { .pos = { 1.0f, 1.0f, 1.0f }, .idx = 3, .uv = { 1.0f, 1.0f } }, + { .pos = { 1.0f, -1.0f, 1.0f }, .idx = 3, .uv = { 0.0f, 1.0f } }, + + { .pos = { -1.0f, -1.0f, -1.0f }, .idx = 4, .uv = { 0.0f, 0.0f } }, + { .pos = { -1.0f, -1.0f, 1.0f }, .idx = 4, .uv = { 1.0f, 0.0f } }, + { .pos = { 1.0f, -1.0f, 1.0f }, .idx = 4, .uv = { 1.0f, 1.0f } }, + { .pos = { 1.0f, -1.0f, -1.0f }, .idx = 4, .uv = { 0.0f, 1.0f } }, + + { .pos = { -1.0f, 1.0f, -1.0f }, .idx = 5, .uv = { 0.0f, 0.0f } }, + { .pos = { -1.0f, 1.0f, 1.0f }, .idx = 5, .uv = { 1.0f, 0.0f } }, + { .pos = { 1.0f, 1.0f, 1.0f }, .idx = 5, .uv = { 1.0f, 1.0f } }, + { .pos = { 1.0f, 1.0f, -1.0f }, .idx = 5, .uv = { 0.0f, 1.0f } }, + }; + state.bind.vs.storage_buffers[SLOT_vertices] = sg_make_buffer(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_STORAGEBUFFER, + .data = SG_RANGE(vertices), + .label = "cube-vertices", + }); + + // an index buffer with the cube indices + uint16_t indices[] = { + 0, 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, + 14, 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20 + }; + state.bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_INDEXBUFFER, + .data = SG_RANGE(indices), + .label = "cube-indices", + }); + + // a storage buffer with a color palette + sb_color_t colors[] = { + { .color = { 1.0f, 0.0f, 0.0f, 1.0f } }, + { .color = { 0.0f, 1.0f, 0.0f, 1.0f } }, + { .color = { 0.0f, 0.0f, 1.0f, 1.0f } }, + { .color = { 0.5f, 0.0f, 1.0f, 1.0f } }, + { .color = { 0.0f, 0.5f, 1.0f, 1.0f } }, + { .color = { 1.0f, 0.5f, 0.0f, 1.0f } }, + }; + state.bind.fs.storage_buffers[SLOT_colors] = sg_make_buffer(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_STORAGEBUFFER, + .data = SG_RANGE(colors), + .label = "color-palette", + }); + + // a greyscale texture which modules the color palette colors + uint8_t pixels[4][4] = { + { 0xFF, 0xCC, 0x88, 0x44 }, + { 0xCC, 0x88, 0x44, 0xFF }, + { 0x88, 0x44, 0xFF, 0xCC }, + { 0x44, 0xFF, 0xCC, 0x88 }, + }; + state.bind.fs.images[SLOT_tex] = sg_make_image(&(sg_image_desc){ + .width = 4, + .height = 4, + .pixel_format = SG_PIXELFORMAT_R8, + .data.subimage[0][0] = SG_RANGE(pixels), + .label = "texture", + }); + + // ...and a matching sampler + state.bind.fs.samplers[SLOT_smp] = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .label = "sampler", + }); + + // ...shader and pipeline object (note: no vertex layout needed because + // the vertex shader pulls vertices from storage buffer) + state.pip = sg_make_pipeline(&(sg_pipeline_desc){ + .shader = sg_make_shader(sbuftex_shader_desc(sg_query_backend())), + .index_type = SG_INDEXTYPE_UINT16, + .cull_mode = SG_CULLMODE_BACK, + .depth = { + .compare = SG_COMPAREFUNC_LESS_EQUAL, + .write_enabled = true, + }, + .label = "pipeline", + }); +} + +static void frame(void) { + const vs_params_t vs_params = make_vs_params(); + sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = sglue_swapchain() }); + if (sg_query_features().storage_buffer) { + sg_apply_pipeline(state.pip); + sg_apply_bindings(&state.bind); + sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vs_params, &SG_RANGE(vs_params)); + sg_draw(0, 36, 1); + } + __dbgui_draw(); + sg_end_pass(); + sg_commit(); +} + +static vs_params_t make_vs_params(void) { + const float t = (float)(sapp_frame_duration() * 60.0); + hmm_mat4 proj = HMM_Perspective(60.0f, sapp_widthf()/sapp_heightf(), 0.01f, 10.0f); + hmm_mat4 view = HMM_LookAt(HMM_Vec3(0.0f, 1.5f, 6.0f), HMM_Vec3(0.0f, 0.0f, 0.0f), HMM_Vec3(0.0f, 1.0f, 0.0f)); + hmm_mat4 view_proj = HMM_MultiplyMat4(proj, view); + state.rx += 1.0f * t; state.ry += 2.0f * t; + hmm_mat4 rxm = HMM_Rotate(state.rx, HMM_Vec3(1.0f, 0.0f, 0.0f)); + hmm_mat4 rym = HMM_Rotate(state.ry, HMM_Vec3(0.0f, 1.0f, 0.0f)); + hmm_mat4 model = HMM_MultiplyMat4(rxm, rym); + return (vs_params_t){ .mvp = HMM_MultiplyMat4(view_proj, model) }; +} + +static void cleanup(void) { + __dbgui_shutdown(); + sg_shutdown(); +} + +sapp_desc sokol_main(int argc, char* argv[]) { + (void)argc; (void)argv; + return (sapp_desc){ + .init_cb = init, + .frame_cb = frame, + .cleanup_cb = cleanup, + .event_cb = __dbgui_event, + .width = 800, + .height = 600, + .sample_count = 4, + .window_title = "sbuftex-sapp.c", + .icon.sokol_default = true, + .logger.func = slog_func, + }; +} diff --git a/sapp/sbuftex-sapp.glsl b/sapp/sbuftex-sapp.glsl new file mode 100644 index 00000000..f413b571 --- /dev/null +++ b/sapp/sbuftex-sapp.glsl @@ -0,0 +1,48 @@ +@ctype mat4 hmm_mat4 + +@vs vs +uniform vs_params { + mat4 mvp; +}; + +struct sb_vertex { + vec3 pos; + uint idx; + vec2 uv; +}; + +readonly buffer vertices { + sb_vertex vtx[]; +}; + +out vec3 uv_idx; + +void main() { + gl_Position = mvp * vec4(vtx[gl_VertexIndex].pos, 1.0); + uv_idx = vec3(vtx[gl_VertexIndex].uv, float(vtx[gl_VertexIndex].idx)); +} +@end + +@fs fs +uniform texture2D tex; +uniform sampler smp; + +struct sb_color { + vec4 color; +}; + +readonly buffer colors { + sb_color clr[]; +}; + +in vec3 uv_idx; +out vec4 frag_color; + +void main() { + uint idx = uint(uv_idx.z); + vec2 uv = uv_idx.xy; + frag_color = vec4(texture(sampler2D(tex,smp), uv).xxx, 1.0) * clr[idx].color; +} +@end + +@program sbuftex vs fs