OpenGL glGetUniformBlockIndex возвращает INVALID_INDEX на графических процессорах nvidea - PullRequest
1 голос
/ 29 мая 2020

Я уже довольно давно работаю над этим движком opengl и делаю на нем игру с процедурно сгенерированными планетами. Однако у меня возникли некоторые странные проблемы, с которыми я застрял в течение 3 недель.

Чтобы генерировать планеты на лету, я использую материал, состоящий из тесселяции и геометрического шейдера для генерации дюйм. Таким образом, это супер быстро. Теперь в шейдере оценки тесселяции я использую единый буферный блок для отправки параметров генерации pl anet в графический процессор (что, я считаю, составляет 48 байтов x количество слоев шума).

Теперь все это отлично работает на моем настольном ПК P C с radeon r9 390, а также на моем ноутбуке с gtx 1060. Однако на следующем графическом процессоре настольного компьютера я получаю INVALID_INDEX при вызове glGetUniformBlockIndex, и, очевидно, он не работает как пытается создать с неверными параметрами. - gtx 1050 - gtx 1060 - gtx 960 - gtx 970 - rtx 2080

На следующих графических процессорах все работает нормально без ошибок: - radeon r9 390 - rx 580 - hd 7770

I у меня нет других доступных графических процессоров для дальнейшего тестирования.

Теперь, после некоторого исследования, я знаю, что существует ограничение на то, насколько большим может быть ваш единый буферный блок, а также сколько и как многие компоненты могут быть у вас c. Однако, учитывая, что я уменьшил максимальное количество слоев до 1 и снова протестировал, проблема все еще сохранялась. Это также не может быть проблемой нехватки памяти, потому что у hd 7770 всего 1 ГБ vram, а у 1060 от 4-6 ГБ vram, и это все еще происходит на 1060.

Еще одна вещь, которую я нашел заключается в том, что переменные шейдера могут быть оптимизированы драйвером, когда переменная не влияет на вывод, и, как вы можете видеть из кода шейдера, она определенно вносит свой вклад в вывод.

Итак, на некоторый код

В классе материала есть функция CreateUniformBuffer, которая получает индекс блока унифицированного буфера и связывает его с буфером, позволяя редактировать его. Обратите внимание, что программа уже вылетает после получения INVALID_INDEX, поэтому проблема должна быть получена.

GLuint Material::CreateUniformBuffer(const std::string& name, GLuint bufferSize)
{
    GLuint uniformBlockIndex = glGetUniformBlockIndex(m_pShader->m_ShaderProgramID, name.data());
    Utilities::Debug::LogGLError(glGetError());
    if (uniformBlockIndex == GL_INVALID_INDEX)
    {
        Utilities::Debug::LogError("Material::CreateUniformBuffer > Uniform buffer block with name " + name + " not found!");
        return 0;
    }

    Utilities::Debug::LogGLError(glGetError());
    glUniformBlockBinding(m_pShader->m_ShaderProgramID, uniformBlockIndex, m_BufferBindIndex);
    Utilities::Debug::LogGLError(glGetError());

    // Uniform buffer object for lights
    GLuint bufferID;
    glGenBuffers(1, &bufferID);
    Utilities::Debug::LogGLError(glGetError());
    glBindBuffer(GL_UNIFORM_BUFFER, bufferID);
    Utilities::Debug::LogGLError(glGetError());
    glBufferData(GL_UNIFORM_BUFFER, bufferSize, NULL, GL_DYNAMIC_DRAW);
    Utilities::Debug::LogGLError(glGetError());
    glBindBufferBase(GL_UNIFORM_BUFFER, uniformBlockIndex, bufferID);
    Utilities::Debug::LogGLError(glGetError());
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
    Utilities::Debug::LogGLError(glGetError());

    m_UniformBufferObjects.push_back(bufferID);

    ++m_BufferBindIndex;
    return bufferID;
}

Это шейдер оценки тесселяции, обратите внимание, что вверху есть строка #include "SimplexNoise" , который не работает в opengl, однако у движка есть этап прекомпиляции для шейдеров, на котором он считывает код шейдера и заменяет любые директивы #include содержимым файла, который он включает перед компиляцией шейдера.

planet_te.shader

#version 450

#include "SimplexNoise.shader"

layout(triangles, equal_spacing, cw) in;
in vec3 tcPosition[];
out vec3 tePosition;
out float teElevation;

uniform int NumNoiseLayers;

struct NoiseLayer
{
    float Strength;
    float BaseRoughness;
    float Roughness;
    float Persistance;

    vec3 Center;
    float MinValue;

    int NumLayers;
    int UseFirstLayerAsMask;
    int NoiseFilterType;
    float Weight;
};

