1#include "sokol.h"
2
3static struct
4{
5 sg_draw_state draw_state;
6 int fb_width;
7 int fb_height;
8 int fb_aspect_scale_x;
9 int fb_aspect_scale_y;
10
11 bool integer_scale;
12 bool portrait_top_align;
13} sokol_gfx;
14
15#if defined(SOKOL_GLCORE33)
16static const char* gfx_vs_src =
17 "#version 330\n"
18 "in vec2 in_pos;\n"
19 "in vec2 in_uv;\n"
20 "out vec2 uv;\n"
21 "void main() {\n"
22 " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n"
23 " uv = in_uv;\n"
24 "}\n";
25static const char* gfx_fs_src =
26 "#version 330\n"
27 "uniform sampler2D tex;\n"
28 "in vec2 uv;\n"
29 "out vec4 frag_color;\n"
30 "void main() {\n"
31 " frag_color = texture(tex, uv);\n"
32 "}\n";
33#elif defined(SOKOL_GLES2)
34static const char* gfx_vs_src =
35 "attribute vec2 in_pos;\n"
36 "attribute vec2 in_uv;\n"
37 "varying vec2 uv;\n"
38 "void main() {\n"
39 " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n"
40 " uv = in_uv;\n"
41 "}\n";
42static const char* gfx_fs_src =
43 "precision mediump float;\n"
44 "uniform sampler2D tex;"
45 "varying vec2 uv;\n"
46 "void main() {\n"
47 " gl_FragColor = texture2D(tex, uv);\n"
48 "}\n";
49#elif defined(SOKOL_METAL)
50static const char* gfx_vs_src =
51 "#include <metal_stdlib>\n"
52 "using namespace metal;\n"
53 "struct vs_in {\n"
54 " float2 pos [[attribute(0)]];\n"
55 " float2 uv [[attribute(1)]];\n"
56 "};\n"
57 "struct vs_out {\n"
58 " float4 pos [[position]];\n"
59 " float2 uv;\n"
60 "};\n"
61 "vertex vs_out _main(vs_in in [[stage_in]]) {\n"
62 " vs_out out;\n"
63 " out.pos = float4(in.pos*2.0-1.0, 0.5, 1.0);\n"
64 " out.uv = in.uv;\n"
65 " return out;\n"
66 "}\n";
67static const char* gfx_fs_src =
68 "#include <metal_stdlib>\n"
69 "using namespace metal;\n"
70 "struct fs_in {\n"
71 " float2 uv;\n"
72 "};\n"
73 "fragment float4 _main(fs_in in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n"
74 " return tex.sample(smp, in.uv);\n"
75 "}\n";
76#elif defined(SOKOL_D3D11)
77static const char* gfx_vs_src =
78 "struct vs_in {\n"
79 " float2 pos: POS;\n"
80 " float2 uv: UV;\n"
81 "};\n"
82 "struct vs_out {\n"
83 " float2 uv: TEXCOORD0;\n"
84 " float4 pos: SV_Position;\n"
85 "};\n"
86 "vs_out main(vs_in inp) {\n"
87 " vs_out outp;\n"
88 " outp.pos = float4(inp.pos*2.0-1.0, 0.5, 1.0);\n"
89 " outp.uv = inp.uv;\n"
90 " return outp;\n"
91 "}\n";
92static const char* gfx_fs_src =
93 "Texture2D<float4> tex: register(t0);\n"
94 "sampler smp: register(s0);\n"
95 "float4 main(float2 uv: TEXCOORD0): SV_Target0 {\n"
96 " return tex.Sample(smp, uv);\n"
97 "}\n";
98#endif
99
100void sokol_gfx_init(int w, int h, int sx, int sy, bool integer_scale, bool portrait_top_align) {
101 sokol_gfx.fb_width = w;
102 sokol_gfx.fb_height = h;
103 sokol_gfx.fb_aspect_scale_x = sx;
104 sokol_gfx.fb_aspect_scale_y = sy;
105 sokol_gfx.integer_scale = integer_scale;
106 sokol_gfx.portrait_top_align = portrait_top_align;
107
108 sg_setup(&(sg_desc){
109 .mtl_device = sapp_metal_get_device(),
110 .mtl_renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor,
111 .mtl_drawable_cb = sapp_metal_get_drawable,
112 .d3d11_device = sapp_d3d11_get_device(),
113 .d3d11_device_context = sapp_d3d11_get_device_context(),
114 .d3d11_render_target_view_cb = sapp_d3d11_get_render_target_view,
115 .d3d11_depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view
116 });
117
118 /* the vertex buffer representing screen rectangle */
119 float verts[] = {
120 0.0f, 0.0f, 0.0f, 1.0f,
121 1.0f, 0.0f, 1.0f, 1.0f,
122 0.0f, 1.0f, 0.0f, 0.0f,
123 1.0f, 1.0f, 1.0f, 0.0f
124 };
125 sokol_gfx.draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
126 .size = sizeof(verts),
127 .content = verts,
128 });
129
130 /* a shader to render a textured quad */
131 sg_shader fsq_shd = sg_make_shader(&(sg_shader_desc){
132 .fs.images = {
133 [0] = { .name="tex", .type=SG_IMAGETYPE_2D },
134 },
135 .vs.source = gfx_vs_src,
136 .fs.source = gfx_fs_src,
137 });
138
139 /* a pipeline-state-object for rendering */
140 sg_pipeline_desc pip_desc = {
141 .layout = {
142 .attrs[0] = { .name="in_pos", .sem_name="POS", .format=SG_VERTEXFORMAT_FLOAT2 },
143 .attrs[1] = { .name="in_uv", .sem_name="UV", .format=SG_VERTEXFORMAT_FLOAT2 }
144 },
145 .shader = fsq_shd,
146 .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP
147 };
148 sokol_gfx.draw_state.pipeline = sg_make_pipeline(&pip_desc);
149
150 /* a texture with the pixel data */
151 sokol_gfx.draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){
152 .width = sokol_gfx.fb_width,
153 .height = sokol_gfx.fb_height,
154 .pixel_format = SG_PIXELFORMAT_RGBA8,
155 .usage = SG_USAGE_STREAM,
156 .min_filter = SG_FILTER_NEAREST,
157 .mag_filter = SG_FILTER_NEAREST,
158 .wrap_u = SG_WRAP_CLAMP_TO_EDGE,
159 .wrap_v = SG_WRAP_CLAMP_TO_EDGE
160 });
161}
162
163static const sg_pass_action gfx_draw_pass_action = {
164 .colors[0] = { .action = SG_ACTION_CLEAR, .val = { 0.05f, 0.05f, 0.05f, 1.0f } }
165};
166
167void sokol_calc_viewport(int* x, int* y, int* w, int* h)
168{
169 int vp_x = 0, vp_y = 0, vp_w = sapp_width(), vp_h = sapp_height();
170
171 if (vp_w * sokol_gfx.fb_height < vp_h * sokol_gfx.fb_width)
172 {
173 int discreteWidth = vp_w - (sokol_gfx.integer_scale ? vp_w % sokol_gfx.fb_width : 0);
174 int discreteHeight = sokol_gfx.fb_height * discreteWidth / sokol_gfx.fb_width;
175
176 vp_x = (vp_w - discreteWidth) / 2;
177 vp_y = sokol_gfx.portrait_top_align && vp_w < vp_h
178 ? 0
179 : (vp_h - discreteHeight) / 2;
180
181 vp_w = discreteWidth;
182 vp_h = discreteHeight;
183 }
184 else
185 {
186 int discreteHeight = vp_h - (sokol_gfx.integer_scale ? vp_h % sokol_gfx.fb_height : 0);
187 int discreteWidth = sokol_gfx.fb_width * discreteHeight / sokol_gfx.fb_height;
188
189 vp_x = (vp_w - discreteWidth) / 2;
190 vp_y = (vp_h - discreteHeight) / 2;
191
192 vp_w = discreteWidth;
193 vp_h = discreteHeight;
194 }
195
196 *x = vp_x;
197 *y = vp_y;
198 *w = vp_w;
199 *h = vp_h;
200}
201
202
203static void apply_viewport(void) {
204
205 int vp_x, vp_y, vp_w, vp_h;
206 sokol_calc_viewport(&vp_x, &vp_y, &vp_w, &vp_h);
207
208 sg_apply_viewport(vp_x, vp_y, vp_w, vp_h, true);
209}
210
211void sokol_gfx_draw(const uint32_t* ptr) {
212
213 /* copy pixel data into the source texture */
214 sg_update_image(sokol_gfx.draw_state.fs_images[0], &(sg_image_content){
215 .subimage[0][0] = {
216 .ptr = ptr,
217 .size = sokol_gfx.fb_width*sokol_gfx.fb_height*sizeof ptr[0]
218 }
219 });
220
221 /* draw to the screen */
222 sg_begin_default_pass(&gfx_draw_pass_action, sapp_width(), sapp_height());
223 apply_viewport();
224 sg_apply_draw_state(&sokol_gfx.draw_state);
225 sg_draw(0, 4, 1);
226 sg_end_pass();
227 sg_commit();
228}
229