shaderc icon indicating copy to clipboard operation
shaderc copied to clipboard

[Bug report] Segmentation fault when using includer interface on MSVC

Open Makogan opened this issue 4 years ago • 2 comments

There seems to be a bug with shaderc on windows.

If you try to use the IncluderInterface object trying to access the GetInclude function will throw an segmentation fault.

This is the shortest code I could come up with that produces this error:


/** @cond */
#include <array>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <string.h>
#include <string>
#include <vector>
/** @endcond */

#include "shaderc/shaderc.hpp"

using namespace shaderc;
using namespace std;
namespace fs = std::filesystem;

string source =
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_GOOGLE_include_directive : require\n"
"\n"
"#include <phong_lighting.glsl>\n"
"\n"
"layout(location = 0) out vec4 color_out;\n"
"\n"
"layout(location = 0) in vec3 position;\n"
"layout(location = 1) in vec2 tex_coord;\n"
"layout(location = 2) in vec3 normal;\n"
"\n"
"layout(binding = 1) uniform CameraInfo {\n"
"    vec3 camera_position;\n"
"};\n"
"\n"
"void main()\n"
"{\n"
"    color_out = BlinnPhong(\n"
"        position,\n"
"        normal,\n"
"        camera_position,\n"
"        vec3(0, 0, 1),\n"
"        vec3(1),\n"
"        normalize(vec3(1, 1, 1)) * 0.3,\n"
"        100);\n"
"}\n";


// This class is designed to be very short lived, do not cache it.
class NEShaderIncluder : public CompileOptions::IncluderInterface
{
    std::string name;
    std::string content;

    shaderc_include_result result;
    shaderc_include_type mtype;

  public:
    NEShaderIncluder()
    {
        cout << "constructed" << endl;
    }

    shaderc_include_result* GetInclude(
        const char* requested_source,
        shaderc_include_type type,
        const char* requesting_source,
        size_t include_depth) override
    {
        cout << "included" << endl;
        return &result;
    };

    void ReleaseInclude(shaderc_include_result* data) override {}
};

int main()
{
    const string phong_shader_frag = source;

    // Compile from GLSL to spirv.
    Compiler compiler = {};
    CompileOptions options = {};
    options.SetIncluder(std::make_unique<NEShaderIncluder>());
    options.SetGenerateDebugInfo();
    shaderc::PreprocessedSourceCompilationResult pre_result =
        compiler.PreprocessGlsl(phong_shader_frag, shaderc_shader_kind::shaderc_vertex_shader, "shaders/Example2/phong_shader.frag", options);

    return EXIT_SUCCESS;
}

I am on Windows 10, compiling with MSVC version 17.

The exact error occurs here:

  void SetIncluder(std::unique_ptr<IncluderInterface>&& includer) {
    includer_ = std::move(includer);
    shaderc_compile_options_set_include_callbacks(
        options_,
        [](void* user_data, const char* requested_source, int type,
           const char* requesting_source, size_t include_depth) {
          auto* sub_includer = static_cast<IncluderInterface*>(user_data);
          return sub_includer->GetInclude(
              requested_source, static_cast<shaderc_include_type>(type),
              requesting_source, include_depth);
        },
        [](void* user_data, shaderc_include_result* include_result) {
          auto* sub_includer = static_cast<IncluderInterface*>(user_data);
          return sub_includer->ReleaseInclude(include_result);
        },
        includer_.get());
  }

I am using shaderc version 2021.1 as downloaded by conan.

Makogan avatar Jan 24 '22 20:01 Makogan

I compiled the code with GCC in windows using mingw and it works, so this problem is specific to MSVC.

Makogan avatar Jan 25 '22 01:01 Makogan

Found the error, the above is not actually an MRE (I thought it was). Turns out it's an issue with returning a value.

In my code I originally had this function:

CompileOptions SetShaderCompilationOptions()
{
    CompileOptions options = CompileOptions();
    options.SetIncluder(std::make_unique<NEShaderIncluder>());
    options.SetGenerateDebugInfo();
    return options;
}

However this is the the constructor of CompilerOptions:

CompileOptions() { options_ = shaderc_compile_options_initialize(); }
~CompileOptions() { shaderc_compile_options_release(options_); }

shaderc_compile_options_t shaderc_compile_options_initialize() {
  return new (std::nothrow) shaderc_compile_options;
}

Depending on how the compiler is implemented returning this type may or may not cause the destructor to be called. If the destructor is not called things will work, if it is however called the copied object is invalid and undefined behaviour will arise.

Why is this object allocating from the heap instead of just behaving like a POD?

Makogan avatar Jan 25 '22 09:01 Makogan