const int MaxNoiseLayers = 4;
layout(std140) uniform NoiseBlock
{
    NoiseLayer NoiseLayers[MaxNoiseLayers];
} _NoiseData;

float Evaluate(vec3 p, int layer)
{
    int filterType = _NoiseData.NoiseLayers[layer].NoiseFilterType;
    if (filterType == 0)
        return SimpleEvaluate(p, int(_NoiseData.NoiseLayers[layer].NumLayers), _NoiseData.NoiseLayers[layer].BaseRoughness, _NoiseData.NoiseLayers[layer].Roughness, _NoiseData.NoiseLayers[layer].Persistance, _NoiseData.NoiseLayers[layer].Center, _NoiseData.NoiseLayers[layer].MinValue, _NoiseData.NoiseLayers[layer].Strength);

    return RigidEvaluate(p, int(_NoiseData.NoiseLayers[layer].NumLayers), _NoiseData.NoiseLayers[layer].BaseRoughness, _NoiseData.NoiseLayers[layer].Roughness, _NoiseData.NoiseLayers[layer].Persistance, _NoiseData.NoiseLayers[layer].Center, _NoiseData.NoiseLayers[layer].MinValue, _NoiseData.NoiseLayers[layer].Strength, _NoiseData.NoiseLayers[layer].Weight);
}

float CalculateTotalStrength()
{
    float strength = 0.0;
    for (int i = 0; i < NumNoiseLayers; i++)
    {
        strength += _NoiseData.NoiseLayers[i].Strength;
    }
    return strength;
}

float LayeredEvaluate(vec3 p)
{
    float firstLayerValue = 0.0;
    float elevationAverage = 0.0;

    float totalStrength = CalculateTotalStrength();

    float unscaledElevation = 0.0;
    float scaledElevation = 0.0;

    float noiseValue = 0.0;
    float strengthPercentage = 0.0;
    if (NumNoiseLayers > 0)
    {
        unscaledElevation = Evaluate(p, 0);
        scaledElevation = max(0.0, unscaledElevation);
        noiseValue = scaledElevation;
        elevationAverage = unscaledElevation;
        firstLayerValue = noiseValue;
    }

    for (int i = 1; i < NumNoiseLayers; i++)
    {
        float mask = (_NoiseData.NoiseLayers[i].UseFirstLayerAsMask == 1) ? firstLayerValue : 1.0;
        unscaledElevation = Evaluate(p, 0);
        scaledElevation = max(0.0, unscaledElevation);
        elevationAverage += unscaledElevation;

        noiseValue += scaledElevation;
    }

    elevationAverage /= totalStrength;

    teElevation = clamp(elevationAverage * 115.0, -0.99, 0.99);

    return noiseValue;
}

void main()
{
    vec3 p0 = gl_TessCoord.x * tcPosition[0];
    vec3 p1 = gl_TessCoord.y * tcPosition[1];
    vec3 p2 = gl_TessCoord.z * tcPosition[2];
    tePosition = normalize(p0 + p1 + p2);
    float hieght = LayeredEvaluate(tePosition);
    gl_Position = vec4(tePosition * (1.0 + hieght), 1);
}

SimplexNoise.shader

const int RandomSize = 256;
const float Sqrt3 = 1.7320508075688772935;
const float Sqrt5 = 2.2360679774997896964;
uniform int _random[512];

/// Skewing and unskewing factors for 2D, 3D and 4D, 
/// some of them pre-multiplied.
const float F2 = 0.5 * (Sqrt3 - 1.0);
const float G2 = (3.0 - Sqrt3) / 6.0;
const float G22 = G2 * 2.0 - 1;
const float F3 = 1.0 / 3.0;
const float G3 = 1.0 / 6.0;
const float F4 = (Sqrt5 - 1.0) / 4.0;
const float G4 = (5.0 - Sqrt5) / 20.0;
const float G42 = G4 * 2.0;
const float G43 = G4 * 3.0;
const float G44 = G4 * 4.0 - 1.0;

const int[] Grad3 =
{
    1, 1, 0, -1, 1, 0, 1, -1, 0,
    -1, -1, 0, 1, 0, 1, -1, 0, 1,
    1, 0, -1, -1, 0, -1, 0, 1, 1,
    0, -1, 1, 0, 1, -1, 0, -1, -1
};

float Dot(int index, float x, float y, float z)
{
    return Grad3[index] * x + Grad3[index + 1] * y + Grad3[index + 2] * z;
}

float Dot(int index, float x, float y)
{
    return Grad3[index] * x + Grad3[index + 1] * y;
}

int FastFloor(float x)
{
    return int(x) >= 0 ? int(x) : int(x) - 1;
}

