gles30: alternative webgl framebuffer_copy impl to work around chrome bug

This commit is contained in:
Andrei Alexeyev 2023-06-18 14:56:00 +02:00
parent 153d809015
commit c74ac7874a
No known key found for this signature in database
GPG key ID: 72D26128040B9690
4 changed files with 260 additions and 1 deletions

View file

@ -0,0 +1,205 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "fbcopy_fallback.h"
#include "gles30.h"
#include "../glescommon/gles.h"
#include "../gl33/gl33.h"
#include "resource/model.h"
static ShaderProgram *blit_shader;
static struct {
Uniform *outputs_enabled;
Uniform *depth_enabled;
Uniform *input_depth;
Uniform *input_colors[FRAMEBUFFER_MAX_COLOR_ATTACHMENTS];
} blit_uniforms;
void gles30_fbcopyfallback_init(void) {
StringBuffer sbuf = {};
strbuf_printf(&sbuf,
"#version 300 es\n"
"precision highp float;\n"
"precision highp int;\n"
"uniform int outputs_enabled[%i];\n"
"uniform int depth_enabled;\n"
"uniform sampler2D input_depth;\n"
"layout(location = 0) out vec4 outputs[%i];\n",
FRAMEBUFFER_MAX_COLOR_ATTACHMENTS, FRAMEBUFFER_MAX_COLOR_ATTACHMENTS
);
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
strbuf_printf(&sbuf, "uniform sampler2D input_color%i;\n", i);
}
strbuf_cat(&sbuf,
"void main(void) {\n"
" ivec2 tc = ivec2(gl_FragCoord.xy);\n"
);
// FIXME: do something about mipmaps?
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
strbuf_printf(&sbuf,
" if(outputs_enabled[%i] != 0) "
"outputs[%i] = texelFetch(input_color%i, tc, 0);\n", i, i, i);
}
strbuf_cat(&sbuf,
" if(depth_enabled != 0) "
"gl_FragDepth = texelFetch(input_depth, tc, 0).r;\n"
"}\n");
ShaderSource frag_src = {
.content = sbuf.start,
.content_size = sbuf.pos - sbuf.start,
.lang.lang = SHLANG_GLSL,
.lang.glsl.version = { 300, GLSL_PROFILE_ES },
.stage = SHADER_STAGE_FRAGMENT,
};
log_info("\n%s\n ", frag_src.content);
ShaderObject *frag_shobj = r_shader_object_compile(&frag_src);
strbuf_free(&sbuf);
if(!frag_shobj) {
log_fatal("Failed to compile internal blit fragment shader");
}
static const char vert_src_str[] =
"#version 300 es\n"
"uniform vec4 r_viewport;\n"
"vec2 verts[4] = vec2[](\n"
" vec2( 1.0, -1.0),\n"
" vec2( 1.0, 1.0),\n"
" vec2(-1.0, -1.0),\n"
" vec2(-1.0, 1.0)\n"
");\n"
"void main(void) {\n"
" gl_Position = vec4(verts[gl_VertexID], 0, 1);\n"
"}\n";
ShaderSource vert_src = {
.content = (char*)vert_src_str,
.content_size = sizeof(vert_src_str),
.lang.lang = SHLANG_GLSL,
.lang.glsl.version = { 300, GLSL_PROFILE_ES },
.stage = SHADER_STAGE_VERTEX,
};
ShaderObject *vert_shobj = r_shader_object_compile(&vert_src);
strbuf_free(&sbuf);
if(!vert_shobj) {
log_fatal("Failed to compile internal blit vertex shader");
}
if(!(blit_shader = r_shader_program_link(2, (ShaderObject*[]) { vert_shobj, frag_shobj }))) {
log_fatal("Failed to link internal blit shader");
}
r_shader_object_destroy(vert_shobj);
r_shader_object_destroy(frag_shobj);
blit_uniforms.outputs_enabled = NOT_NULL(r_shader_uniform(blit_shader, "outputs_enabled[0]"));
blit_uniforms.depth_enabled = NOT_NULL(r_shader_uniform(blit_shader, "depth_enabled"));
blit_uniforms.input_depth = NOT_NULL(r_shader_uniform(blit_shader, "input_depth"));
for(int i = 0; i < ARRAY_SIZE(blit_uniforms.input_colors); ++i) {
char tmp[64];
snprintf(tmp, sizeof(tmp), "input_color%i", i);
blit_uniforms.input_colors[i] = NOT_NULL(r_shader_uniform(blit_shader, tmp));
}
}
void gles30_fbcopyfallback_shutdown(void) {
r_shader_program_destroy(blit_shader);
}
void gles30_fbcopyfallback_framebuffer_copy(Framebuffer *dst, Framebuffer *src, BufferKindFlags flags) {
int u_outputs_enabled[FRAMEBUFFER_MAX_COLOR_ATTACHMENTS] = {};
Texture *u_inputs[FRAMEBUFFER_MAX_COLOR_ATTACHMENTS] = {};
Texture *u_input_depth = NULL;
bool any_enabled = false;
if(flags & BUFFER_DEPTH) {
Texture *depth_src = r_framebuffer_get_attachment(src, FRAMEBUFFER_ATTACH_DEPTH);
if(depth_src && r_framebuffer_get_attachment(dst, FRAMEBUFFER_ATTACH_DEPTH)) {
u_input_depth = depth_src;
any_enabled = true;
}
}
if(flags & BUFFER_COLOR) {
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
FramebufferAttachment a = FRAMEBUFFER_ATTACH_COLOR0 + i;
Texture *csrc = r_framebuffer_get_attachment(src, a);
if(csrc && r_framebuffer_get_attachment(dst, a)) {
u_inputs[i] = csrc;
u_outputs_enabled[i] = 1;
any_enabled = true;
}
}
}
if(!any_enabled) {
return;
}
FramebufferAttachment outputmap_saved[FRAMEBUFFER_MAX_OUTPUTS];
r_framebuffer_get_output_attachments(dst, outputmap_saved);
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
if(u_outputs_enabled[i]) {
r_framebuffer_set_output_attachment(dst, i, FRAMEBUFFER_ATTACH_COLOR0 + i);
} else {
r_framebuffer_set_output_attachment(dst, i, FRAMEBUFFER_ATTACH_NONE);
}
}
FloatRect viewport_saved;
r_framebuffer_viewport_current(dst, &viewport_saved);
IntExtent fbsize = r_framebuffer_get_size(dst);
r_framebuffer_viewport(dst, 0, 0, fbsize.w, fbsize.h);
r_state_push();
r_blend(BLEND_NONE);
r_scissor(0, 0, fbsize.w, fbsize.h);
r_disable(RCAP_CULL_FACE);
if(u_input_depth) {
r_enable(RCAP_DEPTH_TEST);
r_enable(RCAP_DEPTH_WRITE);
r_depth_func(DEPTH_ALWAYS);
} else {
r_disable(RCAP_DEPTH_TEST);
}
r_framebuffer(dst);
r_shader_ptr(blit_shader);
r_uniform_int_array(blit_uniforms.outputs_enabled, 0, ARRAY_SIZE(u_outputs_enabled), u_outputs_enabled);
r_uniform_int(blit_uniforms.depth_enabled, u_input_depth != NULL);
r_uniform_sampler(blit_uniforms.input_depth, u_input_depth);
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
r_uniform_sampler(blit_uniforms.input_colors[i], u_inputs[i]);
}
// FIXME draw without changing VAO somehow
r_draw(r_model_get_quad()->vertex_array, PRIM_TRIANGLE_STRIP, 0, 4, 0, 0);
r_state_pop();
r_framebuffer_set_output_attachments(dst, outputmap_saved);
r_framebuffer_viewport_rect(dst, viewport_saved);
}

