За последние пару часов я пытался отследить ошибку в моей программе, которая возникает только при запуске в режиме выпуска. Я уже разрешил все предупреждения-компиляторы уровня 4, и нет никаких неинициализированных переменных в любом месте (что обычно было бы моим первым подозреваемым в таком случае).Оптимизация компилятора разрывает код
Это сложно объяснить, так как я даже точно не знаю, что именно происходит, так что медведь со мной, пожалуйста.
После много отладки, я сузили причину ошибки вниз куда-то в следующей функции:
void CModelSubMesh::Update()
{
ModelSubMesh::Update();
auto bHasAlphas = (GetAlphaCount() > 0) ? true : false;
auto bAnimated = (!m_vertexWeights.empty() || !m_weightBoneIDs.empty()) ? true : false;
if(bHasAlphas == false && bAnimated == false)
m_glMeshData = std::make_unique<GLMeshData>(m_vertices,m_normals,m_uvs,m_triangles);
else
{
m_glmesh = GLMesh();
auto bufVertex = OpenGL::GenerateBuffer();
auto bufUV = OpenGL::GenerateBuffer();
auto bufNormal = OpenGL::GenerateBuffer();
auto bufIndices = OpenGL::GenerateBuffer();
auto bufAlphas = 0;
if(bHasAlphas == true)
bufAlphas = OpenGL::GenerateBuffer();
auto vao = OpenGL::GenerateVertexArray();
m_glmesh.SetVertexArrayObject(vao);
m_glmesh.SetVertexBuffer(bufVertex);
m_glmesh.SetUVBuffer(bufUV);
m_glmesh.SetNormalBuffer(bufNormal);
if(bHasAlphas == true)
m_glmesh.SetAlphaBuffer(bufAlphas);
m_glmesh.SetIndexBuffer(bufIndices);
m_glmesh.SetVertexCount(CUInt32(m_vertices.size()));
auto numTriangles = CUInt32(m_triangles.size()); // CUInt32 is equivalent to static_cast<unsigned int>
m_glmesh.SetTriangleCount(numTriangles);
// PLACEHOLDER LINE
OpenGL::BindVertexArray(vao);
OpenGL::BindBuffer(bufVertex,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_vertices.size()) *sizeof(glm::vec3),&m_vertices[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_VERTEX_BUFFER_LOCATION);
OpenGL::SetVertexAttribData(
SHADER_VERTEX_BUFFER_LOCATION,
3,
GL_FLOAT,
GL_FALSE,
(void*)0
);
OpenGL::BindBuffer(bufUV,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_uvs.size()) *sizeof(glm::vec2),&m_uvs[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_UV_BUFFER_LOCATION);
OpenGL::SetVertexAttribData(
SHADER_UV_BUFFER_LOCATION,
2,
GL_FLOAT,
GL_FALSE,
(void*)0
);
OpenGL::BindBuffer(bufNormal,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_normals.size()) *sizeof(glm::vec3),&m_normals[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_NORMAL_BUFFER_LOCATION);
OpenGL::SetVertexAttribData(
SHADER_NORMAL_BUFFER_LOCATION,
3,
GL_FLOAT,
GL_FALSE,
(void*)0
);
if(!m_vertexWeights.empty())
{
m_bufVertWeights.bufWeights = OpenGL::GenerateBuffer();
OpenGL::BindBuffer(m_bufVertWeights.bufWeights,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_vertexWeights.size()) *sizeof(float),&m_vertexWeights[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_BONE_WEIGHT_LOCATION);
OpenGL::BindBuffer(m_bufVertWeights.bufWeights,GL_ARRAY_BUFFER);
OpenGL::SetVertexAttribData(
SHADER_BONE_WEIGHT_LOCATION,
4,
GL_FLOAT,
GL_FALSE,
(void*)0
);
}
if(!m_weightBoneIDs.empty())
{
m_bufVertWeights.bufBoneIDs = OpenGL::GenerateBuffer();
OpenGL::BindBuffer(m_bufVertWeights.bufBoneIDs,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_weightBoneIDs.size()) *sizeof(int),&m_weightBoneIDs[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_BONE_WEIGHT_ID_LOCATION);
OpenGL::BindBuffer(m_bufVertWeights.bufBoneIDs,GL_ARRAY_BUFFER);
glVertexAttribIPointer(
SHADER_BONE_WEIGHT_ID_LOCATION,
4,
GL_INT,
0,
(void*)0
);
}
if(bHasAlphas == true)
{
OpenGL::BindBuffer(bufAlphas,GL_ARRAY_BUFFER);
OpenGL::BindBufferData(CInt32(m_alphas.size()) *sizeof(glm::vec2),&m_alphas[0],GL_STATIC_DRAW,GL_ARRAY_BUFFER);
OpenGL::EnableVertexAttribArray(SHADER_USER_BUFFER1_LOCATION);
OpenGL::SetVertexAttribData(
SHADER_USER_BUFFER1_LOCATION,
2,
GL_FLOAT,
GL_FALSE,
(void*)0
);
}
OpenGL::BindBuffer(bufIndices,GL_ELEMENT_ARRAY_BUFFER);
OpenGL::BindBufferData(numTriangles *sizeof(unsigned int),&m_triangles[0],GL_STATIC_DRAW,GL_ELEMENT_ARRAY_BUFFER);
OpenGL::BindVertexArray(0);
OpenGL::BindBuffer(0,GL_ARRAY_BUFFER);
OpenGL::BindBuffer(0,GL_ELEMENT_ARRAY_BUFFER);
}
ComputeTangentBasis(m_vertices,m_uvs,m_normals,m_triangles);
}
Моя программа представляет собой графическое приложение, и этот фрагмент кода создает объект буфера которые необходимы для рендеринга позже. Ошибка в основном приводит к тому, что вершины конкретной сетки отображаются некорректно при выполнении определенных условий. Ошибка является последовательной и происходит каждый раз для той же сетки.
К сожалению, я не могу сузить код до конца, поскольку это приведет к исчезновению ошибки и объяснению того, что каждая строка будет занимать довольно много времени, и здесь не слишком уместна. Я почти уверен, что это проблема с оптимизацией компилятора, поэтому фактическая ошибка в этом случае скорее является побочным эффектом.
С кодом выше ошибка будет происходить, но только в режиме деблокирования. Интересная часть - это строка, которую я обозначил как «PLACEHOLDER LINE».
Если изменить код к одному из следующих 3-х вариантов, то ошибка исчезает:
# 1:
void CModelSubMesh::Update()
{
[...]
// PLACEHOLDER LINE
std::cout<<numTriangles<<std::endl;
[...]
}
# 2:
#pragma optimize("", off)
void CModelSubMesh::Update()
{
[...] // No changes to the code
}
#pragma optimize("", on)
# 3:
static void test()
{
auto *f = new float; // Do something to make sure the compiler doesn't optimize this function away; Doesn't matter what
delete f;
}
void CModelSubMesh::Update()
{
[...]
// PLACEHOLDER LINE
test()
[...]
}
Специально вариант # 2 указывает, что что-то оптимизировано, чего не должно быть.
Я не ожидаю, что кто-нибудь узнает магию о том, что является корнем проблемы, поскольку для этого потребуется более глубокое знание кода. Однако, может быть, кто-то, кто лучше разбирается в процессе оптимизации компилятора, может дать мне несколько советов, что может сделать ?
Поскольку почти любое изменение кода избавляется от ошибки, я просто не уверен, что я могу сделать, чтобы найти причину этого.
Я 99.9 (9)% уверен, что компилятор здесь НЕ виноват. Ошибки, которые возникают только в релизе, обычно связаны с памятью или связаны с несколькими потоками. И ошибка не исчезает, когда вы делаете изменения кода, которые вы описали, они просто скрываются. I. e. вы этого не замечаете, но измените что-то еще, и оно снова станет обнаженным. –
Если это повреждение памяти, оно может быть где угодно в вашем приложении, а не в фрагменте кода, который вы разместили здесь. –
Если это повреждение памяти, оно не объясняет, почему оно работает с 3 вариантами, которые я предоставил. Без одного из вариантов ошибка возникает 10 из 10 раз. С одним из вариантов на месте это происходит 0 из 10 раз. – Silverlan