123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely.
- */
- #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
- #include <SDL3/SDL.h>
- #include <SDL3/SDL_main.h>
- #include "testutils.h"
- #include "testgpurender_effects_grayscale.frag.dxil.h"
- #include "testgpurender_effects_grayscale.frag.msl.h"
- #include "testgpurender_effects_grayscale.frag.spv.h"
- #include "testgpurender_effects_CRT.frag.dxil.h"
- #include "testgpurender_effects_CRT.frag.msl.h"
- #include "testgpurender_effects_CRT.frag.spv.h"
- /* The window is twice the size of the background */
- #define WINDOW_WIDTH (408 * 2)
- #define WINDOW_HEIGHT (167 * 2)
- #define NUM_SPRITES 15
- #define MAX_SPEED 1
- static SDL_Window *window = NULL;
- static SDL_Renderer *renderer = NULL;
- static SDL_Texture *target = NULL;
- static SDL_GPUDevice *device = NULL;
- static SDL_Texture *background;
- static SDL_Texture *sprite;
- static SDL_FRect positions[NUM_SPRITES];
- static SDL_FRect velocities[NUM_SPRITES];
- typedef enum
- {
- EFFECT_NONE,
- EFFECT_GRAYSCALE,
- EFFECT_CRT,
- NUM_EFFECTS
- } FullscreenEffect;
- typedef struct
- {
- const char *name;
- const unsigned char *dxil_shader_source;
- unsigned int dxil_shader_source_len;
- const unsigned char *msl_shader_source;
- unsigned int msl_shader_source_len;
- const unsigned char *spirv_shader_source;
- unsigned int spirv_shader_source_len;
- int num_samplers;
- int num_uniform_buffers;
- SDL_GPUShader *shader;
- SDL_GPURenderState *state;
- } FullscreenEffectData;
- typedef struct
- {
- float texture_width;
- float texture_height;
- } CRTEffectUniforms;
- static FullscreenEffectData effects[] = {
- {
- "NONE",
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- 0,
- 0,
- 0,
- NULL,
- NULL
- },
- {
- "Grayscale",
- testgpu_effects_grayscale_frag_dxil,
- sizeof(testgpu_effects_grayscale_frag_dxil),
- testgpu_effects_grayscale_frag_msl,
- sizeof(testgpu_effects_grayscale_frag_msl),
- testgpu_effects_grayscale_frag_spv,
- sizeof(testgpu_effects_grayscale_frag_spv),
- 1,
- 0,
- NULL,
- NULL
- },
- {
- "CRT monitor",
- testgpu_effects_CRT_frag_dxil,
- sizeof(testgpu_effects_CRT_frag_dxil),
- testgpu_effects_CRT_frag_msl,
- sizeof(testgpu_effects_CRT_frag_msl),
- testgpu_effects_CRT_frag_spv,
- sizeof(testgpu_effects_CRT_frag_spv),
- 1,
- 1,
- NULL,
- NULL
- }
- };
- SDL_COMPILE_TIME_ASSERT(effects, SDL_arraysize(effects) == NUM_EFFECTS);
- static int current_effect = 0;
- static void DrawScene(void)
- {
- int i;
- int window_w = WINDOW_WIDTH;
- int window_h = WINDOW_HEIGHT;
- SDL_FRect *position, *velocity;
- SDL_RenderTexture(renderer, background, NULL, NULL);
- /* Move the sprite, bounce at the wall, and draw */
- for (i = 0; i < NUM_SPRITES; ++i) {
- position = &positions[i];
- velocity = &velocities[i];
- position->x += velocity->x;
- if ((position->x < 0) || (position->x >= (window_w - sprite->w))) {
- velocity->x = -velocity->x;
- position->x += velocity->x;
- }
- position->y += velocity->y;
- if ((position->y < 0) || (position->y >= (window_h - sprite->h))) {
- velocity->y = -velocity->y;
- position->y += velocity->y;
- }
- /* Blit the sprite onto the screen */
- SDL_RenderTexture(renderer, sprite, NULL, position);
- }
- }
- static bool InitGPURenderState(void)
- {
- SDL_GPUShaderFormat formats;
- SDL_GPUShaderCreateInfo info;
- SDL_GPURenderStateDesc desc;
- int i;
- formats = SDL_GetGPUShaderFormats(device);
- if (formats == SDL_GPU_SHADERFORMAT_INVALID) {
- SDL_Log("Couldn't get supported shader formats: %s", SDL_GetError());
- return false;
- }
- for (i = 0; i < SDL_arraysize(effects); ++i) {
- FullscreenEffectData *data = &effects[i];
- if (i == EFFECT_NONE) {
- continue;
- }
- SDL_zero(info);
- if (formats & SDL_GPU_SHADERFORMAT_SPIRV) {
- info.format = SDL_GPU_SHADERFORMAT_SPIRV;
- info.code = data->spirv_shader_source;
- info.code_size = data->spirv_shader_source_len;
- } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) {
- info.format = SDL_GPU_SHADERFORMAT_DXIL;
- info.code = data->dxil_shader_source;
- info.code_size = data->dxil_shader_source_len;
- } else if (formats & SDL_GPU_SHADERFORMAT_MSL) {
- info.format = SDL_GPU_SHADERFORMAT_MSL;
- info.code = data->msl_shader_source;
- info.code_size = data->msl_shader_source_len;
- } else {
- SDL_Log("No supported shader format found");
- return false;
- }
- info.num_samplers = data->num_samplers;
- info.num_uniform_buffers = data->num_uniform_buffers;
- info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
- data->shader = SDL_CreateGPUShader(device, &info);
- if (!data->shader) {
- SDL_Log("Couldn't create shader: %s", SDL_GetError());
- return false;
- }
- SDL_INIT_INTERFACE(&desc);
- desc.fragment_shader = data->shader;
- data->state = SDL_CreateGPURenderState(renderer, &desc);
- if (!data->state) {
- SDL_Log("Couldn't create render state: %s", SDL_GetError());
- return false;
- }
- if (i == EFFECT_CRT) {
- CRTEffectUniforms uniforms;
- SDL_zero(uniforms);
- uniforms.texture_width = (float)target->w;
- uniforms.texture_height = (float)target->h;
- if (!SDL_SetGPURenderStateFragmentUniforms(data->state, 0, &uniforms, sizeof(uniforms))) {
- SDL_Log("Couldn't set uniform data: %s", SDL_GetError());
- return false;
- }
- }
- }
- return true;
- }
- static void QuitGPURenderState(void)
- {
- int i;
- for (i = 0; i < SDL_arraysize(effects); ++i) {
- FullscreenEffectData *data = &effects[i];
- if (i == EFFECT_NONE) {
- continue;
- }
- SDL_DestroyGPURenderState(data->state);
- SDL_ReleaseGPUShader(device, data->shader);
- }
- }
- /* This function runs once at startup. */
- SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
- {
- const char *description = "GPU render effects example";
- int i;
- SDL_SetAppMetadata(description, "1.0", "com.example.testgpurender_effects");
- if (!SDL_Init(SDL_INIT_VIDEO)) {
- SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- window = SDL_CreateWindow(description, WINDOW_WIDTH, WINDOW_HEIGHT, 0);
- if (!window) {
- SDL_Log("Couldn't create window: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- renderer = SDL_CreateGPURenderer(window, SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, &device);
- if (!renderer || !device) {
- SDL_Log("Couldn't create renderer: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- SDL_SetRenderVSync(renderer, 1);
- target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, WINDOW_WIDTH, WINDOW_HEIGHT);
- if (!target) {
- SDL_Log("Couldn't create target texture: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- background = LoadTexture(renderer, "sample.bmp", false);
- if (!background) {
- SDL_Log("Couldn't create background: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- sprite = LoadTexture(renderer, "icon.bmp", true);
- if (!sprite) {
- SDL_Log("Couldn't create sprite: %s", SDL_GetError());
- return SDL_APP_FAILURE;
- }
- /* Initialize the sprite positions */
- for (i = 0; i < NUM_SPRITES; ++i) {
- positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite->w);
- positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite->h);
- positions[i].w = (float)sprite->w;
- positions[i].h = (float)sprite->h;
- velocities[i].x = 0.0f;
- velocities[i].y = 0.0f;
- while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
- velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
- velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
- }
- }
- if (!InitGPURenderState()) {
- return SDL_APP_FAILURE;
- }
- return SDL_APP_CONTINUE; /* carry on with the program! */
- }
- /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
- SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
- {
- if (event->type == SDL_EVENT_QUIT ||
- (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE)) {
- return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
- }
- if (event->type == SDL_EVENT_KEY_DOWN) {
- if (event->key.key == SDLK_SPACE) {
- current_effect = (current_effect + 1) % NUM_EFFECTS;
- }
- }
- return SDL_APP_CONTINUE; /* carry on with the program! */
- }
- /* This function runs once per frame, and is the heart of the program. */
- SDL_AppResult SDL_AppIterate(void *appstate)
- {
- FullscreenEffectData *effect = &effects[current_effect];
- /* Draw the scene to the render target */
- SDL_SetRenderTarget(renderer, target);
- DrawScene();
- SDL_SetRenderTarget(renderer, NULL);
- /* Display the render target with the fullscreen effect */
- SDL_SetRenderGPUState(renderer, effect->state);
- SDL_RenderTexture(renderer, target, NULL, NULL);
- SDL_SetRenderGPUState(renderer, NULL);
- /* Draw some help text */
- SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
- SDL_RenderDebugTextFormat(renderer, 4.0f, WINDOW_HEIGHT - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE - 4.0f,
- "Current effect: %s, press SPACE to cycle", effect->name);
- SDL_RenderPresent(renderer);
- return SDL_APP_CONTINUE; /* carry on with the program! */
- }
- /* This function runs once at shutdown. */
- void SDL_AppQuit(void *appstate, SDL_AppResult result)
- {
- /* SDL will clean up the window/renderer for us. */
- QuitGPURenderState();
- }
|