Atlas - testgpu_spinning_cube.c
Home / ext / SDL / test Lines: 5 | Size: 35457 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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/* Note: This demo shows an example of using the SDL 2D renderer in combination 14 * with the full GPU API. This is purely to demonstrate offline rendering 15 * functionality of the GPU 2D renderer, and is not generally recommended 16 * for real applications. 17 * 18 * A blog post demonstrating a highly efficient method for 2D sprite batching 19 * with the GPU API is available here: 20 * https://moonside.games/posts/sdl-gpu-sprite-batcher/ 21 */ 22 23#include <stdlib.h> 24 25#ifdef __EMSCRIPTEN__ 26#include <emscripten/emscripten.h> 27#endif 28 29#include <SDL3/SDL_test_common.h> 30#include <SDL3/SDL_gpu.h> 31#include <SDL3/SDL_main.h> 32 33#include "icon.h" 34 35/* Regenerate the shaders with testgpu/build-shaders.sh */ 36#include "testgpu/cube.frag.dxil.h" 37#include "testgpu/cube.frag.msl.h" 38#include "testgpu/cube.frag.spv.h" 39#include "testgpu/cube.vert.dxil.h" 40#include "testgpu/cube.vert.msl.h" 41#include "testgpu/cube.vert.spv.h" 42#include "testgpu/overlay.frag.dxil.h" 43#include "testgpu/overlay.frag.msl.h" 44#include "testgpu/overlay.frag.spv.h" 45#include "testgpu/overlay.vert.dxil.h" 46#include "testgpu/overlay.vert.msl.h" 47#include "testgpu/overlay.vert.spv.h" 48 49#define TESTGPU_SUPPORTED_FORMATS (SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_METALLIB) 50 51#define CHECK_CREATE(var, thing) { if (!(var)) { SDL_Log("Failed to create %s: %s", thing, SDL_GetError()); quit(2); } } 52 53static Uint32 frames = 0; 54 55typedef struct RenderState 56{ 57 SDL_GPUBuffer *buf_vertex; 58 SDL_GPUGraphicsPipeline *pipeline; 59 SDL_GPUSampleCount sample_count; 60} RenderState; 61 62#define NUM_SPRITES 100 63#define MAX_SPRITE_SPEED 1 64 65typedef struct SpriteRenderState 66{ 67 SDL_Renderer *renderer; 68 SDL_Texture *sprite; 69 SDL_GPUGraphicsPipeline *pipeline; 70 SDL_GPUSampler *sampler; 71 bool show_sprites; 72} SpriteRenderState; 73 74typedef struct SpriteWindowState 75{ 76 bool initialized; 77 SDL_Texture *target; 78 SDL_GPUTexture *texture; 79 SDL_FRect positions[NUM_SPRITES]; 80 SDL_FPoint velocities[NUM_SPRITES]; 81} SpriteWindowState; 82 83typedef struct WindowState 84{ 85 int angle_x, angle_y, angle_z; 86 SDL_GPUTexture *tex_depth, *tex_msaa, *tex_resolve; 87 Uint32 prev_drawablew, prev_drawableh; 88 SpriteWindowState sprite_state; 89} WindowState; 90 91static SDL_GPUDevice *gpu_device = NULL; 92static RenderState render_state; 93static SpriteRenderState sprite_render_state; 94static SDLTest_CommonState *state = NULL; 95static WindowState *window_states = NULL; 96 97static void QuitSpriteOverlay(SpriteRenderState *rs) 98{ 99 int i; 100 101 if (!rs->renderer) { 102 return; 103 } 104 105 SDL_ReleaseGPUSampler(gpu_device, rs->sampler); 106 SDL_ReleaseGPUGraphicsPipeline(gpu_device, rs->pipeline); 107 108 for (i = 0; i < state->num_windows; ++i) { 109 SpriteWindowState *ws = &window_states[i].sprite_state; 110 if (ws->target) { 111 SDL_DestroyTexture(ws->target); 112 ws->target = NULL; 113 ws->texture = NULL; 114 } 115 } 116 SDL_DestroyRenderer(rs->renderer); 117 118 SDL_zerop(rs); 119} 120 121static SDL_Texture *CreateSpriteTexture(SDL_Renderer *renderer, unsigned char *data, unsigned int len) 122{ 123 SDL_Texture *texture = NULL; 124 SDL_Surface *surface; 125 SDL_IOStream *src = SDL_IOFromConstMem(data, len); 126 if (src) { 127 surface = SDL_LoadPNG_IO(src, true); 128 if (surface) { 129 /* Treat white as transparent */ 130 SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, 255, 255, 255)); 131 132 texture = SDL_CreateTextureFromSurface(renderer, surface); 133 SDL_DestroySurface(surface); 134 } 135 } 136 return texture; 137} 138 139static SDL_GPUShader *LoadOverlayShader(bool is_vertex) 140{ 141 SDL_GPUShaderCreateInfo createinfo; 142 SDL_zero(createinfo); 143 createinfo.num_samplers = is_vertex ? 0 : 1; 144 145 SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu_device); 146 if (format & SDL_GPU_SHADERFORMAT_DXIL) { 147 createinfo.format = SDL_GPU_SHADERFORMAT_DXIL; 148 createinfo.code = is_vertex ? overlay_vert_dxil : overlay_frag_dxil; 149 createinfo.code_size = is_vertex ? overlay_vert_dxil_len : overlay_frag_dxil_len; 150 } else if (format & SDL_GPU_SHADERFORMAT_MSL) { 151 createinfo.format = SDL_GPU_SHADERFORMAT_MSL; 152 createinfo.code = is_vertex ? overlay_vert_msl : overlay_frag_msl; 153 createinfo.code_size = is_vertex ? overlay_vert_msl_len : overlay_frag_msl_len; 154 } else { 155 createinfo.format = SDL_GPU_SHADERFORMAT_SPIRV; 156 createinfo.code = is_vertex ? overlay_vert_spv : overlay_frag_spv; 157 createinfo.code_size = is_vertex ? overlay_vert_spv_len : overlay_frag_spv_len; 158 } 159 160 createinfo.stage = is_vertex ? SDL_GPU_SHADERSTAGE_VERTEX : SDL_GPU_SHADERSTAGE_FRAGMENT; 161 return SDL_CreateGPUShader(gpu_device, &createinfo); 162} 163 164static bool InitSpriteOverlay(SpriteRenderState *rs, SDL_Window *window) 165{ 166 SDL_GPUShader *vertex_shader, *fragment_shader; 167 168 rs->renderer = SDL_CreateGPURenderer(gpu_device, NULL); 169 if (!rs->renderer) { 170 SDL_Log("Couldn't create renderer: %s\n", SDL_GetError()); 171 return false; 172 } 173 174 rs->sprite = CreateSpriteTexture(rs->renderer, icon_png, icon_png_len); 175 if (!rs->sprite) { 176 SDL_Log("Couldn't create sprite: %s\n", SDL_GetError()); 177 QuitSpriteOverlay(rs); 178 return false; 179 } 180 181 vertex_shader = LoadOverlayShader(true); 182 if (!vertex_shader) { 183 SDL_Log("Couldn't create vertex shader: %s\n", SDL_GetError()); 184 QuitSpriteOverlay(rs); 185 return false; 186 } 187 188 fragment_shader = LoadOverlayShader(false); 189 if (!fragment_shader) { 190 SDL_Log("Couldn't create vertex shader: %s\n", SDL_GetError()); 191 SDL_ReleaseGPUShader(gpu_device, vertex_shader); 192 QuitSpriteOverlay(rs); 193 return false; 194 } 195 196 SDL_GPUColorTargetDescription ctd; 197 SDL_zero(ctd); 198 ctd.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, window); 199 ctd.blend_state.enable_blend = true; 200 ctd.blend_state.color_write_mask = 0xF; 201 ctd.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; 202 ctd.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; 203 ctd.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; 204 ctd.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; 205 ctd.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; 206 ctd.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; 207 208 SDL_GPUGraphicsPipelineCreateInfo pci; 209 SDL_zero(pci); 210 pci.target_info.num_color_targets = 1; 211 pci.target_info.color_target_descriptions = &ctd; 212 pci.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; 213 pci.vertex_shader = vertex_shader; 214 pci.fragment_shader = fragment_shader; 215 pci.rasterizer_state.enable_depth_clip = true; 216 rs->pipeline = SDL_CreateGPUGraphicsPipeline(gpu_device, &pci); 217 SDL_ReleaseGPUShader(gpu_device, vertex_shader); 218 SDL_ReleaseGPUShader(gpu_device, fragment_shader); 219 if (!rs->pipeline) { 220 SDL_Log("Couldn't create pipeline: %s", SDL_GetError()); 221 QuitSpriteOverlay(rs); 222 return false; 223 } 224 225 SDL_GPUSamplerCreateInfo sci; 226 SDL_zero(sci); 227 sci.min_filter = SDL_GPU_FILTER_NEAREST; 228 sci.mag_filter = SDL_GPU_FILTER_NEAREST; 229 sci.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; 230 sci.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 231 sci.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 232 sci.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 233 rs->sampler = SDL_CreateGPUSampler(gpu_device, &sci); 234 if (!rs->sampler) { 235 SDL_Log("Couldn't create sampler: %s", SDL_GetError()); 236 QuitSpriteOverlay(rs); 237 return false; 238 } 239 240 return true; 241} 242 243static bool UpdateRenderTarget(SpriteRenderState *rs, SpriteWindowState *ws, int w, int h) 244{ 245 SDL_Renderer *renderer = rs->renderer; 246 SDL_Texture *target = ws->target; 247 248 if (!target || target->w != w || target->w != h) { 249 if (target) { 250 SDL_DestroyTexture(target); 251 ws->target = NULL; 252 ws->texture = NULL; 253 } 254 255 target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, w, h); 256 if (!target) { 257 SDL_Log("Couldn't create render target: %s", SDL_GetError()); 258 return false; 259 } 260 SDL_SetRenderTarget(renderer, target); 261 262 ws->target = target; 263 ws->texture = (SDL_GPUTexture *)SDL_GetPointerProperty(SDL_GetTextureProperties(target), SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, NULL); 264 } 265 return true; 266} 267 268static void UpdateSprites(SpriteRenderState *rs, SpriteWindowState *ws, int w, int h) 269{ 270 SDL_Texture *sprite = rs->sprite; 271 SDL_FRect *positions = ws->positions; 272 SDL_FPoint *velocities = ws->velocities; 273 SDL_FRect *position; 274 SDL_FPoint *velocity; 275 int i; 276 277 /* Initialize the sprite positions */ 278 if (!ws->initialized) { 279 for (i = 0; i < NUM_SPRITES; ++i) { 280 positions[i].x = (float)SDL_rand(w - sprite->w); 281 positions[i].y = (float)SDL_rand(h - sprite->h); 282 positions[i].w = (float)sprite->w; 283 positions[i].h = (float)sprite->h; 284 velocities[i].x = 0.0f; 285 velocities[i].y = 0.0f; 286 while (velocities[i].x == 0.f && velocities[i].y == 0.f) { 287 velocities[i].x = (float)(SDL_rand(MAX_SPRITE_SPEED * 2 + 1) - MAX_SPRITE_SPEED); 288 velocities[i].y = (float)(SDL_rand(MAX_SPRITE_SPEED * 2 + 1) - MAX_SPRITE_SPEED); 289 } 290 } 291 ws->initialized = true; 292 } 293 294 /* Move the sprite, bounce at the wall */ 295 for (i = 0; i < NUM_SPRITES; ++i) { 296 position = &positions[i]; 297 velocity = &velocities[i]; 298 position->x += velocity->x; 299 if ((position->x < 0) || (position->x >= (w - sprite->w))) { 300 velocity->x = -velocity->x; 301 position->x += velocity->x; 302 } 303 position->y += velocity->y; 304 if ((position->y < 0) || (position->y >= (h - sprite->h))) { 305 velocity->y = -velocity->y; 306 position->y += velocity->y; 307 } 308 } 309} 310 311static void RenderSprites(SpriteRenderState *rs, SpriteWindowState *ws) 312{ 313 SDL_Renderer *renderer = rs->renderer; 314 SDL_Texture *sprite = rs->sprite; 315 const SDL_FRect *positions = ws->positions; 316 int i; 317 318 for (i = 0; i < NUM_SPRITES; ++i) { 319 SDL_RenderTexture(renderer, sprite, NULL, &positions[i]); 320 } 321} 322 323static void UpdateSpriteOverlay(SpriteRenderState *rs, SpriteWindowState *ws, int w, int h) 324{ 325 SDL_Renderer *renderer = rs->renderer; 326 327 if (!UpdateRenderTarget(rs, ws, w, h)) { 328 return; 329 } 330 331 SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_TRANSPARENT); 332 SDL_RenderClear(renderer); 333 334 if (rs->show_sprites) { 335 UpdateSprites(rs, ws, w, h); 336 RenderSprites(rs, ws); 337 } 338 339 SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); 340 SDL_RenderDebugText(renderer, 4.0f, 4.0f, "Press 'S' to toggle 2D sprites"); 341 SDL_RenderPresent(renderer); 342} 343 344static void RenderSpriteOverlay(SDL_GPURenderPass *pass, SpriteRenderState *rs, SpriteWindowState *ws) 345{ 346 SDL_GPUTextureSamplerBinding binding; 347 348 if (!ws->texture) { 349 /* Failed to create a texture, nothing to do */ 350 return; 351 } 352 353 SDL_zero(binding); 354 binding.texture = ws->texture; 355 binding.sampler = rs->sampler; 356 357 SDL_BindGPUGraphicsPipeline(pass, rs->pipeline); 358 SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1); 359 SDL_DrawGPUPrimitives(pass, 6, 1, 0, 0); 360} 361 362static void shutdownGPU(void) 363{ 364 QuitSpriteOverlay(&sprite_render_state); 365 366 if (window_states) { 367 int i; 368 for (i = 0; i < state->num_windows; i++) { 369 WindowState *winstate = &window_states[i]; 370 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth); 371 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa); 372 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve); 373 SDL_ReleaseWindowFromGPUDevice(gpu_device, state->windows[i]); 374 } 375 SDL_free(window_states); 376 window_states = NULL; 377 } 378 379 SDL_ReleaseGPUBuffer(gpu_device, render_state.buf_vertex); 380 SDL_ReleaseGPUGraphicsPipeline(gpu_device, render_state.pipeline); 381 SDL_DestroyGPUDevice(gpu_device); 382 383 SDL_zero(render_state); 384 gpu_device = NULL; 385} 386 387 388/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ 389static void 390quit(int rc) 391{ 392 shutdownGPU(); 393 SDLTest_CommonQuit(state); 394 exit(rc); 395} 396 397/* 398 * Simulates desktop's glRotatef. The matrix is returned in column-major 399 * order. 400 */ 401static void rotate_matrix(float angle, float x, float y, float z, float *r) 402{ 403 float radians, c, s, c1, u[3], length; 404 int i, j; 405 406 radians = angle * SDL_PI_F / 180.0f; 407 408 c = SDL_cosf(radians); 409 s = SDL_sinf(radians); 410 411 c1 = 1.0f - SDL_cosf(radians); 412 413 length = (float)SDL_sqrt(x * x + y * y + z * z); 414 415 u[0] = x / length; 416 u[1] = y / length; 417 u[2] = z / length; 418 419 for (i = 0; i < 16; i++) { 420 r[i] = 0.0; 421 } 422 423 r[15] = 1.0; 424 425 for (i = 0; i < 3; i++) { 426 r[i * 4 + (i + 1) % 3] = u[(i + 2) % 3] * s; 427 r[i * 4 + (i + 2) % 3] = -u[(i + 1) % 3] * s; 428 } 429 430 for (i = 0; i < 3; i++) { 431 for (j = 0; j < 3; j++) { 432 r[i * 4 + j] += c1 * u[i] * u[j] + (i == j ? c : 0.0f); 433 } 434 } 435} 436 437/* 438 * Simulates gluPerspectiveMatrix 439 */ 440static void perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r) 441{ 442 int i; 443 float f; 444 445 f = 1.0f/SDL_tanf((fovy / 180.0f) * SDL_PI_F * 0.5f); 446 447 for (i = 0; i < 16; i++) { 448 r[i] = 0.0; 449 } 450 451 r[0] = f / aspect; 452 r[5] = f; 453 r[10] = (znear + zfar) / (znear - zfar); 454 r[11] = -1.0f; 455 r[14] = (2.0f * znear * zfar) / (znear - zfar); 456 r[15] = 0.0f; 457} 458 459/* 460 * Multiplies lhs by rhs and writes out to r. All matrices are 4x4 and column 461 * major. In-place multiplication is supported. 462 */ 463static void multiply_matrix(const float *lhs, const float *rhs, float *r) 464{ 465 int i, j, k; 466 float tmp[16]; 467 468 for (i = 0; i < 4; i++) { 469 for (j = 0; j < 4; j++) { 470 tmp[j * 4 + i] = 0.0; 471 472 for (k = 0; k < 4; k++) { 473 tmp[j * 4 + i] += lhs[k * 4 + i] * rhs[j * 4 + k]; 474 } 475 } 476 } 477 478 for (i = 0; i < 16; i++) { 479 r[i] = tmp[i]; 480 } 481} 482 483typedef struct VertexData 484{ 485 float x, y, z; /* 3D data. Vertex range -0.5..0.5 in all axes. Z -0.5 is near, 0.5 is far. */ 486 float red, green, blue; /* intensity 0 to 1 (alpha is always 1). */ 487} VertexData; 488 489static const VertexData vertex_data[] = { 490 /* Front face. */ 491 /* Bottom left */ 492 { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ 493 { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ 494 { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ 495 496 /* Top right */ 497 { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ 498 { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ 499 { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ 500 501 /* Left face */ 502 /* Bottom left */ 503 { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ 504 { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ 505 { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ 506 507 /* Top right */ 508 { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ 509 { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ 510 { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ 511 512 /* Top face */ 513 /* Bottom left */ 514 { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ 515 { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ 516 { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */ 517 518 /* Top right */ 519 { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ 520 { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ 521 { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ 522 523 /* Right face */ 524 /* Bottom left */ 525 { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ 526 { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ 527 { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ 528 529 /* Top right */ 530 { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */ 531 { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ 532 { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ 533 534 /* Back face */ 535 /* Bottom left */ 536 { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ 537 { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ 538 { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ 539 540 /* Top right */ 541 { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */ 542 { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */ 543 { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ 544 545 /* Bottom face */ 546 /* Bottom left */ 547 { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ 548 { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */ 549 { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */ 550 551 /* Top right */ 552 { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */ 553 { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */ 554 { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 } /* magenta */ 555}; 556 557static SDL_GPUTexture *CreateDepthTexture(Uint32 drawablew, Uint32 drawableh) 558{ 559 SDL_GPUTextureCreateInfo createinfo; 560 SDL_GPUTexture *result; 561 562 createinfo.type = SDL_GPU_TEXTURETYPE_2D; 563 createinfo.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; 564 createinfo.width = drawablew; 565 createinfo.height = drawableh; 566 createinfo.layer_count_or_depth = 1; 567 createinfo.num_levels = 1; 568 createinfo.sample_count = render_state.sample_count; 569 createinfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; 570 createinfo.props = 0; 571 572 result = SDL_CreateGPUTexture(gpu_device, &createinfo); 573 CHECK_CREATE(result, "Depth Texture") 574 575 return result; 576} 577 578static SDL_GPUTexture *CreateMSAATexture(Uint32 drawablew, Uint32 drawableh) 579{ 580 SDL_GPUTextureCreateInfo createinfo; 581 SDL_GPUTexture *result; 582 583 if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) { 584 return NULL; 585 } 586 587 createinfo.type = SDL_GPU_TEXTURETYPE_2D; 588 createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); 589 createinfo.width = drawablew; 590 createinfo.height = drawableh; 591 createinfo.layer_count_or_depth = 1; 592 createinfo.num_levels = 1; 593 createinfo.sample_count = render_state.sample_count; 594 createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; 595 createinfo.props = 0; 596 597 result = SDL_CreateGPUTexture(gpu_device, &createinfo); 598 CHECK_CREATE(result, "MSAA Texture") 599 600 return result; 601} 602 603static SDL_GPUTexture *CreateResolveTexture(Uint32 drawablew, Uint32 drawableh) 604{ 605 SDL_GPUTextureCreateInfo createinfo; 606 SDL_GPUTexture *result; 607 608 if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) { 609 return NULL; 610 } 611 612 createinfo.type = SDL_GPU_TEXTURETYPE_2D; 613 createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); 614 createinfo.width = drawablew; 615 createinfo.height = drawableh; 616 createinfo.layer_count_or_depth = 1; 617 createinfo.num_levels = 1; 618 createinfo.sample_count = SDL_GPU_SAMPLECOUNT_1; 619 createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER; 620 createinfo.props = 0; 621 622 result = SDL_CreateGPUTexture(gpu_device, &createinfo); 623 CHECK_CREATE(result, "Resolve Texture") 624 625 return result; 626} 627 628static void Render(SDL_Window *window, const int windownum) 629{ 630 WindowState *winstate = &window_states[windownum]; 631 SDL_GPUTexture *swapchainTexture; 632 SDL_GPUColorTargetInfo color_target; 633 SDL_GPUDepthStencilTargetInfo depth_target; 634 float matrix_rotate[16], matrix_modelview[16], matrix_perspective[16], matrix_final[16]; 635 SDL_GPUCommandBuffer *cmd; 636 SDL_GPURenderPass *pass; 637 SDL_GPUBufferBinding vertex_binding; 638 SDL_GPUBlitInfo blit_info; 639 Uint32 drawablew, drawableh; 640 641 /* Acquire the swapchain texture */ 642 643 cmd = SDL_AcquireGPUCommandBuffer(gpu_device); 644 if (!cmd) { 645 SDL_Log("Failed to acquire command buffer :%s", SDL_GetError()); 646 quit(2); 647 } 648 if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmd, state->windows[windownum], &swapchainTexture, &drawablew, &drawableh)) { 649 SDL_Log("Failed to acquire swapchain texture: %s", SDL_GetError()); 650 quit(2); 651 } 652 653 if (swapchainTexture == NULL) { 654 /* Swapchain is unavailable, cancel work */ 655 SDL_CancelGPUCommandBuffer(cmd); 656 return; 657 } 658 659 if (sprite_render_state.renderer) { 660 /* Update the sprite positions and render to the 2D render target. 661 * Since we are rendering here, no other render pass should be active. 662 */ 663 UpdateSpriteOverlay(&sprite_render_state, &winstate->sprite_state, drawablew, drawableh); 664 } 665 666 /* 667 * Do some rotation with Euler angles. It is not a fixed axis as 668 * quaternions would be, but the effect is cool. 669 */ 670 rotate_matrix((float)winstate->angle_x, 1.0f, 0.0f, 0.0f, matrix_modelview); 671 rotate_matrix((float)winstate->angle_y, 0.0f, 1.0f, 0.0f, matrix_rotate); 672 673 multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview); 674 675 rotate_matrix((float)winstate->angle_z, 0.0f, 1.0f, 0.0f, matrix_rotate); 676 677 multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview); 678 679 /* Pull the camera back from the cube */ 680 matrix_modelview[14] -= 2.5f; 681 682 perspective_matrix(45.0f, (float)drawablew/drawableh, 0.01f, 100.0f, matrix_perspective); 683 multiply_matrix(matrix_perspective, matrix_modelview, (float*) &matrix_final); 684 685 winstate->angle_x += 3; 686 winstate->angle_y += 2; 687 winstate->angle_z += 1; 688 689 if(winstate->angle_x >= 360) winstate->angle_x -= 360; 690 if(winstate->angle_x < 0) winstate->angle_x += 360; 691 if(winstate->angle_y >= 360) winstate->angle_y -= 360; 692 if(winstate->angle_y < 0) winstate->angle_y += 360; 693 if(winstate->angle_z >= 360) winstate->angle_z -= 360; 694 if(winstate->angle_z < 0) winstate->angle_z += 360; 695 696 /* Resize the depth buffer if the window size changed */ 697 698 if (winstate->prev_drawablew != drawablew || winstate->prev_drawableh != drawableh) { 699 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth); 700 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa); 701 SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve); 702 winstate->tex_depth = CreateDepthTexture(drawablew, drawableh); 703 winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh); 704 winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh); 705 } 706 winstate->prev_drawablew = drawablew; 707 winstate->prev_drawableh = drawableh; 708 709 /* Set up the pass */ 710 711 SDL_zero(color_target); 712 color_target.clear_color.a = 1.0f; 713 if (winstate->tex_msaa) { 714 color_target.load_op = SDL_GPU_LOADOP_CLEAR; 715 color_target.store_op = SDL_GPU_STOREOP_RESOLVE; 716 color_target.texture = winstate->tex_msaa; 717 color_target.resolve_texture = winstate->tex_resolve; 718 color_target.cycle = true; 719 color_target.cycle_resolve_texture = true; 720 } else { 721 color_target.load_op = SDL_GPU_LOADOP_CLEAR; 722 color_target.store_op = SDL_GPU_STOREOP_STORE; 723 color_target.texture = swapchainTexture; 724 } 725 726 SDL_zero(depth_target); 727 depth_target.clear_depth = 1.0f; 728 depth_target.load_op = SDL_GPU_LOADOP_CLEAR; 729 depth_target.store_op = SDL_GPU_STOREOP_DONT_CARE; 730 depth_target.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE; 731 depth_target.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE; 732 depth_target.texture = winstate->tex_depth; 733 depth_target.cycle = true; 734 735 /* Set up the bindings */ 736 737 vertex_binding.buffer = render_state.buf_vertex; 738 vertex_binding.offset = 0; 739 740 /* Draw the cube! */ 741 742 SDL_PushGPUVertexUniformData(cmd, 0, matrix_final, sizeof(matrix_final)); 743 744 pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, &depth_target); 745 SDL_BindGPUGraphicsPipeline(pass, render_state.pipeline); 746 SDL_BindGPUVertexBuffers(pass, 0, &vertex_binding, 1); 747 SDL_DrawGPUPrimitives(pass, 36, 1, 0, 0); 748 SDL_EndGPURenderPass(pass); 749 750 /* Render the sprite overlay! */ 751 752 if (sprite_render_state.renderer) { 753 /* Load the existing color target so we can blend with it */ 754 color_target.load_op = SDL_GPU_LOADOP_LOAD; 755 756 pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, NULL); 757 RenderSpriteOverlay(pass, &sprite_render_state, &winstate->sprite_state); 758 SDL_EndGPURenderPass(pass); 759 } 760 761 /* Blit MSAA resolve target to swapchain, if needed */ 762 if (render_state.sample_count > SDL_GPU_SAMPLECOUNT_1) { 763 SDL_zero(blit_info); 764 blit_info.source.texture = winstate->tex_resolve; 765 blit_info.source.w = drawablew; 766 blit_info.source.h = drawableh; 767 768 blit_info.destination.texture = swapchainTexture; 769 blit_info.destination.w = drawablew; 770 blit_info.destination.h = drawableh; 771 772 blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE; 773 blit_info.filter = SDL_GPU_FILTER_LINEAR; 774 775 SDL_BlitGPUTexture(cmd, &blit_info); 776 } 777 778 /* Submit the command buffer! */ 779 SDL_SubmitGPUCommandBuffer(cmd); 780 781 ++frames; 782} 783 784static SDL_GPUShader *load_shader(bool is_vertex) 785{ 786 SDL_GPUShaderCreateInfo createinfo; 787 SDL_zero(createinfo); 788 createinfo.num_uniform_buffers = is_vertex ? 1 : 0; 789 790 SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu_device); 791 if (format & SDL_GPU_SHADERFORMAT_DXIL) { 792 createinfo.format = SDL_GPU_SHADERFORMAT_DXIL; 793 createinfo.code = is_vertex ? cube_vert_dxil : cube_frag_dxil; 794 createinfo.code_size = is_vertex ? cube_vert_dxil_len : cube_frag_dxil_len; 795 } else if (format & SDL_GPU_SHADERFORMAT_MSL) { 796 createinfo.format = SDL_GPU_SHADERFORMAT_MSL; 797 createinfo.code = is_vertex ? cube_vert_msl : cube_frag_msl; 798 createinfo.code_size = is_vertex ? cube_vert_msl_len : cube_frag_msl_len; 799 } else { 800 createinfo.format = SDL_GPU_SHADERFORMAT_SPIRV; 801 createinfo.code = is_vertex ? cube_vert_spv : cube_frag_spv; 802 createinfo.code_size = is_vertex ? cube_vert_spv_len : cube_frag_spv_len; 803 } 804 805 createinfo.stage = is_vertex ? SDL_GPU_SHADERSTAGE_VERTEX : SDL_GPU_SHADERSTAGE_FRAGMENT; 806 return SDL_CreateGPUShader(gpu_device, &createinfo); 807} 808 809static void init_render_state(int msaa) 810{ 811 SDL_GPUCommandBuffer *cmd; 812 SDL_GPUTransferBuffer *buf_transfer; 813 void *map; 814 SDL_GPUTransferBufferLocation buf_location; 815 SDL_GPUBufferRegion dst_region; 816 SDL_GPUCopyPass *copy_pass; 817 SDL_GPUBufferCreateInfo buffer_desc; 818 SDL_GPUTransferBufferCreateInfo transfer_buffer_desc; 819 SDL_GPUGraphicsPipelineCreateInfo pipelinedesc; 820 SDL_GPUColorTargetDescription color_target_desc; 821 Uint32 drawablew, drawableh; 822 SDL_GPUVertexAttribute vertex_attributes[2]; 823 SDL_GPUVertexBufferDescription vertex_buffer_desc; 824 SDL_GPUShader *vertex_shader; 825 SDL_GPUShader *fragment_shader; 826 int i; 827 828 gpu_device = SDL_CreateGPUDevice( 829 TESTGPU_SUPPORTED_FORMATS, 830 true, 831 state->gpudriver 832 ); 833 CHECK_CREATE(gpu_device, "GPU device"); 834 835 /* Claim the windows */ 836 837 for (i = 0; i < state->num_windows; i++) { 838 SDL_ClaimWindowForGPUDevice( 839 gpu_device, 840 state->windows[i] 841 ); 842 } 843 844 /* Create shaders */ 845 846 vertex_shader = load_shader(true); 847 CHECK_CREATE(vertex_shader, "Vertex Shader") 848 fragment_shader = load_shader(false); 849 CHECK_CREATE(fragment_shader, "Fragment Shader") 850 851 /* Create buffers */ 852 853 buffer_desc.usage = SDL_GPU_BUFFERUSAGE_VERTEX; 854 buffer_desc.size = sizeof(vertex_data); 855 buffer_desc.props = SDL_CreateProperties(); 856 SDL_SetStringProperty(buffer_desc.props, SDL_PROP_GPU_BUFFER_CREATE_NAME_STRING, "космонавт"); 857 render_state.buf_vertex = SDL_CreateGPUBuffer( 858 gpu_device, 859 &buffer_desc 860 ); 861 CHECK_CREATE(render_state.buf_vertex, "Static vertex buffer") 862 SDL_DestroyProperties(buffer_desc.props); 863 864 transfer_buffer_desc.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; 865 transfer_buffer_desc.size = sizeof(vertex_data); 866 transfer_buffer_desc.props = SDL_CreateProperties(); 867 SDL_SetStringProperty(transfer_buffer_desc.props, SDL_PROP_GPU_TRANSFERBUFFER_CREATE_NAME_STRING, "Transfer Buffer"); 868 buf_transfer = SDL_CreateGPUTransferBuffer( 869 gpu_device, 870 &transfer_buffer_desc 871 ); 872 CHECK_CREATE(buf_transfer, "Vertex transfer buffer") 873 SDL_DestroyProperties(transfer_buffer_desc.props); 874 875 /* We just need to upload the static data once. */ 876 map = SDL_MapGPUTransferBuffer(gpu_device, buf_transfer, false); 877 SDL_memcpy(map, vertex_data, sizeof(vertex_data)); 878 SDL_UnmapGPUTransferBuffer(gpu_device, buf_transfer); 879 880 cmd = SDL_AcquireGPUCommandBuffer(gpu_device); 881 copy_pass = SDL_BeginGPUCopyPass(cmd); 882 buf_location.transfer_buffer = buf_transfer; 883 buf_location.offset = 0; 884 dst_region.buffer = render_state.buf_vertex; 885 dst_region.offset = 0; 886 dst_region.size = sizeof(vertex_data); 887 SDL_UploadToGPUBuffer(copy_pass, &buf_location, &dst_region, false); 888 SDL_EndGPUCopyPass(copy_pass); 889 SDL_SubmitGPUCommandBuffer(cmd); 890 891 SDL_ReleaseGPUTransferBuffer(gpu_device, buf_transfer); 892 893 /* Determine which sample count to use */ 894 render_state.sample_count = SDL_GPU_SAMPLECOUNT_1; 895 if (msaa && SDL_GPUTextureSupportsSampleCount( 896 gpu_device, 897 SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]), 898 SDL_GPU_SAMPLECOUNT_4)) { 899 render_state.sample_count = SDL_GPU_SAMPLECOUNT_4; 900 } 901 902 /* Set up the graphics pipeline */ 903 904 SDL_zero(pipelinedesc); 905 SDL_zero(color_target_desc); 906 907 color_target_desc.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]); 908 909 pipelinedesc.target_info.num_color_targets = 1; 910 pipelinedesc.target_info.color_target_descriptions = &color_target_desc; 911 pipelinedesc.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM; 912 pipelinedesc.target_info.has_depth_stencil_target = true; 913 914 pipelinedesc.depth_stencil_state.enable_depth_test = true; 915 pipelinedesc.depth_stencil_state.enable_depth_write = true; 916 pipelinedesc.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL; 917 918 pipelinedesc.multisample_state.sample_count = render_state.sample_count; 919 920 pipelinedesc.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; 921 922 pipelinedesc.vertex_shader = vertex_shader; 923 pipelinedesc.fragment_shader = fragment_shader; 924 925 vertex_buffer_desc.slot = 0; 926 vertex_buffer_desc.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; 927 vertex_buffer_desc.instance_step_rate = 0; 928 vertex_buffer_desc.pitch = sizeof(VertexData); 929 930 vertex_attributes[0].buffer_slot = 0; 931 vertex_attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; 932 vertex_attributes[0].location = 0; 933 vertex_attributes[0].offset = 0; 934 935 vertex_attributes[1].buffer_slot = 0; 936 vertex_attributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; 937 vertex_attributes[1].location = 1; 938 vertex_attributes[1].offset = sizeof(float) * 3; 939 940 pipelinedesc.vertex_input_state.num_vertex_buffers = 1; 941 pipelinedesc.vertex_input_state.vertex_buffer_descriptions = &vertex_buffer_desc; 942 pipelinedesc.vertex_input_state.num_vertex_attributes = 2; 943 pipelinedesc.vertex_input_state.vertex_attributes = (SDL_GPUVertexAttribute*) &vertex_attributes; 944 945 pipelinedesc.props = 0; 946 947 render_state.pipeline = SDL_CreateGPUGraphicsPipeline(gpu_device, &pipelinedesc); 948 CHECK_CREATE(render_state.pipeline, "Render Pipeline") 949 950 /* These are reference-counted; once the pipeline is created, you don't need to keep these. */ 951 SDL_ReleaseGPUShader(gpu_device, vertex_shader); 952 SDL_ReleaseGPUShader(gpu_device, fragment_shader); 953 954 /* Set up per-window state */ 955 956 window_states = (WindowState *) SDL_calloc(state->num_windows, sizeof (WindowState)); 957 if (!window_states) { 958 SDL_Log("Out of memory!"); 959 quit(2); 960 } 961 962 for (i = 0; i < state->num_windows; i++) { 963 WindowState *winstate = &window_states[i]; 964 965 /* create a depth texture for the window */ 966 SDL_GetWindowSizeInPixels(state->windows[i], (int*) &drawablew, (int*) &drawableh); 967 winstate->tex_depth = CreateDepthTexture(drawablew, drawableh); 968 winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh); 969 winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh); 970 971 /* make each window different */ 972 winstate->angle_x = (i * 10) % 360; 973 winstate->angle_y = (i * 20) % 360; 974 winstate->angle_z = (i * 30) % 360; 975 } 976 977 /* Set up 2D sprite render state */ 978 InitSpriteOverlay(&sprite_render_state, state->windows[0]); 979} 980 981static int done = 0; 982 983void loop(void) 984{ 985 SDL_Event event; 986 int i; 987 988 /* Check for events */ 989 while (SDL_PollEvent(&event) && !done) { 990 SDLTest_CommonEvent(state, &event, &done); 991 992 if (event.type == SDL_EVENT_KEY_UP) { 993 if (event.key.key == SDLK_S) { 994 /* Toggle 2D sprite drawing */ 995 sprite_render_state.show_sprites = !sprite_render_state.show_sprites; 996 } 997 } 998 } 999 if (!done) { 1000 for (i = 0; i < state->num_windows; ++i) { 1001 Render(state->windows[i], i); 1002 } 1003 } 1004#ifdef __EMSCRIPTEN__ 1005 else { 1006 emscripten_cancel_main_loop(); 1007 } 1008#endif 1009} 1010 1011int 1012main(int argc, char *argv[]) 1013{ 1014 int msaa; 1015 int i; 1016 const SDL_DisplayMode *mode; 1017 Uint64 then, now; 1018 1019 /* Initialize params */ 1020 msaa = 0; 1021 1022 /* Initialize test framework */ 1023 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); 1024 if (!state) { 1025 return 1; 1026 } 1027 for (i = 1; i < argc;) { 1028 int consumed; 1029 1030 consumed = SDLTest_CommonArg(state, i); 1031 if (consumed == 0) { 1032 if (SDL_strcasecmp(argv[i], "--msaa") == 0) { 1033 ++msaa; 1034 consumed = 1; 1035 } else { 1036 consumed = -1; 1037 } 1038 } 1039 if (consumed < 0) { 1040 static const char *options[] = { "[--msaa]", NULL }; 1041 SDLTest_CommonLogUsage(state, argv[0], options); 1042 quit(1); 1043 } 1044 i += consumed; 1045 } 1046 1047 state->skip_renderer = 1; 1048 state->window_flags |= SDL_WINDOW_RESIZABLE; 1049 1050 if (!SDLTest_CommonInit(state)) { 1051 quit(2); 1052 return 0; 1053 } 1054 1055 mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(state->windows[0])); 1056 SDL_Log("Screen bpp: %d", SDL_BITSPERPIXEL(mode->format)); 1057 1058 init_render_state(msaa); 1059 1060 /* Main render loop */ 1061 frames = 0; 1062 then = SDL_GetTicks(); 1063 done = 0; 1064 1065#ifdef __EMSCRIPTEN__ 1066 emscripten_set_main_loop(loop, 0, 1); 1067#else 1068 while (!done) { 1069 loop(); 1070 } 1071#endif 1072 1073 /* Print out some timing information */ 1074 now = SDL_GetTicks(); 1075 if (now > then) { 1076 SDL_Log("%2.2f frames per second", 1077 ((double) frames * 1000) / (now - then)); 1078 } 1079#if !defined(__ANDROID__) 1080 quit(0); 1081#endif 1082 return 0; 1083} 1084 1085/* vi: set ts=4 sw=4 expandtab: */ 1086[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.