为什么会引发异常:使用opengl纹理指针矢量读取访问冲突?

时间:2018-09-05 13:06:33

标签: c++ opengl memory vector textures

我正在尝试制作即时模式UI面板/容器类。 每个面板类都包含ID,名称,位置,大小,RenderData等。

RenderData是我保存顶点,元素,纹理数据的类。该类还负责设置顶点,元素,纹理。

GUI类是我有小部件,Window指针和Renderer

的向量的类

Renderer是一个类,其中包含Window个指针的指针,即vao,vbo,ebo和shader。此类负责将RenderData的数据添加到缓冲区并绘制所有内容。

struct Vertex
{
    glm::vec2 position;
    glm::vec2 uv;
    float texID;
}

class Panel
{
    public:
        Panel(std::string name, glm::vec2 pos, glm::vec2 size)
        {
            position = pos;
            this->size = size;
            renderData = std::make_unique<RenderData>();
        }
        ~Panel() {}

        RenderData* renderData;
        glm::vec2 position, size;
        GLuint id;
        std::string name;
};

class GUI
{
    public:
        GUI()  {}
        ~GUI() {}

        void Create()
        {
            renderer.Create();
            tex = tex->Create("Textures/Panel.png");
        }

        void Clear()
        {
            for(const auto& panel: panel) {
                delete panel;
            } panel.clear();
        }

        void PushPanel()
        {
            glm::vec2 pos = glm::vec2(100.0f, 100.0f);
            glm::Vec2 size = glm::vec2(300.0f, 300.0f);

            Panel* panel = CreatePanel(name, pos, size);

            panel->renderData->Clear();
            panel->renderData->Draw(pos, size, tex);
        }

        void Render()
        {
            tempRenderData.clear();
            for(const auto& window : context.window) 
            {
                tempRenderData.push_back(window->renderData);
            }

            renderer.AddRenderData(&tempRenderData);
            renderer.Render();
        }

        Panel* CreatePanel(std::string name, glm::vec2 pos, glm::vec2 size)
        {
            Panel* result = new Panel(name, pos, size);
            panel.push_back(result);
            return result;
        }

        std::vector<Panel*> panel;
        std::vector<RenderData*> tempRenderData;
        Renderer renderer;
        Texture* tex;
};

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

        void Create()
        {
            glGenVertexArrays(1, &vao);
            glGenBuffers(1, &vbo);
            glGenBuffers(1, &ebo);

            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glEnableVertexAttribArray(0);
            glEnableVertexAttribArray(1);
            glEnableVertexAttribArray(2);
            glEnableVertexAttribArray(3);

            glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, position));

           glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true,  sizeof(Vertex), (const void*)offsetof(Vertex, color));

            glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, uv));

            glVertexAttribPointer(3, 1, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, texID));

            //Creates default vertex and fragment shaders
            shader.Create();
            shader.Use();
            int textures[32];
            for(int i = 0; i < 32; ++i) textures[i] = i;
            shader.uniform1iv("u_textureSampler", textures, 32);
        }

        void Start()
        {
            shader.UseProgram();
            shader.UniformMat4fv("projection", glm::ortho(0.0f, displayW, 0.0f, displayH);
        }

        void Render()
        {
            glBindVertexArray(vao);

           for(GLuint i = 0; i < renderDataSize; ++i) {
                RenderData* currentRender = renderData[i];

                glBindBuffer(GL_ARRAY_BUFFER, vbo);              
                glBufferData(GL_ARRAY_BUFFER, currentRender->vertexBuffer.size() * sizeof(RenderData), currentRender->vertexBuffer.data(), GL_DYNAMIC_DRAW);

                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
                glBufferData(GL_ARRAY_BUFFER, currentRender->elementBuffer.size() * sizeof(GLuint), currentRender->elementBuffer.data(), GL_DYNAMIC_DRAW);

                for(GLuint i = 0; i < texture.size(); ++i) 
                {
                    // It is just glBindTexture(..);
                    currentRender->texture[i]->BindTexture(i);
                }

                glDrawElements(GL_TRIANGLES, currentRender->elementBuffer.size(), GL_UNSIGNED_INT, nullptr);

                for(GLuint i = 0; i < texture.size(); ++i) 
                {
                    currentRender->texture[i]->UnbindTexture(i);
                }
            }
        }

        void AddRenderData(std::vector<RenderData*>* data)
        {
            renderData = data->data();
            renderDataSize = data->size();
        }

        Shader shader;
        RenderData** renderData;
        GLuint renderDataSize;

        GLuint vao;
        GLuint vbo;
        GLuint ebo;
}; 

class RenderData
{
   public:
        RenderData() {}
        ~RenderData() {}

        void Clear()
        {
            elementBuffer.clear();
            vertexBuffer.clear();
            texture.clear();
        }

        void Draw(glm::vec2 pos, glm::vec2 size, Texture* tex)
        {
            int elementIndex = vertexBuffer.size();

            float texID = 0.0f;
            bool found = false;
            for (GLuint i = 0; i < texture.size(); ++i) {
                if (texture[i] == tex) {
                    texID = static_cast<float>(i + 1);
                    found = true;
                    break;
                }
            }

            if (!found) {
                if (texture.size() >= 32) {
                    //Do something, maybe add another draw call, because one GPU command can handle 32 textures
                }
                texture.push_back(t);
                texID = static_cast<float>(texture.size());
            }

            // texID and color for all vertices is the same.
            //Push 4 vertices
            vertexBuffer.push_back(...);
            vertexBuffer.push_back(...);
            vertexBuffer.push_back(...);
            vertexBuffer.push_back(...);

            elementBuffer.push_back(elementIndex);
            elementBuffer.push_back(elementIndex + 1);
            elementBuffer.push_back(elementIndex + 2);
            elementBuffer.push_back(elementIndex + 2);
            elementBuffer.push_back(elementIndex + 3);
            elementBuffer.push_back(elementIndex);
        }

