@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);
}

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

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

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

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

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

Войти через центр авторизации
Похожие вопросы
15 июл. 2024, в 14:08
5000 руб./за проект
15 июл. 2024, в 13:59
10000 руб./за проект
15 июл. 2024, в 13:58
150000 руб./за проект