testgpurender_effects.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. Copyright (C) 1997-2025 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. #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
  11. #include <SDL3/SDL.h>
  12. #include <SDL3/SDL_main.h>
  13. #include "testutils.h"
  14. #include "testgpurender_effects_grayscale.frag.dxil.h"
  15. #include "testgpurender_effects_grayscale.frag.msl.h"
  16. #include "testgpurender_effects_grayscale.frag.spv.h"
  17. #include "testgpurender_effects_CRT.frag.dxil.h"
  18. #include "testgpurender_effects_CRT.frag.msl.h"
  19. #include "testgpurender_effects_CRT.frag.spv.h"
  20. /* The window is twice the size of the background */
  21. #define WINDOW_WIDTH (408 * 2)
  22. #define WINDOW_HEIGHT (167 * 2)
  23. #define NUM_SPRITES 15
  24. #define MAX_SPEED 1
  25. static SDL_Window *window = NULL;
  26. static SDL_Renderer *renderer = NULL;
  27. static SDL_Texture *target = NULL;
  28. static SDL_GPUDevice *device = NULL;
  29. static SDL_Texture *background;
  30. static SDL_Texture *sprite;
  31. static SDL_FRect positions[NUM_SPRITES];
  32. static SDL_FRect velocities[NUM_SPRITES];
  33. typedef enum
  34. {
  35. EFFECT_NONE,
  36. EFFECT_GRAYSCALE,
  37. EFFECT_CRT,
  38. NUM_EFFECTS
  39. } FullscreenEffect;
  40. typedef struct
  41. {
  42. const char *name;
  43. const unsigned char *dxil_shader_source;
  44. unsigned int dxil_shader_source_len;
  45. const unsigned char *msl_shader_source;
  46. unsigned int msl_shader_source_len;
  47. const unsigned char *spirv_shader_source;
  48. unsigned int spirv_shader_source_len;
  49. int num_samplers;
  50. int num_uniform_buffers;
  51. SDL_GPUShader *shader;
  52. SDL_GPURenderState *state;
  53. } FullscreenEffectData;
  54. typedef struct
  55. {
  56. float texture_width;
  57. float texture_height;
  58. } CRTEffectUniforms;
  59. static FullscreenEffectData effects[] = {
  60. {
  61. "NONE",
  62. NULL,
  63. 0,
  64. NULL,
  65. 0,
  66. NULL,
  67. 0,
  68. 0,
  69. 0,
  70. NULL,
  71. NULL
  72. },
  73. {
  74. "Grayscale",
  75. testgpu_effects_grayscale_frag_dxil,
  76. sizeof(testgpu_effects_grayscale_frag_dxil),
  77. testgpu_effects_grayscale_frag_msl,
  78. sizeof(testgpu_effects_grayscale_frag_msl),
  79. testgpu_effects_grayscale_frag_spv,
  80. sizeof(testgpu_effects_grayscale_frag_spv),
  81. 1,
  82. 0,
  83. NULL,
  84. NULL
  85. },
  86. {
  87. "CRT monitor",
  88. testgpu_effects_CRT_frag_dxil,
  89. sizeof(testgpu_effects_CRT_frag_dxil),
  90. testgpu_effects_CRT_frag_msl,
  91. sizeof(testgpu_effects_CRT_frag_msl),
  92. testgpu_effects_CRT_frag_spv,
  93. sizeof(testgpu_effects_CRT_frag_spv),
  94. 1,
  95. 1,
  96. NULL,
  97. NULL
  98. }
  99. };
  100. SDL_COMPILE_TIME_ASSERT(effects, SDL_arraysize(effects) == NUM_EFFECTS);
  101. static int current_effect = 0;
  102. static void DrawScene(void)
  103. {
  104. int i;
  105. int window_w = WINDOW_WIDTH;
  106. int window_h = WINDOW_HEIGHT;
  107. SDL_FRect *position, *velocity;
  108. SDL_RenderTexture(renderer, background, NULL, NULL);
  109. /* Move the sprite, bounce at the wall, and draw */
  110. for (i = 0; i < NUM_SPRITES; ++i) {
  111. position = &positions[i];
  112. velocity = &velocities[i];
  113. position->x += velocity->x;
  114. if ((position->x < 0) || (position->x >= (window_w - sprite->w))) {
  115. velocity->x = -velocity->x;
  116. position->x += velocity->x;
  117. }
  118. position->y += velocity->y;
  119. if ((position->y < 0) || (position->y >= (window_h - sprite->h))) {
  120. velocity->y = -velocity->y;
  121. position->y += velocity->y;
  122. }
  123. /* Blit the sprite onto the screen */
  124. SDL_RenderTexture(renderer, sprite, NULL, position);
  125. }
  126. }
  127. static bool InitGPURenderState(void)
  128. {
  129. SDL_GPUShaderFormat formats;
  130. SDL_GPUShaderCreateInfo info;
  131. SDL_GPURenderStateDesc desc;
  132. int i;
  133. formats = SDL_GetGPUShaderFormats(device);
  134. if (formats == SDL_GPU_SHADERFORMAT_INVALID) {
  135. SDL_Log("Couldn't get supported shader formats: %s", SDL_GetError());
  136. return false;
  137. }
  138. for (i = 0; i < SDL_arraysize(effects); ++i) {
  139. FullscreenEffectData *data = &effects[i];
  140. if (i == EFFECT_NONE) {
  141. continue;
  142. }
  143. SDL_zero(info);
  144. if (formats & SDL_GPU_SHADERFORMAT_SPIRV) {
  145. info.format = SDL_GPU_SHADERFORMAT_SPIRV;
  146. info.code = data->spirv_shader_source;
  147. info.code_size = data->spirv_shader_source_len;
  148. } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) {
  149. info.format = SDL_GPU_SHADERFORMAT_DXIL;
  150. info.code = data->dxil_shader_source;
  151. info.code_size = data->dxil_shader_source_len;
  152. } else if (formats & SDL_GPU_SHADERFORMAT_MSL) {
  153. info.format = SDL_GPU_SHADERFORMAT_MSL;
  154. info.code = data->msl_shader_source;
  155. info.code_size = data->msl_shader_source_len;
  156. } else {
  157. SDL_Log("No supported shader format found");
  158. return false;
  159. }
  160. info.num_samplers = data->num_samplers;
  161. info.num_uniform_buffers = data->num_uniform_buffers;
  162. info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
  163. data->shader = SDL_CreateGPUShader(device, &info);
  164. if (!data->shader) {
  165. SDL_Log("Couldn't create shader: %s", SDL_GetError());
  166. return false;
  167. }
  168. SDL_INIT_INTERFACE(&desc);
  169. desc.fragment_shader = data->shader;
  170. data->state = SDL_CreateGPURenderState(renderer, &desc);
  171. if (!data->state) {
  172. SDL_Log("Couldn't create render state: %s", SDL_GetError());
  173. return false;
  174. }
  175. if (i == EFFECT_CRT) {
  176. CRTEffectUniforms uniforms;
  177. SDL_zero(uniforms);
  178. uniforms.texture_width = (float)target->w;
  179. uniforms.texture_height = (float)target->h;
  180. if (!SDL_SetGPURenderStateFragmentUniforms(data->state, 0, &uniforms, sizeof(uniforms))) {
  181. SDL_Log("Couldn't set uniform data: %s", SDL_GetError());
  182. return false;
  183. }
  184. }
  185. }
  186. return true;
  187. }
  188. static void QuitGPURenderState(void)
  189. {
  190. int i;
  191. for (i = 0; i < SDL_arraysize(effects); ++i) {
  192. FullscreenEffectData *data = &effects[i];
  193. if (i == EFFECT_NONE) {
  194. continue;
  195. }
  196. SDL_DestroyGPURenderState(data->state);
  197. SDL_ReleaseGPUShader(device, data->shader);
  198. }
  199. }
  200. /* This function runs once at startup. */
  201. SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
  202. {
  203. const char *description = "GPU render effects example";
  204. int i;
  205. SDL_SetAppMetadata(description, "1.0", "com.example.testgpurender_effects");
  206. if (!SDL_Init(SDL_INIT_VIDEO)) {
  207. SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
  208. return SDL_APP_FAILURE;
  209. }
  210. window = SDL_CreateWindow(description, WINDOW_WIDTH, WINDOW_HEIGHT, 0);
  211. if (!window) {
  212. SDL_Log("Couldn't create window: %s", SDL_GetError());
  213. return SDL_APP_FAILURE;
  214. }
  215. renderer = SDL_CreateGPURenderer(window, SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, &device);
  216. if (!renderer || !device) {
  217. SDL_Log("Couldn't create renderer: %s", SDL_GetError());
  218. return SDL_APP_FAILURE;
  219. }
  220. SDL_SetRenderVSync(renderer, 1);
  221. target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, WINDOW_WIDTH, WINDOW_HEIGHT);
  222. if (!target) {
  223. SDL_Log("Couldn't create target texture: %s", SDL_GetError());
  224. return SDL_APP_FAILURE;
  225. }
  226. background = LoadTexture(renderer, "sample.bmp", false);
  227. if (!background) {
  228. SDL_Log("Couldn't create background: %s", SDL_GetError());
  229. return SDL_APP_FAILURE;
  230. }
  231. sprite = LoadTexture(renderer, "icon.bmp", true);
  232. if (!sprite) {
  233. SDL_Log("Couldn't create sprite: %s", SDL_GetError());
  234. return SDL_APP_FAILURE;
  235. }
  236. /* Initialize the sprite positions */
  237. for (i = 0; i < NUM_SPRITES; ++i) {
  238. positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite->w);
  239. positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite->h);
  240. positions[i].w = (float)sprite->w;
  241. positions[i].h = (float)sprite->h;
  242. velocities[i].x = 0.0f;
  243. velocities[i].y = 0.0f;
  244. while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
  245. velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
  246. velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
  247. }
  248. }
  249. if (!InitGPURenderState()) {
  250. return SDL_APP_FAILURE;
  251. }
  252. return SDL_APP_CONTINUE; /* carry on with the program! */
  253. }
  254. /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
  255. SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
  256. {
  257. if (event->type == SDL_EVENT_QUIT ||
  258. (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE)) {
  259. return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
  260. }
  261. if (event->type == SDL_EVENT_KEY_DOWN) {
  262. if (event->key.key == SDLK_SPACE) {
  263. current_effect = (current_effect + 1) % NUM_EFFECTS;
  264. }
  265. }
  266. return SDL_APP_CONTINUE; /* carry on with the program! */
  267. }
  268. /* This function runs once per frame, and is the heart of the program. */
  269. SDL_AppResult SDL_AppIterate(void *appstate)
  270. {
  271. FullscreenEffectData *effect = &effects[current_effect];
  272. /* Draw the scene to the render target */
  273. SDL_SetRenderTarget(renderer, target);
  274. DrawScene();
  275. SDL_SetRenderTarget(renderer, NULL);
  276. /* Display the render target with the fullscreen effect */
  277. SDL_SetRenderGPUState(renderer, effect->state);
  278. SDL_RenderTexture(renderer, target, NULL, NULL);
  279. SDL_SetRenderGPUState(renderer, NULL);
  280. /* Draw some help text */
  281. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  282. SDL_RenderDebugTextFormat(renderer, 4.0f, WINDOW_HEIGHT - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE - 4.0f,
  283. "Current effect: %s, press SPACE to cycle", effect->name);
  284. SDL_RenderPresent(renderer);
  285. return SDL_APP_CONTINUE; /* carry on with the program! */
  286. }
  287. /* This function runs once at shutdown. */
  288. void SDL_AppQuit(void *appstate, SDL_AppResult result)
  289. {
  290. /* SDL will clean up the window/renderer for us. */
  291. QuitGPURenderState();
  292. }