diff options
Diffstat (limited to 'contrib/SDL-3.2.8/test/testgpu_spinning_cube.c')
| -rw-r--r-- | contrib/SDL-3.2.8/test/testgpu_spinning_cube.c | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/test/testgpu_spinning_cube.c b/contrib/SDL-3.2.8/test/testgpu_spinning_cube.c new file mode 100644 index 0000000..0eeb034 --- /dev/null +++ b/contrib/SDL-3.2.8/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: */ | ||
