I launched my first mobile games on the Google Play Store over two years ago. Built with GameMaker Studio 1.4, it ran smoothly on hundreds of Android devices. I’d even started getting user feedback, reviews, and growing organic installs. Life was good until Android Oreo rolled in.
Suddenly, users started reporting instant crashes. The game wouldn’t even show the splash screen it just died on launch. I was baffled. At first, I blamed my phone, then my code, and eventually, I rebuilt the entire game in GameMaker Studio 2 just to test. But no matter what I did, every device running Android Oreo showed the same fatal behavior: an instant crash on tap.
Error Code
So, I did what any developer would do: dug into logcat and ran the game in Debug Mode. Here’s the fatal error that greeted me:
EXCEPTION: GLThread
java.lang.IllegalArgumentException: Unknown type
at android.opengl.GLUtils.getType(GLUtils.java:71)
at android.opengl.GLUtils.texSubImage2D(GLUtils.java:189)
At first, I had no clue what this meant. It seemed related to OpenGL, but this crash was happening before any of my actual game logic even ran. The Google Play Services extension? Removed it still crashed. Achievements? Warnings in the developer console, but not the issue. The crash happened before anything like that even initialized.
I decided to dive deeper into the rendering code. That’s when I found the line that was causing the crash.
The Root Cause
Inside my DemoRenderer.java
file, I found this line:
GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
And here’s what blew my mind: this worked on Android versions below Oreo, but starting in Android 8.0 (API 26), it causes a fatal crash.
So what changed?
Turns out, Android Oreo added stricter checks in the OpenGL layer. In my code, I was calling texSubImage2D()
to load a bitmap without first allocating texture memory using texImage2D()
. This used to be ignored silently in older Android versions but now it’s a hard crash.
The Fix: texImage2D Comes First
To fix this, I needed to initialize the texture with texImage2D()
before calling texSubImage2D()
.
Here’s the original broken code:
// GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, pow2Bitmap, 0); // was commented out
GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, bitmap); // crash!
And here’s the fixed version:
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); // allocates texture properly
// texSubImage2D() is not needed unless you're updating later
After this change, the game loaded flawlessly—even on Android Oreo.
Enhance & Safe Texture Uploading
I didn’t want to stop at just fixing the crash. I wanted to make my splash image loader more robust. Here’s the updated texture-loading block I now use:
InputStream is = null;
Bitmap bitmap = null;
try {
if (getScreenOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
is = getResourceAsReader("splash.png");
} else {
m_splashFilePath = "assets/portrait_splash.png";
is = getResourceAsReader("portrait_splash.png");
}
if (is != null) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeStream(is, null, opt);
if (bitmap != null) {
m_texWidth = bitmap.getWidth();
m_texHeight = bitmap.getHeight();
m_texRawWidth = getNextPow2(m_texWidth);
m_texRawHeight = getNextPow2(m_texHeight);
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// 👇 Critical fix
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
Log.i("yoyo", "Splash screen texture loaded successfully.");
} else {
Log.e("yoyo", "Failed to decode splash bitmap.");
}
}
} catch (Exception e) {
Log.e("yoyo", "Error loading splash image", e);
} finally {
if (is != null) try { is.close(); } catch (IOException e) { /* ignored */ }
if (bitmap != null) bitmap.recycle();
}
Improvement You Should Consider
While I was in the zone, I added a few more practical upgrades:
Runtime OpenGL ES Check
if (info.reqGlEsVersion < 0x20000) {
Log.w("yoyo", "Your device does not support OpenGL ES 2.0+. Expect limited rendering.");
}
Fallback Splash Image
If decoding fails, I try a backup image:
if (bitmap == null) {
is = getResourceAsReader("default_splash.png");
bitmap = BitmapFactory.decodeStream(is);
}
Permissions Check for External Storage
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001);
}
OpenGL Texture Cleanup on Exit
int[] texturesToDelete = {textureId};
gl.glDeleteTextures(1, texturesToDelete, 0);
Final Thought
This bug taught me a lot. At first, it looked like a Google Play Services issue. Then I suspected GameMaker itself. But the truth was hidden deep in the OpenGL lifecycle and it was my code that needed fixing. Android Oreo didn’t break my game; it just forced me to follow the rules more strictly.