testgpu_spinning_cube.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /*
  2. Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely.
  9. */
  10. #include <stdlib.h>
  11. #ifdef __EMSCRIPTEN__
  12. #include <emscripten/emscripten.h>
  13. #endif
  14. #include <SDL3/SDL_test_common.h>
  15. #include <SDL3/SDL_gpu.h>
  16. #include <SDL3/SDL_main.h>
  17. /* Regenerate the shaders with testgpu/build-shaders.sh */
  18. #include "testgpu/testgpu_spirv.h"
  19. #include "testgpu/testgpu_dxbc.h"
  20. #include "testgpu/testgpu_dxil.h"
  21. #include "testgpu/testgpu_metallib.h"
  22. #define TESTGPU_SUPPORTED_FORMATS (SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_METALLIB)
  23. #define CHECK_CREATE(var, thing) { if (!(var)) { SDL_Log("Failed to create %s: %s\n", thing, SDL_GetError()); quit(2); } }
  24. static Uint32 frames = 0;
  25. typedef struct RenderState
  26. {
  27. SDL_GPUBuffer *buf_vertex;
  28. SDL_GPUGraphicsPipeline *pipeline;
  29. SDL_GPUSampleCount sample_count;
  30. } RenderState;
  31. typedef struct WindowState
  32. {
  33. int angle_x, angle_y, angle_z;
  34. SDL_GPUTexture *tex_depth, *tex_msaa;
  35. Uint32 prev_drawablew, prev_drawableh;
  36. } WindowState;
  37. static SDL_GPUDevice *gpu_device = NULL;
  38. static RenderState render_state;
  39. static SDLTest_CommonState *state = NULL;
  40. static WindowState *window_states = NULL;
  41. static void shutdownGPU(void)
  42. {
  43. if (window_states) {
  44. int i;
  45. for (i = 0; i < state->num_windows; i++) {
  46. WindowState *winstate = &window_states[i];
  47. SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth);
  48. SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa);
  49. SDL_ReleaseWindowFromGPUDevice(gpu_device, state->windows[i]);
  50. }
  51. SDL_free(window_states);
  52. window_states = NULL;
  53. }
  54. SDL_ReleaseGPUBuffer(gpu_device, render_state.buf_vertex);
  55. SDL_ReleaseGPUGraphicsPipeline(gpu_device, render_state.pipeline);
  56. SDL_DestroyGPUDevice(gpu_device);
  57. SDL_zero(render_state);
  58. gpu_device = NULL;
  59. }
  60. /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
  61. static void
  62. quit(int rc)
  63. {
  64. shutdownGPU();
  65. SDLTest_CommonQuit(state);
  66. exit(rc);
  67. }
  68. /*
  69. * Simulates desktop's glRotatef. The matrix is returned in column-major
  70. * order.
  71. */
  72. static void
  73. rotate_matrix(float angle, float x, float y, float z, float *r)
  74. {
  75. float radians, c, s, c1, u[3], length;
  76. int i, j;
  77. radians = angle * SDL_PI_F / 180.0f;
  78. c = SDL_cosf(radians);
  79. s = SDL_sinf(radians);
  80. c1 = 1.0f - SDL_cosf(radians);
  81. length = (float)SDL_sqrt(x * x + y * y + z * z);
  82. u[0] = x / length;
  83. u[1] = y / length;
  84. u[2] = z / length;
  85. for (i = 0; i < 16; i++) {
  86. r[i] = 0.0;
  87. }
  88. r[15] = 1.0;
  89. for (i = 0; i < 3; i++) {
  90. r[i * 4 + (i + 1) % 3] = u[(i + 2) % 3] * s;
  91. r[i * 4 + (i + 2) % 3] = -u[(i + 1) % 3] * s;
  92. }
  93. for (i = 0; i < 3; i++) {
  94. for (j = 0; j < 3; j++) {
  95. r[i * 4 + j] += c1 * u[i] * u[j] + (i == j ? c : 0.0f);
  96. }
  97. }
  98. }
  99. /*
  100. * Simulates gluPerspectiveMatrix
  101. */
  102. static void
  103. perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r)
  104. {
  105. int i;
  106. float f;
  107. f = 1.0f/SDL_tanf(fovy * 0.5f);
  108. for (i = 0; i < 16; i++) {
  109. r[i] = 0.0;
  110. }
  111. r[0] = f / aspect;
  112. r[5] = f;
  113. r[10] = (znear + zfar) / (znear - zfar);
  114. r[11] = -1.0f;
  115. r[14] = (2.0f * znear * zfar) / (znear - zfar);
  116. r[15] = 0.0f;
  117. }
  118. /*
  119. * Multiplies lhs by rhs and writes out to r. All matrices are 4x4 and column
  120. * major. In-place multiplication is supported.
  121. */
  122. static void
  123. multiply_matrix(float *lhs, float *rhs, float *r)
  124. {
  125. int i, j, k;
  126. float tmp[16];
  127. for (i = 0; i < 4; i++) {
  128. for (j = 0; j < 4; j++) {
  129. tmp[j * 4 + i] = 0.0;
  130. for (k = 0; k < 4; k++) {
  131. tmp[j * 4 + i] += lhs[k * 4 + i] * rhs[j * 4 + k];
  132. }
  133. }
  134. }
  135. for (i = 0; i < 16; i++) {
  136. r[i] = tmp[i];
  137. }
  138. }
  139. typedef struct VertexData
  140. {
  141. float x, y, z; /* 3D data. Vertex range -0.5..0.5 in all axes. Z -0.5 is near, 0.5 is far. */
  142. float red, green, blue; /* intensity 0 to 1 (alpha is always 1). */
  143. } VertexData;
  144. static const VertexData vertex_data[] = {
  145. /* Front face. */
  146. /* Bottom left */
  147. { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */
  148. { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */
  149. { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */
  150. /* Top right */
  151. { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */
  152. { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */
  153. { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */
  154. /* Left face */
  155. /* Bottom left */
  156. { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */
  157. { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */
  158. { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */
  159. /* Top right */
  160. { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */
  161. { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */
  162. { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */
  163. /* Top face */
  164. /* Bottom left */
  165. { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */
  166. { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */
  167. { -0.5, 0.5, -0.5, 1.0, 0.0, 0.0 }, /* red */
  168. /* Top right */
  169. { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */
  170. { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */
  171. { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */
  172. /* Right face */
  173. /* Bottom left */
  174. { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */
  175. { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */
  176. { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */
  177. /* Top right */
  178. { 0.5, 0.5, -0.5, 1.0, 1.0, 0.0 }, /* yellow */
  179. { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */
  180. { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */
  181. /* Back face */
  182. /* Bottom left */
  183. { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */
  184. { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */
  185. { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */
  186. /* Top right */
  187. { 0.5, 0.5, 0.5, 0.0, 0.0, 0.0 }, /* black */
  188. { -0.5, 0.5, 0.5, 1.0, 1.0, 1.0 }, /* white */
  189. { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */
  190. /* Bottom face */
  191. /* Bottom left */
  192. { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */
  193. { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 }, /* magenta */
  194. { -0.5, -0.5, 0.5, 0.0, 1.0, 1.0 }, /* cyan */
  195. /* Top right */
  196. { -0.5, -0.5, -0.5, 0.0, 1.0, 0.0 }, /* green */
  197. { 0.5, -0.5, -0.5, 0.0, 0.0, 1.0 }, /* blue */
  198. { 0.5, -0.5, 0.5, 1.0, 0.0, 1.0 } /* magenta */
  199. };
  200. static SDL_GPUTexture*
  201. CreateDepthTexture(Uint32 drawablew, Uint32 drawableh)
  202. {
  203. SDL_GPUTextureCreateInfo depthtex_createinfo;
  204. SDL_GPUTexture *result;
  205. depthtex_createinfo.type = SDL_GPU_TEXTURETYPE_2D;
  206. depthtex_createinfo.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
  207. depthtex_createinfo.width = drawablew;
  208. depthtex_createinfo.height = drawableh;
  209. depthtex_createinfo.layer_count_or_depth = 1;
  210. depthtex_createinfo.num_levels = 1;
  211. depthtex_createinfo.sample_count = render_state.sample_count;
  212. depthtex_createinfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
  213. depthtex_createinfo.props = 0;
  214. result = SDL_CreateGPUTexture(gpu_device, &depthtex_createinfo);
  215. CHECK_CREATE(result, "Depth Texture")
  216. return result;
  217. }
  218. static SDL_GPUTexture*
  219. CreateMSAATexture(Uint32 drawablew, Uint32 drawableh)
  220. {
  221. SDL_GPUTextureCreateInfo msaatex_createinfo;
  222. SDL_GPUTexture *result;
  223. if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) {
  224. return NULL;
  225. }
  226. msaatex_createinfo.type = SDL_GPU_TEXTURETYPE_2D;
  227. msaatex_createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]);
  228. msaatex_createinfo.width = drawablew;
  229. msaatex_createinfo.height = drawableh;
  230. msaatex_createinfo.layer_count_or_depth = 1;
  231. msaatex_createinfo.num_levels = 1;
  232. msaatex_createinfo.sample_count = render_state.sample_count;
  233. msaatex_createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER;
  234. msaatex_createinfo.props = 0;
  235. result = SDL_CreateGPUTexture(gpu_device, &msaatex_createinfo);
  236. CHECK_CREATE(result, "MSAA Texture")
  237. return result;
  238. }
  239. static void
  240. Render(SDL_Window *window, const int windownum)
  241. {
  242. WindowState *winstate = &window_states[windownum];
  243. SDL_GPUTexture *swapchain;
  244. SDL_GPUColorTargetInfo color_target;
  245. SDL_GPUDepthStencilTargetInfo depth_target;
  246. float matrix_rotate[16], matrix_modelview[16], matrix_perspective[16], matrix_final[16];
  247. Uint32 drawablew, drawableh;
  248. SDL_GPUCommandBuffer *cmd;
  249. SDL_GPURenderPass *pass;
  250. SDL_GPUBufferBinding vertex_binding;
  251. SDL_GPUBlitInfo blit_info;
  252. /* Acquire the swapchain texture */
  253. cmd = SDL_AcquireGPUCommandBuffer(gpu_device);
  254. swapchain = SDL_AcquireGPUSwapchainTexture(cmd, state->windows[windownum], &drawablew, &drawableh);
  255. if (!swapchain) {
  256. /* No swapchain was acquired, probably too many frames in flight */
  257. SDL_SubmitGPUCommandBuffer(cmd);
  258. return;
  259. }
  260. /*
  261. * Do some rotation with Euler angles. It is not a fixed axis as
  262. * quaterions would be, but the effect is cool.
  263. */
  264. rotate_matrix((float)winstate->angle_x, 1.0f, 0.0f, 0.0f, matrix_modelview);
  265. rotate_matrix((float)winstate->angle_y, 0.0f, 1.0f, 0.0f, matrix_rotate);
  266. multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview);
  267. rotate_matrix((float)winstate->angle_z, 0.0f, 1.0f, 0.0f, matrix_rotate);
  268. multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview);
  269. /* Pull the camera back from the cube */
  270. matrix_modelview[14] -= 2.5f;
  271. perspective_matrix(45.0f, (float)drawablew/drawableh, 0.01f, 100.0f, matrix_perspective);
  272. multiply_matrix(matrix_perspective, matrix_modelview, (float*) &matrix_final);
  273. winstate->angle_x += 3;
  274. winstate->angle_y += 2;
  275. winstate->angle_z += 1;
  276. if(winstate->angle_x >= 360) winstate->angle_x -= 360;
  277. if(winstate->angle_x < 0) winstate->angle_x += 360;
  278. if(winstate->angle_y >= 360) winstate->angle_y -= 360;
  279. if(winstate->angle_y < 0) winstate->angle_y += 360;
  280. if(winstate->angle_z >= 360) winstate->angle_z -= 360;
  281. if(winstate->angle_z < 0) winstate->angle_z += 360;
  282. /* Resize the depth buffer if the window size changed */
  283. if (winstate->prev_drawablew != drawablew || winstate->prev_drawableh != drawableh) {
  284. SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth);
  285. SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa);
  286. winstate->tex_depth = CreateDepthTexture(drawablew, drawableh);
  287. winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh);
  288. }
  289. winstate->prev_drawablew = drawablew;
  290. winstate->prev_drawableh = drawableh;
  291. /* Set up the pass */
  292. SDL_zero(color_target);
  293. color_target.clear_color.a = 1.0f;
  294. color_target.load_op = SDL_GPU_LOADOP_CLEAR;
  295. color_target.store_op = SDL_GPU_STOREOP_STORE;
  296. color_target.texture = winstate->tex_msaa ? winstate->tex_msaa : swapchain;
  297. SDL_zero(depth_target);
  298. depth_target.clear_depth = 1.0f;
  299. depth_target.load_op = SDL_GPU_LOADOP_CLEAR;
  300. depth_target.store_op = SDL_GPU_STOREOP_DONT_CARE;
  301. depth_target.texture = winstate->tex_depth;
  302. depth_target.cycle = SDL_TRUE;
  303. /* Set up the bindings */
  304. vertex_binding.buffer = render_state.buf_vertex;
  305. vertex_binding.offset = 0;
  306. /* Draw the cube! */
  307. SDL_PushGPUVertexUniformData(cmd, 0, matrix_final, sizeof(matrix_final));
  308. pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, &depth_target);
  309. SDL_BindGPUGraphicsPipeline(pass, render_state.pipeline);
  310. SDL_BindGPUVertexBuffers(pass, 0, &vertex_binding, 1);
  311. SDL_DrawGPUPrimitives(pass, 36, 1, 0, 0);
  312. SDL_EndGPURenderPass(pass);
  313. /* Blit MSAA to swapchain, if needed */
  314. if (render_state.sample_count > SDL_GPU_SAMPLECOUNT_1) {
  315. SDL_zero(blit_info);
  316. blit_info.source.texture = winstate->tex_msaa;
  317. blit_info.source.w = drawablew;
  318. blit_info.source.h = drawableh;
  319. blit_info.destination.texture = swapchain;
  320. blit_info.destination.w = drawablew;
  321. blit_info.destination.h = drawableh;
  322. blit_info.load_op = SDL_GPU_LOADOP_DONT_CARE;
  323. blit_info.filter = SDL_GPU_FILTER_LINEAR;
  324. SDL_BlitGPUTexture(cmd, &blit_info);
  325. }
  326. /* Submit the command buffer! */
  327. SDL_SubmitGPUCommandBuffer(cmd);
  328. ++frames;
  329. }
  330. static SDL_GPUShader*
  331. load_shader(SDL_bool is_vertex)
  332. {
  333. SDL_GPUShaderCreateInfo createinfo;
  334. createinfo.num_samplers = 0;
  335. createinfo.num_storage_buffers = 0;
  336. createinfo.num_storage_textures = 0;
  337. createinfo.num_uniform_buffers = is_vertex ? 1 : 0;
  338. createinfo.props = 0;
  339. SDL_GPUDriver backend = SDL_GetGPUDriver(gpu_device);
  340. if (backend == SDL_GPU_DRIVER_D3D11) {
  341. createinfo.format = SDL_GPU_SHADERFORMAT_DXBC;
  342. createinfo.code = is_vertex ? D3D11_CubeVert : D3D11_CubeFrag;
  343. createinfo.code_size = is_vertex ? SDL_arraysize(D3D11_CubeVert) : SDL_arraysize(D3D11_CubeFrag);
  344. createinfo.entrypoint = is_vertex ? "VSMain" : "PSMain";
  345. } else if (backend == SDL_GPU_DRIVER_D3D12) {
  346. createinfo.format = SDL_GPU_SHADERFORMAT_DXIL;
  347. createinfo.code = is_vertex ? D3D12_CubeVert : D3D12_CubeFrag;
  348. createinfo.code_size = is_vertex ? SDL_arraysize(D3D12_CubeVert) : SDL_arraysize(D3D12_CubeFrag);
  349. createinfo.entrypoint = is_vertex ? "VSMain" : "PSMain";
  350. } else if (backend == SDL_GPU_DRIVER_METAL) {
  351. createinfo.format = SDL_GPU_SHADERFORMAT_METALLIB;
  352. createinfo.code = is_vertex ? cube_vert_metallib : cube_frag_metallib;
  353. createinfo.code_size = is_vertex ? cube_vert_metallib_len : cube_frag_metallib_len;
  354. createinfo.entrypoint = is_vertex ? "vs_main" : "fs_main";
  355. } else {
  356. createinfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
  357. createinfo.code = is_vertex ? cube_vert_spv : cube_frag_spv;
  358. createinfo.code_size = is_vertex ? cube_vert_spv_len : cube_frag_spv_len;
  359. createinfo.entrypoint = "main";
  360. }
  361. createinfo.stage = is_vertex ? SDL_GPU_SHADERSTAGE_VERTEX : SDL_GPU_SHADERSTAGE_FRAGMENT;
  362. return SDL_CreateGPUShader(gpu_device, &createinfo);
  363. }
  364. static void
  365. init_render_state(int msaa)
  366. {
  367. SDL_GPUCommandBuffer *cmd;
  368. SDL_GPUTransferBuffer *buf_transfer;
  369. void *map;
  370. SDL_GPUTransferBufferLocation buf_location;
  371. SDL_GPUBufferRegion dst_region;
  372. SDL_GPUCopyPass *copy_pass;
  373. SDL_GPUBufferCreateInfo buffer_desc;
  374. SDL_GPUTransferBufferCreateInfo transfer_buffer_desc;
  375. SDL_GPUGraphicsPipelineCreateInfo pipelinedesc;
  376. SDL_GPUColorTargetDescription color_target_desc;
  377. Uint32 drawablew, drawableh;
  378. SDL_GPUVertexAttribute vertex_attributes[2];
  379. SDL_GPUVertexBinding vertex_binding;
  380. SDL_GPUShader *vertex_shader;
  381. SDL_GPUShader *fragment_shader;
  382. int i;
  383. gpu_device = SDL_CreateGPUDevice(
  384. TESTGPU_SUPPORTED_FORMATS,
  385. SDL_TRUE,
  386. state->gpudriver
  387. );
  388. CHECK_CREATE(gpu_device, "GPU device");
  389. /* Claim the windows */
  390. for (i = 0; i < state->num_windows; i++) {
  391. SDL_ClaimWindowForGPUDevice(
  392. gpu_device,
  393. state->windows[i]
  394. );
  395. }
  396. /* Create shaders */
  397. vertex_shader = load_shader(SDL_TRUE);
  398. CHECK_CREATE(vertex_shader, "Vertex Shader")
  399. fragment_shader = load_shader(SDL_FALSE);
  400. CHECK_CREATE(fragment_shader, "Fragment Shader")
  401. /* Create buffers */
  402. buffer_desc.usage = SDL_GPU_BUFFERUSAGE_VERTEX;
  403. buffer_desc.size = sizeof(vertex_data);
  404. buffer_desc.props = 0;
  405. render_state.buf_vertex = SDL_CreateGPUBuffer(
  406. gpu_device,
  407. &buffer_desc
  408. );
  409. CHECK_CREATE(render_state.buf_vertex, "Static vertex buffer")
  410. SDL_SetGPUBufferName(gpu_device, render_state.buf_vertex, "космонавт");
  411. transfer_buffer_desc.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
  412. transfer_buffer_desc.size = sizeof(vertex_data);
  413. transfer_buffer_desc.props = 0;
  414. buf_transfer = SDL_CreateGPUTransferBuffer(
  415. gpu_device,
  416. &transfer_buffer_desc
  417. );
  418. CHECK_CREATE(buf_transfer, "Vertex transfer buffer")
  419. /* We just need to upload the static data once. */
  420. map = SDL_MapGPUTransferBuffer(gpu_device, buf_transfer, SDL_FALSE);
  421. SDL_memcpy(map, vertex_data, sizeof(vertex_data));
  422. SDL_UnmapGPUTransferBuffer(gpu_device, buf_transfer);
  423. cmd = SDL_AcquireGPUCommandBuffer(gpu_device);
  424. copy_pass = SDL_BeginGPUCopyPass(cmd);
  425. buf_location.transfer_buffer = buf_transfer;
  426. buf_location.offset = 0;
  427. dst_region.buffer = render_state.buf_vertex;
  428. dst_region.offset = 0;
  429. dst_region.size = sizeof(vertex_data);
  430. SDL_UploadToGPUBuffer(copy_pass, &buf_location, &dst_region, SDL_FALSE);
  431. SDL_EndGPUCopyPass(copy_pass);
  432. SDL_SubmitGPUCommandBuffer(cmd);
  433. SDL_ReleaseGPUTransferBuffer(gpu_device, buf_transfer);
  434. /* Determine which sample count to use */
  435. render_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
  436. if (msaa && SDL_GPUTextureSupportsSampleCount(
  437. gpu_device,
  438. SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]),
  439. SDL_GPU_SAMPLECOUNT_4)) {
  440. render_state.sample_count = SDL_GPU_SAMPLECOUNT_4;
  441. }
  442. /* Set up the graphics pipeline */
  443. SDL_zero(pipelinedesc);
  444. SDL_zero(color_target_desc);
  445. color_target_desc.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]);
  446. pipelinedesc.target_info.num_color_targets = 1;
  447. pipelinedesc.target_info.color_target_descriptions = &color_target_desc;
  448. pipelinedesc.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
  449. pipelinedesc.target_info.has_depth_stencil_target = SDL_TRUE;
  450. pipelinedesc.depth_stencil_state.enable_depth_test = 1;
  451. pipelinedesc.depth_stencil_state.enable_depth_write = 1;
  452. pipelinedesc.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL;
  453. pipelinedesc.multisample_state.sample_count = render_state.sample_count;
  454. pipelinedesc.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
  455. pipelinedesc.vertex_shader = vertex_shader;
  456. pipelinedesc.fragment_shader = fragment_shader;
  457. vertex_binding.index = 0;
  458. vertex_binding.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
  459. vertex_binding.instance_step_rate = 0;
  460. vertex_binding.pitch = sizeof(VertexData);
  461. vertex_attributes[0].binding_index = 0;
  462. vertex_attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
  463. vertex_attributes[0].location = 0;
  464. vertex_attributes[0].offset = 0;
  465. vertex_attributes[1].binding_index = 0;
  466. vertex_attributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
  467. vertex_attributes[1].location = 1;
  468. vertex_attributes[1].offset = sizeof(float) * 3;
  469. pipelinedesc.vertex_input_state.num_vertex_bindings = 1;
  470. pipelinedesc.vertex_input_state.vertex_bindings = &vertex_binding;
  471. pipelinedesc.vertex_input_state.num_vertex_attributes = 2;
  472. pipelinedesc.vertex_input_state.vertex_attributes = (SDL_GPUVertexAttribute*) &vertex_attributes;
  473. pipelinedesc.props = 0;
  474. render_state.pipeline = SDL_CreateGPUGraphicsPipeline(gpu_device, &pipelinedesc);
  475. CHECK_CREATE(render_state.pipeline, "Render Pipeline")
  476. /* These are reference-counted; once the pipeline is created, you don't need to keep these. */
  477. SDL_ReleaseGPUShader(gpu_device, vertex_shader);
  478. SDL_ReleaseGPUShader(gpu_device, fragment_shader);
  479. /* Set up per-window state */
  480. window_states = (WindowState *) SDL_calloc(state->num_windows, sizeof (WindowState));
  481. if (!window_states) {
  482. SDL_Log("Out of memory!\n");
  483. quit(2);
  484. }
  485. for (i = 0; i < state->num_windows; i++) {
  486. WindowState *winstate = &window_states[i];
  487. /* create a depth texture for the window */
  488. SDL_GetWindowSizeInPixels(state->windows[i], (int*) &drawablew, (int*) &drawableh);
  489. winstate->tex_depth = CreateDepthTexture(drawablew, drawableh);
  490. winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh);
  491. /* make each window different */
  492. winstate->angle_x = (i * 10) % 360;
  493. winstate->angle_y = (i * 20) % 360;
  494. winstate->angle_z = (i * 30) % 360;
  495. }
  496. }
  497. static int done = 0;
  498. void loop(void)
  499. {
  500. SDL_Event event;
  501. int i;
  502. /* Check for events */
  503. while (SDL_PollEvent(&event) && !done) {
  504. SDLTest_CommonEvent(state, &event, &done);
  505. }
  506. if (!done) {
  507. for (i = 0; i < state->num_windows; ++i) {
  508. Render(state->windows[i], i);
  509. }
  510. }
  511. #ifdef __EMSCRIPTEN__
  512. else {
  513. emscripten_cancel_main_loop();
  514. }
  515. #endif
  516. }
  517. int
  518. main(int argc, char *argv[])
  519. {
  520. int msaa;
  521. int i;
  522. const SDL_DisplayMode *mode;
  523. Uint64 then, now;
  524. /* Initialize params */
  525. msaa = 0;
  526. /* Initialize test framework */
  527. state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
  528. if (!state) {
  529. return 1;
  530. }
  531. for (i = 1; i < argc;) {
  532. int consumed;
  533. consumed = SDLTest_CommonArg(state, i);
  534. if (consumed == 0) {
  535. if (SDL_strcasecmp(argv[i], "--msaa") == 0) {
  536. ++msaa;
  537. consumed = 1;
  538. } else {
  539. consumed = -1;
  540. }
  541. }
  542. if (consumed < 0) {
  543. static const char *options[] = { "[--msaa]", NULL };
  544. SDLTest_CommonLogUsage(state, argv[0], options);
  545. quit(1);
  546. }
  547. i += consumed;
  548. }
  549. state->skip_renderer = 1;
  550. state->window_flags |= SDL_WINDOW_RESIZABLE;
  551. if (!SDLTest_CommonInit(state)) {
  552. quit(2);
  553. return 0;
  554. }
  555. mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(state->windows[0]));
  556. SDL_Log("Screen bpp: %d\n", SDL_BITSPERPIXEL(mode->format));
  557. init_render_state(msaa);
  558. /* Main render loop */
  559. frames = 0;
  560. then = SDL_GetTicks();
  561. done = 0;
  562. #ifdef __EMSCRIPTEN__
  563. emscripten_set_main_loop(loop, 0, 1);
  564. #else
  565. while (!done) {
  566. loop();
  567. }
  568. #endif
  569. /* Print out some timing information */
  570. now = SDL_GetTicks();
  571. if (now > then) {
  572. SDL_Log("%2.2f frames per second\n",
  573. ((double) frames * 1000) / (now - then));
  574. }
  575. #if !defined(__ANDROID__)
  576. quit(0);
  577. #endif
  578. return 0;
  579. }
  580. /* vi: set ts=4 sw=4 expandtab: */