Skip to content

Commit

Permalink
Merge pull request #153 from floooh/customresolve-sample
Browse files Browse the repository at this point in the history
New sample: customresolve-sapp
  • Loading branch information
floooh authored Nov 19, 2024
2 parents f381bfe + 812985f commit b7f25d9
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 0 deletions.
1 change: 1 addition & 0 deletions fips-files/verbs/webpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def get_build_config(api):
[ 'uvwrap', 'uvwrap-sapp.c', 'uvwrap-sapp.glsl'],
[ 'mipmap', 'mipmap-sapp.c', 'mipmap-sapp.glsl'],
[ 'shared-bindings', 'shared-bindings-sapp.c', 'shared-bindings-sapp.glsl' ],
[ 'customresolve', 'customresolve-sapp.c', 'customresolve-sapp.glsl' ],
[ 'uniformtypes', 'uniformtypes-sapp.c', 'uniformtypes-sapp.glsl' ],
[ 'blend', 'blend-sapp.c', 'blend-sapp.glsl' ],
[ 'sdf', 'sdf-sapp.c', 'sdf-sapp.glsl'],
Expand Down
7 changes: 7 additions & 0 deletions sapp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,13 @@ fips_begin_app(shared-bindings-sapp-ui windowed)
target_compile_definitions(shared-bindings-sapp-ui PRIVATE USE_DBG_UI)
fips_end_app()

fips_ide_group(Samples)
fips_begin_app(customresolve-sapp windowed)
fips_files(customresolve-sapp.c)
sokol_shader_debuggable(customresolve-sapp.glsl ${slang})
fips_deps(sokol cimgui)
fips_end_app()

fips_ide_group(Samples)
fips_begin_app(mipmap-sapp windowed)
fips_files(mipmap-sapp.c)
Expand Down
1 change: 1 addition & 0 deletions sapp/cubemaprt-sapp.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <stddef.h> /* offsetof */
#include "cubemaprt-sapp.glsl.h"

// NOTE: cubemaps can't be multisampled, so (OFFSCREEN_SAMPLE_COUNT > 1) will be a validation error
#define OFFSCREEN_SAMPLE_COUNT (1)
#define DISPLAY_SAMPLE_COUNT (4)
#define NUM_SHAPES (32)
Expand Down
250 changes: 250 additions & 0 deletions sapp/customresolve-sapp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
//------------------------------------------------------------------------------
// customresolve-sapp.c
//
// Demonstrate custom MSAA resolve in a render pass which reads individual
// MSAA samples in the fragment shader.
//------------------------------------------------------------------------------
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_log.h"
#include "sokol_glue.h"
#define SOKOL_IMGUI_IMPL
#define SOKOL_GFX_IMGUI_IMPL
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include "cimgui/cimgui.h"
#include "sokol_imgui.h"
#include "sokol_gfx_imgui.h"
#include "customresolve-sapp.glsl.h"

#define WIDTH (160)
#define HEIGHT (120)

static struct {
struct {
sg_image img;
sg_pipeline pip;
sg_attachments atts;
sg_pass_action action;
} msaa;
struct {
sg_image img;
sg_pipeline pip;
sg_attachments atts;
sg_pass_action action;
sg_bindings bind;
fs_params_t fs_params;
} resolve;
struct {
sg_pipeline pip;
sg_pass_action action;
sg_bindings bind;
} display;
struct {
sgimgui_t sgimgui;
} ui;
sg_sampler smp; // a common non-filtering sampler
} state;

const fs_params_t default_weights = {
.weight0 = 0.25f,
.weight1 = 0.25f,
.weight2 = 0.25f,
.weight3 = 0.25f,
};

static void draw_fallback(void);
static void draw_ui(void);

static void init(void) {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
.logger.func = slog_func,
});
simgui_setup(&(simgui_desc_t){ .logger.func = slog_func });
sgimgui_init(&state.ui.sgimgui, &(sgimgui_desc_t){0});

// catch WebGL2/GLES3
if (!sg_query_features().msaa_image_bindings) {
return;
}

// common objects
state.smp = sg_make_sampler(&(sg_sampler_desc){
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
});

