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