aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2025-12-18 16:24:32 +0100
committervimene <vincent.menegaux@gmail.com>2025-12-18 16:24:32 +0100
commit0639c2e9a3e1a1ecf36a66a5f2dd844b772856db (patch)
tree005f2d57fdfb3ddd3801d4478993ba1cfb72f088 /src
parent79e2e5394e25a03022301f7e9c30ff256f2aaddc (diff)
downloadengine-0639c2e9a3e1a1ecf36a66a5f2dd844b772856db.tar.gz
added basic graphics pipeline and shaders
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am50
-rw-r--r--src/engine.cpp222
-rw-r--r--src/shaders/shader.slang30
3 files changed, 281 insertions, 21 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index fd8f7fd..f7b34f3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,29 +1,33 @@
+shadersdir = $(datarootdir)/shaders
+SLANGC = slangc
+
bin_PROGRAMS = engine
+shaders_PROGRAMS = shader.spv
engine_SOURCES = \
- engine.cpp \
- renderer.hpp renderer.cpp \
- obj_parser.hpp obj_parser.cpp \
- fb/chfb.hpp fb/chfb.cpp fb/pixfb.hpp fb/pixfb.cpp \
- math/vector.hpp \
- math/mat4.hpp \
- math/quat.hpp \
- math/tform.hpp \
- o3d/mesh.hpp o3d/mesh.cpp \
- o3d/obj3d.hpp \
- o3d/vertex_data.hpp \
- o3d/vertex.hpp \
- o3d/deriv_vertex.hpp \
- o3d/tri.hpp \
- o3d/tri_deriv.hpp o3d/tri_deriv.cpp \
- o3d/camera.hpp \
- o3d/scene.hpp \
- ctrl/keyboard.hpp \
- ctrl/mouse.hpp
+ engine.cpp \
+ renderer.hpp renderer.cpp \
+ obj_parser.hpp obj_parser.cpp \
+ fb/chfb.hpp fb/chfb.cpp fb/pixfb.hpp fb/pixfb.cpp \
+ math/vector.hpp \
+ math/mat4.hpp \
+ math/quat.hpp \
+ math/tform.hpp \
+ o3d/mesh.hpp o3d/mesh.cpp \
+ o3d/obj3d.hpp \
+ o3d/vertex_data.hpp \
+ o3d/vertex.hpp \
+ o3d/deriv_vertex.hpp \
+ o3d/tri.hpp \
+ o3d/tri_deriv.hpp o3d/tri_deriv.cpp \
+ o3d/camera.hpp \
+ o3d/scene.hpp \
+ ctrl/keyboard.hpp \
+ ctrl/mouse.hpp
engine_CPPFLAGS = -std=gnu++23 -Wall -Wextra
engine_LDFLAGS = -std=gnu++23 -Wall -Wextra
-engine_CPPFLAGS += -DDATADIR='"$(datadir)"'
+engine_CPPFLAGS += -DDATADIR='"$(datadir)"' -DSHADERSDIR='"$(shadersdir)"'
# Not sure why they are needed
engine_LDADD =# -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
@@ -36,3 +40,9 @@ if HAVE_NCURSES
engine_CPPFLAGS += $(NCURSES_CFLAGS)
engine_LDADD += $(NCURSES_LIBS)
endif
+
+shader_spv_SOURCES = shaders/shader.slang
+shader_spv_SPVFLAGS = -target spirv -profile spirv_1_4 -emit-spirv-directly -fvk-use-entrypoint-name -entry vert_main -entry frag_main
+
+shader.spv$(EXEEXT): shaders/shader.slang
+ $(SLANGC) $(shader_spv_SPVFLAGS) -o $@ $<
diff --git a/src/engine.cpp b/src/engine.cpp
index 204b2e9..1c9f109 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -18,6 +18,8 @@
#include <tuple>
#include <limits>
#include <array>
+#include <ios>
+#include <fstream>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
@@ -739,7 +741,7 @@ static int main_graphical() {
device_create_info.pQueueCreateInfos = &deviceQueueCreateInfo;
device_create_info.enabledExtensionCount = static_cast<uint32_t>(device_exts.size());
device_create_info.ppEnabledExtensionNames = device_exts.data();
- // we get device features while selection the physical device
+ // we get device features while selecting the physical device
device_create_info.pNext = &device_features;
VkPhysicalDeviceVulkan13Features device_vk13_features{};
@@ -880,9 +882,12 @@ static int main_graphical() {
for (uint32_t i = 0; i < swapchain_imgs.size(); i++) {
VkImageViewCreateInfo img_view_create_info {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = {},
+ .flags = {},
.image = swapchain_imgs[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = surface_format.format,
+ .components = {},
.subresourceRange = { // VkImageSubresourceRange
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
@@ -897,12 +902,227 @@ static int main_graphical() {
}
}
+ // reading shader file
+ VkShaderModule shader_module;
+ {
+ // shader code has to be 32-bits aligned, which is the case with the default allocator
+ std::vector<char> shader_code;
+ {
+ const char* shader_file_name = SHADERSDIR "/shader.spv";
+ std::ifstream shader_file(shader_file_name, std::ios::ate | std::ios::binary);
+ if (!shader_file.is_open()) {
+ std::cerr << "file `" << shader_file_name << "'not found" << std::endl; // TODO: improve
+ std::exit(EXIT_SUCCESS);
+ }
+ shader_code.resize(shader_file.tellg());
+ shader_file.seekg(0, std::ios::beg);
+ shader_file.read(shader_code.data(), static_cast<std::streamsize>(shader_code.size()));
+ }
+
+ VkShaderModuleCreateInfo shader_module_create_info {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .codeSize = shader_code.size(),
+ .pCode = reinterpret_cast<const uint32_t*>(shader_code.data()),
+ };
+ if (VkResult res = vkCreateShaderModule(logical_device, &shader_module_create_info, nullptr, &shader_module); res != VK_SUCCESS) {
+ std::cerr << "failed to create shader module, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+
+ std::array pl_shader_stage_create_infos {
+ VkPipelineShaderStageCreateInfo {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
+ .module = shader_module,
+ .pName = "vert_main",
+ .pSpecializationInfo = nullptr,
+ },
+ VkPipelineShaderStageCreateInfo {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .module = shader_module,
+ .pName = "frag_main",
+ .pSpecializationInfo = nullptr,
+ },
+ };
+
+ std::array<VkDynamicState, 2> dynamic_states {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ };
+ VkPipelineDynamicStateCreateInfo pl_dyn_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .dynamicStateCount = static_cast<uint32_t>(dynamic_states.size()),
+ .pDynamicStates = dynamic_states.data(),
+ };
+
+ VkPipelineVertexInputStateCreateInfo pl_vert_in_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .vertexBindingDescriptionCount = 0,
+ .pVertexBindingDescriptions = nullptr,
+ .vertexAttributeDescriptionCount = 0,
+ .pVertexAttributeDescriptions = nullptr,
+ };
+
+ VkPipelineInputAssemblyStateCreateInfo pl_in_asm_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ .primitiveRestartEnable = VK_FALSE,
+ };
+
+ VkViewport viewport {
+ .x = 0.f,
+ .y = 0.f,
+ .width = static_cast<float>(swapchain_extent.width),
+ .height = static_cast<float>(swapchain_extent.height),
+ .minDepth = 0.f,
+ .maxDepth = 1.f,
+ };
+ VkRect2D scissor {
+ .offset = { 0, 0 },
+ .extent = swapchain_extent,
+ };
+ VkPipelineViewportStateCreateInfo pl_viewport_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .viewportCount = 1,
+ .pViewports = {},
+ .scissorCount = 1,
+ .pScissors = {},
+ };
+
+ VkPipelineRasterizationStateCreateInfo pl_raster_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .depthClampEnable = VK_FALSE,
+ .rasterizerDiscardEnable = VK_FALSE,
+ .polygonMode = VK_POLYGON_MODE_FILL,
+ .cullMode = VK_CULL_MODE_BACK_BIT,
+ .frontFace = VK_FRONT_FACE_CLOCKWISE,
+ .depthBiasEnable = VK_FALSE,
+ .depthBiasConstantFactor = {},
+ .depthBiasClamp = {},
+ .depthBiasSlopeFactor = {},
+ .lineWidth = 1.f,
+ };
+
+ VkPipelineMultisampleStateCreateInfo pl_ms_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
+ .sampleShadingEnable = VK_FALSE,
+ .minSampleShading = {},
+ .pSampleMask = {},
+ .alphaToCoverageEnable = VK_FALSE,
+ .alphaToOneEnable = VK_FALSE,
+ };
+
+ VkPipelineColorBlendAttachmentState pl_col_blend_attachment_state {
+ .blendEnable = VK_FALSE,
+ .srcColorBlendFactor = {},
+ .dstColorBlendFactor = {},
+ .colorBlendOp = {},
+ .srcAlphaBlendFactor = {},
+ .dstAlphaBlendFactor = {},
+ .alphaBlendOp = {},
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT
+ | VK_COLOR_COMPONENT_G_BIT
+ | VK_COLOR_COMPONENT_B_BIT
+ | VK_COLOR_COMPONENT_A_BIT,
+ };
+
+ VkPipelineColorBlendStateCreateInfo pl_col_blend_state_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .logicOpEnable = VK_FALSE,
+ .logicOp = {},
+ .attachmentCount = 1,
+ .pAttachments = &pl_col_blend_attachment_state,
+ .blendConstants = { 0.f, 0.f, 0.f, 0.f },
+ };
+
+ VkPipelineLayoutCreateInfo pl_layout_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = {},
+ .setLayoutCount = 0,
+ .pSetLayouts = {},
+ .pushConstantRangeCount = 0,
+ .pPushConstantRanges = {},
+ };
+
+ VkPipelineLayout pl_layout;
+ if (VkResult res = vkCreatePipelineLayout(logical_device, &pl_layout_create_info, nullptr, &pl_layout); res != VK_SUCCESS) {
+ std::cerr << "failed to create pipeline layout, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+
+ VkPipelineRenderingCreateInfo pl_render_create_info {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
+ .pNext = nullptr,
+ .viewMask = {},
+ .colorAttachmentCount = 1,
+ .pColorAttachmentFormats = &surface_format.format,
+ .depthAttachmentFormat = {},
+ .stencilAttachmentFormat = {},
+ };
+
+ VkGraphicsPipelineCreateInfo graphics_pl_create_info {
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = &pl_render_create_info,
+ .flags = {},
+ .stageCount = 2,
+ .pStages = pl_shader_stage_create_infos.data(),
+ .pVertexInputState = &pl_vert_in_state_create_info,
+ .pInputAssemblyState = &pl_in_asm_state_create_info,
+ .pTessellationState = {},
+ .pViewportState = &pl_viewport_state_create_info,
+ .pRasterizationState = &pl_raster_state_create_info,
+ .pMultisampleState = &pl_ms_state_create_info,
+ .pDepthStencilState = {},
+ .pColorBlendState = &pl_col_blend_state_create_info,
+ .pDynamicState = &pl_dyn_state_create_info,
+ .layout = pl_layout,
+ .renderPass = nullptr,
+ .subpass = {},
+ .basePipelineHandle = {},
+ .basePipelineIndex = {},
+ };
+
+ VkPipeline graphics_pl;
+ if (VkResult res = vkCreateGraphicsPipelines(logical_device, nullptr, 1, &graphics_pl_create_info, nullptr, &graphics_pl); res != VK_SUCCESS) {
+ std::cerr << "failed to pipeline, error code: " << string_VkResult(res) << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+
+ // destroy when pipeline creation is finished
+ vkDestroyShaderModule(logical_device, shader_module, nullptr);
+
// main loop
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
// cleanup
+ vkDestroyPipeline(logical_device, graphics_pl, nullptr);
+ vkDestroyPipelineLayout(logical_device, pl_layout, nullptr);
for (const auto img_view : swapchain_img_views)
vkDestroyImageView(logical_device, img_view, nullptr);
vkDestroySwapchainKHR(logical_device, swapchain, nullptr);
diff --git a/src/shaders/shader.slang b/src/shaders/shader.slang
new file mode 100644
index 0000000..b5f7a54
--- /dev/null
+++ b/src/shaders/shader.slang
@@ -0,0 +1,30 @@
+static float2 positions[3] = float2[] (
+ float2( 0.0, -0.5),
+ float2( 0.5, 0.5),
+ float2(-0.5, 0.5),
+);
+
+static float3 colors[3] = float3[] (
+ float3(1.0, 0.0, 0.0),
+ float3(0.0, 1.0, 0.0),
+ float3(0.0, 0.0, 1.0),
+);
+
+struct VertexOutput {
+ float3 color;
+ float4 sv_position : SV_Position;
+};
+
+[shader("vertex")]
+VertexOutput vert_main(uint vid : SV_VertexID) {
+ VertexOutput output;
+ output.color = colors[vid];
+ output.sv_position = float4(positions[vid], 0.0, 1.0);
+ return output;
+}
+
+[shader("fragment")]
+float4 frag_main(VertexOutput vert) : SV_Target {
+ float3 color = vert.color;
+ return float4(color, 1.0);
+}