// msaa-render-pass objects
state.msaa.img = sg_make_image(&(sg_image_desc){
.render_target = true,
.width = WIDTH,
.height = HEIGHT,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.sample_count = 4,
.label = "msaa image",
});
state.msaa.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(msaa_shader_desc(sg_query_backend())),
.sample_count = 4,
.depth.pixel_format = SG_PIXELFORMAT_NONE,
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "msaa pipeline",
});
state.msaa.atts = sg_make_attachments(&(sg_attachments_desc){
.colors[0].image = state.msaa.img,
.label = "msaa pass attachments",
});
state.msaa.action = (sg_pass_action){
.colors[0] = {
.load_action = SG_LOADACTION_CLEAR,
.store_action = SG_STOREACTION_STORE,
.clear_value = { 0, 0, 0, 1 },
},
};

// resolve-render-pass objects
state.resolve.img = sg_make_image(&(sg_image_desc){
.render_target = true,
.width = WIDTH,
.height = HEIGHT,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.sample_count = 1,
.label = "resolve image",
});
state.resolve.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(resolve_shader_desc(sg_query_backend())),
.sample_count = 1,
.depth.pixel_format = SG_PIXELFORMAT_NONE,
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
.label = "resolve pipeline",
});
state.resolve.atts = sg_make_attachments(&(sg_attachments_desc){
.colors[0].image = state.resolve.img,
.label = "resolve pass attachments",
});
state.resolve.action = (sg_pass_action){
.colors[0] = {
.load_action = SG_LOADACTION_DONTCARE,
.store_action = SG_STOREACTION_STORE
},
};
state.resolve.bind = (sg_bindings){
.images[IMG_texms] = state.msaa.img,
.samplers[SMP_smp] = state.smp,
};
state.resolve.fs_params = default_weights;

// swapchain-render-pass objects
state.display.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(display_shader_desc(sg_query_backend())),
.label = "display pipeline",
});
state.display.action = (sg_pass_action){
.colors[0] = { .load_action = SG_LOADACTION_DONTCARE }
};
state.display.bind = (sg_bindings){
.images[IMG_tex] = state.resolve.img,
.samplers[SMP_smp] = state.smp,
};
}

static void frame(void) {
draw_ui();
if (!sg_query_features().msaa_image_bindings) {
draw_fallback();
return;
}

// draw a triangle into an msaa render target
sg_begin_pass(&(sg_pass){ .action = state.msaa.action, .attachments = state.msaa.atts });
sg_apply_pipeline(state.msaa.pip);
sg_draw(0, 3, 1);
sg_end_pass();

// custom resolve pass (via a 'fullscreen triangle')
sg_begin_pass(&(sg_pass){ .action = state.resolve.action, .attachments = state.resolve.atts });
sg_apply_pipeline(state.resolve.pip);
sg_apply_bindings(&state.resolve.bind);
sg_apply_uniforms(UB_fs_params, &SG_RANGE(state.resolve.fs_params));
sg_draw(0, 3, 1);
sg_end_pass();

// the final swapchain pass (also via a 'fullscreen triangle')
sg_begin_pass(&(sg_pass){ .action = state.display.action, .swapchain = sglue_swapchain() });
sg_apply_pipeline(state.display.pip);
sg_apply_bindings(&state.display.bind);
sg_draw(0, 3, 1);
simgui_render();
sg_end_pass();
sg_commit();
}

static void cleanup(void) {
sgimgui_discard(&state.ui.sgimgui);
simgui_shutdown();
sg_shutdown();
}

static void draw_ui(void) {
simgui_new_frame(&(simgui_frame_desc_t){
.width = sapp_width(),
.height = sapp_height(),
.dpi_scale = sapp_dpi_scale(),
.delta_time = sapp_frame_duration(),
});
if (igBeginMainMenuBar()) {
sgimgui_draw_menu(&state.ui.sgimgui, "sokol-gfx");
igEndMainMenuBar();
}
sgimgui_draw(&state.ui.sgimgui);

igSetNextWindowPos((ImVec2){10, 20}, ImGuiCond_Once, (ImVec2){0,0});
if (igBegin("#window", 0, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBackground)) {
if (sg_query_features().msaa_image_bindings) {
igText("Sample Weights:");
igSliderFloat("0", &state.resolve.fs_params.weight0, 0.0f, 1.0f, "%.2f", 0);
igSliderFloat("1", &state.resolve.fs_params.weight1, 0.0f, 1.0f, "%.2f", 0);
igSliderFloat("2", &state.resolve.fs_params.weight2, 0.0f, 1.0f, "%.2f", 0);
igSliderFloat("3", &state.resolve.fs_params.weight3, 0.0f, 1.0f, "%.2f", 0);
igCheckboxFlags_IntPtr("show complex pixels", &state.resolve.fs_params.coverage, 1);
if (igButton("Reset", (ImVec2){0,0})) {
state.resolve.fs_params = default_weights;
}
} else {
igText("MSAA TEXTURES NOT SUPPORTED ON WEBGL2/GLES3/macOS+GL");
}
}
igEnd();
}

