W jaki sposób używać bufforów i shaderów stworzonych przez klasy pointer w innym wątku?

0

Witam,
chciałbym żeby moje renderowanie było na osobnym wątku.

Okno oraz kontekst tworzę przy pomocy biblioteki SDL2, a za funkcje opengl odpowiada glew

SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);

window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);
glContext = SDL_GL_CreateContext(window);
initContext = SDL_GL_CreateContext(window);

glewExperimental = true;
glewInit();

w taki sposób chciałbym wykonać to renderowanie w wątku

void mainloop()
{
	std::thread* thread = nullptr;
	std::mutex mutex;

	Cube cube;

	std::lock_guard<std::mutex> guard(mutex);
	thread = new std::thread([&]() {
		while (!quit) {
			SDL_GL_MakeCurrent(window, glContext);

			glCall(glClearColor(0, 0, static_cast<float>(15) / 255, 1));
			glCall(glClear(GL_COLOR_BUFFER_BIT));

			cube.Render({ 1280.0f, 720.0f });

			SDL_GL_SwapWindow(window);
		}
	});
}

buffory są tworzone przez klasę Buffor, która jest matką klasy GLBuffor i metodę Create(), która zwraca stworzony pointer

Buffor* vbo = nullptr;

Cube()
{
    vbo = vbo->Create();
}

                                   //rodzaj bufora, czy to array, czy to element array | sposób użycia - static draw, dynamic draw, copy itd.
Buffer* Buffer::Create(BUFFOR buffor,                                                       USAGE usage)
{
    //Konstruktor GLBuffor wywołuje glGenBuffers i glBindBuffer dla odpowiedniego buffora
    retrun new GLBuffor(buffor, usage);
}

shader działa bardzo podobnie:

class Shader
{
public:
    Shader* Create()
    {
        //ctor GLShadera wywołuje Begin()
        return new GLShader();
    }
    //klasa GLShader nadpisuje Begin() i wywołuje w tej metodzie glCreateProgram(...)
    virtual void Begin() {}
    //tworzy shader o podanym typie - wywołuje glCreateShader, glShaderSource, glCompileShader i glAttachShader
    //wynik końcowy chcemy zapisać do tablicy, razem na przykład z shaderem fragmentów, którą potem podamy do metody End
    virtual int ShaderFromSource(const char* shader, SHADER type) {}
    //Bierze tablice z shaderami i wywołuje - glLinkProgram
    virtual void End(const int* shaders, int count) {}
}

Shadery oraz buffory są tworzone w konstruktorze klasy Cube, konstruktor jest wywoływany w głównym wątku (czyli nie w tym renderującym).
metoda Cube::Render(...) potrzebuje mieć dostęp do tych buforów i shaderów stworzonych w konstruktorze - żeby użyć takich rzeczy jak Shader::UseProgram() - czyli proste glUseProgram(...), Shader::UniformXXX(....) czy nawet DrawArrays(...) czy DrawElements(...).

Gdy wywołuje Cube::Render(...) w wątku renderującym, wyskakuje mi

screenshot-20190101154920.png

