@PrizmMARgh

Как переиспользовать opengl-объекты в разных окнах?

Моя основная программа состоит из нескольких окон, которые открываются и закрываются по очереди. Чтобы не подгружать для каждого окна заново одни и те же модели/текстуры/шейдера, я решил их хранить в глобальном статическом контейнере. Однако, после пересоздания окна они становятся невалидными. Насколько я смог выяснить, дело в том, что после закрытия окна удаляется некий "контекст". Я не до конца понимаю, как он работает. Можно ли на примере показать, как в коде ниже можно повторно использовать шейдер и меш для окон win_0 и win_1?
код

#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <string>

#include <SOIL/SOIL.h>

#include <cstdio>
#include <cstdlib>
#include <fstream>

#include "utils.h"

struct GLFW {
	GLFW() {
		glfwInit();
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		glfwWindowHint(GLFW_SAMPLES, 1);
		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
		glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
	}
	~GLFW() {
		glfwTerminate();
	}
};

struct Window {
	GLFWwindow* id;
	int width, height;	
	Window(int _width, int _heigth, GLFWwindow* reference) {
		width = _width, height = _heigth;
		id = glfwCreateWindow(width, height, "rfoegl", nullptr, reference);
		glewExperimental = GL_TRUE;
		glfwMakeContextCurrent(id);

		if(!id) {
			puts("Failed to create GLFW window\n");
			glfwTerminate();
			exit(-1);
		}
		if(glewInit() != GLEW_OK) {
			puts("Failed to initialize GLEW\n");
			exit(-1);
		}
		glfwGetFramebufferSize(id, &width, &height);
		glViewport(0, 0, width, height);
	}
	~Window() {
		glfwDestroyWindow(id);
	}
};

