summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile17
-rw-r--r--sdl_gpu_test.c219
-rw-r--r--shader/tri.frag8
-rw-r--r--shader/tri.vert9
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;
+}