float Evaluate(vec3 p)
{
    float x = p.x;
    float y = p.y;
    float z = p.z;
    float n0 = 0.0, n1 = 0.0, n2 = 0.0, n3 = 0.0;

    // Noise contributions from the four corners
    // Skew the input space to determine which simplex cell we're in
    float s = (x + y + z) * F3;

    // for 3D
    int i = FastFloor(x + s);
    int j = FastFloor(y + s);
    int k = FastFloor(z + s);

    float t = (i + j + k) * G3;

    // The x,y,z distances from the cell origin
    float x0 = x - (i - t);
    float y0 = y - (j - t);
    float z0 = z - (k - t);

    // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
    // Determine which simplex we are in.
    // Offsets for second corner of simplex in (i,j,k)
    int i1, j1, k1;

    // coords
    int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords

    if (x0 >= y0)
    {
        if (y0 >= z0)
        {
            // X Y Z order
            i1 = 1;
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0;
        }
        else if (x0 >= z0)
        {
            // X Z Y order
            i1 = 1;
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 0;
            k2 = 1;
        }
        else
        {
            // Z X Y order
            i1 = 0;
            j1 = 0;
            k1 = 1;
            i2 = 1;
            j2 = 0;
            k2 = 1;
        }
    }
    else
    {
        // x0 < y0
        if (y0 < z0)
        {
            // Z Y X order
            i1 = 0;
            j1 = 0;
            k1 = 1;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        }
        else if (x0 < z0)
        {
            // Y Z X order
            i1 = 0;
            j1 = 1;
            k1 = 0;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        }
        else
        {
            // Y X Z order
            i1 = 0;
            j1 = 1;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0;
        }
    }

    // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
    // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),
    // and
    // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z),
    // where c = 1/6.

    // Offsets for second corner in (x,y,z) coords
    float x1 = x0 - i1 + G3;
    float y1 = y0 - j1 + G3;
    float z1 = z0 - k1 + G3;

    // Offsets for third corner in (x,y,z)
    float x2 = x0 - i2 + F3;
    float y2 = y0 - j2 + F3;
    float z2 = z0 - k2 + F3;

    // Offsets for last corner in (x,y,z)
    float x3 = x0 - 0.5;
    float y3 = y0 - 0.5;
    float z3 = z0 - 0.5;

    // Work out the hashed gradient indices of the four simplex corners
    int ii = i & 0xff;
    int jj = j & 0xff;
    int kk = k & 0xff;

    // Calculate the contribution from the four corners
    float t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;

    if (t0 > 0)
    {
        t0 *= t0;
        int gi0 = _random[ii + _random[jj + _random[kk]]] % 12;
        n0 = t0 * t0 * Dot(gi0 * 3, x0, y0, z0);
    }

    float t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
    if (t1 > 0)
    {
        t1 *= t1;
        int gi1 = _random[ii + i1 + _random[jj + j1 + _random[kk + k1]]] % 12;
        n1 = t1 * t1 * Dot(gi1 * 3, x1, y1, z1);
    }

    float t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
    if (t2 > 0)
    {
        t2 *= t2;
        int gi2 = _random[ii + i2 + _random[jj + j2 + _random[kk + k2]]] % 12;
        n2 = t2 * t2 * Dot(gi2 * 3, x2, y2, z2);
    }

    float t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
    if (t3 > 0)
    {
        t3 *= t3;
        int gi3 = _random[ii + 1 + _random[jj + 1 + _random[kk + 1]]] % 12;
        n3 = t3 * t3 * Dot(gi3 * 3, x3, y3, z3);
    }

    // Add contributions from each corner to get the final noise value.
    // The result is scaled to stay just inside [-1,1]
    return float(n0 + n1 + n2 + n3) * 32;
}

float Evaluate(vec3 p, float strength, float roughness, vec3 centre)
{
    float noise = (Evaluate(p * roughness + centre) + 1.0) * 0.5;
    return noise * strength;
}

float SimpleEvaluate(vec3 p, int numLayers, float baseRoughness, float roughness, float persistance, vec3 centre, float minValue, float strength)
{
    float noiseValue = 0.0;
    float frequency = baseRoughness;
    float amplitude = 1.0;

    for (int i = 0; i < numLayers; i++)
    {
        float v = Evaluate(p * frequency + centre);
        noiseValue += (v + 1) * 0.5 * amplitude;
        frequency *= roughness;
        amplitude *= persistance;
    }

    //noiseValue = max(0.0, noiseValue - minValue);
    return (noiseValue - minValue) * strength;
}

