/* sdl_gpu_test.c -- -lSDL3 */ #define SDL_MAIN_USE_CALLBACKS 1 #include #include #include #include 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); }