I am currently developing a Vulkan program that I anticipate to use as a game engine, but I am far from my goal. As a first step, I'm just trying to render a triangle using a list of dynamic structures to get data such as vertices and shaders. To work around platform specific hurdles, I'm using GLFW to create windows and Vulkan surfaces.
Right now I'm trying to load SPIR-V shaders from a file and use them to create VkShaderModule
s which I will use in my render pipeline. Currently, the abstracted part of my code starts like this:
main.c
#include "application.h"
#include "config.h"
#include "engine_vulkan.h"
#include "glfw/glfw3.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
printf("VLK Engine - Version %s\n", VERSION_NUMBER);
if (enable_validation_layers) {
printf("Validation layers enabled.\n");
}
printf("\n");
struct VulkanData vulkan_data = {0};
struct Application app = {.window = NULL, .vulkan_data = &vulkan_data, .objects = NULL};
bool ret = application_init(&app);
if (ret == false) {
fprintf(stderr, "Cannot initialize applcation.\n");
return EXIT_FAILURE;
}
while (application_loopcondition(&app)) {
application_loopevent(&app);
}
application_close(&app);
return EXIT_SUCCESS;
}
application.c
#include "application.h"
#include "engine_object.h"
#include "engine_vulkan.h"
bool application_init(struct Application *app) {
bool ret;
// Initialize GLFW
glfwInit();
// Create window
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
app->window = glfwCreateWindow(800, 600, "Vulkan window", NULL, NULL);
if (app->window == NULL) {
fprintf(stderr, "Failed to create window.\n");
return false;
}
printf("Created window @ 0x%p\n", app->window);
// Initialize game object list
objectlink_init(app);
// Create game objects
struct RenderObjectCreateInfo ro_create_info = {.vertex_shader_path = "shaders/shader.vert.spv",
.fragment_shader_path =
"shaders/shader.frag.spv",
.is_static = true};
struct RenderObject triangle = {0};
object_init(app, &ro_create_info, &triangle);
objectlink_add(app, &triangle);
// Init Vulkan
ret = vulkan_init(app);
if (!ret) {
fprintf(stderr, "Failed to initialize Vulkan.\n");
return false;
}
return true;
}
bool application_loopcondition(struct Application *app) {
// Close only if GLFW says so
return !glfwWindowShouldClose(app->window);
}
void application_loopevent(struct Application *app) {
// Poll for events like keyboard & mouse
glfwPollEvents();
}
void application_close(struct Application *app) {
// Destroy objects
objectlink_destroy(app);
// Close Vulkan instance
vulkan_close(app);
// End window & GLFW
glfwDestroyWindow(app->window);
glfwTerminate();
}
Important structures
struct Application {
GLFWwindow *window;
struct VulkanData *vulkan_data;
struct RenderObjectLink *objects;
};
struct RenderObject {
char *vertex_shader_path;
struct ShaderFile vertex_shader_data;
VkShaderModule vertex_shader;
char *fragment_shader_path;
struct ShaderFile fragment_shader_data;
VkShaderModule fragment_shader;
bool is_static;
};
struct RenderObjectCreateInfo {
char *vertex_shader_path;
char *fragment_shader_path;
bool is_static;
};
struct RenderObjectLink {
struct RenderObject *render_object;
struct RenderObjectLink *next;
};
The problem in my application comes when storing the VkShaderModule
s in my application structure. The way that my program is supposed to store information about objects is in the Application
structure called app
, in which it points to a linked list of RenderObjectLink
in the objects
field. My functions objectlink_init
and objectlink_add
work properly, however the data inside the structure the render_object
field points to gets changed/corrupted when entering the GLFW functions glfwWindowShouldClose
and glfwPollEvents
.
Since those two functions are ran after initializing Vulkan and creating the shader modules, I've found with GDB that the Vulkan functions are working properly and that my data structures are only getting corrupted when running the GLFW loop functions. I have debugged with GDB using hardware watch points to determine that the reference to these shaders (among other variables throughout the program) changes upon entering these functions.
Thread 1 hit Hardware watchpoint 4: app->objects->render_object->vertex_shader
Old value = (VkShaderModule) 0x731f0f000000000a
New value = (VkShaderModule) 0x40d625 <glfwPollEvents+43>
_glfwPlatformPollEvents () at C:/Users/dylanweber/Documents/C-Projects/vlkengine/main/glfw/src/win32_window.c:1878
1878 {
This happens consistently but the variable changes to different values when I run it. Memory analysis tools like Dr. Memory (since I'm using Windows I cannot use Valgrind) only show memory problems in system DLLs (like xinput reading for controller inputs through GLFW) and are unrelated to my program. What could be the cause of this corruption? What tools/resources can I use to find these issues?
According to the code, the objectlink_add
function accepts the second argument as a pointer. The most likely use of this is to add the pointer to an underlying data struct and keep it around for further references. A render pipeline was mentioned in the post, which wold be a sort of link.
However, the function was called from the application_init
procedure which has the object triangle
allocated on its stack. By the time the function is returned, its stack is gone and the value at the pointer becomes invalid.
There are several ways to fix it:
To allocate the triangle struct dynamically, using malloc (or similar). Therefore it will no disappear after the return from the function. This is the most generic way, but it calling to the free()
function at some time is expected.
The triangle
object could be static.
the objectlink_add
could copy triangle
fields in some internal struct, however, if there are multiple types of the objects, it cold be problematic.