From 5e84ef0e13b1740d9da0ef35d9e0ae7bf2d8bba3 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Mon, 19 Oct 2026 22:22:55 +0200 Subject: [PATCH] OpenGL ES 3.0 for phones without Vulkan; the old-GPU renderer draws the grid; site fonts - Android: Vulkan 1.3 if the phone has it, else OpenGL ES 3.0 (the fallback renderer, which now also runs on ES); the APK no longer needs Vulkan to install - The fallback renderer draws sprites and streaks: the grid and axes - The website ships its fonts (Inter, JetBrains Mono): no more falling back to whatever the visitor has - Android: link the log library (GitHub's build) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NMSrd5oe1GpUD7E6d825KP --- android/AndroidManifest.xml | 4 +- docs/content.js | 8 +- docs/fonts/OFL-Inter.txt | 92 ++++++++++ docs/fonts/OFL-JetBrainsMono.txt | 93 ++++++++++ docs/fonts/inter-latin-wght-normal.woff2 | Bin 0 -> 48444 bytes .../jetbrains-mono-latin-wght-normal.woff2 | Bin 0 -> 40404 bytes docs/index.html | 2 +- docs/style.css | 11 +- engine/CMakeLists.txt | 3 + engine/engine.cpp | 21 ++- engine/render/backend.h | 6 +- engine/render/renderer.cpp | 1 + engine/rhi/gl/gl_legacy.cpp | 160 ++++++++++++++---- 13 files changed, 348 insertions(+), 53 deletions(-) create mode 100644 docs/fonts/OFL-Inter.txt create mode 100644 docs/fonts/OFL-JetBrainsMono.txt create mode 100644 docs/fonts/inter-latin-wght-normal.woff2 create mode 100644 docs/fonts/jetbrains-mono-latin-wght-normal.woff2 diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 8d6b5b5..38ab730 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,7 +3,9 @@ - + + + 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 N2D$e+|L^@6X8`S>M5F)! literal 0 HcmV?d00001 diff --git a/docs/fonts/jetbrains-mono-latin-wght-normal.woff2 b/docs/fonts/jetbrains-mono-latin-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cd5102a44c763f1cfe9f2b199fd5d7257491c0dd GIT binary patch literal 40404 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

Fry3DX, made by .

-

Open source under the Apache License 2.0 ยท Sky pictures from Poly Haven (CC0).

+

Open source under the Apache License 2.0 ยท Sky pictures from Poly Haven (CC0) ยท Fonts: Inter, JetBrains Mono (OFL).

diff --git a/docs/style.css b/docs/style.css index 89e83dc..b95f16a 100644 --- a/docs/style.css +++ b/docs/style.css @@ -1,3 +1,6 @@ +/* The site's own fonts (SIL Open Font License, fonts/OFL-*.txt), so it looks the same everywhere. */ +@font-face { font-family: "Inter"; src: url("fonts/inter-latin-wght-normal.woff2") format("woff2"); font-weight: 100 900; font-display: swap; } +@font-face { font-family: "JetBrains Mono"; src: url("fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2"); font-weight: 100 800; font-display: swap; } :root { --bg: #161618; --panel: #1f1f23; @@ -16,7 +19,7 @@ body { margin: 0; background: var(--bg); color: var(--ink); - font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + font: 16px/1.6 "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; } a { color: var(--hot); text-decoration: none; } a:hover { text-decoration: underline; } @@ -80,15 +83,15 @@ section:last-child { border-bottom: 0; } .keys { border-collapse: collapse; width: 100%; } .keys td { padding: 7px 0; border-bottom: 1px solid var(--line); } .keys td:first-child { width: 45%; } -kbd { font: .85rem ui-monospace, "DejaVu Sans Mono", monospace; background: var(--panel2); border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 5px; padding: 2px 7px; } +kbd { font: .85rem "JetBrains Mono", ui-monospace, Consolas, monospace; background: var(--panel2); border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 5px; padding: 2px 7px; } .downloads { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin-bottom: 28px; } .download { display: flex; flex-direction: column; gap: 4px; padding: 18px 20px; border-radius: var(--radius); background: var(--panel); border: 1px solid var(--line); color: var(--ink); } .download:hover { text-decoration: none; border-color: var(--hot); } .download strong { font-size: 1.15rem; color: var(--hot); } -.download .file { font: .85rem ui-monospace, "DejaVu Sans Mono", monospace; color: #cfcdc7; } +.download .file { font: .85rem "JetBrains Mono", ui-monospace, Consolas, monospace; color: #cfcdc7; } .code { position: relative; background: #0f0f11; border: 1px solid var(--line); border-left: 3px solid var(--hot); border-radius: var(--radius); } -.code pre { margin: 0; padding: 18px 20px; overflow-x: auto; font: .92rem/1.7 ui-monospace, "DejaVu Sans Mono", monospace; color: #d6d4cd; } +.code pre { margin: 0; padding: 18px 20px; overflow-x: auto; font: .92rem/1.7 "JetBrains Mono", ui-monospace, Consolas, monospace; color: #d6d4cd; } .copy { position: absolute; top: 10px; right: 10px; font: inherit; font-size: .85rem; color: var(--ink); background: var(--panel2); border: 1px solid var(--line); border-radius: 6px; padding: 4px 12px; cursor: pointer; } .copy:hover { border-color: var(--hot); } diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 1bf5f9a..fb2eacd 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -16,6 +16,9 @@ add_custom_target(keel_version COMMAND ${CMAKE_COMMAND} -DSRC=${PROJECT_SOURCE_D add_dependencies(keel keel_version) target_include_directories(keel PRIVATE ${CMAKE_BINARY_DIR}/generated) target_link_libraries(keel PRIVATE stb miniaudio opusfile) +if(ANDROID) + target_link_libraries(keel PUBLIC log) # logcat +endif() target_link_libraries(keel PUBLIC zstd_seekable glm::glm yapf SDL3::SDL3 volk vk-bootstrap::vk-bootstrap GPUOpen::VulkanMemoryAllocator imgui) target_compile_definitions(keel PUBLIC GLM_FORCE_DEPTH_ZERO_TO_ONE GLM_ENABLE_EXPERIMENTAL VK_NO_PROTOTYPES) keel_add_shaders(keel shaders/box.vert shaders/box.frag shaders/mesh.vert shaders/mesh.frag shaders/depth.frag shaders/cull.comp shaders/froxel.comp shaders/froxel_rt.comp shaders/gi.comp shaders/skinned.vert diff --git a/engine/engine.cpp b/engine/engine.cpp index 779a7fb..86f4753 100644 --- a/engine/engine.cpp +++ b/engine/engine.cpp @@ -60,7 +60,7 @@ Cvar vid_mode("vid_mode", "0", CVAR_ARCHIVE, "0 = window, 1 = borderless fullscr Cvar vid_width("vid_width", "1600", CVAR_ARCHIVE, "Window / fullscreen width"); Cvar vid_height("vid_height", "900", CVAR_ARCHIVE, "Window / fullscreen height"); Cvar r_renderer("r_renderer", "vulkan", CVAR_ARCHIVE, - "Graphics API: vulkan, opengl (4.6), or the cut-down fallbacks for old GPUs: opengl40, opengl31 (restart needed)"); + "Graphics API: vulkan, opengl (4.6), or the cut-down fallbacks for old GPUs: opengl40, opengl31, gles (OpenGL ES 3.0) (restart needed)"); Cvar hotreload("hotreload", "1", CVAR_ARCHIVE, "Reload textures, models, sounds, scripts and the map when their files change (with tools/watch.py compiling as you save)"); Cvar fps_max_menu("fps_max_menu", "120", CVAR_ARCHIVE, "Frame rate limit in menus and while paused (0 = same as fps_max)"); @@ -283,12 +283,25 @@ int runClient(Game& game) { const int w = std::max(vid_width.i(), 640), h = std::max(vid_height.i(), 480); // The API asked for, then the others: the modern pair, then the fallbacks for old GPUs. const std::string want = r_renderer.str(); - const RenderApi first = want == "opengl" ? RenderApi::OpenGL : want == "opengl40" ? RenderApi::OpenGL40 : want == "opengl31" ? RenderApi::OpenGL31 : RenderApi::Vulkan; + // (A phone has only Vulkan and OpenGL ES.) + RenderApi first = want == "opengl" ? RenderApi::OpenGL : want == "opengl40" ? RenderApi::OpenGL40 : want == "opengl31" ? RenderApi::OpenGL31 + : want == "gles" ? RenderApi::GLES30 : RenderApi::Vulkan; +#if defined(__ANDROID__) + if (first != RenderApi::Vulkan) first = RenderApi::GLES30; + const std::initializer_list all{RenderApi::Vulkan, RenderApi::GLES30}; +#else + const std::initializer_list all{RenderApi::Vulkan, RenderApi::OpenGL, RenderApi::OpenGL40, RenderApi::OpenGL31, RenderApi::GLES30}; +#endif std::vector order{first}; - for (RenderApi a : {RenderApi::Vulkan, RenderApi::OpenGL, RenderApi::OpenGL40, RenderApi::OpenGL31}) - if (std::find(order.begin(), order.end(), a) == order.end() && (first != RenderApi::OpenGL31 || a == RenderApi::OpenGL31)) order.push_back(a); + for (RenderApi a : all) + if (std::find(order.begin(), order.end(), a) == order.end() && (first != RenderApi::OpenGL31 || a == RenderApi::OpenGL31 || a == RenderApi::GLES30)) order.push_back(a); bool ok = false; for (RenderApi api : order) { + if (api == RenderApi::GLES30) { // (before the window: a phone picks its surface's kind when it's made) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } ok = window.init(game.title(), w, h, api != RenderApi::Vulkan) && renderer.init(window, r_validation.b(), api); if (ok) { if (api != first) diff --git a/engine/render/backend.h b/engine/render/backend.h index 9bb76e0..6c2ade5 100644 --- a/engine/render/backend.h +++ b/engine/render/backend.h @@ -19,8 +19,8 @@ namespace keel { class Window; using TextureId = uint32_t; -// OpenGL40/OpenGL31: the cut-down fallback for old GPUs (rhi/gl/gl_legacy.cpp). -enum class RenderApi { Vulkan, OpenGL, OpenGL40, OpenGL31 }; +// OpenGL40/OpenGL31/GLES30: the cut-down fallback for old GPUs and phones (rhi/gl/gl_legacy.cpp). +enum class RenderApi { Vulkan, OpenGL, OpenGL40, OpenGL31, GLES30 }; // Texture slots. 0 and 1 are created by the renderer; 2-4 are the backend's render targets // (sampled through the same table); loaded textures start at kFirstTextureSlot. @@ -235,6 +235,6 @@ public: std::unique_ptr createVulkanBackend(); std::unique_ptr createOpenGLBackend(); -std::unique_ptr createLegacyOpenGLBackend(int major, int minor); +std::unique_ptr createLegacyOpenGLBackend(int major, int minor, bool es = false); } // namespace keel diff --git a/engine/render/renderer.cpp b/engine/render/renderer.cpp index 9d3816c..c93030d 100644 --- a/engine/render/renderer.cpp +++ b/engine/render/renderer.cpp @@ -114,6 +114,7 @@ bool Renderer::init(const Window& window, bool validation, RenderApi api) { case RenderApi::OpenGL: backend_ = createOpenGLBackend(); break; case RenderApi::OpenGL40: backend_ = createLegacyOpenGLBackend(4, 0); break; case RenderApi::OpenGL31: backend_ = createLegacyOpenGLBackend(3, 1); break; + case RenderApi::GLES30: backend_ = createLegacyOpenGLBackend(3, 0, true); break; default: backend_ = createVulkanBackend(); break; } if (!backend_->init(window, validation)) { diff --git a/engine/rhi/gl/gl_legacy.cpp b/engine/rhi/gl/gl_legacy.cpp index 90251b5..cd10eb5 100644 --- a/engine/rhi/gl/gl_legacy.cpp +++ b/engine/rhi/gl/gl_legacy.cpp @@ -1,4 +1,5 @@ -// The fallback renderer for old GPUs: OpenGL 4.0 or 3.1, GLSL 1.40, nothing newer โ€” bound +// The fallback renderer for old GPUs: OpenGL 4.0 or 3.1 (GLSL 1.40), or OpenGL ES 3.0 for phones +// without Vulkan (GLSL ES 3.00: no skinning, as it has no texture buffers), nothing newer โ€” bound // textures, vertex attributes, one uniform block for the frame, a texture buffer for skinning. // Two tiers, cut down from the main renderer: // 4.0 lightmaps, sun with cascaded shadows (PCF), HDR sky, fog, normal maps, GGX highlights, @@ -38,13 +39,11 @@ namespace { X(void, Viewport, (GLint, GLint, GLsizei, GLsizei)) \ X(void, Clear, (GLbitfield)) \ X(void, ClearColor, (GLfloat, GLfloat, GLfloat, GLfloat)) \ - X(void, ClearDepth, (GLdouble)) \ X(void, DepthFunc, (GLenum)) \ X(void, DepthMask, (GLboolean)) \ X(void, BlendFuncSeparate, (GLenum, GLenum, GLenum, GLenum)) \ X(void, PolygonOffset, (GLfloat, GLfloat)) \ X(void, PixelStorei, (GLenum, GLint)) \ - X(void, DrawBuffer, (GLenum)) \ X(void, ReadBuffer, (GLenum)) \ X(void, ReadPixels, (GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*)) \ X(void, GenBuffers, (GLsizei, GLuint*)) \ @@ -59,7 +58,6 @@ namespace { X(void, ActiveTexture, (GLenum)) \ X(void, TexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*)) \ X(void, TexParameteri, (GLenum, GLenum, GLint)) \ - X(void, TexBuffer, (GLenum, GLenum, GLuint)) \ X(void, GenFramebuffers, (GLsizei, GLuint*)) \ X(void, DeleteFramebuffers, (GLsizei, const GLuint*)) \ X(void, BindFramebuffer, (GLenum, GLuint)) \ @@ -84,7 +82,6 @@ namespace { X(GLuint, CreateProgram, (void)) \ X(void, AttachShader, (GLuint, GLuint)) \ X(void, BindAttribLocation, (GLuint, GLuint, const GLchar*)) \ - X(void, BindFragDataLocation, (GLuint, GLuint, const GLchar*)) \ X(void, LinkProgram, (GLuint)) \ X(void, GetProgramiv, (GLuint, GLenum, GLint*)) \ X(void, GetProgramInfoLog, (GLuint, GLsizei, GLsizei*, GLchar*)) \ @@ -96,10 +93,19 @@ namespace { X(void, Uniform1i, (GLint, GLint)) \ X(void, Uniform4fv, (GLint, GLsizei, const GLfloat*)) \ X(void, UniformMatrix4fv, (GLint, GLsizei, GLboolean, const GLfloat*)) +// Desktop GL only, or ES only: whichever there is. +#define KEEL_GLX_OPTIONAL(X) \ + X(void, ClearDepth, (GLdouble)) \ + X(void, ClearDepthf, (GLfloat)) \ + X(void, DrawBuffer, (GLenum)) \ + X(void, DrawBuffers, (GLsizei, const GLenum*)) \ + X(void, TexBuffer, (GLenum, GLenum, GLuint)) \ + X(void, BindFragDataLocation, (GLuint, GLuint, const GLchar*)) namespace glx { #define KEEL_GLX_DECLARE(ret, name, params) using name##_t = ret(APIENTRY*) params; name##_t name = nullptr; KEEL_GLX_FUNCTIONS(KEEL_GLX_DECLARE) +KEEL_GLX_OPTIONAL(KEEL_GLX_DECLARE) #undef KEEL_GLX_DECLARE bool load() { std::string missing; @@ -108,12 +114,15 @@ bool load() { if (!name) missing += " gl" #name; KEEL_GLX_FUNCTIONS(KEEL_GLX_LOAD) #undef KEEL_GLX_LOAD +#define KEEL_GLX_LOAD_OPTIONAL(ret, name, params) name = reinterpret_cast(SDL_GL_GetProcAddress("gl" #name)); + KEEL_GLX_OPTIONAL(KEEL_GLX_LOAD_OPTIONAL) +#undef KEEL_GLX_LOAD_OPTIONAL if (!missing.empty()) log::warn("OpenGL (legacy): missing functions:{}", missing); return missing.empty(); } } // namespace glx -// โ”€โ”€ Shaders (GLSL 1.40; TIER is 1 for GL 3.1, 2 for 4.0) โ”€โ”€ +// โ”€โ”€ Shaders (GLSL 1.40 or ES 3.00; TIER is 1 for GL 3.1, 2 for 4.0 and ES 3.0) โ”€โ”€ const char* kFrameBlock = R"( layout(std140) uniform Frame { // (GLSL 1.40: no instance name, so the members say F_) mat4 F_cascades[4]; @@ -126,12 +135,16 @@ const char* kMeshVert = R"( in vec3 a_pos; in vec3 a_normal; in vec2 a_uv; in vec2 a_lmuv; in uvec4 a_joints; in vec4 a_weights; uniform mat4 u_viewProj, u_model; uniform int u_skinned, u_firstJoint; -uniform samplerBuffer u_joints; out vec3 v_normal, v_world; out vec2 v_uv, v_lmuv; +#ifdef GL_ES +mat4 joint(uint j) { return u_model; } // (never skinned here) +#else +uniform samplerBuffer u_joints; mat4 joint(uint j) { int b = (u_firstJoint + int(j)) * 4; return mat4(texelFetch(u_joints, b), texelFetch(u_joints, b + 1), texelFetch(u_joints, b + 2), texelFetch(u_joints, b + 3)); } +#endif void main() { mat4 m = u_model; if (u_skinned != 0) @@ -345,6 +358,33 @@ out vec4 o_color; void main() { o_color = vec4(u_color.rgb * (0.5 + 0.5 * max(dot(v_normal, normalize(vec3(0.4, 0.3, 0.85))), 0.0)), u_color.a); } )"; +// Sprites and streaks (the grid, the axes, particles): a quad facing the camera, textured, unlit. +const char* kSpriteVert = R"( +uniform mat4 u_viewProj; +uniform vec4 u_posSize, u_right, u_up; // right.w: turn (radians) +out vec2 v_uv; +const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1)); +void main() { + vec2 q = kQuad[gl_VertexID]; + float c = cos(u_right.w), s = sin(u_right.w); + vec2 r = vec2(c * q.x - s * q.y, s * q.x + c * q.y); + vec3 world = u_posSize.xyz + (u_right.xyz * r.x + u_up.xyz * r.y) * u_posSize.w; + v_uv = vec2(q.x * 0.5 + 0.5, 0.5 - q.y * 0.5); + gl_Position = u_viewProj * vec4(world, 1.0); + gl_Position.z = gl_Position.z * 2.0 - gl_Position.w; +} +)"; +const char* kSpriteFrag = R"( +in vec2 v_uv; +uniform sampler2D u_albedo; +uniform vec4 u_color; +out vec4 o_color; +void main() { + vec4 t = texture(u_albedo, v_uv); + o_color = vec4(t.rgb * u_color.rgb, clamp((t.a - 0.03) / 0.97, 0.0, 1.0) * u_color.a); +} +)"; + const char* kTonemapVert = R"( out vec2 v_uv; void main() { @@ -376,13 +416,13 @@ constexpr int kMaxLegacyLights = 16; struct Program { GLuint id = 0; GLint viewProj = -1, model = -1, skinned = -1, firstJoint = -1, tint = -1, params = -1, plane = -1, flags = -1, cube = -1, - invViewProj = -1, center = -1, extents = -1, color = -1, exposure = -1; + invViewProj = -1, center = -1, extents = -1, color = -1, exposure = -1, posSize = -1, right = -1, up = -1; }; class LegacyGLBackend final : public RenderBackend { public: - explicit LegacyGLBackend(int major, int minor) : major_(major), minor_(minor), tier_(major >= 4 ? 2 : 1) { - name_ = "OpenGL " + std::to_string(major) + "." + std::to_string(minor) + " (fallback)"; + LegacyGLBackend(int major, int minor, bool es) : major_(major), minor_(minor), tier_(major >= 4 || es ? 2 : 1), es_(es) { + name_ = std::string(es ? "OpenGL ES " : "OpenGL ") + std::to_string(major) + "." + std::to_string(minor) + " (fallback)"; } const char* name() const override { return name_.c_str(); } GpuInfo gpuInfo() const override { return gpu_; } @@ -393,8 +433,8 @@ public: SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major_); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor_); // 3.1 has no profiles; 4.0 asks for core. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, major_ >= 4 ? SDL_GL_CONTEXT_PROFILE_CORE : 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, major_ >= 4 ? 0 : SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, es_ ? SDL_GL_CONTEXT_PROFILE_ES : major_ >= 4 ? SDL_GL_CONTEXT_PROFILE_CORE : 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, major_ >= 4 || es_ ? 0 : SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); context_ = SDL_GL_CreateContext(window_); @@ -409,23 +449,29 @@ public: glx::PixelStorei(GL_UNPACK_ALIGNMENT, 1); glx::PixelStorei(GL_PACK_ALIGNMENT, 1); - const std::string header = "#version 140\n#define TIER " + std::to_string(tier_) + "\n" + kFrameBlock; + const std::string header = std::string(es_ ? "#version 300 es\nprecision highp float;\nprecision highp int;\nprecision highp sampler2D;\n" + "precision highp sampler2DShadow;\n" + : "#version 140\n") + + "#define TIER " + std::to_string(tier_) + "\n" + kFrameBlock; mesh_ = link(header + kMeshVert, header + kMeshFrag); depth_ = link(header + kMeshVert, header + kDepthFrag); sky_ = link(header + kSkyVert, header + kSkyFrag); box_ = link(header + kBoxVert, header + kBoxFrag); tonemap_ = link(header + kTonemapVert, header + kTonemapFrag); - if (!mesh_.id || !depth_.id || !sky_.id || !box_.id || !tonemap_.id) return false; + sprite_ = link(header + kSpriteVert, header + kSpriteFrag); + if (!mesh_.id || !depth_.id || !sky_.id || !box_.id || !tonemap_.id || !sprite_.id) return false; glx::GenBuffers(1, &frameUbo_); glx::BindBuffer(GL_UNIFORM_BUFFER, frameUbo_); glx::BufferData(GL_UNIFORM_BUFFER, sizeof(FrameBlock), nullptr, GL_DYNAMIC_DRAW); - glx::GenBuffers(1, &jointBuffer_); - glx::BindBuffer(GL_TEXTURE_BUFFER, jointBuffer_); - glx::BufferData(GL_TEXTURE_BUFFER, kMaxJointsPerFrame * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW); - glx::GenTextures(1, &jointTexture_); - glx::BindTexture(GL_TEXTURE_BUFFER, jointTexture_); - glx::TexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, jointBuffer_); + if (glx::TexBuffer && !es_) { + glx::GenBuffers(1, &jointBuffer_); + glx::BindBuffer(GL_TEXTURE_BUFFER, jointBuffer_); + glx::BufferData(GL_TEXTURE_BUFFER, kMaxJointsPerFrame * sizeof(glm::mat4), nullptr, GL_DYNAMIC_DRAW); + glx::GenTextures(1, &jointTexture_); + glx::BindTexture(GL_TEXTURE_BUFFER, jointTexture_); + glx::TexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, jointBuffer_); + } glx::GenVertexArrays(1, &emptyVao_); // The sun's shadow map: 2ร—2 cascades, smaller on the 3.1 tier. @@ -442,7 +488,9 @@ public: glx::GenFramebuffers(1, &shadowFbo_); glx::BindFramebuffer(GL_FRAMEBUFFER, shadowFbo_); glx::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMap_, 0); - glx::DrawBuffer(GL_NONE); + const GLenum none = GL_NONE; + if (glx::DrawBuffer) glx::DrawBuffer(GL_NONE); + else if (glx::DrawBuffers) glx::DrawBuffers(1, &none); glx::ReadBuffer(GL_NONE); glx::BindFramebuffer(GL_FRAMEBUFFER, 0); @@ -450,7 +498,7 @@ public: ImGui::CreateContext(); ImGui::GetIO().IniFilename = nullptr; ImGui::StyleColorsDark(); - imgui_ = ImGui_ImplSDL3_InitForOpenGL(window_, context_) && ImGui_ImplOpenGL3_Init("#version 140"); + imgui_ = ImGui_ImplSDL3_InitForOpenGL(window_, context_) && ImGui_ImplOpenGL3_Init(es_ ? "#version 300 es" : "#version 140"); log::info("Fallback renderer: tier {} ({})", tier_, tier_ >= 2 ? "shadows, normal maps, highlights, sky reflections, 16 lights" : "lightmaps, sun shadows, sky"); return imgui_; } @@ -584,7 +632,7 @@ private: glx::AttachShader(p.id, f); const char* attrs[] = {"a_pos", "a_normal", "a_uv", "a_lmuv", "a_joints", "a_weights"}; for (GLuint i = 0; i < 6; ++i) glx::BindAttribLocation(p.id, i, attrs[i]); - glx::BindFragDataLocation(p.id, 0, "o_color"); + if (glx::BindFragDataLocation) glx::BindFragDataLocation(p.id, 0, "o_color"); // (ES: the only output is 0) glx::LinkProgram(p.id); glx::DeleteShader(v); glx::DeleteShader(f); @@ -603,6 +651,7 @@ private: p.viewProj = u("u_viewProj"), p.model = u("u_model"), p.skinned = u("u_skinned"), p.firstJoint = u("u_firstJoint"); p.tint = u("u_tint"), p.params = u("u_params"), p.plane = u("u_plane"), p.flags = u("u_flags"), p.cube = u("u_cube"); p.invViewProj = u("u_invViewProj"), p.center = u("u_center"), p.extents = u("u_extents"), p.color = u("u_color"), p.exposure = u("u_exposure"); + p.posSize = u("u_posSize"), p.right = u("u_right"), p.up = u("u_up"); // Fixed texture units. glx::UseProgram(p.id); const struct { const char* name; GLint unit; } units[] = {{"u_albedo", 0}, {"u_lightmap", 1}, {"u_material", 2}, {"u_skyVis", 3}, @@ -628,13 +677,18 @@ private: void ensureTargets(uint32_t w, uint32_t h) { if (sceneFbo_ && w == targetW_ && h == targetH_) return; releaseTargets(); - glx::GenTextures(1, &sceneTex_); - glx::BindTexture(GL_TEXTURE_2D, sceneTex_); - glx::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, GLsizei(w), GLsizei(h), 0, GL_RGBA, GL_FLOAT, nullptr); - glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // HDR, or (a phone that can't draw into half floats) plain 8 bits: brights clip. + auto colour = [&](bool hdr) { + if (sceneTex_) glx::DeleteTextures(1, &sceneTex_); + glx::GenTextures(1, &sceneTex_); + glx::BindTexture(GL_TEXTURE_2D, sceneTex_); + glx::TexImage2D(GL_TEXTURE_2D, 0, hdr ? GL_RGBA16F : GL_RGBA8, GLsizei(w), GLsizei(h), 0, GL_RGBA, hdr ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); + glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glx::TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + }; + colour(!ldrTarget_); glx::GenTextures(1, &sceneDepth_); glx::BindTexture(GL_TEXTURE_2D, sceneDepth_); glx::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, GLsizei(w), GLsizei(h), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); @@ -644,6 +698,12 @@ private: glx::BindFramebuffer(GL_FRAMEBUFFER, sceneFbo_); glx::FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneTex_, 0); glx::FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sceneDepth_, 0); + if (!ldrTarget_ && glx::CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + log::warn("OpenGL (legacy): can't draw into half floats; the scene is 8 bits"); + ldrTarget_ = true; + colour(false); + glx::FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneTex_, 0); + } if (glx::CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) log::error("OpenGL (legacy): scene framebuffer incomplete"); glx::BindFramebuffer(GL_FRAMEBUFFER, 0); targetW_ = w, targetH_ = h; @@ -656,10 +716,11 @@ private: std::string name_; int major_, minor_, tier_; + bool es_ = false, ldrTarget_ = false; SDL_Window* window_ = nullptr; SDL_GLContext context_ = nullptr; bool imgui_ = false; - Program mesh_, depth_, sky_, box_, tonemap_; + Program mesh_, depth_, sky_, box_, tonemap_, sprite_; std::vector textures_; std::vector models_; GLuint frameUbo_ = 0, jointBuffer_ = 0, jointTexture_ = 0, emptyVao_ = 0; @@ -724,12 +785,14 @@ void LegacyGLBackend::render(FramePacket& p) { glx::BindBuffer(GL_UNIFORM_BUFFER, frameUbo_); glx::BufferSubData(GL_UNIFORM_BUFFER, 0, sizeof fb, &fb); glx::BindBufferBase(GL_UNIFORM_BUFFER, 0, frameUbo_); - if (!p.joints.empty()) { + if (!p.joints.empty() && jointBuffer_) { glx::BindBuffer(GL_TEXTURE_BUFFER, jointBuffer_); glx::BufferSubData(GL_TEXTURE_BUFFER, 0, GLsizeiptr(std::min(p.joints.size(), size_t(kMaxJointsPerFrame)) * sizeof(glm::mat4)), p.joints.data()); } - glx::ActiveTexture(GL_TEXTURE6); - glx::BindTexture(GL_TEXTURE_BUFFER, jointTexture_); + if (jointTexture_) { + glx::ActiveTexture(GL_TEXTURE6); + glx::BindTexture(GL_TEXTURE_BUFFER, jointTexture_); + } glx::Disable(GL_CULL_FACE); glx::Disable(GL_BLEND); glx::Enable(GL_DEPTH_TEST); @@ -739,7 +802,7 @@ void LegacyGLBackend::render(FramePacket& p) { // โ”€โ”€ Sun shadows (light shadows aren't drawn on this renderer) โ”€โ”€ glx::BindFramebuffer(GL_FRAMEBUFFER, shadowFbo_); glx::Viewport(0, 0, GLsizei(shadowSize_), GLsizei(shadowSize_)); - glx::ClearDepth(1.0); + glx::ClearDepth ? glx::ClearDepth(1.0) : glx::ClearDepthf(1.0f); glx::Clear(GL_DEPTH_BUFFER_BIT); glx::Enable(GL_POLYGON_OFFSET_FILL); glx::PolygonOffset(2.5f, 2.0f); @@ -789,6 +852,31 @@ void LegacyGLBackend::render(FramePacket& p) { glx::DepthMask(GL_TRUE); glx::Disable(GL_BLEND); } + if (!p.sprites.empty() || !p.glowSprites.empty()) { // depth-tested, not written; blended, then added on + glx::UseProgram(sprite_.id); + glx::UniformMatrix4fv(sprite_.viewProj, 1, GL_FALSE, glm::value_ptr(p.viewProj)); + glx::BindVertexArray(emptyVao_); + glx::Enable(GL_BLEND); + glx::DepthMask(GL_FALSE); + auto draw = [&](const std::vector& sprites) { + for (const gpu::Sprite& sp : sprites) { + uint32_t tex = 0; + std::memcpy(&tex, &sp.up.w, 4); // (the texture's index, as the float's bits) + bindTexture(0, TextureId(tex)); + glx::Uniform4fv(sprite_.posSize, 1, glm::value_ptr(sp.posSize)); + glx::Uniform4fv(sprite_.color, 1, glm::value_ptr(sp.color)); + glx::Uniform4fv(sprite_.right, 1, glm::value_ptr(sp.right)); + glx::Uniform4fv(sprite_.up, 1, glm::value_ptr(sp.up)); + glx::DrawArrays(GL_TRIANGLES, 0, 6); + } + }; + glx::BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + draw(p.sprites); + glx::BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); + draw(p.glowSprites); + glx::DepthMask(GL_TRUE); + glx::Disable(GL_BLEND); + } // โ”€โ”€ Tonemap to the window, then UI โ”€โ”€ glx::BindFramebuffer(GL_FRAMEBUFFER, 0); @@ -817,6 +905,6 @@ void LegacyGLBackend::render(FramePacket& p) { } } // namespace -std::unique_ptr createLegacyOpenGLBackend(int major, int minor) { return std::make_unique(major, minor); } +std::unique_ptr createLegacyOpenGLBackend(int major, int minor, bool es) { return std::make_unique(major, minor, es); } } // namespace keel