bool pressed_keys[1024] = {false};
void default_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
	if(action == GLFW_PRESS) {
		pressed_keys[key] = true;
	} else if(action == GLFW_RELEASE) {
		pressed_keys[key] = false;
		if(key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE);
	}
}
void set_default_key_callback(Window* w) {
	glfwSetKeyCallback(w->id, default_key_callback);
}
void clear(vec3 rgb = {0.f, 0.f, 0.f}) {
	glClearColor(rgb.r, rgb.g, rgb.b, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

std::string read_entire_file(const std::string& path) {
	std::ifstream file(path);
	if(!file) {
		printf("failed to open file: %s\n", path.c_str());
		exit(-1);
	}
	std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
	return content;
}
u32 get_shader(const std::string& path, decltype(GL_VERTEX_SHADER) type) {
	u32 id = glCreateShader(type);
	std::string _shader_source = read_entire_file(path);
	const char* shader_source = _shader_source.c_str();
	glShaderSource(id, 1, &shader_source, NULL);
	glCompileShader(id);
	s32 succes;
	s8 info_log[512];
	glGetShaderiv(id, GL_COMPILE_STATUS, &succes);
	if(!succes) {
		glGetShaderInfoLog(id, 512, NULL, (GLchar*)info_log);
		printf("failed to compile shader (path: %s): %s\n", path.c_str(), info_log);
		exit(-1);
	}
	printf("shader compiled succesfully (path: %s)\n", path.c_str());
	return id;
}
u32 get_shader_program_VF(const std::string& path_vert, const std::string& path_frag) {
	u32 vertex_shader = get_shader(path_vert, GL_VERTEX_SHADER);
	u32 fragment_shader = get_shader(path_frag, GL_FRAGMENT_SHADER);
	u32 id = glCreateProgram();
	glAttachShader(id, vertex_shader);
	glAttachShader(id, fragment_shader);
	glLinkProgram(id);
	glDeleteShader(vertex_shader);
	glDeleteShader(fragment_shader);
	puts("shader linked succesfully");
	return id;
}

#define ADD_ATTRIB(id, struct_name, field_name)\
glVertexAttribPointer(id, sizeof(std::declval<struct_name>().field_name) / sizeof(float), GL_FLOAT, GL_FALSE, sizeof(struct_name), (void*)offsetof(struct_name, field_name));\
glEnableVertexAttribArray(id);

struct Point_UV { vec3 pos; vec2 uv; };
struct Mesh {
	u32 vbo, vao, ebo, points_count, indices_count;
};

Mesh make_mesh_uv(const float *points, const u32 *indices, u32 points_count, u32 indices_count) {
	Mesh mesh;
	mesh.points_count = points_count, mesh.indices_count = indices_count;

	//prepare
	glGenVertexArrays(1, &(mesh.vao));
	glGenBuffers(1, &(mesh.vbo));
	glBindVertexArray(mesh.vao);
	glBindBuffer(GL_ARRAY_BUFFER, mesh.vbo);

	glGenBuffers(1, &(mesh.ebo));
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ebo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_count * sizeof(u32), indices, GL_STATIC_DRAW);


	//link
	glBufferData(GL_ARRAY_BUFFER, points_count * sizeof(Point_UV), points, GL_STATIC_DRAW);
	ADD_ATTRIB(0, Point_UV, pos);
	ADD_ATTRIB(1, Point_UV, uv);

	//unbind
	glBindVertexArray(0);
	return mesh;
}
void clear(Mesh mesh) {
	glDeleteVertexArrays(1, &(mesh.vao));
	glDeleteBuffers(1, &(mesh.vbo));
	glDeleteBuffers(1, &(mesh.ebo));
}

int main() {
	GLFW glfw;

	Point_UV quad_points[] = {
		{{-1, -1,  0},  {0, 0}},
		{{ 1, -1,  0},  {1, 0}},
		{{ 1,  1,  0},  {1, 1}},
		{{-1,  1,  0},  {0, 1}},
	};
	u32 quad_ids[] = {0, 1, 2, 0, 2, 3};

	// glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
	// GLFWwindow* offscreen_context = glfwCreateWindow(1200, 800, "", NULL, NULL);
	// glfwMakeContextCurrent(offscreen_context);

	Mesh quad_mesh;
	u32 quad_shader;

	{
		glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
		Window win_0(1200, 800, nullptr);

		quad_mesh = make_mesh_uv((float*)quad_points, quad_ids, 4, 6);
		quad_shader = get_shader_program_VF("./default.vert", "./uvmap.frag");

		set_default_key_callback(&win_0);

		while(!glfwWindowShouldClose(win_0.id)) {
			glfwPollEvents();
			clear();

			glUseProgram(quad_shader);

			glBindVertexArray(quad_mesh.vao);
			glDrawElements(GL_TRIANGLES, quad_mesh.indices_count, GL_UNSIGNED_INT, 0);
			glBindVertexArray(0);

			glfwSwapBuffers(win_0.id);
		}
		// glDeleteProgram(quad_shader);
		// clear(quad_mesh);
	}

	{
		Window win_1(1200, 800, nullptr);

		set_default_key_callback(&win_1);

		while(!glfwWindowShouldClose(win_1.id)) {
			glfwPollEvents();
			clear();

			glUseProgram(quad_shader);

			glBindVertexArray(quad_mesh.vao);
			glDrawElements(GL_TRIANGLES, quad_mesh.indices_count, GL_UNSIGNED_INT, 0);
			glBindVertexArray(0);

			glfwSwapBuffers(win_1.id);
		}

	}

	glDeleteProgram(quad_shader);
	clear(quad_mesh);

	return 0;
}


код шейдеров

//default.vert
#version 330 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec2 a_uv;
out vec2 uv;
void main() {
	gl_Position = vec4(a_pos, 1);
	uv = a_uv;
}
//uvmap.frag
#version 430 core

in vec2 uv;
out vec4 o_color;

void main() {
	o_color = vec4(uv, 1, 1);
}

  • Вопрос задан
  • 141 просмотр
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Для начала стоит разобраться в том, написан ли код в вопросе на OpenGL - нет, не написан.
Код в вопросе использует обертку GLFW, которая довольно сильно абстрагирует пользователя от самого по себе OpenGL. Отсюда у тебя и непонимание самых основ работы с OpenGL, а ведь самым основным в этой работе является управление контекстом устройства.
Коротко описать механизмы работы с контекстом не получится, в трех словах половину документации API не пересказать. Но есть источники, которые обязательно нужно изучить: [1], [2], [3], [4].

Как, собственно, решить твою проблему с потерей ресурсов после удаления окна. По мере изучения приведенных мной ссылок можно заметить за контекстом некоторые особенности. Контекст привязывается к конкретному потоку исполнения, вызовы OpenGL проваливаются если к потоку где они вызваны не привязан контекст. Еще контекст обладает своей таблицей ресурсов. Все эти модели/текстуры/шейдеры локальны только для того контекста, который был привязан к потоку во время их создания. И еще - что контексты могут совместно владеть таблицей ресурсов, имея тем самым возможность работать с одними и теми же ресурсами.

Эта информация и является решающей в твоем случае. Конструктор твоего класса Window не даром имеет третий параметр - reference. Ты не мог ввести его специально и не знать о том, что я сейчас напишу.
Если обратиться к документации на glfwCreateWindow[?], то можно узнать что пятым параметром функции заявлено The window whose context to share resources with, т.е. окно, с контекстом которого следует разделить таблицу ресурсов.

Решением для тебя будет создание главного окна, с которым первое и второе окна будут разделять таблицу ресурсов. Главное окно не нужно активировать, оно должно просто присутствовать чтобы выступать основным держателем таблицы ресурсов. Указатель на главное окно нужно передать третьим параметром в оба отображаемых окна. Больше ничего менять не надо, все заработает сразу.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы