重载“着色器”类中的赋值运算符

Posted

技术标签:

【中文标题】重载“着色器”类中的赋值运算符【英文标题】:Overloading assignement operators in the "Shader" class 【发布时间】:2015-07-15 15:00:26 【问题描述】:

我需要在我的一个基于 opengl 的类中重载赋值:问题是这种情况不是那么简单,因为它使用 Opengl“对象”:所以复制类属性是不够的。那么重载 = 和复制构造函数方法的正确方法是什么?这是我的代码:

着色器.h:

#pragma once

#include <GL/glew.h>
#include <map>
#include <string>
#include "alpha/LogManager.h"
#include "alpha/Component.h"
#include "alpha/Exceptions.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

#define NUM_SHADER_TYPES 3
#define NO_UNIFORM -1

class Shader : public Component

public:
    Shader();
    virtual ~Shader();

    void loadFromText(GLenum type, const std::string& src);
    void loadFromFile(GLenum type, const char* fileName);
    void loadFromPreCompiledText(GLenum type, const std::string& src)
    void loadFromPreCompiledFile(GLenum type, const char* fileName)
    void CreateAndLink();
    GLuint GetObject() const;
    ///accesses elements : shaders/uniforms;
    GLuint GetAttribLocation(const char* attrib);
    GLuint GetUniformLocation(const char* unif);
    void UpdateUniformMatrix4f(const char* unif, const glm::mat4& val, GLboolean inverse = false);
    void UpdateUniformMatrix3f(const char* unif, const glm::mat3& val, GLboolean inverse = false);
    void UpdateUniformMatrix2f(const char* unif, const glm::mat2& val, GLboolean inverse = false);
    void Bind() const override;
    void UnBind() const override;
    void Dispose() override;

    ///MEMBER FUNCTION GENERATORS
    #define __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(OGL_TYPE, TYPE_SUFFIX)\
        void UpdateUniform1##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0); \
        void UpdateUniform2##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1); \
        void UpdateUniform3##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1, OGL_TYPE v2); \
        void UpdateUniform4##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1, OGL_TYPE v2, OGL_TYPE v3); \
        void UpdateUniform1##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count = 1);\
        void UpdateUniform2##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count = 1);\
        void UpdateUniform3##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count = 1);\
        void UpdateUniform4##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count = 1);

    ///Implement Methods
    __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(GLfloat, f);
    __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(GLdouble, d);
    __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(GLint, i);
    __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(GLuint, ui);

    ///undef macro
    #undef __ALPHA_SHADER_PROGRAM_UNIFORM_DECLERATION(OGL_TYPE, TYPE_SUFFIX)

    Shader& operator=(const Shader& other) = delete;
    Shader(const Shader& shader) = delete;

private:
    enum ShaderType
        VERTEX_SHADER,
        FRAGMENT_SHADER,
        GEOMETRY_SHADER
    ;
    int m_numShaders;
    GLuint m_object;
    bool _hasCreated;
    bool _hasDisposed;
    GLuint _shaders[NUM_SHADER_TYPES]; /// VERTEX, FRAGMENT, GEOMETRY
    std::map<std::string, GLuint> _attribList;
    std::map<std::string, GLuint> _unifLocationList;
    std::map<std::string, bool> _registeredUnifs;
;

着色器.cpp

#include "alpha/Shader.h"
#include "alpha/LogManager.h"
#include <fstream>
#include <assert.h>

#define GL_NULL 0

Shader::Shader()
    : m_numShaders(0), m_object(0), _hasCreated(false), _hasDisposed(false)

    _shaders[VERTEX_SHADER] = 0;
    _shaders[FRAGMENT_SHADER] = 0;
    _shaders[GEOMETRY_SHADER] = 0;
    _attribList.clear();
    _unifLocationList.clear();


Shader::~Shader()
    Dispose();


void Shader::loadFromText(GLenum type, const std::string& text)
    try
    
        switch (type)
        
            case GL_VERTEX_SHADER:
                if(glIsShader(_shaders[VERTEX_SHADER]) == GL_TRUE)
                    throw TypeRegisteredTwiceException();
                break;
            case GL_FRAGMENT_SHADER:
                if(glIsShader(_shaders[FRAGMENT_SHADER]) == GL_TRUE)
                    throw TypeRegisteredTwiceException();
                break;
            case GL_GEOMETRY_SHADER:
                if(glIsShader(_shaders[GEOMETRY_SHADER]) == GL_TRUE  )
                    throw TypeRegisteredTwiceException();
                break;
            default:
                throw UnknownTypeException();
                break;
        

        GLuint shader = glCreateShader(type);
        const char* cstr = text.c_str();
        glShaderSource(shader, 1, &cstr, nullptr);

        ///compile + check shader load status
        GLint status;
        glCompileShader(shader);
        glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

        if(status == GL_FALSE)
            GLint infoLogSize;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogSize);
            GLchar *infoLog = new GLchar[infoLogSize];
            glGetShaderInfoLog(shader, infoLogSize, nullptr, infoLog);
            LOG_ERROR("Shader", infoLog);
            delete [] infoLog; /// no throw between new and delete !
        

        if(m_numShaders >= 4)
        
            throw OutOfBoundsExcpetion();
        
        _shaders[m_numShaders++]=shader;
    
    catch(std::exception & e)
        LOG_ERROR_INFO("AN ERROR OCCURED", "\n", e.what() );
        LOG_ERROR("Shader", "IGNORING ADDED SHADER !");
    


void Shader::CreateAndLink()
    try
        if(glIsProgram(m_object) == GL_TRUE)
            throw CreatedTwiceException();

        m_object = glCreateProgram();

        ///OK CREATED PROGRAM !
        _hasCreated = true;

        if(_shaders[VERTEX_SHADER] != 0)
            glAttachShader(m_object, _shaders[VERTEX_SHADER]);
        else
            throw NotLoadedException();
        if(_shaders[FRAGMENT_SHADER] != 0)
            glAttachShader(m_object, _shaders[FRAGMENT_SHADER]);
        else
            throw NotLoadedException();
        if(_shaders[GEOMETRY_SHADER] != 0)
            glAttachShader(m_object, _shaders[GEOMETRY_SHADER]);/// OK, not compulsary...

        ///link + check
        GLint status;
        glLinkProgram(m_object);
        glGetProgramiv(m_object, GL_LINK_STATUS, &status);
        if(status == GL_FALSE)
            GLint infoLogSize;
            glGetProgramiv(m_object, GL_INFO_LOG_LENGTH, &infoLogSize);
            GLchar *infoLog = new GLchar[infoLogSize];
            glGetProgramInfoLog(m_object, infoLogSize, nullptr, infoLog);
            delete [] infoLog;
        

        std::cout << "Shader created : ID = " << m_object << std::endl;

        glDetachShader(m_object, _shaders[VERTEX_SHADER]);
        glDetachShader(m_object, _shaders[FRAGMENT_SHADER]);
        glDetachShader(m_object, _shaders[GEOMETRY_SHADER]);

        glDeleteShader(_shaders[VERTEX_SHADER]);
        glDeleteShader(_shaders[FRAGMENT_SHADER]);
        glDeleteShader(_shaders[GEOMETRY_SHADER]);
    
    catch(std::exception & e)
         LOG_ERROR_INFO("Shader", "AN ERROR OCCURED\n", e.what() );
         EXIT(1);
    


void Shader::Bind() const
    glUseProgram(m_object);

void Shader::UnBind() const
    glUseProgram(GL_NULL);


GLuint Shader::GetAttribLocation(const char* attrib)
    auto it = _attribList.find(attrib);
    if(it == _attribList.end())
    
        GLuint loc = glGetAttribLocation(m_object, attrib);
        if(loc == (unsigned)-1)
        
            LOG_INFO("Shader Warning : Attrib", attrib, "is inactive !");
        
        it = _attribList.insert(attrib, loc).first;
    
    return it->second;


GLuint Shader::GetUniformLocation(const char* unif)
    auto it = _unifLocationList.find(unif);
    if(it == _unifLocationList.end())
    
        GLuint loc = glGetUniformLocation(m_object, unif);
        if(loc == (unsigned)-1)
        
            LOG_INFO("Shader Warning : Uniform", unif, "is inactive !");
        
        it = _unifLocationList.insert(unif, loc).first;
    
    return it->second;



