diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | sdl_gpu_test.c | 219 | ||||
-rw-r--r-- | shader/tri.frag | 8 | ||||
-rw-r--r-- | shader/tri.vert | 9 |
5 files changed, 256 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..482e79c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +a.out +*.spv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c71d9e --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +EXE = a.out +SPIRV != find -type f '(' -name '*.frag' -o -name '*.vert' ')' | sed 's/$$/.spv/' + +.PHONY: all run clean + +all: ${EXE} ${SPIRV} +run: ${EXE} ${SPIRV} + ./${EXE} + +clean: + rm -fv ${EXE} ${SPIRV} + +${EXE}: *.c + ${CC} -Os -Wall -std=c23 $< -o $@ -lSDL3 + +%.spv: % + glslc -O -c "$<" -o "$@" diff --git a/sdl_gpu_test.c b/sdl_gpu_test.c new file mode 100644 index 0000000..d77e137 --- /dev/null +++ b/sdl_gpu_test.c @@ -0,0 +1,219 @@ +/* sdl_gpu_test.c -- -lSDL3 */ + +#define SDL_MAIN_USE_CALLBACKS 1 +#include <SDL3/SDL_main.h> +#include <SDL3/SDL_gpu.h> +#include <stdio.h> +#include <err.h> + +SDL_Window *win; +SDL_GPUDevice *gpudev; +SDL_GPUGraphicsPipeline *pipeline_fill; +SDL_GPUBuffer *buf_vert; +SDL_GPUTransferBuffer *buf_vert_xfer; + +float vertices[][2] = { + { 0.5, -0.5 }, + { 0.5, 0.5 }, + { -0.5, -0.5 }, + + { -0.5, -0.5 }, + { -0.5, 0.5 }, + { 0.5, 0.5 }, +}; + +SDL_GPUShader* load_shader( + SDL_GPUDevice* dev, + const char *path, + SDL_GPUShaderStage stage, + Uint32 samplers, + Uint32 uniformbufs, + Uint32 storagebufs, + Uint32 storagetextures) +{ + size_t code_size; + void *code = SDL_LoadFile(path, &code_size); + if (!code) { + fprintf(stderr, "couldn't read file: %s\n", SDL_GetError()); + return NULL; + } + + SDL_GPUShaderCreateInfo info = { + .code = code, + .code_size = code_size, + .entrypoint = "main", + .format = SDL_GPU_SHADERFORMAT_SPIRV, + .stage = stage, + .num_samplers = samplers, + .num_uniform_buffers = uniformbufs, + .num_storage_buffers = storagebufs, + .num_storage_textures = storagetextures + }; + + SDL_GPUShader *sh = SDL_CreateGPUShader(dev, &info); + if (!sh) { + fprintf(stderr, "failed to create shader: %s\n", SDL_GetError()); + return NULL; + } + + SDL_free(code); + return sh; +} + +#define CHECK(t, msg) do { if (!(t)) { fprintf(stderr, "%s: %s\n", msg, SDL_GetError()); return SDL_APP_FAILURE; } } while(0) + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { + (void)argc; + (void)argv; + (void)appstate; + /* TODO: support more than just SPIR-V */ + win = SDL_CreateWindow("Hello, world!", 1024, 768, SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN); + CHECK(win, "failed to create window"); + gpudev = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, 0, NULL); + CHECK(gpudev, "failed to create gpu device"); + CHECK(SDL_ClaimWindowForGPUDevice(gpudev, win), "failed to claim window"); + + SDL_GPUShader *shader_vert = load_shader(gpudev, "shader/tri.vert.spv", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0, 0, 0); + CHECK(shader_vert, "load vertex shader"); + SDL_GPUShader *shader_frag = load_shader(gpudev, "shader/tri.frag.spv", SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 0, 0, 0); + CHECK(shader_frag, "load fragment shader"); + + SDL_GPUBufferCreateInfo buf_desc = { + .usage = SDL_GPU_BUFFERUSAGE_VERTEX, + .size = sizeof(vertices), + .props = 0 + }; + buf_vert = SDL_CreateGPUBuffer(gpudev, &buf_desc); + CHECK(buf_vert, "vertex buffer"); + + SDL_GPUTransferBufferCreateInfo buf_xfer_desc = { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = sizeof(buf_vert), + .props = 0 + }; + buf_vert_xfer = SDL_CreateGPUTransferBuffer(gpudev, &buf_xfer_desc); + CHECK(buf_vert_xfer, "vertex transfer buffer"); + + void *map = SDL_MapGPUTransferBuffer(gpudev, buf_vert_xfer, false); + SDL_memcpy(map, vertices, sizeof(vertices)); + SDL_UnmapGPUTransferBuffer(gpudev, buf_vert_xfer); + + SDL_GPUCommandBuffer *cmdbuf = SDL_AcquireGPUCommandBuffer(gpudev); + CHECK(cmdbuf, "gpu command buffer acquisition"); + SDL_GPUCopyPass *copypass = SDL_BeginGPUCopyPass(cmdbuf); + CHECK(copypass, "gpu copy pass"); + + SDL_GPUTransferBufferLocation src = { + .transfer_buffer = buf_vert_xfer, + .offset = 0 + }; + + SDL_GPUBufferRegion dest = { + .buffer = buf_vert, + .offset = 0, + .size = sizeof(vertices) + }; + + SDL_UploadToGPUBuffer(copypass, &src, &dest, false); + SDL_EndGPUCopyPass(copypass); + SDL_SubmitGPUCommandBuffer(cmdbuf); + SDL_ReleaseGPUTransferBuffer(gpudev, buf_vert_xfer); + + SDL_GPUGraphicsPipelineCreateInfo pipeline_info = { + .target_info = { + .num_color_targets = 1, + .color_target_descriptions = (SDL_GPUColorTargetDescription[]) {{ + .format = SDL_GetGPUSwapchainTextureFormat(gpudev, win) + }}, + }, + .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .vertex_shader = shader_vert, + .fragment_shader = shader_frag, + .vertex_input_state = { + .num_vertex_buffers = 1, + .vertex_buffer_descriptions = &(SDL_GPUVertexBufferDescription) { + .slot = 0, + .pitch = sizeof(float) * 2, + .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX, + .instance_step_rate = 0, + }, + .num_vertex_attributes = 1, + .vertex_attributes = (SDL_GPUVertexAttribute[]) { + { + .location = 0, + .buffer_slot = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, + .offset = 0 + } + } + }, + .props = 0 + }; + + pipeline_info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL; + pipeline_fill = SDL_CreateGPUGraphicsPipeline(gpudev, &pipeline_info); + CHECK(pipeline_fill, "graphics pipeline"); + + SDL_ReleaseGPUShader(gpudev, shader_frag); + SDL_ReleaseGPUShader(gpudev, shader_vert); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { + (void)appstate; + switch (event->type) { + case SDL_EVENT_QUIT: + puts("Quit!"); + return SDL_APP_SUCCESS; + default: + break; + } + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void *appstate) { + (void)appstate; + + SDL_GPUCommandBuffer *cmdbuf = SDL_AcquireGPUCommandBuffer(gpudev); + if (!cmdbuf) { + fprintf(stderr, "failed to acquire GPU command buffer: %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + SDL_GPUTexture *swapchain; + if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmdbuf, win, &swapchain, NULL, NULL)) { + fprintf(stderr, "failed to acquire swapchain texture: %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!swapchain) { + fprintf(stderr, "null swapchain texture: %s\n", SDL_GetError()); + SDL_SubmitGPUCommandBuffer(cmdbuf); + return SDL_APP_FAILURE; + } + + SDL_GPUColorTargetInfo color_target_info = { 0 }; + color_target_info.texture = swapchain; + color_target_info.clear_color = (SDL_FColor) { 0, 0, 0, 1 }; + color_target_info.load_op = SDL_GPU_LOADOP_CLEAR; + color_target_info.store_op = SDL_GPU_STOREOP_STORE; + + SDL_GPURenderPass *render_pass = SDL_BeginGPURenderPass(cmdbuf, &color_target_info, 1, NULL); + SDL_BindGPUGraphicsPipeline(render_pass, pipeline_fill); + SDL_BindGPUVertexBuffers(render_pass, 0, &(SDL_GPUBufferBinding) { .buffer = buf_vert, .offset = 0 }, 1); + SDL_DrawGPUPrimitives(render_pass, sizeof(vertices) / (sizeof(float)*2), 1, 0, 0); + SDL_EndGPURenderPass(render_pass); + + SDL_SubmitGPUCommandBuffer(cmdbuf); + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) { + (void)appstate; + (void)result; + SDL_ReleaseGPUGraphicsPipeline(gpudev, pipeline_fill); + SDL_ReleaseWindowFromGPUDevice(gpudev, win); + SDL_DestroyGPUDevice(gpudev); + SDL_DestroyWindow(win); +} diff --git a/shader/tri.frag b/shader/tri.frag new file mode 100644 index 0000000..5599421 --- /dev/null +++ b/shader/tri.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) in vec4 in_pos; +layout(location = 0) out vec4 out_color; + +void main(void) { + out_color = vec4(in_pos.xy + vec2(0.5,0.5), 0, 1.0); +} diff --git a/shader/tri.vert b/shader/tri.vert new file mode 100644 index 0000000..701d7c6 --- /dev/null +++ b/shader/tri.vert @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec2 in_pos; +layout(location = 0) out vec4 out_pos; + +void main() { + gl_Position = vec4(in_pos.x, in_pos.y, 0.0, 1.0); + out_pos = gl_Position; +} |