Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: "An internal OpenGL call failed in Shader.cpp(849)" when defining macros  (Read 1789 times)

0 Members and 1 Guest are viewing this topic.

vokazoo

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Hello.
This code across 4 files:

Base.h
#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace std;

#ifdef DEFINE_MACRO
#define MY_MACRO true
#endif

class Base
{
public:
#if MY_MACRO
        bool b = true;
#endif
        sf::Shader shader;
        void func();
};

Base.cpp
#define DEFINE_MACRO
#include "Base.h"
void Base::func()
{
        shader.loadFromFile("shader.frag", sf::Shader::Fragment);
        shader.setUniform("color", sf::Glsl::Vec4(1.f, 0.5f, 1.f, 1.f));
        cout << MY_MACRO << endl;
}

shader.frag
#version 120
uniform vec4 color;
void main()
{
        gl_FragColor = color;
}

main.cpp
#include "Base.h"
int main()
{
    Base base;
    base.func();
}

compiles, but upon running it in default visual studio debug configuration this gets printed out:
An internal OpenGL call failed in Shader.cpp(849).
Expression:
   GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))
Error description:
   GL_INVALID_VALUE
   A numeric argument is out of range.

 
("1" from cout is not printed and program never exits by itself).

In release configuration the program throws: Unhandled exception at 0x7A66BC3F (sfml-graphics-2.dll) in main.exe: 0xC0000005: Access violation reading location 0x00000004.


If lines "#if MY_MACRO" and "#endif" are removed, the program runs as expected, printing out "1".

May be worth mentioning that this minimal example doesn't produce the exact errors the bigger SFML project does, them being: "Exception thrown: read access violation. _Pnext was 0xDDDDDDE1." in debug and either: "Exception thrown at 0x7997AFE0 (sfml-graphics-2.dll) in main.exe: 0xC0000005: Access violation reading location 0x0114818D." or: "Unhandled exception at 0x777B6D13 (ntdll.dll) in main.exe: 0xC0000374: A heap has been corrupted (parameters: 0x777F3960)." in release configuration. There were few (but very rare) instances when the program ran normally despite not changing anything in the code. So, by just restarting the program over and over, I keep getting different errors, or very rarely no errors. Removing "#if MY_MACRO" and "#endif" lines fixes everything just like in the example. All of this is tested with SFML 2.5.1 though. I'm not sure what causes this as nothing else breaks when using macros like this.

While compromising fix of not using macros is fine, I wonder what is the problem here? What causes this? Is it possible to fix? Is this way of using macros incorrect or unintended by SFML?

Thank you for your time

kojack

  • Sr. Member
  • ****
  • Posts: 343
  • C++/C# game dev teacher.
    • View Profile
When C++ is compiled, each .cpp is separate. They only understand each other via the include files (well, technically just forward declarations, but that's usually done via includes).
In your code there's two .cpp files. One has definitions of the functions in class Base and the other makes a Base and calls it.
The problem is each .cpp has a different understanding of what class Base is.
Base.cpp does a #define DEFINE_MACRO then includes Base.h, so it's understanding of class Base is it contains a bool, a shader and a function.
main.cpp doesn't define the macro, so when it includes Base.h it understands class Base as containing a shader and a function.
This means Base.cpp and main.cpp think Base is two different sizes in memory. One of them calling the other (main.cpp calling base.func()) will mean the shader member is accessed from the wrong address.

You need to either do the #define DEFINE_MACRO before every time you include base.h, or make it a project setting (eg. in Visual Studio you can set a preprocessor macro once and every cpp will get it).

vokazoo

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Thank you for such a clear explanation. What threw me off is the fact that everything else is fine except the shader. I rarely use macros and never had issue like this, so I thought it might be related to shaders.

Originally, I wanted to define a single macro that's visible only in one header and source file pair and invisible to the rest of codebase. This approach was fine for that purpose until the shader.setUniform() line.

Still, are all of these errors and unpredictable behaviour the consequence only of Base.cpp and main.cpp thinking Base is of two different sizes? If it is, does that mean that using macros this way is fine as long as no class fields (variables) are conditionally declared with them?

kojack

  • Sr. Member
  • ****
  • Posts: 343
  • C++/C# game dev teacher.
    • View Profile
Yep, macros themselves are fine, it's just some of the code thinks Base has a different memory layout to another part of the code.

I tested in visual studio. Base.cpp would think Base is 192 bytes with the shader starting at byte 8. main.cpp thinks Base is 184 bytes with shader starting at byte 0.
Since main.cpp is creating the base object, it only allocated 184 bytes. But func() is in Base.cpp, so it thinks the shader starts later in memory, so all of its data is in the wrong spot and there's 8 bytes missing from the end. Once you try to setUniform on the shader, it tries to access bits of the shader that are in the wrong location.

The same thing can happen when people mix up debug and release libraries. STD classes can be different sizes, debug builds add extra members to some classes (like string). So passing an std::string from debug to release or vice versa is using two different memory layouts for one object.