Skip to content

Commit b7f25d9

Browse files
authored
Merge pull request #153 from floooh/customresolve-sample
New sample: customresolve-sapp
2 parents f381bfe + 812985f commit b7f25d9

File tree

6 files changed

+375
-0
lines changed

6 files changed

+375
-0
lines changed

fips-files/verbs/webpage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def get_build_config(api):
7272
[ 'uvwrap', 'uvwrap-sapp.c', 'uvwrap-sapp.glsl'],
7373
[ 'mipmap', 'mipmap-sapp.c', 'mipmap-sapp.glsl'],
7474
[ 'shared-bindings', 'shared-bindings-sapp.c', 'shared-bindings-sapp.glsl' ],
75+
[ 'customresolve', 'customresolve-sapp.c', 'customresolve-sapp.glsl' ],
7576
[ 'uniformtypes', 'uniformtypes-sapp.c', 'uniformtypes-sapp.glsl' ],
7677
[ 'blend', 'blend-sapp.c', 'blend-sapp.glsl' ],
7778
[ 'sdf', 'sdf-sapp.c', 'sdf-sapp.glsl'],

sapp/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,13 @@ fips_begin_app(shared-bindings-sapp-ui windowed)
314314
target_compile_definitions(shared-bindings-sapp-ui PRIVATE USE_DBG_UI)
315315
fips_end_app()
316316

317+
fips_ide_group(Samples)
318+
fips_begin_app(customresolve-sapp windowed)
319+
fips_files(customresolve-sapp.c)
320+
sokol_shader_debuggable(customresolve-sapp.glsl ${slang})
321+
fips_deps(sokol cimgui)
322+
fips_end_app()
323+
317324
fips_ide_group(Samples)
318325
fips_begin_app(mipmap-sapp windowed)
319326
fips_files(mipmap-sapp.c)

sapp/cubemaprt-sapp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <stddef.h> /* offsetof */
1414
#include "cubemaprt-sapp.glsl.h"
1515

16+
// NOTE: cubemaps can't be multisampled, so (OFFSCREEN_SAMPLE_COUNT > 1) will be a validation error
1617
#define OFFSCREEN_SAMPLE_COUNT (1)
1718
#define DISPLAY_SAMPLE_COUNT (4)
1819
#define NUM_SHAPES (32)

