From 270132a58e0e7da80e36cb162961857fdb8c655a Mon Sep 17 00:00:00 2001 From: azraptor <154612819+azraptor@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:49:15 -0400 Subject: [PATCH] Triangle with per vertex colors --- .gitignore | 57 ++++++++++++ CMakeLists.txt | 75 ++++++++++++++++ main.c | 201 ++++++++++++++++++++++++++++++++++++++++++ shaders/color.frag | 13 +++ shaders/triangle.vert | 42 +++++++++ 5 files changed, 388 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 main.c create mode 100644 shaders/color.frag create mode 100644 shaders/triangle.vert diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d211175 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +SDL3/ +build/ +.cache/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..90c2da9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.10) +project(sdlgpu1 VERSION 0.1.0 LANGUAGES C CXX) + +SET(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) +set(EXE_NAME ${CMAKE_PROJECT_NAME}) + +#set(PROGRAM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/vulkanapp.cc) + +# Get our GLSL shaders +set(SHADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/shaders) +set(SHADER_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}) +file(GLOB SHADERS ${SHADER_DIR}/*.vert + ${SHADER_DIR}/*.frag + ${SHADER_DIR}/*.comp + ${SHADER_DIR}/*.geom + ${SHADER_DIR}/*.tesc + ${SHADER_DIR}/*.tese + ${SHADER_DIR}/*.mesh + ${SHADER_DIR}/*.task + ${SHADER_DIR}/*.rgen + ${SHADER_DIR}/*.rchit + ${SHADER_DIR}/*.rmiss + ) + +option(SDL3_NONSYSTEM "Use SDL2 from folder in source tree" ON) + +if(SDL3_NONSYSTEM) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/SDL3 ${CMAKE_CURRENT_BINARY_DIR}/SDL3 EXCLUDE_FROM_ALL) +else() + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared) +endif() + +find_package(Vulkan REQUIRED COMPONENTS glslc) + +# Compile each shader +foreach(SHADER IN LISTS SHADERS) + get_filename_component(FILENAME ${SHADER} NAME) + add_custom_command(OUTPUT ${SHADER_BIN_DIR}/${FILENAME}.spv + COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${SHADER_BIN_DIR}/${FILENAME}.spv + DEPENDS ${SHADER} + COMMENT "Compiling Shader ${FILENAME}") + list(APPEND SPV_SHADERS ${SHADER_BIN_DIR}/${FILENAME}.spv) +endForeach() + +# Shader target +add_custom_target(shaders ALL DEPENDS ${SPV_SHADERS}) + +add_executable(${EXE_NAME} ${CMAKE_SOURCE_DIR}/main.c ${SHADERS}) + +if(WIN32) + add_custom_command( + TARGET ${EXE_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy $ $ + VERBATIM + ) +endif() + +set_target_properties(${EXE_NAME} + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS ON +) + +target_include_directories(${EXE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/include) +add_dependencies(${EXE_NAME} shaders) + +if(CMAKE_COMPILER_IS_GNUCC) + target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra) +elseif(MSVC) + target_compile_options(${EXE_NAME} PRIVATE /W4) +endif() + +# Linking +target_link_libraries(${EXE_NAME} PRIVATE SDL3::SDL3) \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..9dce2fb --- /dev/null +++ b/main.c @@ -0,0 +1,201 @@ +#include + +SDL_GPUShader *loadShader(SDL_GPUDevice *device, + const char *filename, + Uint32 samplerCount, + Uint32 uniformBufferCount, + Uint32 storageBufferCount, + Uint32 storageTextureCount) { + SDL_GPUShaderStage stage; + if(SDL_strstr(filename, ".vert")) + stage = SDL_GPU_SHADERSTAGE_VERTEX; + else if(SDL_strstr(filename, ".frag")) + stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + else { + SDL_Log("Invalid shader stage!"); + return NULL; + } + + SDL_GPUShaderFormat backendFmt = SDL_GetGPUShaderFormats(device); + + SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_INVALID; + const char *entryPoint; + + if(backendFmt & SDL_GPU_SHADERFORMAT_SPIRV) { + format = SDL_GPU_SHADERFORMAT_SPIRV; + entryPoint = "main"; + } else { + SDL_Log("Unsupported shader format!"); + return NULL; + } + + size_t fSize = 0; + void *byteCode = SDL_LoadFile(filename, &fSize); + + if(byteCode == NULL) { + SDL_Log("Could not load shader bytecode from file: %s", filename); + return NULL; + } + + SDL_GPUShaderCreateInfo shaderInfo = { + .code = (Uint8*)byteCode, + .code_size = fSize, + .entrypoint = entryPoint, + .format = format, + .stage = stage, + .num_samplers = samplerCount, + .num_uniform_buffers = uniformBufferCount, + .num_storage_buffers = storageBufferCount, + .num_storage_textures = storageTextureCount + }; + SDL_GPUShader *shader = SDL_CreateGPUShader(device, &shaderInfo); + if(shader == NULL) { + SDL_Log("Could not create shader!"); + SDL_free(byteCode); + return NULL; + } + + SDL_free(byteCode); + return shader; +} + +int main(int argc, char **argv) { + SDL_Window *window = NULL; + SDL_GPUDevice *GPUDevice = NULL; + SDL_GPUTexture *swapchainTexture = NULL; + SDL_GPUCommandBuffer *commandBuf = NULL; + SDL_GPURenderPass *renderPass = NULL; + + SDL_GPUShader *vertexShader = NULL; + SDL_GPUShader *fragShader = NULL; + + SDL_GPUGraphicsPipeline *pipeline = NULL; + + bool done = false; + + if(!SDL_Init(SDL_INIT_VIDEO)) { + SDL_Log("Unable to initialize SDL3: %s", SDL_GetError()); + return 1; + } + + // Create the GPU device + GPUDevice = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, "vulkan"); + + const char* device_driver = SDL_GetGPUDeviceDriver(GPUDevice); + SDL_Log("Created GPU device with driver: %s\n", device_driver); + + if(GPUDevice == NULL) { + SDL_Log("Create GPU failed"); + return 1; + } + + // Create the window + window = SDL_CreateWindow("SDL3 Window", 640, 480, SDL_WINDOW_RESIZABLE); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + + if (window == NULL) { + SDL_Log("Could not create window: %s\n", SDL_GetError()); + return 1; + } + + // Tie the window to our GPU + if(!SDL_ClaimWindowForGPUDevice(GPUDevice, window)) { + SDL_Log("Claiming SDL3 window failed"); + return 1; + } + + // Shader time + vertexShader = loadShader(GPUDevice, "triangle.vert.spv", 0, 0, 0, 0); + if(vertexShader == NULL) + { + SDL_Log("Could not create vertex shader!"); + return 1; + } + + fragShader = loadShader(GPUDevice, "color.frag.spv", 0, 0, 0, 0); + if(fragShader == NULL) + { + SDL_Log("Could not create fragment shader!"); + return 1; + } + + // Creating the pipelines + SDL_GPUGraphicsPipelineCreateInfo pipelineCreateInfo = { + .target_info = { + .num_color_targets = 1, + .color_target_descriptions = (SDL_GPUColorTargetDescription[]){{ + .format = SDL_GetGPUSwapchainTextureFormat(GPUDevice, window) + }}, + }, + .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .vertex_shader = vertexShader, + .fragment_shader = fragShader, + }; + + pipelineCreateInfo.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL; + pipeline = SDL_CreateGPUGraphicsPipeline(GPUDevice, &pipelineCreateInfo); + if(pipeline == NULL) { + SDL_Log("Could not create pipeline"); + return 1; + } + + // Free up shaders that are now bound to pipeline + SDL_ReleaseGPUShader(GPUDevice, vertexShader); + SDL_ReleaseGPUShader(GPUDevice, fragShader); + + // Now to loop + while (!done) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_EVENT_QUIT) { + done = true; + } + } + + // Get our command buffer + commandBuf = SDL_AcquireGPUCommandBuffer(GPUDevice); + if(commandBuf == NULL) { + SDL_Log("Could not get command buf!"); + return 1; + } + + // Get our swapchain + if(!SDL_WaitAndAcquireGPUSwapchainTexture(commandBuf, window, &swapchainTexture, NULL, NULL)) { + SDL_Log("Could not create swapchain!"); + return 1; + } + + // Handle the swapchain + if(swapchainTexture != NULL) { + // Clear (background) color + SDL_GPUColorTargetInfo colorInfo = { + .texture = swapchainTexture, + .clear_color = (SDL_FColor){0.25f, 0.5f, 0.1f, 1.0f}, + .load_op = SDL_GPU_LOADOP_CLEAR, + .store_op = SDL_GPU_STOREOP_STORE + }; + + // Now for the render pass + renderPass = SDL_BeginGPURenderPass(commandBuf, &colorInfo, 1, NULL); + SDL_BindGPUGraphicsPipeline(renderPass, pipeline); + SDL_DrawGPUPrimitives(renderPass, 3, 1, 0, 0); + SDL_EndGPURenderPass(renderPass); + } else { + SDL_Log("Swapchain texture just does not exist! %s", SDL_GetError()); + return 1; + } + + SDL_SubmitGPUCommandBuffer(commandBuf); + } + + // Cleanup + SDL_ReleaseGPUGraphicsPipeline(GPUDevice, pipeline); + + SDL_ReleaseWindowFromGPUDevice(GPUDevice, window); + SDL_DestroyWindow(window); + SDL_DestroyGPUDevice(GPUDevice); + + SDL_Quit(); + return 0; +} \ No newline at end of file diff --git a/shaders/color.frag b/shaders/color.frag new file mode 100644 index 0000000..d635d10 --- /dev/null +++ b/shaders/color.frag @@ -0,0 +1,13 @@ +#version 450 + +// layout(location = 0) in vec4 in_var_TEXCOORD0; +// layout(location = 0) out vec4 out_var_SV_Target0; + +layout(location = 0) in vec4 inColor; +layout(location = 0) out vec4 outColor; + +void main() +{ + outColor = inColor; +} + diff --git a/shaders/triangle.vert b/shaders/triangle.vert new file mode 100644 index 0000000..9f4e701 --- /dev/null +++ b/shaders/triangle.vert @@ -0,0 +1,42 @@ +#version 450 + +vec2 temp_pos; +vec4 temp_color; + +// layout(location = 0) out vec4 out_var_TEXCOORD0; +layout(location = 0) out vec4 outColor; + +void main() +{ + vec4 color; + vec2 pos; + + if (uint(gl_VertexIndex) == 0u) + { + color = vec4(1.0, 0.0, 0.0, 1.0); + pos = vec2(-0.5); + } + else + { + vec4 vert_color; + vec2 vert_pos; + if (uint(gl_VertexIndex) == 1u) + { + vert_color = vec4(0.0, 1.0, 0.0, 1.0); + vert_pos = vec2(0.5, -0.5); + } + else + { + bool index_exists = uint(gl_VertexIndex) == 2u; + vert_color = mix(temp_color, vec4(0.0, 0.0, 1.0, 1.0), bvec4(index_exists)); + vert_pos = mix(temp_pos, vec2(0.0, 0.5), bvec2(index_exists)); + } + color = vert_color; + pos = vert_pos; + } + + // out_var_TEXCOORD0 = color; + outColor = color; + gl_Position = vec4(pos, 0.0, 1.0); +} +