GLuint Shader::GetObject() const return m_object; 


void Shader::UpdateUniformMatrix4f(const char* unif, const glm::mat4& val, GLboolean inverse)
    glUniformMatrix4fv(this->GetUniformLocation(unif), 1, inverse, glm::value_ptr(val));


void Shader::UpdateUniformMatrix3f(const char* unif, const glm::mat3& val, GLboolean inverse)
    glUniformMatrix3fv(this->GetUniformLocation(unif), 1, inverse, glm::value_ptr(val));


void Shader::UpdateUniformMatrix2f(const char* unif, const glm::mat2& val, GLboolean inverse)
    glUniformMatrix2fv(this->GetUniformLocation(unif), 1, inverse, glm::value_ptr(val));



#define __ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION(OGL_TYPE, TYPE_SUFFIX)\
    void Shader::UpdateUniform1##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0)\
     glUniform1 ## TYPE_SUFFIX (this->GetUniformLocation(name), v0); \
\
    void Shader::UpdateUniform2##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1)\
     glUniform2 ## TYPE_SUFFIX (this->GetUniformLocation(name), v0, v1); \
\
    void Shader::UpdateUniform3##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1, OGL_TYPE v2)\
     glUniform3 ## TYPE_SUFFIX (this->GetUniformLocation(name), v0, v1, v2); \
\
    void Shader::UpdateUniform4##TYPE_SUFFIX(const GLchar* name, OGL_TYPE v0, OGL_TYPE v1, OGL_TYPE v2, OGL_TYPE v3)\
     glUniform4 ## TYPE_SUFFIX (this->GetUniformLocation(name), v0, v1, v2, v3); \
\
    void Shader::UpdateUniform1##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count) /** sizei=1 by default */\
     glUniform1 ## TYPE_SUFFIX ## v (this->GetUniformLocation(name), count, v); \
\
    void Shader::UpdateUniform2##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count) /** sizei=1 by default */\
     glUniform2 ## TYPE_SUFFIX ## v (this->GetUniformLocation(name), count, v); \
\
    void Shader::UpdateUniform3##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count) /** sizei=1 by default */\
     glUniform3 ## TYPE_SUFFIX ## v (this->GetUniformLocation(name), count, v); \
\
    void Shader::UpdateUniform4##TYPE_SUFFIX##v(const GLchar* name, const OGL_TYPE* v, GLsizei count) /** sizei=1 by default */\
     glUniform4 ## TYPE_SUFFIX ## v (this->GetUniformLocation(name), count, v); \

__ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION(GLfloat, f);
__ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION(GLdouble, d);
__ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION(GLint, i);
__ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION(GLuint, ui);

///undef macro
#undef __ALPHA_SHADER_PROGRAM_UNIFORM_IMPLEMENTATION()

void Shader::loadFromFile(GLenum which, const char* fileName)
    std::ifstream fparser;
    fparser.open(fileName, std::ios_base::in);
    if(fparser)
        ///read + load
        std::string buffer(std::istreambuf_iterator<char>(fparser), (std::istreambuf_iterator<char>()));
        loadFromText(which, buffer);
    
    else
        LOG_ERROR_INFO("Shader", "Invalid fileName path", fileName);
    


void Shader::Dispose()
    if(!_hasDisposed)
    
        glDeleteProgram(m_object);

        std::cout << "Disposed Shader object : ID was " <<  m_object << std::endl;

        m_object = -1;
        _hasDisposed = true;///no more calls of Dispose() !
    

编辑:我最终按照您的建议添加了一个移动构造函数:

Shader::Shader(Shader&& other)
    this->m_object = other.m_object;
    other.m_object = 0;
    this->m_numShaders = other.m_numShaders;
    this->_hasCreated = other._hasCreated;
    this->_hasDisposed = other._hasDisposed;
    for(unsigned i = 0; i < NUM_SHADER_TYPES; ++i)
    
        this->_shaders[i] = other._shaders[i];
        other._shaders[i] = 0;
    
    this->_attribList = std::move(other._attribList);
    this->_unifLocationList = std::move(other._unifLocationList);
    this->_unifLocationList = std::move(other._unifLocationList);

感谢您的帮助:)

【问题讨论】:

相关:***.com/questions/28929452/…。我的回答提到了一些处理这个问题的选项。 好的...但是我包装对象的方式好吗?你能帮我吗:)? 好吧,不知道如何解决您的问题,但有一些注意事项:1-您没有使用 has_created,使用它比调用 glIsProgram(m_object) 更好 2-与 (loc==(无符号)-1) ?我假设 (unsigned) 是一个演员,你为什么不写 (loc==1) ? 谢谢,我会改的:) 【参考方案1】:

我想这是一个有趣的练习,因为复制着色器程序并不像复制 GLuint 句柄那么简单。在我的脑海中,我能想到两个选择:

    浅拷贝:将句柄实现为引用计数的共享资源(类似于shared_ptr 的工作方式)。这是更便宜的选择,但是,我猜这不是您想要的,因为这样做意味着一个 Shader 对象的更改将影响所有其他对象。

    Deep Copy:将glDetachShader()glDeleteShader()操作延迟到析构函数(或Dispose()方法),然后在复制赋值运算符中重新编译着色器程序。你可以这样做(没有错误检查):

    Shader(const Shader &other)
    
        DetachAllShaders();
        GLuint shaders[NUM_SHADER_TYPES];
        GLsizei shaderCount;
        glGetAttachedShaders(other.GetObject(), NUM_SHADER_TYPES, &shaderCount, shaders);
    
        for(auto shader : shaders)
        
            glAttachShader(m_program, shader);
        
    
        if(other.isLinked())
        
            glLinkProgram(m_program);
        
    
    

如果我是你,我不会去。我想不出你为什么需要同一个着色器程序的多个副本的原因。例如,如果您从函数中返回它,我建议您简单地实现一个移动构造函数。像这样简单的东西:

Shader(Shader &&other)

    m_object = other.m_object;
    other.m_object = 0;

    _attribList = std::move(other._attribList);
    // ...etc

在这种情况下,删除 m_object 会在它为 0 时被 OpenGL 静默忽略。

【讨论】:

谢谢 :) 我想我会为我所有的 gl 包装器做同样的事情!对图书馆来说很好吗?它不会打扰用户无法使用 = 运算符吗?虽然我认为我也会包装这些类(比如在 gameObject 中)...... 也许它会打扰用户,但如果由于多次重新编译导致程序出现性能问题,它会更加困扰她。这将鼓励他们为他们的程序考虑更好的设计。以QOpenGLShaderProgram 为例。他们只是实现了一个默认构造函数,没有复制任何东西。如果您想比较实现和学习,可以在线获得他们的源代码。 我在我的问题中发布了新的移动构造器。【参考方案2】:

OpenGL 对象由驱动程序管理,如果您想要进行深拷贝,除非 API 公开了这样做的选项,否则您只能创建对它的引用而不是深拷贝。 AFAIK,OpenGL 没有公开这样的功能。

例如由整数 1 表示的着色器程序将是 API 用户可以处理的唯一实例。无法制作副本。在您的 C++ 对象中,如果您封装了相同的着色器对象 (1),您实际上是在对同一个 OpenGL 对象进行另一个引用,如果没有正确的引用计数,您最终会遇到双重删除等问题。

【讨论】:

所以我不能实现=操作符,我应该删除它们 我会说最好不要允许复制而只允许移动。 你能告诉我如何在编辑部分实现“移动”吗?您是在谈论 C++11 中的移动语义吗? 是的。 Reto 的链接答案包含您需要的详细信息。当你移动时,被移动对象拥有的资源现在将由被移动对象拥有,被移动对象将没有资源可以拥有,因此析构函数将是一个空操作。 我不太明白...你能给我举个例子吗?

以上是关于重载“着色器”类中的赋值运算符的主要内容,如果未能解决你的问题,请参考以下文章

在c ++中的赋值运算符重载方法中删除旧的动态分配的内存

C++类与对象(详解构造函数,析构函数,拷贝构造函数,赋值重载函数)

C++ 继承多态关系中的赋值运算符的重载=operator()

C++ 继承多态关系中的赋值运算符的重载=operator()

C++中的重载赋值运算符

[ C++ ] C++类与对象(中) 类中6个默认成员函数 -- 运算符重载