dokładnie klasa Cube wygląda tak

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

	void Render(const Vector2& windowSize);

	VertexArray* vao = nullptr,
		          * lightVao = nullptr;
	Buffer* vbo = nullptr;
	Shader* shader = nullptr;

	Camera camera; 
};
Cube::Cube()
{
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

		-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

		-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

		 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,								  
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
	};
																							   
	vao = vao->Create();

	vbo = vbo->Create(Array_Buffer, Static_Draw);
	vbo->AddData(sizeof(vertices), vertices);
	vbo->AddAttribute(0, 3, FLOAT, false, 6 * sizeof(float), (const void*)(0));
	vbo->AddAttribute(1, 3, FLOAT, false, 6 * sizeof(float), (const void*)(3 * sizeof(float)));

	lightVao = lightVao->Create();																				 
	vbo->BindBuffer();
	vbo->AddAttribute(0, 3, FLOAT, false, 6 * sizeof(float), 0);

	shader = shader->Create();
	const int shaders[2] = {
	   shader->ShaderFromSource(vs, VERTEX),											 
	   shader->ShaderFromSource(fs, FRAGMENT)
	};
	shader->End(shaders, 2);	
}
void Cube::Render(const Vector2& windowSize)																			
{																		
	struct Material
	{
		Vector3 ambient{ 1.0f, 0.5f, 0.31f }, diffuse{ 1.0f, 0.5f, 0.31f }, specular = 0.5f;
		int shininess = 32;
	} static material;

	struct Light {
		Vector3 ambient = 0.2f, diffuse = 0.5f, specular = 1.0f;
	} static light;

			shader->UseProgram();
	
			Vector3 lightPosition{ 0.0f, 0.0f, 0.0f };
			shader->Uniform3fv("lightPosition", 1, &lightPosition.x);
			Vector3 viewPosition = 0.0f;
			shader->Uniform3fv("viewPosition", 1, &camera->Position.x);
			Vector3 objectColor{ 1.0f, 0.5f, 0.31f };
			shader->Uniform3fv("objectColor", 1, &objectColor.x);
			Vector3 lightColor{ 1.0f, 1.0f, 1.0f };
			shader->Uniform3fv("lightColor", 1, &lightColor.x);

			shader->Uniform3fv("material.ambient", 1, &material.ambient.x);
			shader->Uniform3fv("material.diffuse", 1, &material.diffuse.x);
			shader->Uniform3fv("material.specular", 1, &material.specular.x);
			shader->Uniform1f("material.shininess", static_cast<float>(material.shininess));

			shader->Uniform3fv("light.ambient", 1, &light.ambient.x);
			shader->Uniform3fv("light.diffuse", 1, &light.diffuse.x);
			shader->Uniform3fv("light.specular", 1, &light.specular.x);

			Mat4 projection = Mat4(1.0f);
			projection = glm::perspective(45.0f, ratio, 0.001f, 1000.0f);

			Mat4 view = Mat4(1.0f);
			view = camera->GetViewMatrix();

			Mat4 model = Mat4(1.0f);
			{
				Mat4 rotate = Rotate(-(SDL_GetTicks() / 100.0f), Vector3(0.0f, 1.0f, 0.0f));
				model = rotate;
			}

			Mat4 mvp = Mat4(1.0f);
			mvp = projection * view * model;

			shader->UniformMatrix4fv("u_camera", mvp);
			shader->UniformMatrix4fv("u_model", model);
			shader->UniformMatrix4fv("u_normalModel", glm::transpose(glm::inverse(model)));
			shader->Uniform1i("index", 0);
			vao->DrawArrays(Triangles, 0, 36);

			model = Mat4(1.0f);
			{
				Mat4 traslate = Translate(lightPosition);
				Mat4 scale = Scale(Vector3(0.2f));
				model = traslate * scale;
			}
			mvp = projection * view * model;
			shader->UniformMatrix4fv("u_camera", mvp);
			shader->Uniform1i("index", 1);
			lightVao->DrawArrays(Triangles, 0, 36);
}

Dlaczego tak się dzieje? W jaki sposób mogę to naprawić?

0

Shadery oraz buffory są tworzone w konstruktorze klasy Cube, konstruktor jest wywoływany w głównym wątku (czyli nie w tym renderującym).

Jesteś pewien że nie próbujesz używać tych obiektów zanim nie zostaną stworzone?

Tworzenie obiektów ściśle związanych z renderowaniem w innym wątku niż renderujący nie brzmi jak dobry pomysł…

0

Jesteś pewien że nie próbujesz używać tych obiektów zanim nie zostaną stworzone?

Nie wydaje mi się. Najpierw tworze obiekt Cube - w tym tworzone są buffory i dodawane dane do nich, potem dopiero jest tworzony wątek w którym jest używany shader i rysowana kostka. No chyba, że tworzenie wątków kompilator jakoś traktuje specjalnie i przepisuje wątek nad tworzenie obiektu, ale nie wydaje mi się, że tak jest.

Tworzenie obiektów ściśle związanych z renderowaniem w innym wątku niż renderujący nie brzmi jak dobry pomysł…

Gdy tworze obiekt w wątku to pokazuje mi się taki komunikat
screenshot-20190101171520.png

0

Odkryłem, że ten drugi błąd jest przez spdlog - jest to biblioteka do logowania (logowałem błędy opengl tym). Gdy użyłem zwykłego cout to okazało się, że glGenVertexArrays i glBindVertexArray dają 1282 error, co oznacza GL_INVALID_OPERATION. Najzabawniejsze jest to, że jest to zwracane dla glGenVertexArrays a na stronie https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGenVertexArrays.xhtml nie ma w ogóle opisanego takiego błędu. Jakieś pomysły?

1 użytkowników online, w tym zalogowanych: 0, gości: 1