float RigidEvaluate(vec3 p, int numLayers, float baseRoughness, float roughness, float persistance, vec3 centre, float minValue, float strength, float weight)
{
    float noiseValue = 0.0;
    float frequency = baseRoughness;
    float amplitude = 1.0;
    weight = 1.0;

    for (int i = 0; i < numLayers; i++)
    {
        float v = 1.0 - abs(Evaluate(p * frequency + centre));
        v *= v;
        v *= weight;
        weight = v;
        noiseValue += v * amplitude;
        frequency *= roughness;
        amplitude *= persistance;
    }

    //noiseValue = max(0.0, noiseValue - minValue);
    return (noiseValue - minValue) * strength;
}

Обратите внимание, что это алгоритм шума, который я нашел в Интернете и преобразовал в код glsl (привет Себастьяну Лагу за его потрясающую серию на процедурных планетах в Unity)

Я использую SDL для открытия окна и обработки ввода, аудио и рендеринга текста, OpenGL 4.6 и GLEW 2.1.0

Вещи, которые я пробовал: - Ниже размер блока за счет уменьшения количества слоев (поэтому меньший массив) - однородный массив вместо un блок iform (тот же результат, только cra sh отсутствует, но для графических процессоров с cra sh он просто неправильно отображает все планеты) - Обновление SDL и всех его плагинов до последней версии и обновление OpenGL до 4.6 с 3.1, также обновляя GLEW с 1.10.0 до 2.1.0 - Изменение имени унифицированного блока - Игра с количеством значений внутри блока (при соблюдении размера, который должен быть умножением на 4 числа с плавающей запятой) - Привязка = 0 рядом с std140 в коде glsl - ведение журнала любых ошибок или предупреждений от компиляции шейдера в консоль (ошибок или предупреждений не обнаружено)

У меня немного крайний срок в следующую пятницу для загрузки сборки в Steam, и они не будут исключать его, если он работает только на amd gpu (понятно, конечно), поэтому я надеюсь, что кто-то имеет представление о том, что я делаю здесь не так, или у кого-то была аналогичная проблема раньше в прошлом, любая помощь добро пожаловать!

РЕДАКТИРОВАТЬ: Я пробовал использовать SSBO, это оказало сильное sh влияние на производительность, и хотя sh больше не работает (потому что я не ищу индекс блока по имени), он не отображает что-нибудь на nvidea gpu и выплюнуть Неизвестные ошибки в файл журнала.

Код для привязки SSBO

GLuint Material::CreateShaderStorageBuffer(const std::string& name, GLsizeiptr bufferSize, const void* data, GLint bindingIndex, GLenum usage)
{
    GLuint ssbo;
    glGenBuffers(1, &ssbo);
    Utilities::Debug::LogGLError(glGetError());
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
    Utilities::Debug::LogGLError(glGetError());
    glBufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, data, usage);
    Utilities::Debug::LogGLError(glGetError());
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingIndex, ssbo);
    Utilities::Debug::LogGLError(glGetError());

    /*GLuint blockIndex = glGetProgramResourceIndex(m_pShader->m_ShaderProgramID, GL_SHADER_STORAGE_BLOCK, name.c_str());
    Utilities::Debug::LogGLError(glGetError());

    if (blockIndex == GL_INVALID_INDEX)
    {
        Utilities::Debug::LogError("Material::CreateShaderStorageBuffer > Shader Storage Buffer Block with name " + name + " not found!");
        return 0;
    }

    glShaderStorageBlockBinding(m_pShader->m_ShaderProgramID, blockIndex, bindingIndex);
    Utilities::Debug::LogGLError(glGetError());*/

    glBindBuffer(GL_SHADER_STORAGE_BUFFER, NULL);
    Utilities::Debug::LogGLError(glGetError());
    return ssbo;
}

Код для установки данных SSBO

void Material::WriteToShaderStorageBuffer(GLuint ssboID, const void* data, GLsizeiptr size)
{
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboID);
    Utilities::Debug::LogGLError(glGetError());
    GLvoid* bufferData = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
    Utilities::Debug::LogGLError(glGetError());
    memcpy(bufferData, data, size);
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    Utilities::Debug::LogGLError(glGetError());
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, NULL);
    Utilities::Debug::LogGLError(glGetError());
}

Изменение в шейдере, чтобы превратить единый блок в SSBO

const int MaxNoiseLayers = 4;
layout(std430, binding = 0) buffer NoiseBlock
{
    NoiseLayer NoiseLayers[MaxNoiseLayers];
} _NoiseData;

1 Ответ

0 голосов
/ 31 мая 2020

Я понял, что проблема была вызвана буфером массива int _random [512] в файле шейдера SimplexNoise, я не уверен, почему это вызывало проблемы только на графическом процессоре nvidea, но я предполагаю, что это был какой-то предел он был превышен на nvideas gpus, что привело к поломке всего шейдера без каких-либо предупреждений или ошибок.

Я исправил это, превратив этот массив в другой UBO и привязав его к индексу 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...