static void draw_fallback(void) {
sg_begin_pass(&(sg_pass){
.action = {
.colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.5f, 0, 0, 1} },
},
.swapchain = sglue_swapchain(),
});
simgui_render();
sg_end_pass();
sg_commit();
}

static void input(const sapp_event* ev) {
simgui_handle_event(ev);
}

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 = input,
.width = 640,
.height = 480,
.sample_count = 1,
.window_title = "customresolve-sapp.c",
.icon.sokol_default = true,
.logger.func = slog_func,
};
}
116 changes: 116 additions & 0 deletions sapp/customresolve-sapp.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// triangle shader which renders into an msaa render target
@vs triangle_vs

const vec4 colors[3] = {
vec4(1, 1, 0, 1),
vec4(0, 1, 1, 1),
vec4(1, 0, 1, 1),
};
const vec3 positions[3] = {
vec3(0.0, 0.6, 0.0),
vec3(0.5, -0.6, 0.0),
vec3(-0.5, -0.4, 0.0),
};

out vec4 color;

void main() {
gl_Position = vec4(positions[gl_VertexIndex], 1.0);
color = colors[gl_VertexIndex];
}
@end

@fs triangle_fs
in vec4 color;
out vec4 frag_color;

void main() {
float a = 0;
if ((gl_SampleMaskIn[0] & 15) == 15) {
a = 1;
}
frag_color = vec4(color.xyz, a);
}
@end

@program msaa triangle_vs triangle_fs

@block fullscreen_triangle
const vec3 positions[3] = {
vec3(-1.0, -1.0, 0.0),
vec3(3.0, -1.0, 0.0),
vec3(-1.0, 3.0, 0.0),
};
@end

// custom resolve shader
@vs resolve_vs
@include_block fullscreen_triangle

void main() {
gl_Position = vec4(positions[gl_VertexIndex], 1);
}
@end

@fs resolve_fs
layout(binding=0) uniform texture2DMS texms;
layout(binding=0) uniform sampler smp;
layout(binding=0) uniform fs_params {
float weight0;
float weight1;
float weight2;
float weight3;
int coverage;
};

out vec4 frag_color;

void main() {
ivec2 uv = ivec2(gl_FragCoord.xy);
vec4 s0 = texelFetch(sampler2DMS(texms, smp), uv, 0);
vec4 s1 = texelFetch(sampler2DMS(texms, smp), uv, 1);
vec4 s2 = texelFetch(sampler2DMS(texms, smp), uv, 2);
vec4 s3 = texelFetch(sampler2DMS(texms, smp), uv, 3);
if (coverage != 0) {
if ((s0.w + s1.w + s2.w + s3.w) < 4) {
// complex pixel
frag_color = vec4(1, 0, 0, 1);
} else {
// simple pixel
frag_color = vec4(0, 0, 0, 0);
}
} else {
frag_color = vec4(s0.xyz*weight0 + s1.xyz*weight1 + s2.xyz*weight2 + s3.xyz*weight3, 1);
}
}
@end

@program resolve resolve_vs resolve_fs

// the final display pass shader which renders the custom-resolved texture to the display
@vs display_vs
@glsl_options flip_vert_y
@include_block fullscreen_triangle

out vec2 uv;

void main() {
const vec4 pos = vec4(positions[gl_VertexIndex], 1);
gl_Position = pos;
uv = (pos.xy + 1.0) * 0.5;
}
@end

@fs display_fs
layout(binding=0) uniform texture2D tex;
layout(binding=0) uniform sampler smp;

in vec2 uv;
out vec4 frag_color;

void main() {
frag_color = texture(sampler2D(tex, smp), uv);
}
@end

@program display display_vs display_fs
Binary file added webpage/customresolve.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b7f25d9

Please sign in to comment.