GL_MAX_TEXTURE_SIZE: el límite invisible de tu GPU y cómo Kirin maneja texturas gigantes
Una GPU moderna puede mover juegos 3D complejos y aun así no aceptar un tileset 2D demasiado alto. El motivo está en los límites físicos de las texturas, la precisión con la que se muestrean y la forma en que un motor heredado intenta representar imágenes que nunca fueron pensadas para crecer tanto.
Un teléfono moderno puede tener 8 o 12 GB de memoria, una GPU capaz de ejecutar juegos tridimensionales y una potencia muy superior a la de cualquier PC de la época de RPG Maker XP. Y aun así puede fallar con algo aparentemente mucho más sencillo: una imagen 2D demasiado grande.
En proyectos grandes de RPG Maker XP y Pokémon Essentials esto aparece sobre todo con tilesets que han crecido durante años y con spritesheets animados que reúnen una enorme cantidad de frames. El archivo PNG puede existir, decodificarse correctamente y ocupar una cantidad razonable de RAM. El problema llega cuando el renderer intenta convertirlo en una textura que la GPU pueda utilizar.
La pregunta no es solamente cuánto pesa el PNG. También importa qué dimensiones tiene la imagen, cuánta memoria ocupa una vez decodificada y cuál es la textura más grande que el driver de la GPU permite crear.
Una imagen y una textura no son lo mismo
Cuando RPG Maker XP solicita un recurso como Graphics/Tilesets/Exterior.png, el archivo almacenado todavía no es una textura de GPU. Antes debe atravesar varias etapas.
Un PNG puede ocupar pocos megabytes gracias a la compresión y convertirse en una superficie mucho mayor una vez expandido. Una imagen RGBA de 8192 × 8192 necesita aproximadamente 256 MiB sin comprimir. Una de 16384 × 16384 ronda 1 GiB. Y una superficie de 32768 × 32768 alcanzaría aproximadamente 4 GiB.
Por eso existen dos preguntas diferentes: ¿la imagen cabe en memoria? y ¿la GPU permite representarla como una textura individual?. La segunda está directamente relacionada con GL_MAX_TEXTURE_SIZE.
Qué es GL_MAX_TEXTURE_SIZE
GL_MAX_TEXTURE_SIZE es un límite que OpenGL expone a través del driver. Indica la mayor dimensión que puede manejar una textura 2D convencional. Khronos lo describe como el tamaño máximo de textura que la implementación puede soportar.
Si un teléfono informa:
una textura de 8192 píxeles de ancho o alto todavía está dentro del límite anunciado. Una imagen de 8193 × 256 ya lo supera, aunque su otra dimensión sea pequeña.
Dentro del límite
- 4096 × 4096
- 256 × 8000
- 8192 × 512
- Puede crearse como textura si además hay recursos suficientes
Fuera del límite
- 8193 × 256
- 1024 × 10000
- 16384 × 20000 en una GPU limitada a 16384
- No puede existir como una única textura convencional
El número no representa la cantidad de VRAM y tampoco garantiza que una textura situada justo debajo del máximo sea barata. Es una frontera de capacidad, no una recomendación de tamaño.
La documentación de Khronos permite consultar estos valores de implementación, y es precisamente por eso que Kirin no debería asumir un límite fijo para todos los dispositivos: debe preguntar al hardware que realmente está ejecutando el juego.
Quieres saber cuánto soporta tu teléfono: compruébalo con GLView
En Android puedes comprobar las capacidades gráficas del dispositivo con GLView Extensions Viewer. La aplicación muestra información de OpenGL ES y Vulkan reportada por el teléfono, incluyendo renderer, versión, extensiones y límites del hardware.
Dentro de la información de OpenGL ES busca:
Puedes encontrarte, según GPU y driver, valores como 4096, 8192, 16384 o 32768. Dos teléfonos que ejecutan la misma versión de Android no tienen por qué ofrecer la misma capacidad gráfica.
Esto ayuda a explicar un comportamiento que de otro modo parece arbitrario: el mismo proyecto puede funcionar en una PC y fallar al abrir una imagen concreta en un teléfono.
Una GPU de escritorio puede ocultar el problema durante años
Imaginemos que el desarrollador trabaja con una GPU de escritorio cuyo límite es 32768 y mantiene un tileset de 256 × 20000. Para esa computadora, 20000 está por debajo de 32768. El juego funciona, el mapa se dibuja y no hay ninguna razón visible para pensar que el recurso vaya a causar problemas.
Después el proyecto llega a un teléfono con:
El mismo tileset ahora tiene una dimensión de 20000. Nada cambió en el PNG. Nada cambió en Ruby. Cambió el hardware encargado de representarlo.
El propio proyecto mkxp-z documenta este problema y señala que el máximo puede variar ampliamente entre familias de GPU; también contempla una excepción para Bitmaps mayores que el límite mediante su sistema Mega Surface.
El caso clásico: tilesets que crecen hacia abajo
RPG Maker XP utiliza tilesets que pueden crecer verticalmente. Un recurso puede comenzar con dimensiones modestas y acumular contenido durante años:
- 256 × 4000
- 256 × 8000
- 256 × 12000
- 256 × 18000
- 256 × 22000
El ancho continúa siendo pequeño. El PNG puede seguir pareciendo un archivo normal. Pero basta con que una sola dimensión supere el máximo del dispositivo para que ya no pueda representarse como una textura OpenGL convencional.
Esta clase de crecimiento es particularmente peligrosa porque el desarrollador puede no notarlo nunca en un equipo de escritorio con un límite elevado.
El mismo problema aparece en spritesheets gigantes
Una hoja de sprites o animaciones puede sufrir exactamente la misma evolución. Cada frame individual es pequeño, pero todos terminan contenidos dentro de una única imagen lógica.
El frame que el juego necesita mostrar puede medir solamente unos cientos de píxeles. Sin embargo, la arquitectura tradicional intenta subir toda la hoja como una sola textura. El problema está en la representación física, no necesariamente en el contenido lógico que Ruby necesita utilizar.
Pero antes del máximo aparece otro problema: la precisión
Existe un síntoma todavía más frecuente que un error completo de creación de textura. La imagen sigue estando dentro de GL_MAX_TEXTURE_SIZE, el juego arranca y el mapa se renderiza, pero aparecen líneas negras o pequeñas costuras entre los tiles.
Es un caso típico: el tileset original no contiene esas líneas. Al abrir el PNG fuera del juego se ve perfecto. Sin embargo, en el mapa aparece una cuadrícula fina que puede hacerse más visible al desplazarse.
Una textura grande no pierde calidad automáticamente por ser grande. Lo que ocurre es que cuanto mayor es la textura, más pequeñas son las diferencias entre las coordenadas normalizadas que señalan texeles vecinos. Eso hace al renderer más sensible a precisión, redondeos y filtrado.
Por qué un mapa puede terminar lleno de líneas negras
La GPU no suele recibir una orden del tipo “dibuja el píxel 1234”. El renderer trabaja con coordenadas de textura, habitualmente expresadas en un espacio normalizado entre 0 y 1.
En una dimensión de 256 texeles, la separación normalizada aproximada entre texeles es:
En una dimensión de 16384:
A medida que la textura crece, las fronteras entre texeles se representan mediante diferencias numéricas cada vez más pequeñas. Si una coordenada UV llega ligeramente desplazada al shader, el muestreo puede terminar tocando el texel vecino, una zona transparente o el borde de otra región del atlas.
En un sprite aislado ese error puede pasar inadvertido. En un Tilemap se repite cientos de veces siguiendo una cuadrícula y se vuelve inmediatamente visible.
La precisión del shader importa
OpenGL ES define calificadores de precisión como lowp, mediump y highp. La especificación de GLSL ES permite que mediump tenga una precisión mínima considerablemente inferior a highp. En GLSL ES moderno, highp utiliza precisión de coma flotante de 32 bits, mientras que mediump sólo debe cumplir un mínimo de precisión relativa mucho menor.
Eso no significa que toda línea negra sea automáticamente culpa de mediump. También intervienen el filtrado, el cálculo de UV, el redondeo a texeles, la interpolación y la posición final en pantalla. Pero una precisión insuficiente vuelve más difícil separar correctamente regiones muy pequeñas dentro de una textura enorme.
La idea importante es ésta:
Texture bleeding: cuando el tile vecino se filtra dentro del actual
Otro fenómeno relacionado es el texture bleeding. Si el filtrado toma muestras alrededor de una coordenada situada exactamente sobre el borde de un tile, puede mezclar información de ambos lados.
El resultado puede ser una línea:
- negra, si existe negro o transparencia alrededor del tile;
- del color del tile vecino;
- transparente;
- intermitente, si cambia con el movimiento de cámara.
Por eso un mapa puede parecer correcto estando quieto y comenzar a mostrar líneas al caminar. El desplazamiento de la cámara cambia las posiciones exactas en pantalla y puede hacer visibles pequeños errores de muestreo que antes caían justo dentro de la región esperada.
Un mismo recurso puede atravesar tres zonas
Textura razonable
- Dimensiones moderadas
- Coordenadas fáciles de representar
- Menor presión de memoria
- Camino normal del renderer
Textura extrema
- Sigue dentro del máximo
- Mayor sensibilidad UV y filtrado
- Mayor coste de RAM/VRAM
- Pueden aparecer seams entre tiles
Después existe una tercera zona: cuando una dimensión supera directamente GL_MAX_TEXTURE_SIZE. Allí la GPU ya no puede crear la textura convencional sin importar cuánto afinemos las coordenadas.
Qué hace Kirin cuando la textura ya no cabe
Kirin no puede obligar a una GPU a crear una textura mayor que el máximo anunciado por su driver. Cuando un Bitmap cargado desde archivo supera ese límite, entra en juego el concepto de Mega Surface, heredado de la línea mkxp/mkxp-z y utilizado como vía de compatibilidad para superficies sobredimensionadas.
En lugar de asumir siempre:
el runtime puede mantener la superficie gigantesca en memoria principal y utilizarla mediante operaciones compatibles que trabajan con las regiones necesarias. mkxp-z documenta Mega Surface precisamente como una excepción para Bitmaps mayores que el límite de textura, conservándolos en RAM para el caso de tilesets.
Esto resulta especialmente útil en tilesets enormes: el recurso lógico puede medir decenas de miles de píxeles, pero el mapa nunca necesita mostrar la imagen completa al mismo tiempo. Sólo consume pequeñas regiones que corresponden a los tiles utilizados.
El objetivo no es fingir que el límite no existe
Mega Surface no convierte una imagen gigantesca en un recurso gratuito. La imagen todavía necesita RAM, decodificación y transferencias de las regiones que terminen utilizándose. Tampoco corrige por sí sola todos los problemas de precisión o filtrado.
Lo que hace es separar dos ideas que en un renderer simple suelen estar demasiado unidas:
RPG Maker XP puede continuar pensando en un Bitmap de 256 × 20000. El dispositivo puede declarar un máximo de 16384. Kirin no necesita cambiar el script del juego para entender que la representación interna debe tomar otro camino.
Por qué esto importa todavía más en Android
Android no representa una única GPU. Kirin puede ejecutarse sobre Adreno, Mali, PowerVR y otras arquitecturas, acompañadas por drivers y generaciones muy diferentes. Un recurso que nunca produjo problemas en la PC del creador puede comportarse de otra manera en un dispositivo móvil.
Por eso la compatibilidad no puede construirse suponiendo:
El runtime tiene que consultar las capacidades reales del dispositivo y escoger su estrategia. GL_MAX_TEXTURE_SIZE es uno de esos límites concretos que convierten una abstracción de RPG Maker XP en una decisión de hardware moderno.
Diagnóstico rápido: qué puede significar lo que ves
Mapa con líneas negras
- El recurso puede seguir dentro del máximo
- Revisar precisión UV
- Revisar filtrado y bordes
- Revisar texture bleeding
- Puede variar entre GPUs
Bitmap que no puede crearse
- Comprobar dimensiones
- Consultar GL_MAX_TEXTURE_SIZE
- Comparar contra ancho y alto
- Activar camino Mega Surface cuando corresponda
- No confundirlo con RAM disponible
De una limitación de OpenGL a una decisión de arquitectura
Lo interesante de este problema no es solamente descubrir que una GPU tiene un máximo. El límite obliga a preguntarse qué significa realmente un Bitmap dentro de Kirin.
Si cada Bitmap tiene que convertirse siempre en una única textura, RGSS queda atado a los límites físicos de una GPU concreta. Si el runtime separa el recurso lógico de su representación, puede decidir cómo conservar el resultado del juego sobre hardware diferente.
Eso también explica por qué una evolución futura puede ir más allá de Mega Surface: división en regiones, cacheado de partes, procesamiento previo o paquetes gráficos pueden reducir todavía más la necesidad de tratar imágenes monolíticas como una única textura.
La frontera que el desarrollador nunca tuvo que ver
RPG Maker XP fue creado en 2004 y sus proyectos originales no estaban pensados para mantener tilesets y hojas de animación creciendo durante dos décadas. Pokémon Essentials y los fangames construidos sobre él llevaron esa estructura mucho más lejos.
El creador del juego puede no haber cometido ningún error desde su perspectiva. Su recurso funcionaba en la GPU de escritorio donde fue creado. Es Kirin, al llevar ese contenido a Android, quien se encuentra con una frontera que antes estaba escondida.
Por eso GL_MAX_TEXTURE_SIZE resulta un ejemplo perfecto de la filosofía del proyecto:
La compatibilidad no consiste en obligar al teléfono a comportarse como la PC original. Consiste en conservar el resultado del juego mientras el runtime adapta su representación a las capacidades reales del dispositivo.
Fuentes y herramientas
- Khronos OpenGL Wiki: parámetros consultables y GL_MAX_TEXTURE_SIZE
- Khronos: especificación GLSL ES y calificadores de precisión
- GLView Extensions Viewer para Android
- mkxp-z: documentación de límites de textura y Mega Surface
Comprueba tu GPU y prueba Kirin
Consulta GL_MAX_TEXTURE_SIZE en tu teléfono con GLView y, si tienes un proyecto con tilesets o spritesheets especialmente grandes, comprueba cómo se comporta en hardware móvil.
