/*
* Tuxánci 2 - A first person shooter
* Copyright (C) 2025-2026 Connor Thomson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "ta_sdl.h"
#include "window.h"
#include "files.h"
#include "text.h"
#include "logging.h"
#include "screen.h"
#define SPIRV_ENTRY_POINT "main"
// NOTE: Does this formatting look good?
SDL_GPUDevice *ta_gpu_device;
SDL_GPUGraphicsPipeline *ta_gpu_text_pipeline;
SDL_GPUTexture *ta_gpu_text_texture;
SDL_GPUSampler *ta_gpu_text_sampler;
Uint32 ta_gpu_text_texture_width;
Uint32 ta_gpu_text_texture_height;
static SDL_GPUTexture *ta_gpu_msaa_texture;
static SDL_GPUTexture *ta_gpu_depth_texture;
SDL_GPUSampleCount ta_gpu_sample_count = SDL_GPU_SAMPLECOUNT_8;
SDL_GPUTextureFormat ta_gpu_swapchain_format;
static Uint32 ta_gpu_msaa_texture_width;
static Uint32 ta_gpu_msaa_texture_height;
static SDL_GPUCommandBuffer *ta_gpu_command_buffer;
static SDL_GPUTexture *ta_gpu_swapchain_texture;
static void ta_gpu_resize_msaa_texture(SDL_GPUTextureFormat format) {
if (ta_gpu_msaa_texture_width == (Uint32)ta_window_width &&
ta_gpu_msaa_texture_height == (Uint32)ta_window_height &&
ta_gpu_msaa_texture) {
return;
}
SDL_ReleaseGPUTexture(ta_gpu_device, ta_gpu_msaa_texture);
SDL_ReleaseGPUTexture(ta_gpu_device, ta_gpu_depth_texture);
ta_gpu_msaa_texture = NULL;
ta_gpu_depth_texture = NULL;
SDL_GPUTextureCreateInfo texture_info = {
.type = SDL_GPU_TEXTURETYPE_2D,
.format = format,
.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET,
.width = (Uint32)ta_window_width,
.height = (Uint32)ta_window_height,
.layer_count_or_depth = 1,
.num_levels = 1,
.sample_count = ta_gpu_sample_count,
};
ta_gpu_msaa_texture = ta_sdl_create_gpu_texture(ta_gpu_device, &texture_info);
texture_info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT;
texture_info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
ta_gpu_depth_texture = ta_sdl_create_gpu_texture(ta_gpu_device, &texture_info);
ta_gpu_msaa_texture_width = (Uint32)ta_window_width;
ta_gpu_msaa_texture_height = (Uint32)ta_window_height;
}
static SDL_GPUShader *ta_gpu_load_shader(SDL_GPUDevice *device, const void *code, size_t code_size, SDL_GPUShaderStage stage, Uint32 num_samplers, Uint32 num_uniform_buffers) {
SDL_GPUShaderCreateInfo info = {
.code = code,
.code_size = code_size,
.entrypoint = SPIRV_ENTRY_POINT,
.format = SDL_GPU_SHADERFORMAT_SPIRV,
.stage = stage,
.num_samplers = num_samplers,
.num_uniform_buffers = num_uniform_buffers,
};
SDL_GPUShader *shader = ta_sdl_create_gpu_shader(device, &info);
return shader;
}
void ta_gpu_init(void) {
ta_gpu_device = ta_sdl_create_gpu_device();
ta_sdl_claim_window_for_gpu_device(ta_gpu_device, ta_window);
ta_gpu_swapchain_format = SDL_GetGPUSwapchainTextureFormat(ta_gpu_device, ta_window);
ta_gpu_resize_msaa_texture(ta_gpu_swapchain_format);
SDL_GPUShader *vertex_shader = ta_gpu_load_shader(
ta_gpu_device,
file_text_vert_slang_spv_start,
file_text_vert_slang_spv_size,
SDL_GPU_SHADERSTAGE_VERTEX,
0,
1
);
SDL_GPUShader *fragment_shader = ta_gpu_load_shader(
ta_gpu_device,
file_text_frag_slang_spv_start,
file_text_frag_slang_spv_size,
SDL_GPU_SHADERSTAGE_FRAGMENT,
1,
0
);
SDL_GPUColorTargetDescription color_target = {
.format = ta_gpu_swapchain_format,
.blend_state = {
.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,
.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.color_blend_op = SDL_GPU_BLENDOP_ADD,
.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE,
.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
.alpha_blend_op = SDL_GPU_BLENDOP_ADD,
.enable_blend = true,
},
};
SDL_GPUGraphicsPipelineCreateInfo pipeline_info = {
.vertex_shader = vertex_shader,
.fragment_shader = fragment_shader,
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
.target_info = {
.num_color_targets = 1,
.color_target_descriptions = &color_target,
},
.multisample_state = {
.sample_count = ta_gpu_sample_count,
},
};
ta_gpu_text_pipeline = ta_sdl_create_gpu_graphics_pipeline(ta_gpu_device, &pipeline_info);
SDL_ReleaseGPUShader(ta_gpu_device, vertex_shader);
SDL_ReleaseGPUShader(ta_gpu_device, fragment_shader);
SDL_GPUSamplerCreateInfo sampler_info = {
.min_filter = SDL_GPU_FILTER_LINEAR,
.mag_filter = SDL_GPU_FILTER_LINEAR,
.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
};
ta_gpu_text_sampler = ta_sdl_create_gpu_sampler(ta_gpu_device, &sampler_info);
}
void ta_gpu_start_frame(void) {
ta_gpu_command_buffer = ta_sdl_acquire_gpu_command_buffer(ta_gpu_device);
ta_gpu_swapchain_texture = NULL;
ta_gpu_swapchain_format = SDL_GetGPUSwapchainTextureFormat(ta_gpu_device, ta_window);
ta_sdl_wait_and_acquire_gpu_swapchain_texture(ta_gpu_command_buffer, ta_window, &ta_gpu_swapchain_texture);
if (ta_gpu_swapchain_texture) {
ta_gpu_resize_msaa_texture(ta_gpu_swapchain_format);
}
}
void ta_gpu_end_frame(void) {
if (ta_gpu_swapchain_texture) {
ta_gpu_upload_text(ta_gpu_command_buffer);
ta_screen_upload(ta_gpu_command_buffer);
SDL_GPUColorTargetInfo color_target = {
.texture = ta_gpu_msaa_texture,
.load_op = SDL_GPU_LOADOP_CLEAR,
.store_op = SDL_GPU_STOREOP_RESOLVE,
.resolve_texture = ta_gpu_swapchain_texture,
.clear_color = {
ta_color_black.red,
ta_color_black.green,
ta_color_black.blue,
ta_color_black.alpha
},
};
SDL_GPUDepthStencilTargetInfo depth_target = {
.texture = ta_gpu_depth_texture,
.clear_depth = 1.0f,
.load_op = SDL_GPU_LOADOP_CLEAR,
.store_op = SDL_GPU_STOREOP_DONT_CARE,
.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE,
.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE,
};
SDL_GPURenderPass *render_pass = ta_sdl_begin_gpu_render_pass(
ta_gpu_command_buffer,
&color_target,
1,
&depth_target
);
ta_text_render(ta_gpu_command_buffer, render_pass);
ta_screen_render(ta_gpu_command_buffer, render_pass);
SDL_EndGPURenderPass(render_pass);
}
ta_sdl_submit_gpu_command_buffer(ta_gpu_command_buffer);
}