diff --git a/.vscode/settings.json b/.vscode/settings.json index 243599b..e39b76f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,7 @@ "limits": "cpp", "ratio": "cpp", "sokol_log.h": "c", - "syslog.h": "c" + "syslog.h": "c", + "base.h": "c" } } \ No newline at end of file diff --git a/debug.bat b/debug.bat index 14ad243..c9d62ef 100644 --- a/debug.bat +++ b/debug.bat @@ -1 +1,2 @@ -emcc src/main.c -o app.html -sUSE_WEBGL2 -sASSERTIONS -sWASM_BIGINT -sALLOW_MEMORY_GROWTH -pthread -I../sokol -I../sokol_gp -I../flecs/distr --shell-file=shell.html -sFILESYSTEM=0 -g \ No newline at end of file +sokol-shdc --slang=wgsl -i src/shaders/base.glsl -o src/generated/base.h --ifdef +emcc src/main.c -o app.html -sUSE_WEBGPU -sASSERTIONS -sWASM_BIGINT -sALLOW_MEMORY_GROWTH -pthread -I../sokol -I../sokol_gp -I../flecs/distr --shell-file=shell.html -sFILESYSTEM=0 -g \ No newline at end of file diff --git a/src/generated/base.h b/src/generated/base.h new file mode 100644 index 0000000..9fa8bf3 --- /dev/null +++ b/src/generated/base.h @@ -0,0 +1,427 @@ +#pragma once +/* + #version:1# (machine generated, don't edit!) + + Generated by sokol-shdc (https://github.com/floooh/sokol-tools) + + Cmdline: + sokol-shdc --slang=wgsl -i src/shaders/base.glsl -o src/generated/base.h --ifdef + + Overview: + ========= + Shader program: 'base': + Get shader desc: base_shader_desc(sg_query_backend()); + Vertex Shader: vs + Fragment Shader: fs + Attributes: + ATTR_base_in_quad => 0 + Bindings: + Uniform block 'vs_uniform': + C struct: vs_uniform_t + Bind slot: UB_vs_uniform => 0 + Uniform block 'fs_uniform': + C struct: fs_uniform_t + Bind slot: UB_fs_uniform => 1 + Storage buffer 'vs_ssbo': + C struct: cell_t + Bind slot: SBUF_vs_ssbo => 0 + Readonly: true +*/ +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before base.h" +#endif +#if !defined(SOKOL_SHDC_ALIGN) +#if defined(_MSC_VER) +#define SOKOL_SHDC_ALIGN(a) __declspec(align(a)) +#else +#define SOKOL_SHDC_ALIGN(a) __attribute__((aligned(a))) +#endif +#endif +#define ATTR_base_in_quad (0) +#define UB_vs_uniform (0) +#define UB_fs_uniform (1) +#define SBUF_vs_ssbo (0) +#pragma pack(push,1) +SOKOL_SHDC_ALIGN(16) typedef struct vs_uniform_t { + float radius; + uint8_t _pad_4[12]; +} vs_uniform_t; +#pragma pack(pop) +#pragma pack(push,1) +SOKOL_SHDC_ALIGN(16) typedef struct fs_uniform_t { + float radius; + uint8_t _pad_4[12]; +} fs_uniform_t; +#pragma pack(pop) +#pragma pack(push,1) +SOKOL_SHDC_ALIGN(8) typedef struct cell_t { + float pos[2]; +} cell_t; +#pragma pack(pop) +/* + diagnostic(off, derivative_uniformity); + + struct cell { + /_ @offset(0) _/ + pos : vec2f, + } + + alias RTArr = array; + + struct vs_ssbo { + /_ @offset(0) _/ + cells : RTArr, + } + + struct vs_uniform { + /_ @offset(0) _/ + radius : f32, + } + + var x_centroid : vec2f; + + @group(1) @binding(32) var x_51 : vs_ssbo; + + var gl_InstanceIndex : i32; + + var x_color : vec3f; + + var x_quad : vec2f; + + @group(0) @binding(0) var x_66 : vs_uniform; + + var in_quad : vec2f; + + var gl_Position : vec4f; + + fn color_i1_(i : ptr) -> vec3f { + var r : f32; + var g : f32; + var b : f32; + let x_16 : i32 = *(i); + r = (f32(((x_16 >> bitcast(0i)) & 255i)) / 255.0f); + let x_25 : i32 = *(i); + g = (f32(((x_25 >> bitcast(8i)) & 255i)) / 255.0f); + let x_32 : i32 = *(i); + b = (f32(((x_32 >> bitcast(16i)) & 255i)) / 255.0f); + let x_38 : f32 = r; + let x_39 : f32 = g; + let x_40 : f32 = b; + return vec3f(x_38, x_39, x_40); + } + + fn main_1() { + var param : i32; + let x_54 : i32 = gl_InstanceIndex; + let x_57 : vec2f = x_51.cells[x_54].pos; + x_centroid = x_57; + let x_61 : i32 = gl_InstanceIndex; + param = x_61; + let x_62 : vec3f = color_i1_(&(param)); + x_color = x_62; + let x_69 : f32 = x_66.radius; + let x_72 : vec2f = in_quad; + let x_74 : vec2f = x_centroid; + x_quad = ((x_72 * x_69) + x_74); + let x_83 : vec2f = x_quad; + gl_Position = vec4f(x_83.x, x_83.y, 0.0f, 1.0f); + return; + } + + struct main_out { + @location(2) + x_centroid_1 : vec2f, + @location(3) @interpolate(flat) + x_color_1 : vec3f, + @location(1) + x_quad_1 : vec2f, + @builtin(position) + gl_Position : vec4f, + } + + @vertex + fn main(@builtin(instance_index) gl_InstanceIndex_param : u32, @location(0) in_quad_param : vec2f) -> main_out { + gl_InstanceIndex = bitcast(gl_InstanceIndex_param); + in_quad = in_quad_param; + main_1(); + return main_out(x_centroid, x_color, x_quad, gl_Position); + } + +*/ +#if defined(SOKOL_WGPU) +static const uint8_t vs_source_wgsl[1944] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x63,0x65,0x6c,0x6c,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66, + 0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x70,0x6f,0x73,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x61,0x6c,0x69,0x61, + 0x73,0x20,0x52,0x54,0x41,0x72,0x72,0x20,0x3d,0x20,0x61,0x72,0x72,0x61,0x79,0x3c, + 0x63,0x65,0x6c,0x6c,0x3e,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x73,0x73,0x62,0x6f,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f, + 0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x63,0x65, + 0x6c,0x6c,0x73,0x20,0x3a,0x20,0x52,0x54,0x41,0x72,0x72,0x2c,0x0a,0x7d,0x0a,0x0a, + 0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x73,0x5f,0x75,0x6e,0x69,0x66,0x6f,0x72, + 0x6d,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74, + 0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x72,0x61,0x64,0x69,0x75,0x73,0x20, + 0x3a,0x20,0x66,0x33,0x32,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x78,0x5f,0x63,0x65,0x6e,0x74,0x72,0x6f,0x69, + 0x64,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f, + 0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33, + 0x32,0x29,0x20,0x76,0x61,0x72,0x3c,0x73,0x74,0x6f,0x72,0x61,0x67,0x65,0x2c,0x20, + 0x72,0x65,0x61,0x64,0x3e,0x20,0x78,0x5f,0x35,0x31,0x20,0x3a,0x20,0x76,0x73,0x5f, + 0x73,0x73,0x62,0x6f,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49, + 0x6e,0x64,0x65,0x78,0x20,0x3a,0x20,0x69,0x33,0x32,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x78,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c, + 0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x78,0x5f,0x71,0x75,0x61,0x64,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20, + 0x76,0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x36, + 0x36,0x20,0x3a,0x20,0x76,0x73,0x5f,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3b,0x0a, + 0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x69,0x6e, + 0x5f,0x71,0x75,0x61,0x64,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a, + 0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x69,0x31,0x5f,0x28, + 0x69,0x20,0x3a,0x20,0x70,0x74,0x72,0x3c,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e, + 0x2c,0x20,0x69,0x33,0x32,0x3e,0x29,0x20,0x2d,0x3e,0x20,0x76,0x65,0x63,0x33,0x66, + 0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x72,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x67,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b, + 0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x62,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x36,0x20,0x3a,0x20,0x69,0x33,0x32, + 0x20,0x3d,0x20,0x2a,0x28,0x69,0x29,0x3b,0x0a,0x20,0x20,0x72,0x20,0x3d,0x20,0x28, + 0x66,0x33,0x32,0x28,0x28,0x28,0x78,0x5f,0x31,0x36,0x20,0x3e,0x3e,0x20,0x62,0x69, + 0x74,0x63,0x61,0x73,0x74,0x3c,0x75,0x33,0x32,0x3e,0x28,0x30,0x69,0x29,0x29,0x20, + 0x26,0x20,0x32,0x35,0x35,0x69,0x29,0x29,0x20,0x2f,0x20,0x32,0x35,0x35,0x2e,0x30, + 0x66,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a, + 0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x2a,0x28,0x69,0x29,0x3b,0x0a,0x20,0x20,0x67, + 0x20,0x3d,0x20,0x28,0x66,0x33,0x32,0x28,0x28,0x28,0x78,0x5f,0x32,0x35,0x20,0x3e, + 0x3e,0x20,0x62,0x69,0x74,0x63,0x61,0x73,0x74,0x3c,0x75,0x33,0x32,0x3e,0x28,0x38, + 0x69,0x29,0x29,0x20,0x26,0x20,0x32,0x35,0x35,0x69,0x29,0x29,0x20,0x2f,0x20,0x32, + 0x35,0x35,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x33,0x32,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x2a,0x28,0x69,0x29,0x3b, + 0x0a,0x20,0x20,0x62,0x20,0x3d,0x20,0x28,0x66,0x33,0x32,0x28,0x28,0x28,0x78,0x5f, + 0x33,0x32,0x20,0x3e,0x3e,0x20,0x62,0x69,0x74,0x63,0x61,0x73,0x74,0x3c,0x75,0x33, + 0x32,0x3e,0x28,0x31,0x36,0x69,0x29,0x29,0x20,0x26,0x20,0x32,0x35,0x35,0x69,0x29, + 0x29,0x20,0x2f,0x20,0x32,0x35,0x35,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20, + 0x72,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x39,0x20,0x3a,0x20, + 0x66,0x33,0x32,0x20,0x3d,0x20,0x67,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, + 0x5f,0x34,0x30,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x62,0x3b,0x0a,0x20, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x76,0x65,0x63,0x33,0x66,0x28,0x78,0x5f, + 0x33,0x38,0x2c,0x20,0x78,0x5f,0x33,0x39,0x2c,0x20,0x78,0x5f,0x34,0x30,0x29,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20, + 0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20, + 0x69,0x33,0x32,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x34,0x20, + 0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61, + 0x6e,0x63,0x65,0x49,0x6e,0x64,0x65,0x78,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x35,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78, + 0x5f,0x35,0x31,0x2e,0x63,0x65,0x6c,0x6c,0x73,0x5b,0x78,0x5f,0x35,0x34,0x5d,0x2e, + 0x70,0x6f,0x73,0x3b,0x0a,0x20,0x20,0x78,0x5f,0x63,0x65,0x6e,0x74,0x72,0x6f,0x69, + 0x64,0x20,0x3d,0x20,0x78,0x5f,0x35,0x37,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x36,0x31,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x67,0x6c,0x5f, + 0x49,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x6e,0x64,0x65,0x78,0x3b,0x0a,0x20, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d,0x20,0x78,0x5f,0x36,0x31,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x33, + 0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x69,0x31,0x5f,0x28,0x26,0x28, + 0x70,0x61,0x72,0x61,0x6d,0x29,0x29,0x3b,0x0a,0x20,0x20,0x78,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x36,0x32,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x36,0x39,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f, + 0x36,0x36,0x2e,0x72,0x61,0x64,0x69,0x75,0x73,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x37,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20, + 0x69,0x6e,0x5f,0x71,0x75,0x61,0x64,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, + 0x5f,0x37,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f, + 0x63,0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x3b,0x0a,0x20,0x20,0x78,0x5f,0x71,0x75, + 0x61,0x64,0x20,0x3d,0x20,0x28,0x28,0x78,0x5f,0x37,0x32,0x20,0x2a,0x20,0x78,0x5f, + 0x36,0x39,0x29,0x20,0x2b,0x20,0x78,0x5f,0x37,0x34,0x29,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x38,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20, + 0x3d,0x20,0x78,0x5f,0x71,0x75,0x61,0x64,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28, + 0x78,0x5f,0x38,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x38,0x33,0x2e,0x79,0x2c,0x20, + 0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x72, + 0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74, + 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x0a,0x20,0x20,0x78,0x5f,0x63, + 0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x33, + 0x29,0x20,0x40,0x69,0x6e,0x74,0x65,0x72,0x70,0x6f,0x6c,0x61,0x74,0x65,0x28,0x66, + 0x6c,0x61,0x74,0x29,0x0a,0x20,0x20,0x78,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x78,0x5f,0x71,0x75,0x61, + 0x64,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x40, + 0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72, + 0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x62,0x75,0x69, + 0x6c,0x74,0x69,0x6e,0x28,0x69,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x5f,0x69,0x6e, + 0x64,0x65,0x78,0x29,0x20,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e,0x63,0x65, + 0x49,0x6e,0x64,0x65,0x78,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x75,0x33, + 0x32,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20, + 0x69,0x6e,0x5f,0x71,0x75,0x61,0x64,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f, + 0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e, + 0x63,0x65,0x49,0x6e,0x64,0x65,0x78,0x20,0x3d,0x20,0x62,0x69,0x74,0x63,0x61,0x73, + 0x74,0x3c,0x69,0x33,0x32,0x3e,0x28,0x67,0x6c,0x5f,0x49,0x6e,0x73,0x74,0x61,0x6e, + 0x63,0x65,0x49,0x6e,0x64,0x65,0x78,0x5f,0x70,0x61,0x72,0x61,0x6d,0x29,0x3b,0x0a, + 0x20,0x20,0x69,0x6e,0x5f,0x71,0x75,0x61,0x64,0x20,0x3d,0x20,0x69,0x6e,0x5f,0x71, + 0x75,0x61,0x64,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x78,0x5f,0x63,0x65,0x6e,0x74,0x72, + 0x6f,0x69,0x64,0x2c,0x20,0x78,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x2c,0x20,0x78,0x5f, + 0x71,0x75,0x61,0x64,0x2c,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#endif +/* + diagnostic(off, derivative_uniformity); + + struct fs_uniform { + /_ @offset(0) _/ + radius : f32, + } + + var gl_FragDepth : f32; + + var x_quad : vec2f; + + var x_centroid : vec2f; + + @group(0) @binding(8) var x_20 : fs_uniform; + + var frag_color : vec4f; + + var x_color : vec3f; + + fn main_1() { + let x_12 : vec2f = x_quad; + let x_14 : vec2f = x_centroid; + gl_FragDepth = length((x_12 - x_14)); + let x_17 : f32 = gl_FragDepth; + let x_25 : f32 = x_20.radius; + if ((x_17 > x_25)) { + discard; + } + let x_37 : vec3f = x_color; + frag_color = vec4f(x_37.x, x_37.y, x_37.z, 1.0f); + return; + } + + struct main_out { + @builtin(frag_depth) + gl_FragDepth_1 : f32, + @location(4) + frag_color_1 : vec4f, + } + + @fragment + fn main(@location(1) x_quad_param : vec2f, @location(2) x_centroid_param : vec2f, @location(3) @interpolate(flat) x_color_param : vec3f) -> main_out { + x_quad = x_quad_param; + x_centroid = x_centroid_param; + x_color = x_color_param; + main_1(); + return main_out(gl_FragDepth, frag_color); + } + +*/ +#if defined(SOKOL_WGPU) +static const uint8_t fs_source_wgsl[1047] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x66,0x73,0x5f,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x7b,0x0a,0x20,0x20,0x2f, + 0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a, + 0x20,0x20,0x72,0x61,0x64,0x69,0x75,0x73,0x20,0x3a,0x20,0x66,0x33,0x32,0x2c,0x0a, + 0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x67,0x6c,0x5f,0x46,0x72,0x61,0x67,0x44,0x65,0x70,0x74,0x68,0x20,0x3a,0x20,0x66, + 0x33,0x32,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x78,0x5f,0x71,0x75,0x61,0x64,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x78,0x5f,0x63,0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x40, + 0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x38,0x29,0x20,0x76,0x61,0x72,0x3c,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x30,0x20,0x3a,0x20,0x66, + 0x73,0x5f,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c, + 0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61, + 0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x78,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x31,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20, + 0x78,0x5f,0x71,0x75,0x61,0x64,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x31,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x63, + 0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x46,0x72, + 0x61,0x67,0x44,0x65,0x70,0x74,0x68,0x20,0x3d,0x20,0x6c,0x65,0x6e,0x67,0x74,0x68, + 0x28,0x28,0x78,0x5f,0x31,0x32,0x20,0x2d,0x20,0x78,0x5f,0x31,0x34,0x29,0x29,0x3b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x37,0x20,0x3a,0x20,0x66,0x33, + 0x32,0x20,0x3d,0x20,0x67,0x6c,0x5f,0x46,0x72,0x61,0x67,0x44,0x65,0x70,0x74,0x68, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a,0x20,0x66, + 0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x32,0x30,0x2e,0x72,0x61,0x64,0x69,0x75,0x73, + 0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x78,0x5f,0x31,0x37,0x20,0x3e,0x20, + 0x78,0x5f,0x32,0x35,0x29,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x64,0x69,0x73, + 0x63,0x61,0x72,0x64,0x3b,0x0a,0x20,0x20,0x7d,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x33,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x20,0x3d,0x20,0x78, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33, + 0x37,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x37,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x33, + 0x37,0x2e,0x7a,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75, + 0x69,0x6c,0x74,0x69,0x6e,0x28,0x66,0x72,0x61,0x67,0x5f,0x64,0x65,0x70,0x74,0x68, + 0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x46,0x72,0x61,0x67,0x44,0x65,0x70,0x74,0x68, + 0x5f,0x31,0x20,0x3a,0x20,0x66,0x33,0x32,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x34,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c, + 0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x20,0x78,0x5f,0x71,0x75,0x61,0x64,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x32,0x29,0x20,0x78,0x5f,0x63,0x65,0x6e,0x74,0x72,0x6f,0x69,0x64, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x33,0x29,0x20,0x40,0x69,0x6e, + 0x74,0x65,0x72,0x70,0x6f,0x6c,0x61,0x74,0x65,0x28,0x66,0x6c,0x61,0x74,0x29,0x20, + 0x78,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x33,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f, + 0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x78,0x5f,0x71,0x75,0x61,0x64,0x20,0x3d,0x20, + 0x78,0x5f,0x71,0x75,0x61,0x64,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20, + 0x78,0x5f,0x63,0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x20,0x3d,0x20,0x78,0x5f,0x63, + 0x65,0x6e,0x74,0x72,0x6f,0x69,0x64,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x78,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d, + 0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f,0x46,0x72,0x61,0x67,0x44, + 0x65,0x70,0x74,0x68,0x2c,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#endif +static inline const sg_shader_desc* base_shader_desc(sg_backend backend) { + #if defined(SOKOL_WGPU) + if (backend == SG_BACKEND_WGPU) { + static sg_shader_desc desc; + static bool valid; + if (!valid) { + valid = true; + desc.vertex_func.source = (const char*)vs_source_wgsl; + desc.vertex_func.entry = "main"; + desc.fragment_func.source = (const char*)fs_source_wgsl; + desc.fragment_func.entry = "main"; + desc.attrs[0].base_type = SG_SHADERATTRBASETYPE_FLOAT; + desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX; + desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140; + desc.uniform_blocks[0].size = 16; + desc.uniform_blocks[0].wgsl_group0_binding_n = 0; + desc.uniform_blocks[1].stage = SG_SHADERSTAGE_FRAGMENT; + desc.uniform_blocks[1].layout = SG_UNIFORMLAYOUT_STD140; + desc.uniform_blocks[1].size = 16; + desc.uniform_blocks[1].wgsl_group0_binding_n = 8; + desc.storage_buffers[0].stage = SG_SHADERSTAGE_VERTEX; + desc.storage_buffers[0].readonly = true; + desc.storage_buffers[0].wgsl_group1_binding_n = 32; + desc.label = "base_shader"; + } + return &desc; + } + #endif /* SOKOL_WGPU */ + return 0; +} diff --git a/src/main.c b/src/main.c index c28d08f..4a991ea 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,7 @@ // Includes Sokol GFX, Sokol GP and Sokol APP, doing all implementations. #define SOKOL_IMPL -#define SOKOL_GLES3 +#define SOKOL_WGPU #include "sokol_gfx.h" #include "sokol_gp.h" @@ -11,6 +11,8 @@ #include "sokol_log.h" #include "util/sokol_memtrack.h" +#include "generated/base.h" + #include #include #include @@ -19,6 +21,8 @@ #define ALLOC(arg) smemtrack_alloc(arg, NULL) #define FREE(arg) smemtrack_free(arg, NULL) +#define SAMPLE_COUNT 100000 + void js_log(int severity, const char* format, ...) { char buffer[_SLOG_LINE_LENGTH]; @@ -37,52 +41,42 @@ typedef struct position_t float x, y; } position_t; +typedef struct renderer_t { + sg_pipeline pipeline; + sg_pipeline compute; + sg_bindings* binding; + sg_range uniform; +} renderer_t; + typedef struct userdata_t { - position_t* cells; position_t pan; float zoom; - sg_pipeline* pipeline; + renderer_t renderer; } userdata_t; // Called on every frame of the application. static void frame(void* _userdata) { userdata_t* userdata = (userdata_t*) _userdata; - // Get current window size. - int width = sapp_width(), height = sapp_height(); - float ratio = width/(float)height; - // Begin recording draw commands for a frame buffer of size (width, height). - sgp_begin(width, height); - // Set frame buffer drawing region to (0,0,width,height). - sgp_viewport(0, 0, width, height); - // Set drawing coordinate space to (left=-ratio, right=ratio, top=1, bottom=-1). - sgp_project(-ratio, ratio, 1.0f, -1.0f); + sg_begin_pass(&(sg_pass) { + .action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f}, + }, + }, + .swapchain = sglue_swapchain(), + }); + sg_apply_pipeline(userdata->renderer.pipeline); + sg_apply_bindings(userdata->renderer.binding); + sg_apply_uniforms(UB_vs_uniform, &userdata->renderer.uniform); + sg_apply_uniforms(UB_fs_uniform, &userdata->renderer.uniform); - sgp_translate(userdata->pan.x, userdata->pan.y); - sgp_scale(userdata->zoom, userdata->zoom); + sg_draw(0, 4, SAMPLE_COUNT); - // Clear the frame buffer. - sgp_set_color(0.0f, 0.0f, 0.0f, 1.0f); - sgp_clear(); - - // Draw an animated rectangle that rotates and changes its colors. - /*float time = sapp_frame_count() * sapp_frame_duration();*/ - sgp_state* state = sgp_query_state(); - state->thickness = 0.01f; - sgp_set_color(0.5f, 0.6f, 0.3f, 1.0f); - - // Begin a render pass. - sg_pass pass = {.swapchain = sglue_swapchain()}; - sg_begin_pass(&pass); - // Dispatch all draw commands to Sokol GFX. - sgp_flush(); - // Finish a draw command queue, clearing it. - sgp_end(); - // End render pass. sg_end_pass(); - // Commit Sokol render. sg_commit(); } @@ -100,14 +94,63 @@ static void init(void* _userdata) fprintf(stderr, "Failed to create Sokol GFX context!\n"); exit(-1); } + + const position_t quad[4] = { + {0.0f, 1.0f}, // bottom left + {1.0f, 1.0f}, // bottom right + {1.0f, 0.0f}, // top right + {0.0f, 0.0f}, // top left + }; + const uint16_t indices[] = { + 0, 1, 2, 0, 2, 3, + }; + + const vs_uniform_t uniform = { + .radius = 1, + }; + + void* tmp_buffer = malloc(sizeof(position_t) * SAMPLE_COUNT); + + userdata->renderer = (renderer_t) { + .pipeline = sg_make_pipeline(&(sg_pipeline_desc) { + .shader = sg_make_shader(base_shader_desc(sg_query_backend())), + .depth.write_enabled = true, + .layout.attrs = { + [ATTR_base_in_quad].format = SG_VERTEXFORMAT_FLOAT2, + }, + }), + /*.compute = sg_make_pipeline(&(sg_pipeline_desc) { + .compute = true, + .shader = sg_make_shader(compute_shader_desc(sg_query_backend())), + }),*/ + .binding = &(sg_bindings) { + .vertex_buffers = { + [0] = sg_make_buffer(&(sg_buffer_desc) { + .type = SG_BUFFERTYPE_VERTEXBUFFER, + .data = SG_RANGE(quad), + }), + }, + .index_buffer = sg_make_buffer(&(sg_buffer_desc) { + .type = SG_BUFFERTYPE_INDEXBUFFER, + .data = SG_RANGE(indices), + }), + .storage_buffers = { + [SBUF_vs_ssbo] = sg_make_buffer(&(sg_buffer_desc) { + .type = SG_BUFFERTYPE_STORAGEBUFFER, + .data = { + .ptr = tmp_buffer, + .size = sizeof(position_t) * SAMPLE_COUNT, + }, + }), + }, + }, + .uniform = &SG_RANGE(uniform), + }; - // Initialize Sokol GP, adjust the size of command buffers for your own use. - sgp_desc sgpdesc = { 0 }; - sgp_setup(&sgpdesc); - if (!sgp_is_valid()) { - fprintf(stderr, "Failed to create Sokol GP context: %s\n", sgp_get_error_message(sgp_get_last_error())); - exit(-1); - } + userdata->pan = (position_t) { .x = 0, .y = 0 }; + userdata->zoom = 1; + + FREE(tmp_buffer); } // Called when the application is shutting down. @@ -115,15 +158,12 @@ static void cleanup(void* _userdata) { userdata_t* userdata = (userdata_t*) _userdata; - FREE(userdata->cells); FREE(userdata); - // Cleanup Sokol GP and Sokol GFX resources. - sgp_shutdown(); - sg_shutdown(); - smemtrack_info_t info = smemtrack_info(); - //info.num_allocs + js_log(3, "Leaks: %d. Amount: %db", info.num_allocs, info.num_bytes); + + sg_shutdown(); } static void event(const sapp_event* event, void* _userdata) @@ -163,8 +203,6 @@ static void event(const sapp_event* event, void* _userdata) sapp_desc sokol_main(int argc, char* argv[]) { userdata_t* userdata = (userdata_t*) ALLOC(sizeof(userdata_t)); - userdata->pan = (position_t) { .x = 0, .y = 0 }; - userdata->zoom = 1; (void)argc; (void)argv; @@ -174,7 +212,7 @@ sapp_desc sokol_main(int argc, char* argv[]) .frame_userdata_cb = frame, .cleanup_userdata_cb = cleanup, .event_userdata_cb = event, - .window_title = "Rectangle (Sokol GP)", + .window_title = "Sokol", .allocator = { .alloc_fn = smemtrack_alloc, .free_fn = smemtrack_free, diff --git a/src/shaders/base.glsl b/src/shaders/base.glsl new file mode 100644 index 0000000..1281457 --- /dev/null +++ b/src/shaders/base.glsl @@ -0,0 +1,60 @@ +@block common +struct cell +{ + vec2 pos; +}; +vec3 color(int i) +{ + float r = ((i >> 0) & 0xff)/255.0f; + float g = ((i >> 8) & 0xff)/255.0f; + float b = ((i >> 16) & 0xff)/255.0f; + return vec3(r, g, b); +} +@end + +@vs vs +@include_block common +layout(binding = 0) uniform vs_uniform { + float radius; +}; + +layout(location = 0) in vec2 in_quad; +layout(location = 1) out vec2 _quad; +layout(location = 2) out vec2 _centroid; +layout(location = 3) flat out vec3 _color; + +layout(binding = 0) readonly buffer vs_ssbo +{ + cell cells[]; +}; + +void main() +{ + _centroid = cells[gl_InstanceIndex].pos; + _color = color(gl_InstanceIndex); + _quad = radius * in_quad + _centroid; + gl_Position = vec4(_quad, 0.0, 1.0); +} +@end + +@fs fs +@include_block common + +layout(binding = 1) uniform fs_uniform { + float radius; +}; + +layout(location = 1) in vec2 _quad; +layout(location = 2) in vec2 _centroid; +layout(location = 3) flat in vec3 _color; +layout(location = 4) out vec4 frag_color; + +void main() +{ + gl_FragDepth = length(_quad - _centroid); + if(gl_FragDepth > radius) discard; + frag_color = vec4(_color, 1.0); +} +@end + +@program base vs fs \ No newline at end of file