Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-09-29 23:28:32 +03:00
parent 22c005999c
commit 72a6ac3d00

View File

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