Search code examples
openglbuffertransformcompute-shaderuniform

OpenGL Misreading UniformBuffer in ComputeShader


I was doing a test of new way of transforming vertex positions in compute shader for some reason and I encountered a problem. What I want is just transforming the vertex (whose id is 2) 1.0f up but after the compute shader the vertex has always been transformed 1.2f right with same height.

I checked the shaders storage buffer and uniform buffer several times by Nvidia Nsight and buffer datas looked good.

ComputeShader

#version 460 core

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(std140, binding = 0) uniform Matrix 
{
    float u_id;
    mat4 modelM;
};


struct VertexLayout 
{
    vec3 positions;
    float v_id;
};

layout(std430, binding = 1) buffer VertexBuffer 
{
    VertexLayout vertex[];
};


void main() 
{
    if(vertex[gl_GlobalInvocationID.x].v_id == u_id) 
    {
        vertex[gl_GlobalInvocationID.x].positions = vec3(modelM * vec4(vertex[gl_GlobalInvocationID.x].positions, 1.0));
    }
}

Main.cpp

#include "main.h"
#include "Test1.h"

Renderer* renderer;

int main()
{
    if (!InitGLFW())
        return -1;
    
    renderer = new Renderer();
    renderer->loadMatrix();
    

    while (!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT);

        renderer->Render();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    delete renderer;
    glfwTerminate();
    return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (action == GLFW_PRESS)
    {
        switch (key)
        {
        case GLFW_KEY_ESCAPE:
            glfwSetWindowShouldClose(window, true);
            break;

        case GLFW_KEY_KP_1:
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
            break;

        case GLFW_KEY_KP_2:
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
            break;

        case GLFW_KEY_KP_4:
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            break;

        case GLFW_KEY_KP_5:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            break;

        case GLFW_KEY_KP_6:
            renderer->Transform();
            break;
        }
    }
}

Test1

#pragma once
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

struct Data
{
    float id;
    glm::mat4 modelM;
};

class Renderer
{
public:
    Renderer();
    ~Renderer();

    void loadMatrix();
    void Render();
    void Transform();
    Data data;

private:
    unsigned int shaderID;
    unsigned int programID;
    unsigned int vertexBufferID;
    unsigned int vertexArrayID;
    unsigned int uniformBufferID;

    unsigned int vertexShader;
    unsigned int fragmentShader;
    unsigned int prog;

};



#include "Test1.h"
#include "IO.h"
#include "glad/glad.h"
#include <iostream>


Renderer::Renderer()
{
    shaderID = ShaderIO::loadShaderFromFile("resource\\shaders\\Test1\\compute.glsl", GL_COMPUTE_SHADER);

    vertexShader = ShaderIO::loadShaderFromFile("resource\\shaders\\Test1\\vertex.glsl", GL_VERTEX_SHADER);
    fragmentShader = ShaderIO::loadShaderFromFile("resource\\shaders\\Test1\\fragment.glsl", GL_FRAGMENT_SHADER);

    prog = glCreateProgram();
    glAttachShader(prog, vertexShader);
    glAttachShader(prog, fragmentShader);
    glLinkProgram(prog);
    glValidateProgram(prog);


    programID = glCreateProgram();
    glAttachShader(programID, shaderID);
    glLinkProgram(programID);
    glValidateProgram(programID);

    int isValid;
    glGetProgramiv(programID, GL_VALIDATE_STATUS, &isValid);
    if (!isValid)
    {
        std::cout << "[ERROR]: Program is not valid!" << std::endl;
        glDeleteProgram(programID);
        glDeleteShader(shaderID);
    }


    float positions[] = 
    {
        -0.5f, -0.5f, 0.0f, 1.0f,
         0.0f,  0.5f, 0.0f, 2.0f,
         0.5f, -0.5f, 0.0f, 3.0f
    };

    glCreateVertexArrays(1, &vertexArrayID);
    glEnableVertexArrayAttrib(vertexArrayID, 0);
    glVertexArrayAttribBinding(vertexArrayID, 0, 0);
    glVertexArrayAttribFormat(vertexArrayID, 0, 3, GL_FLOAT, GL_FALSE, 0);

    glCreateBuffers(1, &vertexBufferID);
    glNamedBufferData(vertexBufferID, sizeof(positions), positions, GL_DYNAMIC_DRAW);

    glVertexArrayVertexBuffer(vertexArrayID, 0, vertexBufferID, 0, 4 * sizeof(float));

    glBindVertexArray(vertexArrayID);
    glUseProgram(prog);

    glCreateBuffers(1, &uniformBufferID);
    glNamedBufferData(uniformBufferID, sizeof(Data), nullptr, GL_DYNAMIC_DRAW);

    glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniformBufferID);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, vertexBufferID);

    data.id = 2;
    data.modelM = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
}

Renderer::~Renderer()
{
    glDeleteProgram(programID);
    glDeleteShader(shaderID);
    glDeleteBuffers(1, &vertexBufferID);
    glDeleteBuffers(1, &uniformBufferID);
    glDeleteVertexArrays(1, &vertexArrayID);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    glDeleteProgram(prog);
}

void Renderer::loadMatrix()
{
    glNamedBufferSubData(uniformBufferID, 0, sizeof(Data), &data);
}

void Renderer::Render()
{
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

void Renderer::Transform()
{
    glUseProgram(programID);
    glDispatchCompute(3, 1, 1);
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
    glUseProgram(prog);
}

I have tried so many things and finally I found that when I exclude the u_id from uniform buffer and changed the size to just mat4's size, it worked! On the other hand, I don't know why it does not work with u_id and I have to use it. Please Help!

This is uniform buffer memory This is uniform buffer memory

This is shader stroge buffer memory after compute shader This is shader stroge buffer memory


Solution

  • With the std140 standard, a member of type mat4 or vec4 is aligned to 16 bytes. This is specified in the OpenGL specification See Uniform Blocks:

    1. If the member is a two- or four-component vector with components consuming N basic machine units, the base alignment is 2N or 4N, respectively.
      [...]
    2. If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The array may have padding at the end; the base offset of the member following the array is rounded up to the next multiple of the base alignment.
    3. If the member is a column-major matrix with C columns and R rows, the matrix is stored identically to an array of C column vectors with R components each, according to rule (4).