cmingwglfwvulkanmemory-corruption

How do I determine the source of this memory change/corruption?


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 VkShaderModules 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 VkShaderModules 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?


Solution

  • 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:

    1. 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.

    2. The triangle object could be static.

    3. the objectlink_add could copy triangle fields in some internal struct, however, if there are multiple types of the objects, it cold be problematic.