diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 66f82c3..09f857f 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -26,8 +26,8 @@ jobs: # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: - build_type: [Debug, Release] - c_compiler: [gcc, clang] + build_type: [ Debug, Release ] + c_compiler: [ gcc, clang ] include: - c_compiler: gcc cpp_compiler: g++ @@ -35,56 +35,56 @@ jobs: cpp_compiler: clang++ steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. - id: strings - shell: bash - run: | - echo "build-output-dir=$GITHUB_WORKSPACE/build" >> "$GITHUB_OUTPUT" - echo "install-dir=$GITHUB_WORKSPACE/install/archlinux-latest-${{ matrix.c_compiler }}-${{ matrix.build_type }}" >> "$GITHUB_OUTPUT" + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=$GITHUB_WORKSPACE/build" >> "$GITHUB_OUTPUT" + echo "install-dir=$GITHUB_WORKSPACE/install/archlinux-latest-${{ matrix.c_compiler }}-${{ matrix.build_type }}" >> "$GITHUB_OUTPUT" - - - name: Install SDL windowing deps (Linux) - run: | - pacman -Syu --noconfirm - pacman -S --noconfirm --needed \ - base-devel cmake ninja pkgconf gcc clang git python3\ - alsa-lib jack libpulse \ - xorgproto libx11 libxext libxrandr libxcursor libxfixes libxi libxss libxtst \ - libxkbcommon wayland wayland-protocols \ - libdrm mesa mesa-utils - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: > - cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -S $GITHUB_WORKSPACE - -G "Ninja Multi-Config" + - name: Install SDL windowing deps (Linux) + run: | + pacman -Syu --noconfirm + pacman -S --noconfirm --needed \ + base-devel cmake ninja pkgconf gcc clang git python3\ + alsa-lib jack libpulse \ + xorgproto libx11 libxext libxrandr libxcursor libxfixes libxi libxss libxtst \ + libxkbcommon wayland wayland-protocols \ + libdrm mesa mesa-utils - - name: Build - # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S $GITHUB_WORKSPACE + -G "Ninja Multi-Config" - - name: Test - working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - - name: Install - run: > - cmake --install ${{ steps.strings.outputs.build-output-dir }} - --config ${{ matrix.build_type }} - --prefix "${{ steps.strings.outputs.install-dir }}" + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} - - name: Upload install folder - uses: actions/upload-artifact@v4 - with: - name: archlinux-latest-${{ matrix.c_compiler }}-${{ matrix.build_type }}-install-${{ github.sha }} - path: ${{ steps.strings.outputs.install-dir }}/ + - name: Install + run: > + cmake --install ${{ steps.strings.outputs.build-output-dir }} + --config ${{ matrix.build_type }} + --prefix "${{ steps.strings.outputs.install-dir }}" + + - name: Upload install folder + uses: actions/upload-artifact@v4 + with: + name: archlinux-latest-${{ matrix.c_compiler }}-${{ matrix.build_type }}-install-${{ github.sha }} + path: ${{ steps.strings.outputs.install-dir }}/ diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index ec28945..970e4ab 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -23,9 +23,9 @@ jobs: # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: - os: [windows-latest] - build_type: [Debug, Release] - c_compiler: [gcc, clang, cl] + os: [ windows-latest ] + build_type: [ Debug, Release ] + c_compiler: [ gcc, clang, cl ] include: - os: windows-latest c_compiler: cl @@ -38,45 +38,45 @@ jobs: cpp_compiler: clang++ steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. - id: strings - shell: bash - run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - echo "install-dir=${{ github.workspace }}/install/${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }}" >> "$GITHUB_OUTPUT" - + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + echo "install-dir=${{ github.workspace }}/install/${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }}" >> "$GITHUB_OUTPUT" - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: > - cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - -S ${{ github.workspace }} - - name: Build - # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} - - name: Test - working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - - name: Install - run: > - cmake --install ${{ steps.strings.outputs.build-output-dir }} - --config ${{ matrix.build_type }} - --prefix "${{ steps.strings.outputs.install-dir }}" + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} - - name: Upload install folder - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }}-install-${{ github.sha }} - path: ${{ steps.strings.outputs.install-dir }}/ + - name: Install + run: > + cmake --install ${{ steps.strings.outputs.build-output-dir }} + --config ${{ matrix.build_type }} + --prefix "${{ steps.strings.outputs.install-dir }}" + + - name: Upload install folder + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }}-install-${{ github.sha }} + path: ${{ steps.strings.outputs.install-dir }}/ diff --git a/main.cpp b/main.cpp index 3808de2..c1b3cad 100644 --- a/main.cpp +++ b/main.cpp @@ -5,9 +5,7 @@ #include "imgui_impl_sdl3.h" #include "imgui_impl_sdlgpu3.h" -#define SDL_MAIN_USE_CALLBACKS #include -#include import sdl_wrapper; @@ -50,217 +48,142 @@ void main() FragColor = v_color; })WSQ"; -sopho::BufferWrapper BufferWrapper{}; - -SDL_Window *window; -SDL_GPUDevice *device; -SDL_GPUBuffer *vertexBuffer; -SDL_GPUTransferBuffer *transferBuffer; -SDL_GPUGraphicsPipeline *graphicsPipeline; - -SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { - // create a window - window = SDL_CreateWindow("Hello, Triangle!", 960, 540, SDL_WINDOW_RESIZABLE); - - // create the device - device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, NULL); - SDL_ClaimWindowForGPUDevice(device, window); - - shaderc::Compiler compiler{}; - shaderc::CompileOptions options{}; - options.SetTargetEnvironment(shaderc_target_env_vulkan, 0); - auto result = compiler.CompileGlslToSpv(vertex_source, shaderc_glsl_vertex_shader, "test.glsl", options); - - if (result.GetCompilationStatus() != shaderc_compilation_status_success) { - std::cerr << "[shaderc] compile error in " << "test.glsl" << ":\n" - << result.GetErrorMessage() << std::endl; - } - - // load the vertex shader code - std::vector vertexCode{result.cbegin(), result.cend()}; - - // create the vertex shader - SDL_GPUShaderCreateInfo vertexInfo{}; - vertexInfo.code = (Uint8 *) vertexCode.data(); - vertexInfo.code_size = vertexCode.size() * 4; - vertexInfo.entrypoint = "main"; - vertexInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; - vertexInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX; - vertexInfo.num_samplers = 0; - vertexInfo.num_storage_buffers = 0; - vertexInfo.num_storage_textures = 0; - vertexInfo.num_uniform_buffers = 0; - - SDL_GPUShader *vertexShader = SDL_CreateGPUShader(device, &vertexInfo); - - result = compiler.CompileGlslToSpv(fragment_source, shaderc_glsl_fragment_shader, "test.frag", options); - - if (result.GetCompilationStatus() != shaderc_compilation_status_success) { - std::cerr << "[shaderc] compile error in " << "test.frag" << ":\n" - << result.GetErrorMessage() << std::endl; - } - - // load the fragment shader code - std::vector fragmentCode{result.cbegin(), result.cend()}; - - // create the fragment shader - SDL_GPUShaderCreateInfo fragmentInfo{}; - fragmentInfo.code = (Uint8 *) fragmentCode.data(); - fragmentInfo.code_size = fragmentCode.size() * 4; - fragmentInfo.entrypoint = "main"; - fragmentInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; - fragmentInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; - fragmentInfo.num_samplers = 0; - fragmentInfo.num_storage_buffers = 0; - fragmentInfo.num_storage_textures = 0; - fragmentInfo.num_uniform_buffers = 0; - - SDL_GPUShader *fragmentShader = SDL_CreateGPUShader(device, &fragmentInfo); - - // create the graphics pipeline - SDL_GPUGraphicsPipelineCreateInfo pipelineInfo{}; - pipelineInfo.vertex_shader = vertexShader; - pipelineInfo.fragment_shader = fragmentShader; - pipelineInfo.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; - - // describe the vertex buffers - SDL_GPUVertexBufferDescription vertexBufferDesctiptions[1]; - vertexBufferDesctiptions[0].slot = 0; - vertexBufferDesctiptions[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; - vertexBufferDesctiptions[0].instance_step_rate = 0; - vertexBufferDesctiptions[0].pitch = sizeof(Vertex); - - pipelineInfo.vertex_input_state.num_vertex_buffers = 1; - pipelineInfo.vertex_input_state.vertex_buffer_descriptions = vertexBufferDesctiptions; - - // describe the vertex attribute - SDL_GPUVertexAttribute vertexAttributes[2]; - - // a_position - vertexAttributes[0].buffer_slot = 0; - vertexAttributes[0].location = 0; - vertexAttributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; - vertexAttributes[0].offset = 0; - - // a_color - vertexAttributes[1].buffer_slot = 0; - vertexAttributes[1].location = 1; - vertexAttributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; - vertexAttributes[1].offset = sizeof(float) * 3; - - pipelineInfo.vertex_input_state.num_vertex_attributes = 2; - pipelineInfo.vertex_input_state.vertex_attributes = vertexAttributes; - - // describe the color target - SDL_GPUColorTargetDescription colorTargetDescriptions[1]; - colorTargetDescriptions[0] = {}; - colorTargetDescriptions[0].blend_state.enable_blend = true; - colorTargetDescriptions[0].blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; - colorTargetDescriptions[0].blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; - colorTargetDescriptions[0].blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; - colorTargetDescriptions[0].blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; - colorTargetDescriptions[0].blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; - colorTargetDescriptions[0].blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; - colorTargetDescriptions[0].format = SDL_GetGPUSwapchainTextureFormat(device, window); - - pipelineInfo.target_info.num_color_targets = 1; - pipelineInfo.target_info.color_target_descriptions = colorTargetDescriptions; - - // create the pipeline - graphicsPipeline = SDL_CreateGPUGraphicsPipeline(device, &pipelineInfo); - - // we don't need to store the shaders after creating the pipeline - SDL_ReleaseGPUShader(device, vertexShader); - SDL_ReleaseGPUShader(device, fragmentShader); - - // create the vertex buffer - SDL_GPUBufferCreateInfo bufferInfo{}; - bufferInfo.size = sizeof(vertices); - bufferInfo.usage = SDL_GPU_BUFFERUSAGE_VERTEX; - vertexBuffer = SDL_CreateGPUBuffer(device, &bufferInfo); - - // create a transfer buffer to upload to the vertex buffer - SDL_GPUTransferBufferCreateInfo transferInfo{}; - transferInfo.size = sizeof(vertices); - transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - transferBuffer = SDL_CreateGPUTransferBuffer(device, &transferInfo); - - // fill the transfer buffer - Vertex *data = (Vertex *) SDL_MapGPUTransferBuffer(device, transferBuffer, false); - - SDL_memcpy(data, (void *) vertices, sizeof(vertices)); - - SDL_UnmapGPUTransferBuffer(device, transferBuffer); - - // start a copy pass - SDL_GPUCommandBuffer *commandBuffer = SDL_AcquireGPUCommandBuffer(device); - SDL_GPUCopyPass *copyPass = SDL_BeginGPUCopyPass(commandBuffer); - - // where is the data - SDL_GPUTransferBufferLocation location{}; - location.transfer_buffer = transferBuffer; - location.offset = 0; - - // where to upload the data - SDL_GPUBufferRegion region{}; - region.buffer = vertexBuffer; - region.size = sizeof(vertices); - region.offset = 0; - - // upload the data - SDL_UploadToGPUBuffer(copyPass, &location, ®ion, true); - - // end the copy pass - SDL_EndGPUCopyPass(copyPass); - SDL_SubmitGPUCommandBuffer(commandBuffer); - - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO &io = ImGui::GetIO(); - (void) io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsLight(); - - - float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); - // Setup scaling - ImGuiStyle &style = ImGui::GetStyle(); - style.ScaleAllSizes(main_scale); - // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) - style.FontScaleDpi = main_scale; - // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose) - - // Setup Platform/Renderer backends - ImGui_ImplSDL3_InitForSDLGPU(window); - ImGui_ImplSDLGPU3_InitInfo init_info = {}; - init_info.Device = device; - init_info.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(device, window); - init_info.MSAASamples = SDL_GPU_SAMPLECOUNT_1; // Only used in multi-viewports mode. - init_info.SwapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; // Only used in multi-viewports mode. - init_info.PresentMode = SDL_GPU_PRESENTMODE_VSYNC; - ImGui_ImplSDLGPU3_Init(&init_info); - - return SDL_APP_CONTINUE; -} - -SDL_AppResult SDL_AppIterate(void *appstate) { - ImGui_ImplSDLGPU3_NewFrame(); - ImGui_ImplSDL3_NewFrame(); - ImGui::NewFrame(); - - ImGui::ShowDemoWindow(); - - { - ImGui::Begin("Editor"); - ImGui::DragFloat3("node1",node,0.01f,-1.f,1.f); - vertices[0].x = node[0]; - vertices[0].y = node[1]; - vertices[0].z = node[2]; +class UserApp : public sopho::App { + + sopho::BufferWrapper BufferWrapper{}; + + SDL_Window *window{}; + SDL_GPUDevice *device{}; + SDL_GPUBuffer *vertexBuffer{}; + SDL_GPUTransferBuffer *transferBuffer{}; + SDL_GPUGraphicsPipeline *graphicsPipeline{}; + + virtual SDL_AppResult init(int argc, char **argv) override { + // create a window + window = SDL_CreateWindow("Hello, Triangle!", 960, 540, SDL_WINDOW_RESIZABLE); + + // create the device + device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, NULL); + SDL_ClaimWindowForGPUDevice(device, window); + + shaderc::Compiler compiler{}; + shaderc::CompileOptions options{}; + options.SetTargetEnvironment(shaderc_target_env_vulkan, 0); + auto result = compiler.CompileGlslToSpv(vertex_source, shaderc_glsl_vertex_shader, "test.glsl", options); + + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + std::cerr << "[shaderc] compile error in " << "test.glsl" << ":\n" + << result.GetErrorMessage() << std::endl; + } + + // load the vertex shader code + std::vector vertexCode{result.cbegin(), result.cend()}; + + // create the vertex shader + SDL_GPUShaderCreateInfo vertexInfo{}; + vertexInfo.code = (Uint8 *) vertexCode.data(); + vertexInfo.code_size = vertexCode.size() * 4; + vertexInfo.entrypoint = "main"; + vertexInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; + vertexInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX; + vertexInfo.num_samplers = 0; + vertexInfo.num_storage_buffers = 0; + vertexInfo.num_storage_textures = 0; + vertexInfo.num_uniform_buffers = 0; + + SDL_GPUShader *vertexShader = SDL_CreateGPUShader(device, &vertexInfo); + + result = compiler.CompileGlslToSpv(fragment_source, shaderc_glsl_fragment_shader, "test.frag", options); + + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + std::cerr << "[shaderc] compile error in " << "test.frag" << ":\n" + << result.GetErrorMessage() << std::endl; + } + + // load the fragment shader code + std::vector fragmentCode{result.cbegin(), result.cend()}; + + // create the fragment shader + SDL_GPUShaderCreateInfo fragmentInfo{}; + fragmentInfo.code = (Uint8 *) fragmentCode.data(); + fragmentInfo.code_size = fragmentCode.size() * 4; + fragmentInfo.entrypoint = "main"; + fragmentInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; + fragmentInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + fragmentInfo.num_samplers = 0; + fragmentInfo.num_storage_buffers = 0; + fragmentInfo.num_storage_textures = 0; + fragmentInfo.num_uniform_buffers = 0; + + SDL_GPUShader *fragmentShader = SDL_CreateGPUShader(device, &fragmentInfo); + + // create the graphics pipeline + SDL_GPUGraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.vertex_shader = vertexShader; + pipelineInfo.fragment_shader = fragmentShader; + pipelineInfo.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; + + // describe the vertex buffers + SDL_GPUVertexBufferDescription vertexBufferDesctiptions[1]; + vertexBufferDesctiptions[0].slot = 0; + vertexBufferDesctiptions[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; + vertexBufferDesctiptions[0].instance_step_rate = 0; + vertexBufferDesctiptions[0].pitch = sizeof(Vertex); + + pipelineInfo.vertex_input_state.num_vertex_buffers = 1; + pipelineInfo.vertex_input_state.vertex_buffer_descriptions = vertexBufferDesctiptions; + + // describe the vertex attribute + SDL_GPUVertexAttribute vertexAttributes[2]; + + // a_position + vertexAttributes[0].buffer_slot = 0; + vertexAttributes[0].location = 0; + vertexAttributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; + vertexAttributes[0].offset = 0; + + // a_color + vertexAttributes[1].buffer_slot = 0; + vertexAttributes[1].location = 1; + vertexAttributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; + vertexAttributes[1].offset = sizeof(float) * 3; + + pipelineInfo.vertex_input_state.num_vertex_attributes = 2; + pipelineInfo.vertex_input_state.vertex_attributes = vertexAttributes; + + // describe the color target + SDL_GPUColorTargetDescription colorTargetDescriptions[1]; + colorTargetDescriptions[0] = {}; + colorTargetDescriptions[0].blend_state.enable_blend = true; + colorTargetDescriptions[0].blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD; + colorTargetDescriptions[0].blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD; + colorTargetDescriptions[0].blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; + colorTargetDescriptions[0].blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + colorTargetDescriptions[0].blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA; + colorTargetDescriptions[0].blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + colorTargetDescriptions[0].format = SDL_GetGPUSwapchainTextureFormat(device, window); + + pipelineInfo.target_info.num_color_targets = 1; + pipelineInfo.target_info.color_target_descriptions = colorTargetDescriptions; + + // create the pipeline + graphicsPipeline = SDL_CreateGPUGraphicsPipeline(device, &pipelineInfo); + + // we don't need to store the shaders after creating the pipeline + SDL_ReleaseGPUShader(device, vertexShader); + SDL_ReleaseGPUShader(device, fragmentShader); + + // create the vertex buffer + SDL_GPUBufferCreateInfo bufferInfo{}; + bufferInfo.size = sizeof(vertices); + bufferInfo.usage = SDL_GPU_BUFFERUSAGE_VERTEX; + vertexBuffer = SDL_CreateGPUBuffer(device, &bufferInfo); + + // create a transfer buffer to upload to the vertex buffer + SDL_GPUTransferBufferCreateInfo transferInfo{}; + transferInfo.size = sizeof(vertices); + transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferBuffer = SDL_CreateGPUTransferBuffer(device, &transferInfo); // fill the transfer buffer Vertex *data = (Vertex *) SDL_MapGPUTransferBuffer(device, transferBuffer, false); @@ -290,86 +213,168 @@ SDL_AppResult SDL_AppIterate(void *appstate) { // end the copy pass SDL_EndGPUCopyPass(copyPass); SDL_SubmitGPUCommandBuffer(commandBuffer); - ImGui::End(); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + (void) io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + // Setup scaling + ImGuiStyle &style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); + // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; + // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForSDLGPU(window); + ImGui_ImplSDLGPU3_InitInfo init_info = {}; + init_info.Device = device; + init_info.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(device, window); + init_info.MSAASamples = SDL_GPU_SAMPLECOUNT_1; // Only used in multi-viewports mode. + init_info.SwapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR; // Only used in multi-viewports mode. + init_info.PresentMode = SDL_GPU_PRESENTMODE_VSYNC; + ImGui_ImplSDLGPU3_Init(&init_info); + + return SDL_APP_CONTINUE; } - ImGui::Render(); - ImDrawData *draw_data = ImGui::GetDrawData(); + virtual SDL_AppResult iterate() override { + ImGui_ImplSDLGPU3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); - // acquire the command buffer - SDL_GPUCommandBuffer *commandBuffer = SDL_AcquireGPUCommandBuffer(device); + ImGui::ShowDemoWindow(); - // get the swapchain texture - SDL_GPUTexture *swapchainTexture; - Uint32 width, height; - SDL_WaitAndAcquireGPUSwapchainTexture(commandBuffer, window, &swapchainTexture, &width, &height); + { + ImGui::Begin("Editor"); + ImGui::DragFloat3("node1", node, 0.01f, -1.f, 1.f); + vertices[0].x = node[0]; + vertices[0].y = node[1]; + vertices[0].z = node[2]; - // end the frame early if a swapchain texture is not available - if (swapchainTexture == NULL) { - // you must always submit the command buffer - SDL_SubmitGPUCommandBuffer(commandBuffer); - return SDL_APP_CONTINUE; - } + // fill the transfer buffer + Vertex *data = (Vertex *) SDL_MapGPUTransferBuffer(device, transferBuffer, false); - ImGui_ImplSDLGPU3_PrepareDrawData(draw_data, commandBuffer); + SDL_memcpy(data, (void *) vertices, sizeof(vertices)); - // create the color target - SDL_GPUColorTargetInfo colorTargetInfo{}; - colorTargetInfo.clear_color = {240 / 255.0f, 240 / 255.0f, 240 / 255.0f, 255 / 255.0f}; - colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; - colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE; - colorTargetInfo.texture = swapchainTexture; + SDL_UnmapGPUTransferBuffer(device, transferBuffer); - // begin a render pass - SDL_GPURenderPass *renderPass = SDL_BeginGPURenderPass(commandBuffer, &colorTargetInfo, 1, NULL); + // start a copy pass + SDL_GPUCommandBuffer *commandBuffer = SDL_AcquireGPUCommandBuffer(device); + SDL_GPUCopyPass *copyPass = SDL_BeginGPUCopyPass(commandBuffer); + // where is the data + SDL_GPUTransferBufferLocation location{}; + location.transfer_buffer = transferBuffer; + location.offset = 0; - // draw calls go here - SDL_BindGPUGraphicsPipeline(renderPass, graphicsPipeline); + // where to upload the data + SDL_GPUBufferRegion region{}; + region.buffer = vertexBuffer; + region.size = sizeof(vertices); + region.offset = 0; - // bind the vertex buffer - SDL_GPUBufferBinding bufferBindings[1]; - bufferBindings[0].buffer = vertexBuffer; // index 0 is slot 0 in this example - bufferBindings[0].offset = 0; // start from the first byte + // upload the data + SDL_UploadToGPUBuffer(copyPass, &location, ®ion, true); - SDL_BindGPUVertexBuffers(renderPass, 0, bufferBindings, 1); // bind one buffer starting from slot 0 + // end the copy pass + SDL_EndGPUCopyPass(copyPass); + SDL_SubmitGPUCommandBuffer(commandBuffer); + ImGui::End(); + } - SDL_DrawGPUPrimitives(renderPass, 3, 1, 0, 0); + ImGui::Render(); + ImDrawData *draw_data = ImGui::GetDrawData(); - ImGui_ImplSDLGPU3_RenderDrawData(draw_data, commandBuffer, renderPass); - // end the render pass - SDL_EndGPURenderPass(renderPass); + // acquire the command buffer + SDL_GPUCommandBuffer *commandBuffer = SDL_AcquireGPUCommandBuffer(device); - // submit the command buffer - SDL_SubmitGPUCommandBuffer(commandBuffer); + // get the swapchain texture + SDL_GPUTexture *swapchainTexture; + Uint32 width, height; + SDL_WaitAndAcquireGPUSwapchainTexture(commandBuffer, window, &swapchainTexture, &width, &height); - return SDL_APP_CONTINUE; -} + // end the frame early if a swapchain texture is not available + if (swapchainTexture == NULL) { + // you must always submit the command buffer + SDL_SubmitGPUCommandBuffer(commandBuffer); + return SDL_APP_CONTINUE; + } + + ImGui_ImplSDLGPU3_PrepareDrawData(draw_data, commandBuffer); + + // create the color target + SDL_GPUColorTargetInfo colorTargetInfo{}; + colorTargetInfo.clear_color = {240 / 255.0f, 240 / 255.0f, 240 / 255.0f, 255 / 255.0f}; + colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; + colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE; + colorTargetInfo.texture = swapchainTexture; + + // begin a render pass + SDL_GPURenderPass *renderPass = SDL_BeginGPURenderPass(commandBuffer, &colorTargetInfo, 1, NULL); + + + // draw calls go here + SDL_BindGPUGraphicsPipeline(renderPass, graphicsPipeline); + + // bind the vertex buffer + SDL_GPUBufferBinding bufferBindings[1]; + bufferBindings[0].buffer = vertexBuffer; // index 0 is slot 0 in this example + bufferBindings[0].offset = 0; // start from the first byte + + SDL_BindGPUVertexBuffers(renderPass, 0, bufferBindings, 1); // bind one buffer starting from slot 0 + + SDL_DrawGPUPrimitives(renderPass, 3, 1, 0, 0); -SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { - ImGui_ImplSDL3_ProcessEvent(event); - // close the window on request - if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { - return SDL_APP_SUCCESS; + ImGui_ImplSDLGPU3_RenderDrawData(draw_data, commandBuffer, renderPass); + // end the render pass + SDL_EndGPURenderPass(renderPass); + + // submit the command buffer + SDL_SubmitGPUCommandBuffer(commandBuffer); + + return SDL_APP_CONTINUE; } - return SDL_APP_CONTINUE; -} + virtual SDL_AppResult event(SDL_Event *event) override { + ImGui_ImplSDL3_ProcessEvent(event); + // close the window on request + if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { + return SDL_APP_SUCCESS; + } -void SDL_AppQuit(void *appstate, SDL_AppResult result) { - ImGui_ImplSDL3_Shutdown(); - ImGui_ImplSDLGPU3_Shutdown(); - ImGui::DestroyContext(); - // release buffers - SDL_ReleaseGPUBuffer(device, vertexBuffer); - SDL_ReleaseGPUTransferBuffer(device, transferBuffer); + return SDL_APP_CONTINUE; + } + + virtual void quit(SDL_AppResult result) override { + ImGui_ImplSDL3_Shutdown(); + ImGui_ImplSDLGPU3_Shutdown(); + ImGui::DestroyContext(); + // release buffers + SDL_ReleaseGPUBuffer(device, vertexBuffer); + SDL_ReleaseGPUTransferBuffer(device, transferBuffer); + + // release the pipeline + SDL_ReleaseGPUGraphicsPipeline(device, graphicsPipeline); - // release the pipeline - SDL_ReleaseGPUGraphicsPipeline(device, graphicsPipeline); + // destroy the GPU device + SDL_DestroyGPUDevice(device); - // destroy the GPU device - SDL_DestroyGPUDevice(device); + // destroy the window + SDL_DestroyWindow(window); + } +}; - // destroy the window - SDL_DestroyWindow(window); +sopho::App *create_app() { + return new UserApp(); } diff --git a/sdl_wrapper/CMakeLists.txt b/sdl_wrapper/CMakeLists.txt index 50badf6..690d78c 100644 --- a/sdl_wrapper/CMakeLists.txt +++ b/sdl_wrapper/CMakeLists.txt @@ -9,6 +9,9 @@ target_sources(sdl_wrapper FILE_SET cxx_modules TYPE CXX_MODULES FILES ${CMAKE_CURRENT_SOURCE_DIR}/sdl_wrapper.ixx ${CMAKE_CURRENT_SOURCE_DIR}/sdl_wrapper.buffer.ixx + ${CMAKE_CURRENT_SOURCE_DIR}/sdl_wrapper.app.ixx + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/sdl_callback_implement.cpp ) target_link_libraries(sdl_wrapper PUBLIC SDL3::SDL3) diff --git a/sdl_wrapper/sdl_callback_implement.cpp b/sdl_wrapper/sdl_callback_implement.cpp new file mode 100644 index 0000000..1c5a460 --- /dev/null +++ b/sdl_wrapper/sdl_callback_implement.cpp @@ -0,0 +1,31 @@ +// +// Created by sophomore on 11/9/25. +// +#define SDL_MAIN_USE_CALLBACKS +#include +#include +import sdl_wrapper; + + +extern sopho::App *create_app(); + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { + auto app = create_app(); + *appstate = app; + return app->init(argc, argv); +} + +SDL_AppResult SDL_AppIterate(void *appstate) { + auto *app = static_cast(appstate); + return app->iterate(); +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { + auto *app = static_cast(appstate); + return app->event(event); +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) { + auto *app = static_cast(appstate); + app->quit(result); +} diff --git a/sdl_wrapper/sdl_wrapper.app.ixx b/sdl_wrapper/sdl_wrapper.app.ixx new file mode 100644 index 0000000..5ac5bc3 --- /dev/null +++ b/sdl_wrapper/sdl_wrapper.app.ixx @@ -0,0 +1,20 @@ +// +// Created by sophomore on 11/9/25. +// +module; +#include "SDL3/SDL_init.h" +export module sdl_wrapper:app; +namespace sopho { + export class App { + public: + virtual ~App() = default; + + virtual SDL_AppResult init(int argc, char **argv) = 0; + + virtual SDL_AppResult iterate() = 0; + + virtual SDL_AppResult event(SDL_Event *event) = 0; + + virtual void quit(SDL_AppResult result) = 0; + }; +} diff --git a/sdl_wrapper/sdl_wrapper.buffer.ixx b/sdl_wrapper/sdl_wrapper.buffer.ixx index cb44360..b046374 100644 --- a/sdl_wrapper/sdl_wrapper.buffer.ixx +++ b/sdl_wrapper/sdl_wrapper.buffer.ixx @@ -1,13 +1,12 @@ // // Created by sophomore on 11/8/25. // - -export module sdl_wrapper:buffer; +module; #include "SDL3/SDL_gpu.h" +export module sdl_wrapper:buffer; namespace sopho { export class BufferWrapper { SDL_GPUBuffer *vertexBuffer{}; - }; } diff --git a/sdl_wrapper/sdl_wrapper.ixx b/sdl_wrapper/sdl_wrapper.ixx index b57802a..27f47c6 100644 --- a/sdl_wrapper/sdl_wrapper.ixx +++ b/sdl_wrapper/sdl_wrapper.ixx @@ -3,4 +3,5 @@ // export module sdl_wrapper; -export import :buffer; \ No newline at end of file +export import :buffer; +export import :app;