diff options
author | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-08-30 16:53:58 -0700 |
commit | 6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch) | |
tree | 34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c | |
parent | 8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff) |
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c')
-rw-r--r-- | src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c b/src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c new file mode 100644 index 0000000..0eeb034 --- /dev/null +++ b/src/contrib/SDL-3.2.20/test/testgpu_spinning_cube.c | |||
@@ -0,0 +1,761 @@ | |||
1 | /* | ||
2 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
3 | |||
4 | This software is provided 'as-is', without any express or implied | ||
5 | warranty. In no event will the authors be held liable for any damages | ||
6 | arising from the use of this software. | ||
7 | |||
8 | Permission is granted to anyone to use this software for any purpose, | ||
9 | including commercial applications, and to alter it and redistribute it | ||
10 | freely. | ||
11 | */ | ||
12 | |||
13 | #include <stdlib.h> | ||
14 | |||
15 | #ifdef __EMSCRIPTEN__ | ||
16 | #include <emscripten/emscripten.h> | ||
17 | #endif | ||
18 | |||
19 | #include <SDL3/SDL_test_common.h> | ||
20 | #include <SDL3/SDL_gpu.h> | ||
21 | #include <SDL3/SDL_main.h> | ||
22 | |||
23 | /* Regenerate the shaders with testgpu/build-shaders.sh */ | ||
24 | #include "testgpu/testgpu_spirv.h" | ||
25 | #include "testgpu/testgpu_dxil.h" | ||
26 | #include "testgpu/testgpu_metallib.h" | ||
27 | |||
28 | #define TESTGPU_SUPPORTED_FORMATS (SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_METALLIB) | ||
29 | |||
30 | #define CHECK_CREATE(var, thing) { if (!(var)) { SDL_Log("Failed to create %s: %s", thing, SDL_GetError()); quit(2); } } | ||
31 | |||
32 | static Uint32 frames = 0; | ||
33 | |||
34 | typedef struct RenderState | ||
35 | { | ||
36 | SDL_GPUBuffer *buf_vertex; | ||
37 | SDL_GPUGraphicsPipeline *pipeline; | ||
38 | SDL_GPUSampleCount sample_count; | ||
39 | } RenderState; | ||
40 | |||
41 | typedef struct WindowState | ||
42 | { | ||
43 | int angle_x, angle_y, angle_z; | ||
44 | SDL_GPUTexture *tex_depth, *tex_msaa, *tex_resolve; | ||
45 | Uint32 prev_drawablew, prev_drawableh; | ||
46 | } WindowState; | ||
47 | |||
48 | static SDL_GPUDevice *gpu_device = NULL; | ||
49 | static RenderState render_state; | ||
50 | static SDLTest_CommonState *state = NULL; | ||
51 | static WindowState *window_states = NULL; | ||
52 | |||
53 | static void shutdownGPU(void) | ||
54 | { | ||
55 | if (window_states) { | ||
56 | int i; | ||
57 | for (i = 0; i < state->num_windows; i++) { | ||
58 | WindowState *winstate = &window_states[i]; | ||
59 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth); | ||
60 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa); | ||
61 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve); | ||
62 | SDL_ReleaseWindowFromGPUDevice(gpu_device, state->windows[i]); | ||
63 | } | ||
64 | SDL_free(window_states); | ||
65 | window_states = NULL; | ||
66 | } | ||
67 | |||
68 | SDL_ReleaseGPUBuffer(gpu_device, render_state.buf_vertex); | ||
69 | SDL_ReleaseGPUGraphicsPipeline(gpu_device, render_state.pipeline); | ||
70 | SDL_DestroyGPUDevice(gpu_device); | ||
71 | |||
72 | SDL_zero(render_state); | ||
73 | gpu_device = NULL; | ||
74 | } | ||
75 | |||
76 | |||
77 | /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ | ||
78 | static void | ||
79 | quit(int rc) | ||
80 | { | ||
81 | shutdownGPU(); | ||
82 | SDLTest_CommonQuit(state); | ||
83 | exit(rc); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Simulates desktop's glRotatef. The matrix is returned in column-major | ||
88 | * order. | ||
89 | */ | ||
90 | static void | ||
91 | rotate_matrix(float angle, float x, float y, float z, float *r) | ||
92 | { | ||
93 | float radians, c, s, c1, u[3], length; | ||
94 | int i, j; | ||
95 | |||
96 | radians = angle * SDL_PI_F / 180.0f; | ||
97 | |||
98 | c = SDL_cosf(radians); | ||
99 | s = SDL_sinf(radians); | ||
100 | |||
101 | c1 = 1.0f - SDL_cosf(radians); | ||
102 | |||
103 | length = (float)SDL_sqrt(x * x + y * y + z * z); | ||
104 | |||
105 | u[0] = x / length; | ||
106 | u[1] = y / length; | ||
107 | u[2] = z / length; | ||
108 | |||
109 | for (i = 0; i < 16; i++) { | ||
110 | r[i] = 0.0; | ||
111 | } | ||
112 | |||
113 | r[15] = 1.0; | ||
114 | |||
115 | for (i = 0; i < 3; i++) { | ||
116 | r[i * 4 + (i + 1) % 3] = u[(i + 2) % 3] * s; | ||
117 | r[i * 4 + (i + 2) % 3] = -u[(i + 1) % 3] * s; | ||
118 | } | ||
119 | |||
120 | for (i = 0; i < 3; i++) { | ||
121 | for (j = 0; j < 3; j++) { | ||
122 | r[i * 4 + j] += c1 * u[i] * u[j] + (i == j ? c : 0.0f); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Simulates gluPerspectiveMatrix | ||
129 | */ | ||
130 | static void | ||
131 | perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r) | ||
132 | { | ||
133 | int i; | ||
134 | float f; | ||
135 | |||
136 | f = 1.0f/SDL_tanf(fovy * 0.5f); | ||
137 | |||
138 | for (i = 0; i < 16; i++) { | ||
139 | r[i] = 0.0; | ||
140 | } | ||
141 | |||
142 | r[0] = f / aspect; | ||
143 | r[5] = f; | ||
144 | r[10] = (znear + zfar) / (znear - zfar); | ||
145 | r[11] = -1.0f; | ||
146 | r[14] = (2.0f * znear * zfar) / (znear - zfar); | ||
147 | r[15] = 0.0f; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Multiplies lhs by rhs and writes out to r. All matrices are 4x4 and column | ||
152 | * major. In-place multiplication is supported. | ||
153 | */ | ||
154 | static void | ||
155 | multiply_matrix(const float *lhs, const float *rhs, float *r) | ||
156 | { | ||
157 | int i, j, k; | ||
158 | float tmp[16]; | ||
159 | |||
160 | for (i = 0; i < 4; i++) { | ||
161 | for (j = 0; j < 4; j++) { | ||
162 | tmp[j * 4 + i] = 0.0; | ||
163 | |||
164 | for (k = 0; k < 4; k++) { | ||
165 | tmp[j * 4 + i] += lhs[k * 4 + i] * rhs[j * 4 + k]; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | for (i = 0; i < 16; i++) { | ||
171 | r[i] = tmp[i]; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | typedef struct VertexData | ||
176 | { | ||
177 | float x, y, z; /* 3D data. Vertex range -0.5..0.5 in all axes. Z -0.5 is near, 0.5 is far. */ | ||
178 | float red, green, blue; /* intensity 0 to 1 (alpha is always 1). */ | ||
179 | } VertexData; | ||
180 | |||
181 | static const VertexData vertex_data[] = { | ||
182 | /* Front face. */ | ||
183 | /* Bottom left */ | ||
184 | { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ | ||
185 | { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ | ||
186 | { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ | ||
187 | |||
188 | /* Top right */ | ||
189 | { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ | ||
190 | { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ | ||
191 | { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ | ||
192 | |||
193 | /* Left face */ | ||
194 | /* Bottom left */ | ||
195 | { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ | ||
196 | { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ | ||
197 | { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ | ||
198 | |||
199 | /* Top right */ | ||
200 | { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ | ||
201 | { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ | ||
202 | { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ | ||
203 | |||
204 | /* Top face */ | ||
205 | /* Bottom left */ | ||
206 | { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ | ||
207 | { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ | ||
208 | { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ | ||
209 | |||
210 | /* Top right */ | ||
211 | { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ | ||
212 | { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ | ||
213 | { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ | ||
214 | |||
215 | /* Right face */ | ||
216 | /* Bottom left */ | ||
217 | { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ | ||
218 | { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ | ||
219 | { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ | ||
220 | |||
221 | /* Top right */ | ||
222 | { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ | ||
223 | { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ | ||
224 | { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ | ||
225 | |||
226 | /* Back face */ | ||
227 | /* Bottom left */ | ||
228 | { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ | ||
229 | { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ | ||
230 | { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ | ||
231 | |||
232 | /* Top right */ | ||
233 | { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ | ||
234 | { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ | ||
235 | { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ | ||
236 | |||
237 | /* Bottom face */ | ||
238 | /* Bottom left */ | ||
239 | { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ | ||
240 | { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ | ||
241 | { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ | ||
242 | |||
243 | /* Top right */ | ||
244 | { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ | ||
245 | { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ | ||
246 | { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 } /* magenta */ | ||
247 | }; | ||
248 | |||
249 | static SDL_GPUTexture* | ||
250 | CreateDepthTexture(Uint32 drawablew, Uint32 drawableh) | ||
251 | { | ||
252 | SDL_GPUTextureCreateInfo createinfo; | ||
253 | SDL_GPUTexture *result; | ||
254 | |||
255 | createinfo.type = SDL_GPU_TEXTURETYPE_2D; | ||
256 | createinfo.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; | ||
257 | createinfo.width = drawablew; | ||
258 | createinfo.height = drawableh; | ||
259 | createinfo.layer_count_or_depth = 1; | ||
260 | createinfo.num_levels = 1; | ||
261 | createinfo.sample_count = render_state.sample_count; | ||
262 | createinfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; | ||
263 | createinfo.props = 0; | ||
264 | |||
265 | result = SDL_CreateGPUTexture(gpu_device, &createinfo); | ||
266 | CHECK_CREATE(result, "Depth Texture") | ||
267 | |||
268 | return result; | ||
269 | } | ||
270 | |||
271 | static SDL_GPUTexture* | ||
272 | CreateMSAATexture(Uint32 drawablew, Uint32 drawableh) | ||
273 | { | ||
274 | SDL_GPUTextureCreateInfo createinfo; | ||
275 | SDL_GPUTexture *result; | ||
276 | |||
277 | if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) { | ||
278 | return NULL; | ||
279 | } | ||
280 | |||
281 | createinfo.type = SDL_GPU_TEXTURETYPE_2D; | ||
282 | createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); | ||
283 | createinfo.width = drawablew; | ||
284 | createinfo.height = drawableh; | ||
285 | createinfo.layer_count_or_depth = 1; | ||
286 | createinfo.num_levels = 1; | ||
287 | createinfo.sample_count = render_state.sample_count; | ||
288 | createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; | ||
289 | createinfo.props = 0; | ||
290 | |||
291 | result = SDL_CreateGPUTexture(gpu_device, &createinfo); | ||
292 | CHECK_CREATE(result, "MSAA Texture") | ||
293 | |||
294 | return result; | ||
295 | } | ||
296 | |||
297 | static SDL_GPUTexture * | ||
298 | CreateResolveTexture(Uint32 drawablew, Uint32 drawableh) | ||
299 | { | ||
300 | SDL_GPUTextureCreateInfo createinfo; | ||
301 | SDL_GPUTexture *result; | ||
302 | |||
303 | if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) { | ||
304 | return NULL; | ||
305 | } | ||
306 | |||
307 | createinfo.type = SDL_GPU_TEXTURETYPE_2D; | ||
308 | createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); | ||
309 | createinfo.width = drawablew; | ||
310 | createinfo.height = drawableh; | ||
311 | createinfo.layer_count_or_depth = 1; | ||
312 | createinfo.num_levels = 1; | ||
313 | createinfo.sample_count = SDL_GPU_SAMPLECOUNT_1; | ||
314 | createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER; | ||
315 | createinfo.props = 0; | ||
316 | |||
317 | result = SDL_CreateGPUTexture(gpu_device, &createinfo); | ||
318 | CHECK_CREATE(result, "Resolve Texture") | ||
319 | |||
320 | return result; | ||
321 | } | ||
322 | |||
323 | static void | ||
324 | Render(SDL_Window *window, const int windownum) | ||
325 | { | ||
326 | WindowState *winstate = &window_states[windownum]; | ||
327 | SDL_GPUTexture *swapchainTexture; | ||
328 | SDL_GPUColorTargetInfo color_target; | ||
329 | SDL_GPUDepthStencilTargetInfo depth_target; | ||
330 | float matrix_rotate[16], matrix_modelview[16], matrix_perspective[16], matrix_final[16]; | ||
331 | SDL_GPUCommandBuffer *cmd; | ||
332 | SDL_GPURenderPass *pass; | ||
333 | SDL_GPUBufferBinding vertex_binding; | ||
334 | SDL_GPUBlitInfo blit_info; | ||
335 | Uint32 drawablew, drawableh; | ||
336 | |||
337 | /* Acquire the swapchain texture */ | ||
338 | |||
339 | cmd = SDL_AcquireGPUCommandBuffer(gpu_device); | ||
340 | if (!cmd) { | ||
341 | SDL_Log("Failed to acquire command buffer :%s", SDL_GetError()); | ||
342 | quit(2); | ||
343 | } | ||
344 | if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) { | ||
345 | SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError()); | ||
346 | quit(2); | ||
347 | } | ||
348 | |||
349 | if (swapchainTexture == NULL) { | ||
350 | /* Swapchain is unavailable, cancel work */ | ||
351 | SDL_CancelGPUCommandBuffer(cmd); | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Do some rotation with Euler angles. It is not a fixed axis as | ||
357 | * quaternions would be, but the effect is cool. | ||
358 | */ | ||
359 | rotate_matrix((float)winstate->angle_x, 1.0f, 0.0f, 0.0f, matrix_modelview); | ||
360 | rotate_matrix((float)winstate->angle_y, 0.0f, 1.0f, 0.0f, matrix_rotate); | ||
361 | |||
362 | multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview); | ||
363 | |||
364 | rotate_matrix((float)winstate->angle_z, 0.0f, 1.0f, 0.0f, matrix_rotate); | ||
365 | |||
366 | multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview); | ||
367 | |||
368 | /* Pull the camera back from the cube */ | ||
369 | matrix_modelview[14] -= 2.5f; | ||
370 | |||
371 | perspective_matrix(45.0f, (float)drawablew/drawableh, 0.01f, 100.0f, matrix_perspective); | ||
372 | multiply_matrix(matrix_perspective, matrix_modelview, (float*) &matrix_final); | ||
373 | |||
374 | winstate->angle_x += 3; | ||
375 | winstate->angle_y += 2; | ||
376 | winstate->angle_z += 1; | ||
377 | |||
378 | if(winstate->angle_x >= 360) winstate->angle_x -= 360; | ||
379 | if(winstate->angle_x < 0) winstate->angle_x += 360; | ||
380 | if(winstate->angle_y >= 360) winstate->angle_y -= 360; | ||
381 | if(winstate->angle_y < 0) winstate->angle_y += 360; | ||
382 | if(winstate->angle_z >= 360) winstate->angle_z -= 360; | ||
383 | if(winstate->angle_z < 0) winstate->angle_z += 360; | ||
384 | |||
385 | /* Resize the depth buffer if the window size changed */ | ||
386 | |||
387 | if (winstate->prev_drawablew != drawablew || winstate->prev_drawableh != drawableh) { | ||
388 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth); | ||
389 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa); | ||
390 | SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve); | ||
391 | winstate->tex_depth = CreateDepthTexture(drawablew, drawableh); | ||
392 | winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh); | ||
393 | winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh); | ||
394 | } | ||
395 | winstate->prev_drawablew = drawablew; | ||
396 | winstate->prev_drawableh = drawableh; | ||
397 | |||
398 | /* Set up the pass */ | ||
399 | |||
400 | SDL_zero(color_target); | ||
401 | color_target.clear_color.a = 1.0f; | ||
402 | if (winstate->tex_msaa) { | ||
403 | color_target.load_op = SDL_GPU_LOADOP_CLEAR; | ||
404 | color_target.store_op = SDL_GPU_STOREOP_RESOLVE; | ||
405 | color_target.texture = winstate->tex_msaa; | ||
406 | color_target.resolve_texture = winstate->tex_resolve; | ||
407 | color_target.cycle = true; | ||
408 | color_target.cycle_resolve_texture = true; | ||
409 | } else { | ||
410 | color_target.load_op = SDL_GPU_LOADOP_CLEAR; | ||
411 | color_target.store_op = SDL_GPU_STOREOP_STORE; | ||
412 | color_target.texture = swapchainTexture; | ||
413 | } | ||
414 | |||
415 | SDL_zero(depth_target); | ||
416 | depth_target.clear_depth = 1.0f; | ||
417 | depth_target.load_op = SDL_GPU_LOADOP_CLEAR; | ||
418 | depth_target.store_op = SDL_GPU_STOREOP_DONT_CARE; | ||
419 | depth_target.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE; | ||
420 | depth_target.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE; | ||
421 | depth_target.texture = winstate->tex_depth; | ||
422 | depth_target.cycle = true; | ||
423 | |||
424 | /* Set up the bindings */ | ||
425 | |||
426 | vertex_binding.buffer = render_state.buf_vertex; | ||
427 | vertex_binding.offset = 0; | ||
428 | |||
429 | /* Draw the cube! */ | ||
430 | |||
431 | SDL_PushGPUVertexUniformData(cmd, 0, matrix_final, sizeof(matrix_final)); | ||
432 | |||
433 | pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, &depth_target); | ||
434 | SDL_BindGPUGraphicsPipeline(pass, render_state.pipeline); | ||
435 | SDL_BindGPUVertexBuffers(pass, 0, &vertex_binding, 1); | ||
436 | SDL_DrawGPUPrimitives(pass, 36, 1, 0, 0); | ||
437 | SDL_EndGPURenderPass(pass); | ||
438 | |||
439 | /* Blit MSAA resolve target to swapchain, if needed */ | ||
440 | if (render_state.sample_count > SDL_GPU_SAMPLECOUNT_1) { | ||
441 | SDL_zero(blit_info); | ||
442 | blit_info.source.texture = winstate->tex_resolve; | ||
443 | blit_info.source.w = drawablew; | ||
444 | blit_info.source.h = drawableh; | ||
445 | |||
446 | blit_info.destination.texture = swapchainTexture; | ||
447 | blit_info.destination.w = drawablew; | ||
448 | blit_info.destination.h = drawableh; | ||
449 | |||
450 | blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE; | ||
451 | blit_info.filter = SDL_GPU_FILTER_LINEAR; | ||
452 | |||
453 | SDL_BlitGPUTexture(cmd, &blit_info); | ||
454 | } | ||
455 | |||
456 | /* Submit the command buffer! */ | ||
457 | SDL_SubmitGPUCommandBuffer(cmd); | ||
458 | |||
459 | ++frames; | ||
460 | } | ||
461 | |||
462 | static SDL_GPUShader* | ||
463 | load_shader(bool is_vertex) | ||
464 | { | ||
465 | SDL_GPUShaderCreateInfo createinfo; | ||
466 | createinfo.num_samplers = 0; | ||
467 | createinfo.num_storage_buffers = 0; | ||
468 | createinfo.num_storage_textures = 0; | ||
469 | createinfo.num_uniform_buffers = is_vertex ? 1 : 0; | ||
470 | createinfo.props = 0; | ||
471 | |||
472 | SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu_device); | ||
473 | if (format & SDL_GPU_SHADERFORMAT_DXIL) { | ||
474 | createinfo.format = SDL_GPU_SHADERFORMAT_DXIL; | ||
475 | createinfo.code = is_vertex ? D3D12_CubeVert : D3D12_CubeFrag; | ||
476 | createinfo.code_size = is_vertex ? SDL_arraysize(D3D12_CubeVert) : SDL_arraysize(D3D12_CubeFrag); | ||
477 | createinfo.entrypoint = is_vertex ? "VSMain" : "PSMain"; | ||
478 | } else if (format & SDL_GPU_SHADERFORMAT_METALLIB) { | ||
479 | createinfo.format = SDL_GPU_SHADERFORMAT_METALLIB; | ||
480 | createinfo.code = is_vertex ? cube_vert_metallib : cube_frag_metallib; | ||
481 | createinfo.code_size = is_vertex ? cube_vert_metallib_len : cube_frag_metallib_len; | ||
482 | createinfo.entrypoint = is_vertex ? "vs_main" : "fs_main"; | ||
483 | } else { | ||
484 | createinfo.format = SDL_GPU_SHADERFORMAT_SPIRV; | ||
485 | createinfo.code = is_vertex ? cube_vert_spv : cube_frag_spv; | ||
486 | createinfo.code_size = is_vertex ? cube_vert_spv_len : cube_frag_spv_len; | ||
487 | createinfo.entrypoint = "main"; | ||
488 | } | ||
489 | |||
490 | createinfo.stage = is_vertex ? SDL_GPU_SHADERSTAGE_VERTEX : SDL_GPU_SHADERSTAGE_FRAGMENT; | ||
491 | return SDL_CreateGPUShader(gpu_device, &createinfo); | ||
492 | } | ||
493 | |||
494 | static void | ||
495 | init_render_state(int msaa) | ||
496 | { | ||
497 | SDL_GPUCommandBuffer *cmd; | ||
498 | SDL_GPUTransferBuffer *buf_transfer; | ||
499 | void *map; | ||
500 | SDL_GPUTransferBufferLocation buf_location; | ||
501 | SDL_GPUBufferRegion dst_region; | ||
502 | SDL_GPUCopyPass *copy_pass; | ||
503 | SDL_GPUBufferCreateInfo buffer_desc; | ||
504 | SDL_GPUTransferBufferCreateInfo transfer_buffer_desc; | ||
505 | SDL_GPUGraphicsPipelineCreateInfo pipelinedesc; | ||
506 | SDL_GPUColorTargetDescription color_target_desc; | ||
507 | Uint32 drawablew, drawableh; | ||
508 | SDL_GPUVertexAttribute vertex_attributes[2]; | ||
509 | SDL_GPUVertexBufferDescription vertex_buffer_desc; | ||
510 | SDL_GPUShader *vertex_shader; | ||
511 | SDL_GPUShader *fragment_shader; | ||
512 | int i; | ||
513 | |||
514 | gpu_device = SDL_CreateGPUDevice( | ||
515 | TESTGPU_SUPPORTED_FORMATS, | ||
516 | true, | ||
517 | state->gpudriver | ||
518 | ); | ||
519 | CHECK_CREATE(gpu_device, "GPU device"); | ||
520 | |||
521 | /* Claim the windows */ | ||
522 | |||
523 | for (i = 0; i < state->num_windows; i++) { | ||
524 | SDL_ClaimWindowForGPUDevice( | ||
525 | gpu_device, | ||
526 | state->windows[i] | ||
527 | ); | ||
528 | } | ||
529 | |||
530 | /* Create shaders */ | ||
531 | |||
532 | vertex_shader = load_shader(true); | ||
533 | CHECK_CREATE(vertex_shader, "Vertex Shader") | ||
534 | fragment_shader = load_shader(false); | ||
535 | CHECK_CREATE(fragment_shader, "Fragment Shader") | ||
536 | |||
537 | /* Create buffers */ | ||
538 | |||
539 | buffer_desc.usage = SDL_GPU_BUFFERUSAGE_VERTEX; | ||
540 | buffer_desc.size = sizeof(vertex_data); | ||
541 | buffer_desc.props = SDL_CreateProperties(); | ||
542 | SDL_SetStringProperty(buffer_desc.props, SDL_PROP_GPU_BUFFER_CREATE_NAME_STRING, "космонавт"); | ||
543 | render_state.buf_vertex = SDL_CreateGPUBuffer( | ||
544 | gpu_device, | ||
545 | &buffer_desc | ||
546 | ); | ||
547 | CHECK_CREATE(render_state.buf_vertex, "Static vertex buffer") | ||
548 | SDL_DestroyProperties(buffer_desc.props); | ||
549 | |||
550 | transfer_buffer_desc.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; | ||
551 | transfer_buffer_desc.size = sizeof(vertex_data); | ||
552 | transfer_buffer_desc.props = SDL_CreateProperties(); | ||
553 | SDL_SetStringProperty(transfer_buffer_desc.props, SDL_PROP_GPU_TRANSFERBUFFER_CREATE_NAME_STRING, "Transfer Buffer"); | ||
554 | buf_transfer = SDL_CreateGPUTransferBuffer( | ||
555 | gpu_device, | ||
556 | &transfer_buffer_desc | ||
557 | ); | ||
558 | CHECK_CREATE(buf_transfer, "Vertex transfer buffer") | ||
559 | SDL_DestroyProperties(transfer_buffer_desc.props); | ||
560 | |||
561 | /* We just need to upload the static data once. */ | ||
562 | map = SDL_MapGPUTransferBuffer(gpu_device, buf_transfer, false); | ||
563 | SDL_memcpy(map, vertex_data, sizeof(vertex_data)); | ||
564 | SDL_UnmapGPUTransferBuffer(gpu_device, buf_transfer); | ||
565 | |||
566 | cmd = SDL_AcquireGPUCommandBuffer(gpu_device); | ||
567 | copy_pass = SDL_BeginGPUCopyPass(cmd); | ||
568 | buf_location.transfer_buffer = buf_transfer; | ||
569 | buf_location.offset = 0; | ||
570 | dst_region.buffer = render_state.buf_vertex; | ||
571 | dst_region.offset = 0; | ||
572 | dst_region.size = sizeof(vertex_data); | ||
573 | SDL_UploadToGPUBuffer(copy_pass, &buf_location, &dst_region, false); | ||
574 | SDL_EndGPUCopyPass(copy_pass); | ||
575 | SDL_SubmitGPUCommandBuffer(cmd); | ||
576 | |||
577 | SDL_ReleaseGPUTransferBuffer(gpu_device, buf_transfer); | ||
578 | |||
579 | /* Determine which sample count to use */ | ||
580 | render_state.sample_count = SDL_GPU_SAMPLECOUNT_1; | ||
581 | if (msaa && SDL_GPUTextureSupportsSampleCount( | ||
582 | gpu_device, | ||
583 | SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]), | ||
584 | SDL_GPU_SAMPLECOUNT_4)) { | ||
585 | render_state.sample_count = SDL_GPU_SAMPLECOUNT_4; | ||
586 | } | ||
587 | |||
588 | /* Set up the graphics pipeline */ | ||
589 | |||
590 | SDL_zero(pipelinedesc); | ||
591 | SDL_zero(color_target_desc); | ||
592 | |||
593 | color_target_desc.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); | ||
594 | |||
595 | pipelinedesc.target_info.num_color_targets = 1; | ||
596 | pipelinedesc.target_info.color_target_descriptions = &color_target_desc; | ||
597 | pipelinedesc.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; | ||
598 | pipelinedesc.target_info.has_depth_stencil_target = true; | ||
599 | |||
600 | pipelinedesc.depth_stencil_state.enable_depth_test = true; | ||
601 | pipelinedesc.depth_stencil_state.enable_depth_write = true; | ||
602 | pipelinedesc.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL; | ||
603 | |||
604 | pipelinedesc.multisample_state.sample_count = render_state.sample_count; | ||
605 | |||
606 | pipelinedesc.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; | ||
607 | |||
608 | pipelinedesc.vertex_shader = vertex_shader; | ||
609 | pipelinedesc.fragment_shader = fragment_shader; | ||
610 | |||
611 | vertex_buffer_desc.slot = 0; | ||
612 | vertex_buffer_desc.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; | ||
613 | vertex_buffer_desc.instance_step_rate = 0; | ||
614 | vertex_buffer_desc.pitch = sizeof(VertexData); | ||
615 | |||
616 | vertex_attributes[0].buffer_slot = 0; | ||
617 | vertex_attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; | ||
618 | vertex_attributes[0].location = 0; | ||
619 | vertex_attributes[0].offset = 0; | ||
620 | |||
621 | vertex_attributes[1].buffer_slot = 0; | ||
622 | vertex_attributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; | ||
623 | vertex_attributes[1].location = 1; | ||
624 | vertex_attributes[1].offset = sizeof(float) * 3; | ||
625 | |||
626 | pipelinedesc.vertex_input_state.num_vertex_buffers = 1; | ||
627 | pipelinedesc.vertex_input_state.vertex_buffer_descriptions = &vertex_buffer_desc; | ||
628 | pipelinedesc.vertex_input_state.num_vertex_attributes = 2; | ||
629 | pipelinedesc.vertex_input_state.vertex_attributes = (SDL_GPUVertexAttribute*) &vertex_attributes; | ||
630 | |||
631 | pipelinedesc.props = 0; | ||
632 | |||
633 | render_state.pipeline = SDL_CreateGPUGraphicsPipeline(gpu_device, &pipelinedesc); | ||
634 | CHECK_CREATE(render_state.pipeline, "Render Pipeline") | ||
635 | |||
636 | /* These are reference-counted; once the pipeline is created, you don't need to keep these. */ | ||
637 | SDL_ReleaseGPUShader(gpu_device, vertex_shader); | ||
638 | SDL_ReleaseGPUShader(gpu_device, fragment_shader); | ||
639 | |||
640 | /* Set up per-window state */ | ||
641 | |||
642 | window_states = (WindowState *) SDL_calloc(state->num_windows, sizeof (WindowState)); | ||
643 | if (!window_states) { | ||
644 | SDL_Log("Out of memory!"); | ||
645 | quit(2); | ||
646 | } | ||
647 | |||
648 | for (i = 0; i < state->num_windows; i++) { | ||
649 | WindowState *winstate = &window_states[i]; | ||
650 | |||
651 | /* create a depth texture for the window */ | ||
652 | SDL_GetWindowSizeInPixels(state->windows[i], (int*) &drawablew, (int*) &drawableh); | ||
653 | winstate->tex_depth = CreateDepthTexture(drawablew, drawableh); | ||
654 | winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh); | ||
655 | winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh); | ||
656 | |||
657 | /* make each window different */ | ||
658 | winstate->angle_x = (i * 10) % 360; | ||
659 | winstate->angle_y = (i * 20) % 360; | ||
660 | winstate->angle_z = (i * 30) % 360; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | static int done = 0; | ||
665 | |||
666 | void loop(void) | ||
667 | { | ||
668 | SDL_Event event; | ||
669 | int i; | ||
670 | |||
671 | /* Check for events */ | ||
672 | while (SDL_PollEvent(&event) && !done) { | ||
673 | SDLTest_CommonEvent(state, &event, &done); | ||
674 | } | ||
675 | if (!done) { | ||
676 | for (i = 0; i < state->num_windows; ++i) { | ||
677 | Render(state->windows[i], i); | ||
678 | } | ||
679 | } | ||
680 | #ifdef __EMSCRIPTEN__ | ||
681 | else { | ||
682 | emscripten_cancel_main_loop(); | ||
683 | } | ||
684 | #endif | ||
685 | } | ||
686 | |||
687 | int | ||
688 | main(int argc, char *argv[]) | ||
689 | { | ||
690 | int msaa; | ||
691 | int i; | ||
692 | const SDL_DisplayMode *mode; | ||
693 | Uint64 then, now; | ||
694 | |||
695 | /* Initialize params */ | ||
696 | msaa = 0; | ||
697 | |||
698 | /* Initialize test framework */ | ||
699 | state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); | ||
700 | if (!state) { | ||
701 | return 1; | ||
702 | } | ||
703 | for (i = 1; i < argc;) { | ||
704 | int consumed; | ||
705 | |||
706 | consumed = SDLTest_CommonArg(state, i); | ||
707 | if (consumed == 0) { | ||
708 | if (SDL_strcasecmp(argv[i], "--msaa") == 0) { | ||
709 | ++msaa; | ||
710 | consumed = 1; | ||
711 | } else { | ||
712 | consumed = -1; | ||
713 | } | ||
714 | } | ||
715 | if (consumed < 0) { | ||
716 | static const char *options[] = { "[--msaa]", NULL }; | ||
717 | SDLTest_CommonLogUsage(state, argv[0], options); | ||
718 | quit(1); | ||
719 | } | ||
720 | i += consumed; | ||
721 | } | ||
722 | |||
723 | state->skip_renderer = 1; | ||
724 | state->window_flags |= SDL_WINDOW_RESIZABLE; | ||
725 | |||
726 | if (!SDLTest_CommonInit(state)) { | ||
727 | quit(2); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(state->windows[0])); | ||
732 | SDL_Log("Screen bpp: %d", SDL_BITSPERPIXEL(mode->format)); | ||
733 | |||
734 | init_render_state(msaa); | ||
735 | |||
736 | /* Main render loop */ | ||
737 | frames = 0; | ||
738 | then = SDL_GetTicks(); | ||
739 | done = 0; | ||
740 | |||
741 | #ifdef __EMSCRIPTEN__ | ||
742 | emscripten_set_main_loop(loop, 0, 1); | ||
743 | #else | ||
744 | while (!done) { | ||
745 | loop(); | ||
746 | } | ||
747 | #endif | ||
748 | |||
749 | /* Print out some timing information */ | ||
750 | now = SDL_GetTicks(); | ||
751 | if (now > then) { | ||
752 | SDL_Log("%2.2f frames per second", | ||
753 | ((double) frames * 1000) / (now - then)); | ||
754 | } | ||
755 | #if !defined(__ANDROID__) | ||
756 | quit(0); | ||
757 | #endif | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | /* vi: set ts=4 sw=4 expandtab: */ | ||