View file

@ -0,0 +1,16 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
#include "renderer/api.h"
void gles30_fbcopyfallback_init(void);
void gles30_fbcopyfallback_shutdown(void);
void gles30_fbcopyfallback_framebuffer_copy(Framebuffer *dst, Framebuffer *src, BufferKindFlags flags);

View file

@ -10,21 +10,56 @@
#include "gles30.h"
#include "../glescommon/gles.h"
#include "../gl33/gl33.h"
#include "fbcopy_fallback.h"
// NOTE: Actually WebGL
#ifdef STATIC_GLES3
/*
* For reasons I don't even want to begin to imagine, using glBlitFramebuffer seems
* to trigger a Lovecraftian unholy abomination of a bug in Chromium that makes
* Taisei unplayable past the first stage. So we fake it with a draw command instead.
*/
#define BROKEN_GL_BLIT_FRAMEBUFFER 1
#else
#define BROKEN_GL_BLIT_FRAMEBUFFER 0
#endif
static void gles30_init(void) {
gles_init(&_r_backend_gles30, 3, 0);
}
#if BROKEN_GL_BLIT_FRAMEBUFFER
static void gles30_init_context(SDL_Window *w) {
gles_init_context(w);
gles30_fbcopyfallback_init();
}
static void gles30_shutdown(void) {
gles30_fbcopyfallback_shutdown();
_r_backend_gl33.funcs.shutdown();
}
#endif
RendererBackend _r_backend_gles30 = {
.name = "gles30",
.funcs = {
.init = gles30_init,
.texture_dump = gles_texture_dump,
.screenshot = gles_screenshot,
#if BROKEN_GL_BLIT_FRAMEBUFFER
.shutdown = gles30_shutdown,
.framebuffer_copy = gles30_fbcopyfallback_framebuffer_copy,
#endif
},
.custom = &(GLBackendData) {
.vtable = {
#if BROKEN_GL_BLIT_FRAMEBUFFER
.init_context = gles30_init_context,
#else
.init_context = gles_init_context,
#endif
}
},
};

View file

@ -3,8 +3,11 @@ r_gles30_src = files(
'gles30.c',
)
if static_gles30
r_gles30_src += files('fbcopy_fallback.c')
endif
r_gles30_deps = ['glescommon'] + r_glescommon_deps
r_gles30_libdeps = r_glescommon_libdeps
config.set('TAISEI_BUILDCONF_RENDERER_STATIC_GLES30', static_gles30)