| @@ -4,7 +4,6 @@ | ||||
| #include "vec.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <ctype.h> | ||||
| #include <math.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| @@ -26,6 +25,301 @@ static inline SphericalCoord get_forward_spherical_with_nearest( | ||||
| 	return Vector3ToSpherical(vec); | ||||
| } | ||||
|  | ||||
| struct ExternalTexturePipeline { | ||||
| 	bool attempted_init; | ||||
| 	bool ready; | ||||
| 	GLuint program; | ||||
| 	GLint sampler_loc; | ||||
| 	GLuint vao; | ||||
| 	GLuint vbo; | ||||
| }; | ||||
|  | ||||
| static struct ExternalTexturePipeline g_external_pipeline = { 0 }; | ||||
|  | ||||
| static GLuint compile_shader(GLenum type, char const *source) | ||||
| { | ||||
| 	GLuint shader = glCreateShader(type); | ||||
| 	if (shader == 0) { | ||||
| 		TraceLog( | ||||
| 		    LOG_ERROR, "Failed to create shader object for external texture"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	glShaderSource(shader, 1, &source, NULL); | ||||
| 	glCompileShader(shader); | ||||
| 	GLint status = GL_FALSE; | ||||
| 	glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | ||||
| 	if (status != GL_TRUE) { | ||||
| 		GLint log_len = 0; | ||||
| 		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); | ||||
| 		if (log_len > 1) { | ||||
| 			char *log = malloc((size_t)log_len); | ||||
| 			if (log) { | ||||
| 				glGetShaderInfoLog(shader, log_len, NULL, log); | ||||
| 				TraceLog(LOG_ERROR, | ||||
| 				    "External texture shader compile failed: %s", log); | ||||
| 				free(log); | ||||
| 			} | ||||
| 		} | ||||
| 		glDeleteShader(shader); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return shader; | ||||
| } | ||||
|  | ||||
| static bool ensure_external_pipeline(void) | ||||
| { | ||||
| 	if (g_external_pipeline.attempted_init) | ||||
| 		return g_external_pipeline.ready; | ||||
|  | ||||
| 	g_external_pipeline.attempted_init = true; | ||||
|  | ||||
| 	static char const *vertex_src_300 | ||||
| 	    = "#version 300 es\n" | ||||
| 	      "precision highp float;\n" | ||||
| 	      "layout(location = 0) in vec2 a_pos;\n" | ||||
| 	      "layout(location = 1) in vec2 a_uv;\n" | ||||
| 	      "out vec2 v_uv;\n" | ||||
| 	      "void main() {\n" | ||||
| 	      "    v_uv = a_uv;\n" | ||||
| 	      "    gl_Position = vec4(a_pos, 0.0, 1.0);\n" | ||||
| 	      "}\n"; | ||||
|  | ||||
| 	static char const *fragment_src_300 | ||||
| 	    = "#version 300 es\n" | ||||
| 	      "#extension GL_OES_EGL_image_external_essl3 : require\n" | ||||
| 	      "precision mediump float;\n" | ||||
| 	      "in vec2 v_uv;\n" | ||||
| 	      "layout(location = 0) out vec4 fragColor;\n" | ||||
| 	      "uniform samplerExternalOES u_texture;\n" | ||||
| 	      "void main() {\n" | ||||
| 	      "    fragColor = texture(u_texture, v_uv);\n" | ||||
| 	      "}\n"; | ||||
|  | ||||
| 	static char const *vertex_src_100 | ||||
| 	    = "#version 100\n" | ||||
| 	      "attribute vec2 a_pos;\n" | ||||
| 	      "attribute vec2 a_uv;\n" | ||||
| 	      "varying vec2 v_uv;\n" | ||||
| 	      "void main() {\n" | ||||
| 	      "    v_uv = a_uv;\n" | ||||
| 	      "    gl_Position = vec4(a_pos, 0.0, 1.0);\n" | ||||
| 	      "}\n"; | ||||
|  | ||||
| 	static char const *fragment_src_100 | ||||
| 	    = "#version 100\n" | ||||
| 	      "#extension GL_OES_EGL_image_external : require\n" | ||||
| 	      "precision mediump float;\n" | ||||
| 	      "varying vec2 v_uv;\n" | ||||
| 	      "uniform samplerExternalOES u_texture;\n" | ||||
| 	      "void main() {\n" | ||||
| 	      "    gl_FragColor = texture2D(u_texture, v_uv);\n" | ||||
| 	      "}\n"; | ||||
|  | ||||
| 	struct { | ||||
| 		char const *vs; | ||||
| 		char const *fs; | ||||
| 	} const variants[] = { | ||||
| 		{ vertex_src_300, fragment_src_300 }, | ||||
| 		{ vertex_src_100, fragment_src_100 }, | ||||
| 	}; | ||||
|  | ||||
| 	GLuint program = 0; | ||||
| 	GLint sampler_loc = -1; | ||||
| 	size_t chosen_idx = (size_t)-1; | ||||
| 	for (size_t i = 0; i < ARRAY_SZ(variants); ++i) { | ||||
| 		GLuint vert = compile_shader(GL_VERTEX_SHADER, variants[i].vs); | ||||
| 		if (vert == 0) | ||||
| 			continue; | ||||
| 		GLuint frag = compile_shader(GL_FRAGMENT_SHADER, variants[i].fs); | ||||
| 		if (frag == 0) { | ||||
| 			glDeleteShader(vert); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		GLuint prog = glCreateProgram(); | ||||
| 		if (prog == 0) { | ||||
| 			TraceLog(LOG_ERROR, | ||||
| 			    "Failed to create shader program for external texture"); | ||||
| 			glDeleteShader(vert); | ||||
| 			glDeleteShader(frag); | ||||
| 			continue; | ||||
| 		} | ||||
| 		glAttachShader(prog, vert); | ||||
| 		glAttachShader(prog, frag); | ||||
| 		glBindAttribLocation(prog, 0, "a_pos"); | ||||
| 		glBindAttribLocation(prog, 1, "a_uv"); | ||||
| 		glLinkProgram(prog); | ||||
| 		GLint link_status = GL_FALSE; | ||||
| 		glGetProgramiv(prog, GL_LINK_STATUS, &link_status); | ||||
| 		if (link_status != GL_TRUE) { | ||||
| 			GLint log_len = 0; | ||||
| 			glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); | ||||
| 			if (log_len > 1) { | ||||
| 				char *log = malloc((size_t)log_len); | ||||
| 				if (log) { | ||||
| 					glGetProgramInfoLog(prog, log_len, NULL, log); | ||||
| 					TraceLog(LOG_ERROR, | ||||
| 					    "External texture program link failed: %s", log); | ||||
| 					free(log); | ||||
| 				} | ||||
| 			} | ||||
| 			glDeleteShader(vert); | ||||
| 			glDeleteShader(frag); | ||||
| 			glDeleteProgram(prog); | ||||
| 			continue; | ||||
| 		} | ||||
| 		glDetachShader(prog, vert); | ||||
| 		glDetachShader(prog, frag); | ||||
| 		glDeleteShader(vert); | ||||
| 		glDeleteShader(frag); | ||||
|  | ||||
| 		sampler_loc = glGetUniformLocation(prog, "u_texture"); | ||||
| 		if (sampler_loc < 0) { | ||||
| 			TraceLog(LOG_ERROR, | ||||
| 			    "External texture program missing u_texture uniform"); | ||||
| 			glDeleteProgram(prog); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		program = prog; | ||||
| 		chosen_idx = i; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if (program == 0) | ||||
| 		return false; | ||||
|  | ||||
| 	if (chosen_idx == 1) { | ||||
| 		TraceLog( | ||||
| 		    LOG_DEBUG, "External texture shader fallback to ESSL 100 variant"); | ||||
| 	} | ||||
|  | ||||
| 	GLuint vao = 0; | ||||
| 	GLuint vbo = 0; | ||||
| 	glGenVertexArrays(1, &vao); | ||||
| 	glGenBuffers(1, &vbo); | ||||
| 	if (vao == 0 || vbo == 0) { | ||||
| 		TraceLog(LOG_ERROR, "Failed to allocate buffers for external texture"); | ||||
| 		if (vao) | ||||
| 			glDeleteVertexArrays(1, &vao); | ||||
| 		if (vbo) | ||||
| 			glDeleteBuffers(1, &vbo); | ||||
| 		glDeleteProgram(program); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	GLint prev_vao = 0; | ||||
| 	glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prev_vao); | ||||
| 	GLint prev_array_buffer = 0; | ||||
| 	glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev_array_buffer); | ||||
| 	glBindVertexArray(vao); | ||||
| 	glBindBuffer(GL_ARRAY_BUFFER, vbo); | ||||
| 	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, NULL, GL_DYNAMIC_DRAW); | ||||
| 	glEnableVertexAttribArray(0); | ||||
| 	glVertexAttribPointer( | ||||
| 	    0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)0); | ||||
| 	glEnableVertexAttribArray(1); | ||||
| 	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, | ||||
| 	    (void *)(sizeof(float) * 2)); | ||||
| 	glBindBuffer(GL_ARRAY_BUFFER, (GLuint)prev_array_buffer); | ||||
| 	glBindVertexArray((GLuint)prev_vao); | ||||
|  | ||||
| 	g_external_pipeline.program = program; | ||||
| 	g_external_pipeline.sampler_loc = sampler_loc; | ||||
| 	g_external_pipeline.vao = vao; | ||||
| 	g_external_pipeline.vbo = vbo; | ||||
| 	g_external_pipeline.ready = true; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static bool draw_external_texture( | ||||
|     struct wlr_gles2_texture_attribs const *attribs, int tex_width, | ||||
|     int tex_height, Rectangle src, Rectangle dst, int target_width, | ||||
|     int target_height) | ||||
| { | ||||
| 	if (!attribs || attribs->target != GL_TEXTURE_EXTERNAL_OES) | ||||
| 		return false; | ||||
| 	if (tex_width <= 0 || tex_height <= 0 || target_width <= 0 | ||||
| 	    || target_height <= 0) | ||||
| 		return false; | ||||
| 	if (!ensure_external_pipeline()) | ||||
| 		return false; | ||||
|  | ||||
| 	rlDrawRenderBatchActive(); | ||||
|  | ||||
| 	float x0 = dst.x; | ||||
| 	float y0 = dst.y; | ||||
| 	float x1 = dst.x + dst.width; | ||||
| 	float y1 = dst.y + dst.height; | ||||
|  | ||||
| 	float ndc_x0 = (x0 / (float)target_width) * 2.0f - 1.0f; | ||||
| 	float ndc_x1 = (x1 / (float)target_width) * 2.0f - 1.0f; | ||||
| 	float ndc_y0 = 1.0f - (y0 / (float)target_height) * 2.0f; | ||||
| 	float ndc_y1 = 1.0f - (y1 / (float)target_height) * 2.0f; | ||||
|  | ||||
| 	float inv_tex_w = 1.0f / (float)tex_width; | ||||
| 	float inv_tex_h = 1.0f / (float)tex_height; | ||||
| 	float u0 = src.x * inv_tex_w; | ||||
| 	float v0 = src.y * inv_tex_h; | ||||
| 	float u1 = (src.x + src.width) * inv_tex_w; | ||||
| 	float v1 = (src.y + src.height) * inv_tex_h; | ||||
|  | ||||
| 	GLfloat vertices[] = { | ||||
| 		ndc_x0, | ||||
| 		ndc_y0, | ||||
| 		u0, | ||||
| 		v0, | ||||
| 		ndc_x1, | ||||
| 		ndc_y0, | ||||
| 		u1, | ||||
| 		v0, | ||||
| 		ndc_x0, | ||||
| 		ndc_y1, | ||||
| 		u0, | ||||
| 		v1, | ||||
| 		ndc_x1, | ||||
| 		ndc_y1, | ||||
| 		u1, | ||||
| 		v1, | ||||
| 	}; | ||||
|  | ||||
| 	GLint prev_program = 0; | ||||
| 	glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program); | ||||
|  | ||||
| 	GLint prev_active_texture = 0; | ||||
| 	glGetIntegerv(GL_ACTIVE_TEXTURE, &prev_active_texture); | ||||
| 	glActiveTexture(GL_TEXTURE0); | ||||
| 	GLint prev_tex0_external = 0; | ||||
| 	glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &prev_tex0_external); | ||||
| 	GLint prev_tex0_2d = 0; | ||||
| 	glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex0_2d); | ||||
|  | ||||
| 	GLint prev_array_buffer = 0; | ||||
| 	glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev_array_buffer); | ||||
| 	GLint prev_element_array_buffer = 0; | ||||
| 	glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prev_element_array_buffer); | ||||
| 	GLint prev_vertex_array = 0; | ||||
| 	glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prev_vertex_array); | ||||
|  | ||||
| 	glUseProgram(g_external_pipeline.program); | ||||
| 	glUniform1i(g_external_pipeline.sampler_loc, 0); | ||||
| 	glBindTexture(GL_TEXTURE_EXTERNAL_OES, attribs->tex); | ||||
| 	glBindVertexArray(g_external_pipeline.vao); | ||||
| 	glBindBuffer(GL_ARRAY_BUFFER, g_external_pipeline.vbo); | ||||
| 	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); | ||||
| 	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
|  | ||||
| 	glBindBuffer(GL_ARRAY_BUFFER, (GLuint)prev_array_buffer); | ||||
| 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)prev_element_array_buffer); | ||||
| 	glBindVertexArray((GLuint)prev_vertex_array); | ||||
| 	glBindTexture(GL_TEXTURE_EXTERNAL_OES, (GLuint)prev_tex0_external); | ||||
| 	glBindTexture(GL_TEXTURE_2D, (GLuint)prev_tex0_2d); | ||||
| 	glUseProgram((GLuint)prev_program); | ||||
| 	glActiveTexture((GLenum)prev_active_texture); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void LunarWM_Toplevel_release_surface_rt(LunarWM_Toplevel *tl) | ||||
| { | ||||
| 	if (!tl) | ||||
| @@ -220,6 +514,36 @@ struct SurfaceComposeCtx { | ||||
| 	bool *has_alpha; | ||||
| }; | ||||
|  | ||||
| char const *GLInternalFormatName(GLenum format) | ||||
| { | ||||
| 	switch (format) { | ||||
| 	case GL_RGBA8: | ||||
| 		return "GL_RGBA8"; | ||||
| 	case GL_RGB8: | ||||
| 		return "GL_RGB8"; | ||||
| 	case GL_RGB565: | ||||
| 		return "GL_RGB565"; | ||||
| 	case GL_RGBA4: | ||||
| 		return "GL_RGBA4"; | ||||
| 	case GL_RGB5_A1: | ||||
| 		return "GL_RGB5_A1"; | ||||
| 	case GL_DEPTH_COMPONENT16: | ||||
| 		return "GL_DEPTH_COMPONENT16"; | ||||
| 	case GL_DEPTH_COMPONENT24: | ||||
| 		return "GL_DEPTH_COMPONENT24"; | ||||
| 	case GL_DEPTH24_STENCIL8: | ||||
| 		return "GL_DEPTH24_STENCIL8"; | ||||
| 	case GL_DEPTH32F_STENCIL8: | ||||
| 		return "GL_DEPTH32F_STENCIL8"; | ||||
| 	case GL_R8: | ||||
| 		return "GL_R8"; | ||||
| 	case GL_RG8: | ||||
| 		return "GL_RG8"; | ||||
| 	default: | ||||
| 		return "Unknown format"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void surface_compose_draw( | ||||
|     struct wlr_surface *surface, int sx, int sy, void *user_data) | ||||
| { | ||||
| @@ -244,9 +568,8 @@ static void surface_compose_draw( | ||||
| 		.width = (int)wlr_tex->width, | ||||
| 		.height = (int)wlr_tex->height, | ||||
| 		.mipmaps = 1, | ||||
| 		.format = gles_tex->has_alpha | ||||
| 		    ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 | ||||
| 		    : PIXELFORMAT_UNCOMPRESSED_R8G8B8, | ||||
| 		.format = gles_tex->has_alpha ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 | ||||
| 		                              : PIXELFORMAT_UNCOMPRESSED_R8G8B8, | ||||
| 	}; | ||||
|  | ||||
| 	float dest_w = (float)surface->current.width; | ||||
| @@ -264,7 +587,20 @@ static void surface_compose_draw( | ||||
| 		dest_h, | ||||
| 	}; | ||||
|  | ||||
| 	DrawTexturePro(tex, src, dst, (Vector2) { 0.0f, 0.0f }, 0.0f, WHITE); | ||||
| 	if (attribs.target == GL_TEXTURE_EXTERNAL_OES) { | ||||
| 		static bool external_draw_warned = false; | ||||
| 		if (!draw_external_texture(&attribs, tex.width, tex.height, src, dst, | ||||
| 		        ctx->extents.width, ctx->extents.height)) { | ||||
| 			if (!external_draw_warned) { | ||||
| 				TraceLog(LOG_WARNING, | ||||
| 				    "Failed to draw external texture, skipping frame"); | ||||
| 				external_draw_warned = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		assert(attribs.target == GL_TEXTURE_2D); | ||||
| 		DrawTexturePro(tex, src, dst, (Vector2) { 0.0f, 0.0f }, 0.0f, WHITE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool LunarWM_Toplevel_update(LunarWM_Toplevel *this) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user