sapp/customresolve-sapp.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
//------------------------------------------------------------------------------
2+
// customresolve-sapp.c
3+
//
4+
// Demonstrate custom MSAA resolve in a render pass which reads individual
5+
// MSAA samples in the fragment shader.
6+
//------------------------------------------------------------------------------
7+
#include "sokol_app.h"
8+
#include "sokol_gfx.h"
9+
#include "sokol_log.h"
10+
#include "sokol_glue.h"
11+
#define SOKOL_IMGUI_IMPL
12+
#define SOKOL_GFX_IMGUI_IMPL
13+
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
14+
#include "cimgui/cimgui.h"
15+
#include "sokol_imgui.h"
16+
#include "sokol_gfx_imgui.h"
17+
#include "customresolve-sapp.glsl.h"
18+
19+
#define WIDTH (160)
20+
#define HEIGHT (120)
21+
22+
static struct {
23+
struct {
24+
sg_image img;
25+
sg_pipeline pip;
26+
sg_attachments atts;
27+
sg_pass_action action;
28+
} msaa;
29+
struct {
30+
sg_image img;
31+
sg_pipeline pip;
32+
sg_attachments atts;
33+
sg_pass_action action;
34+
sg_bindings bind;
35+
fs_params_t fs_params;
36+
} resolve;
37+
struct {
38+
sg_pipeline pip;
39+
sg_pass_action action;
40+
sg_bindings bind;
41+
} display;
42+
struct {
43+
sgimgui_t sgimgui;
44+
} ui;
45+
sg_sampler smp; // a common non-filtering sampler
46+
} state;
47+
48+
const fs_params_t default_weights = {
49+
.weight0 = 0.25f,
50+
.weight1 = 0.25f,
51+
.weight2 = 0.25f,
52+
.weight3 = 0.25f,
53+
};
54+
55+
static void draw_fallback(void);
56+
static void draw_ui(void);
57+
58+
static void init(void) {
59+
sg_setup(&(sg_desc){
60+
.environment = sglue_environment(),
61+
.logger.func = slog_func,
62+
});
63+
simgui_setup(&(simgui_desc_t){ .logger.func = slog_func });
64+
sgimgui_init(&state.ui.sgimgui, &(sgimgui_desc_t){0});
65+
66+
// catch WebGL2/GLES3
67+
if (!sg_query_features().msaa_image_bindings) {
68+
return;
69+
}
70+
71+
// common objects
72+
state.smp = sg_make_sampler(&(sg_sampler_desc){
73+
.min_filter = SG_FILTER_NEAREST,
74+
.mag_filter = SG_FILTER_NEAREST,
75+
});
76+
77+
// msaa-render-pass objects
78+
state.msaa.img = sg_make_image(&(sg_image_desc){
79+
.render_target = true,
80+
.width = WIDTH,
81+
.height = HEIGHT,
82+
.pixel_format = SG_PIXELFORMAT_RGBA8,
83+
.sample_count = 4,
84+
.label = "msaa image",
85+
});
86+
state.msaa.pip = sg_make_pipeline(&(sg_pipeline_desc){
87+
.shader = sg_make_shader(msaa_shader_desc(sg_query_backend())),
88+
.sample_count = 4,
89+
.depth.pixel_format = SG_PIXELFORMAT_NONE,
90+
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
91+
.label = "msaa pipeline",
92+
});
93+
state.msaa.atts = sg_make_attachments(&(sg_attachments_desc){
94+
.colors[0].image = state.msaa.img,
95+
.label = "msaa pass attachments",
96+
});
97+
state.msaa.action = (sg_pass_action){
98+
.colors[0] = {
99+
.load_action = SG_LOADACTION_CLEAR,
100+
.store_action = SG_STOREACTION_STORE,
101+
.clear_value = { 0, 0, 0, 1 },
102+
},
103+
};
104+
105+
// resolve-render-pass objects
106+
state.resolve.img = sg_make_image(&(sg_image_desc){
107+
.render_target = true,
108+
.width = WIDTH,
109+
.height = HEIGHT,
110+
.pixel_format = SG_PIXELFORMAT_RGBA8,
111+
.sample_count = 1,
112+
.label = "resolve image",
113+
});
114+
state.resolve.pip = sg_make_pipeline(&(sg_pipeline_desc){
115+
.shader = sg_make_shader(resolve_shader_desc(sg_query_backend())),
116+
.sample_count = 1,
117+
.depth.pixel_format = SG_PIXELFORMAT_NONE,
118+
.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8,
119+
.label = "resolve pipeline",
120+
});
121+
state.resolve.atts = sg_make_attachments(&(sg_attachments_desc){
122+
.colors[0].image = state.resolve.img,
123+
.label = "resolve pass attachments",
124+
});
125+
state.resolve.action = (sg_pass_action){
126+
.colors[0] = {
127+
.load_action = SG_LOADACTION_DONTCARE,
128+
.store_action = SG_STOREACTION_STORE
129+
},
130+
};
131+
state.resolve.bind = (sg_bindings){
132+
.images[IMG_texms] = state.msaa.img,
133+
.samplers[SMP_smp] = state.smp,
134+
};
135+
state.resolve.fs_params = default_weights;
136+
137+
// swapchain-render-pass objects
138+
state.display.pip = sg_make_pipeline(&(sg_pipeline_desc){
139+
.shader = sg_make_shader(display_shader_desc(sg_query_backend())),
140+
.label = "display pipeline",
141+
});
142+
state.display.action = (sg_pass_action){
143+
.colors[0] = { .load_action = SG_LOADACTION_DONTCARE }
144+
};
145+
state.display.bind = (sg_bindings){
146+
.images[IMG_tex] = state.resolve.img,
147+
.samplers[SMP_smp] = state.smp,
148+
};
149+
}
150+
151+
static void frame(void) {
152+
draw_ui();
153+
if (!sg_query_features().msaa_image_bindings) {
154+
draw_fallback();
155+
return;
156+
}
157+
158+
// draw a triangle into an msaa render target
159+
sg_begin_pass(&(sg_pass){ .action = state.msaa.action, .attachments = state.msaa.atts });
160+
sg_apply_pipeline(state.msaa.pip);
161+
sg_draw(0, 3, 1);
162+
sg_end_pass();
163+
164+
// custom resolve pass (via a 'fullscreen triangle')
165+
sg_begin_pass(&(sg_pass){ .action = state.resolve.action, .attachments = state.resolve.atts });
166+
sg_apply_pipeline(state.resolve.pip);
167+
sg_apply_bindings(&state.resolve.bind);
168+
sg_apply_uniforms(UB_fs_params, &SG_RANGE(state.resolve.fs_params));
169+
sg_draw(0, 3, 1);
170+
sg_end_pass();
171+
172+
// the final swapchain pass (also via a 'fullscreen triangle')
173+
sg_begin_pass(&(sg_pass){ .action = state.display.action, .swapchain = sglue_swapchain() });
174+
sg_apply_pipeline(state.display.pip);
175+
sg_apply_bindings(&state.display.bind);
176+
sg_draw(0, 3, 1);
177+
simgui_render();
178+
sg_end_pass();
179+
sg_commit();
180+
}
181+
182+
static void cleanup(void) {
183+
sgimgui_discard(&state.ui.sgimgui);
184+
simgui_shutdown();
185+
sg_shutdown();
186+
}
187+
188+
static void draw_ui(void) {
189+
simgui_new_frame(&(simgui_frame_desc_t){
190+
.width = sapp_width(),
191+
.height = sapp_height(),
192+
.dpi_scale = sapp_dpi_scale(),
193+
.delta_time = sapp_frame_duration(),
194+
});
195+
if (igBeginMainMenuBar()) {
196+
sgimgui_draw_menu(&state.ui.sgimgui, "sokol-gfx");
197+
igEndMainMenuBar();
198+
}
199+
sgimgui_draw(&state.ui.sgimgui);
200+
201+
igSetNextWindowPos((ImVec2){10, 20}, ImGuiCond_Once, (ImVec2){0,0});
202+
if (igBegin("#window", 0, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBackground)) {
203+
if (sg_query_features().msaa_image_bindings) {
204+
igText("Sample Weights:");
205+
igSliderFloat("0", &state.resolve.fs_params.weight0, 0.0f, 1.0f, "%.2f", 0);
206+
igSliderFloat("1", &state.resolve.fs_params.weight1, 0.0f, 1.0f, "%.2f", 0);
207+
igSliderFloat("2", &state.resolve.fs_params.weight2, 0.0f, 1.0f, "%.2f", 0);
208+
igSliderFloat("3", &state.resolve.fs_params.weight3, 0.0f, 1.0f, "%.2f", 0);
209+
igCheckboxFlags_IntPtr("show complex pixels", &state.resolve.fs_params.coverage, 1);
210+
if (igButton("Reset", (ImVec2){0,0})) {
211+
state.resolve.fs_params = default_weights;
212+
}
213+
} else {
214+
igText("MSAA TEXTURES NOT SUPPORTED ON WEBGL2/GLES3/macOS+GL");
215+
}
216+
}
217+
igEnd();
218+
}
219+
220+
static void draw_fallback(void) {
221+
sg_begin_pass(&(sg_pass){
222+
.action = {
223+
.colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.5f, 0, 0, 1} },
224+
},
225+
.swapchain = sglue_swapchain(),
226+
});
227+
simgui_render();
228+
sg_end_pass();
229+
sg_commit();
230+
}
231+
232+
static void input(const sapp_event* ev) {
233+
simgui_handle_event(ev);
234+
}
235+
236+
sapp_desc sokol_main(int argc, char* argv[]) {
237+
(void)argc; (void)argv;
238+
return (sapp_desc){
239+
.init_cb = init,
240+
.frame_cb = frame,
241+
.cleanup_cb = cleanup,
242+
.event_cb = input,
243+
.width = 640,
244+
.height = 480,
245+
.sample_count = 1,
246+
.window_title = "customresolve-sapp.c",
247+
.icon.sokol_default = true,
248+
.logger.func = slog_func,
249+
};
250+
}

sapp/customresolve-sapp.glsl

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// triangle shader which renders into an msaa render target
2+
@vs triangle_vs
3+
4+
const vec4 colors[3] = {
5+
vec4(1, 1, 0, 1),
6+
vec4(0, 1, 1, 1),
7+
vec4(1, 0, 1, 1),
8+
};
9+
const vec3 positions[3] = {
10+
vec3(0.0, 0.6, 0.0),
11+
vec3(0.5, -0.6, 0.0),
12+
vec3(-0.5, -0.4, 0.0),
13+
};
14+
15+
out vec4 color;
16+
17+
void main() {
18+
gl_Position = vec4(positions[gl_VertexIndex], 1.0);
19+
color = colors[gl_VertexIndex];
20+
}
21+
@end
22+
23+
@fs triangle_fs
24+
in vec4 color;
25+
out vec4 frag_color;
26+
27+
void main() {
28+
float a = 0;
29+
if ((gl_SampleMaskIn[0] & 15) == 15) {
30+
a = 1;
31+
}
32+
frag_color = vec4(color.xyz, a);
33+
}
34+
@end
35+
36+
@program msaa triangle_vs triangle_fs
37+
38+
@block fullscreen_triangle
39+
const vec3 positions[3] = {
40+
vec3(-1.0, -1.0, 0.0),
41+
vec3(3.0, -1.0, 0.0),
42+
vec3(-1.0, 3.0, 0.0),
43+
};
44+
@end
45+
46+
// custom resolve shader
47+
@vs resolve_vs
48+
@include_block fullscreen_triangle
49+
50+
void main() {
51+
gl_Position = vec4(positions[gl_VertexIndex], 1);
52+
}
53+
@end
54+
55+
@fs resolve_fs
56+
layout(binding=0) uniform texture2DMS texms;
57+
layout(binding=0) uniform sampler smp;
58+
layout(binding=0) uniform fs_params {
59+
float weight0;
60+
float weight1;
61+
float weight2;
62+
float weight3;
63+
int coverage;
64+
};
65+
66+
out vec4 frag_color;
67+
68+
void main() {
69+
ivec2 uv = ivec2(gl_FragCoord.xy);
70+
vec4 s0 = texelFetch(sampler2DMS(texms, smp), uv, 0);
71+
vec4 s1 = texelFetch(sampler2DMS(texms, smp), uv, 1);
72+
vec4 s2 = texelFetch(sampler2DMS(texms, smp), uv, 2);
73+
vec4 s3 = texelFetch(sampler2DMS(texms, smp), uv, 3);
74+
if (coverage != 0) {
75+
if ((s0.w + s1.w + s2.w + s3.w) < 4) {
76+
// complex pixel
77+
frag_color = vec4(1, 0, 0, 1);
78+
} else {
79+
// simple pixel
80+
frag_color = vec4(0, 0, 0, 0);
81+
}
82+
} else {
83+
frag_color = vec4(s0.xyz*weight0 + s1.xyz*weight1 + s2.xyz*weight2 + s3.xyz*weight3, 1);
84+
}
85+
}
86+
@end
87+
88+
@program resolve resolve_vs resolve_fs
89+
90+
// the final display pass shader which renders the custom-resolved texture to the display
91+
@vs display_vs
92+
@glsl_options flip_vert_y
93+
@include_block fullscreen_triangle
94+
95+
out vec2 uv;
96+
97+
void main() {
98+
const vec4 pos = vec4(positions[gl_VertexIndex], 1);
99+
gl_Position = pos;
100+
uv = (pos.xy + 1.0) * 0.5;
101+
}
102+
@end
103+
104+
@fs display_fs
105+
layout(binding=0) uniform texture2D tex;
106+
layout(binding=0) uniform sampler smp;
107+
108+
in vec2 uv;
109+
out vec4 frag_color;
110+
111+
void main() {
112+
frag_color = texture(sampler2D(tex, smp), uv);
113+
}
114+
@end
115+
116+
@program display display_vs display_fs

webpage/customresolve.jpg

26.7 KB
Loading

0 commit comments

Comments
 (0)