        std::vector<Vertex> vertexBuffer;
        std::vector<GLuint> elementBuffer;
        std::vector<Texture*> texture;
};

int main()
{
    GUI gui;
    gui.Create();
    while(!quit)
    {
        gui.Clear();
        gui.PushPanel();

        gui.Render()
    }

    return 0;
}

class Texture
{
    public:
        Texture() {}
        ~Texture() {}

        Texture* Create(std::string texPath)
        {
            return new GLTexture(texPath);
        }

        virtual void BindTexture(GLuint slot) {}
        virtual void UnbindTexture(GLuint slot) {}

        virtual void setData(const void* data) {}
};

class GLTexture : public Texture 
{
    public:
        GLTexture(std::string texPath)
        {
            SDL_Surface* surface = nullptr;
            surface = IMG_Load(texPath.c_str());
            w = surface->w;
            h = surface->h;

            GLuint format = surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB;

            glGenTextures(1, &texture));
            glBindTexture(GL_TEXTURE_2D, texture));

            glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, surface ? surface->pixels : nullptr));

            //wrap, filters etc.
            glTexParameteri(...);
            glTexParameteri(...);
            glTexParameteri(...);
            glTexParameteri(...);

            glGenerateMipmap(target));

            glBindTexture(target, 0));

            SDL_FreeSurface(surface);
        }

        void BindTexture(GLuint slot) override
       {
             glBindTexture(GL_TEXTURE_2D, texture);
             glActiveTexture(GL_TEXTURE0 + slot)
        }

        void UnbindTexture(GLuint slot) override
        {
             glBindTexture(GL_TEXTURE_2D, 0);
             glActiveTexture(GL_TEXTURE0 + slot)
        }

        void setData(const void* data) override
        {
            glBindTexture(GL_TEXTURE_2D, texture));
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, offsetY, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data));
        }

        int w, h;
};

纹理等级100%没问题

此代码应使用纹理渲染gui面板。这应该。不幸的是,我遇到了Exception thrown: read access violation. std::_Vector_alloc<std::_Vec_base_types<MiE::Texture *,std::allocator<MiE::Texture *> > >::_Mylast(...) returned 0xFDFDFEE5.问题。

它来自Draw()方法。

我认为tempRenderData的大小不是Panel的大小,当我尝试渲染纹理时,它返回了问题,因为它无法得到正确的RenderData

我检查了tempRenderData的大小,还可以。

没有纹理的几何图形呈现良好。

错误消息: Problem

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

我建议您创建一个名为struct的{​​{1}},并保存每个GPU绘制数据的所有位置。

DrawCall

RenderData应该包含一些新的向量,例如:

struct DrawCall
{
    Texture* texture = nullptr;
    Shader shader;
    Camera camera;
    GLuint lastFreeTexture = 0;
};    

vector<DrawCall> drawCall;
vector<Texture*> texture;
vector<Shader>
vector<Camera>

渲染器

void AddDrawCall()
{
    DrawCall newCall;
    newCall.shader = ...;
    newCall.camera = ...;
    newCall.texture = texture.back();
    drawCall.push_back(newCall);
}

void AddTexture(Texture* tex)
{
    texture.push_back(tex);
    CheckTextureForDrawCall();
}

void CheckTextureForDrawCall()
{
    Texture* currentTexture = texture.back();
    DrawCall* currentDraw = drawCall.back();
    if (!currentDraw || (currentDraw->elemCount != 0 && currentDraw->texture != currentTexture)) {
        AddDrawCall();
        return;
    }

    DrawCall * prevDraw = drawCall.size() > 1 ? currentDraw - 1 : nullptr;
    if (prevDraw 
     && prevDraw->shader == shader.back()
     && prevDraw->camera == camera.back()
     && prevDraw->texture == currentTexture) {
        drawCall.pop_back();
    } else { currentDraw->texture = currentTexture; }
}

void Draw(...) 
{
    AddTexture(...);

    ... other things
}

drawcall结构将使您对代码有更多的控制。

如果您希望每个绘图调用32个纹理,只需创建void RenderPresent() { ... add attributes etc. for(GLuint i all RenderData) { RenderData* currentRenderData = renderData[i]; ... add data to the buffers for(auto drawCall : currentRenderData->drawCall) { drawCall.shader.bind(); drawCall.texture.bind(); drawElements(...); } } } , 查找自由纹理索引,如果自由纹理索引大于32,则添加一个新的绘制调用,如果先前绘制调用纹理索引小于32(并检查着色器或照相机等其他内容,因为即使纹理索引小于32,但是着色器有所不同,您仍然可以保留当前的绘制调用),可以删除当前的绘制调用-只需Texture* texture[32];

检查最后一个空闲索引的方法可能是这样的:

drawCall.pop_back();

,然后纹理插槽应该为 enum { DrawCall_Max_Textures = 32 }; GLuint FindFreeTextureIndex() { for (GLuint i = lastFreeTexture; i < DrawCall_Max_Textures; ++i) { if (texture[i] == nullptr) { return i; } } for (GLuint i = 0; i < lastFreeTexture; ++i) { if (texture[i] == nullptr) { return i; } } return 0; } ,如果您将float textureSlot = static_cast<float>(getDrawCall()->FindFreeTextureIndex() + 1);丢弃在片段着色器中,则纹理插槽应该为+1(我想您是这样做的,因为我看到了一些thecherno sparky engine series的东西)。