Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.lwjglx</groupId>
<artifactId>lwjglx-debug</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<name>LWJGLX/debug</name>
<description>Debug LWJGL3 OpenGL applications</description>
<inceptionYear>2017</inceptionYear>
Expand Down
84 changes: 82 additions & 2 deletions src/org/lwjglx/debug/InterceptClassGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ of this software and associated documentation files (the "Software"), to deal
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

Expand Down Expand Up @@ -148,6 +151,23 @@ class InterceptClassGenerator implements Opcodes {
"SDL_GL_MakeCurrent", "SDL_GL_GetCurrentContext", "SDL_GL_SwapWindow", "SDL_GL_DestroyContext"
)));

private static final Set<String> SDL_PRE_INIT_METHODS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
"SDL_Init", "SDL_InitSubSystem", "SDL_SetMemoryFunctions", "SDL_GetMemoryFunctions",
"SDL_malloc", "SDL_calloc", "SDL_realloc", "SDL_free", "SDL_GetVersion", "SDL_GetRevision",
"SDL_GetPlatform", "SDL_GetError", "SDL_ClearError", "SDL_SetError", "SDL_SetMainReady",
"SDL_RunApp"
)));

private static final NavigableSet<String> SDL_PRE_INIT_PREFIXES =
Collections.unmodifiableNavigableSet(new TreeSet<>(Arrays.asList(
"SDL_Hint", "SDL_SetHint", "SDL_GetHint", "SDL_ResetHint", "SDL_AddHint", "SDL_RemoveHint",
"SDL_Environment", "SDL_SetEnvironment", "SDL_GetEnvironment", "SDL_UnsetEnvironment",
"SDL_CreateEnvironment", "SDL_DestroyEnvironment", "SDL_Log", "SDL_CreateProperties",
"SDL_DestroyProperties", "SDL_SetProperty", "SDL_GetProperty", "SDL_SetStringProperty",
"SDL_GetStringProperty", "SDL_SetNumberProperty", "SDL_GetNumberProperty", "SDL_SetFloatProperty",
"SDL_GetFloatProperty", "SDL_SetPointerProperty", "SDL_GetPointerProperty"
)));

private static boolean isGLcall(InterceptedCall call) {
return (call.name.startsWith("gl") || call.name.startsWith("ngl")) && call.resolvedReceiverInternalName.startsWith("org/lwjgl/opengl/");
}
Expand Down Expand Up @@ -195,11 +215,70 @@ private static boolean requiresGlfwInit(InterceptedCall call) {

private static boolean requiresSdlInit(InterceptedCall call) {
if (call.resolvedReceiverInternalName.startsWith("org/lwjgl/sdl/")) {
return call.name.startsWith("SDL_") && !call.name.equals("SDL_Init") && !call.name.equals("SDL_InitSubSystem") && !call.name.equals("SDL_SetMemoryFunctions");
switch (call.resolvedReceiverInternalName) {
case "org/lwjgl/sdl/SDLStdinc":
case "org/lwjgl/sdl/SDLHints":
case "org/lwjgl/sdl/SDLError":
case "org/lwjgl/sdl/SDLLog":
case "org/lwjgl/sdl/SDLProperties":
case "org/lwjgl/sdl/SDLVersion":
case "org/lwjgl/sdl/SDLInit":
return false;
}
if (!call.name.startsWith("SDL_")) {
return false;
}
if (SDL_PRE_INIT_METHODS.contains(call.name)) {
return false;
}

String prefix = SDL_PRE_INIT_PREFIXES.floor(call.name);
return null == prefix || !call.name.startsWith(prefix);
}
return false;
}

private static int getRequiredSdlSubsystem(String receiver) {
switch (receiver) {
case "org/lwjgl/sdl/SDLVideo":
case "org/lwjgl/sdl/SDLRenderer":
case "org/lwjgl/sdl/SDLMessagebox":
case "org/lwjgl/sdl/SDLMouse":
case "org/lwjgl/sdl/SDLKeyboard":
case "org/lwjgl/sdl/SDLTouch":
case "org/lwjgl/sdl/SDLPen":
case "org/lwjgl/sdl/SDLGPU":
case "org/lwjgl/sdl/SDLMetal":
case "org/lwjgl/sdl/SDLVulkan":
case "org/lwjgl/sdl/SDLSystray":
return 0x00000020; // SDL_INIT_VIDEO

case "org/lwjgl/sdl/SDLAudio":
return 0x00000010; // SDL_INIT_AUDIO

case "org/lwjgl/sdl/SDLJoystick":
return 0x00000200; // SDL_INIT_JOYSTICK

case "org/lwjgl/sdl/SDLGamepad":
return 0x00002000; // SDL_INIT_GAMEPAD

case "org/lwjgl/sdl/SDLEvents":
return 0x00004000; // SDL_INIT_EVENTS

case "org/lwjgl/sdl/SDLSensor":
return 0x00008000; // SDL_INIT_SENSOR

case "org/lwjgl/sdl/SDLCamera":
return 0x00010000; // SDL_INIT_CAMERA

case "org/lwjgl/sdl/SDLHaptic":
return 0x00001000; // SDL_INIT_HAPTIC

}
return 0;
}


private static void checkFunctionSupported(MethodVisitor mv, String name) {
mv.visitFieldInsn(GETFIELD, "org/lwjgl/opengl/GLCapabilities", name, "J");
mv.visitLdcInsn(name);
Expand Down Expand Up @@ -274,7 +353,8 @@ public static Class<?> generate(ClassLoader classLoader, String proxyInternalNam
/* and whether it was an SDL method that requires SDL_Init() to have been called */
if (requiresSdlInit(call)) {
mv.visitLdcInsn(call.name);
mv.visitMethodInsn(INVOKESTATIC, RT_InternalName, "checkSdlInitialized", "(Ljava/lang/String;)V", false);
mv.visitLdcInsn(getRequiredSdlSubsystem(call.resolvedReceiverInternalName));
mv.visitMethodInsn(INVOKESTATIC, RT_InternalName, "checkSdlInitialized", "(Ljava/lang/String;I)V", false);
}
}
/* Validate buffer arguments and also load all arguments onto stack */
Expand Down
61 changes: 42 additions & 19 deletions src/org/lwjglx/debug/RT.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ of this software and associated documentation files (the "Software"), to deal
package org.lwjglx.debug;

import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFW;
import org.lwjglx.debug.org.lwjgl.opengl.Context;

import java.nio.Buffer;
Expand All @@ -41,6 +42,7 @@ of this software and associated documentation files (the "Software"), to deal
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import static org.lwjgl.sdl.SDLInit.SDL_WasInit;
import static org.lwjglx.debug.Log.error;
import static org.lwjglx.debug.Log.trace;
import static org.lwjglx.debug.org.lwjgl.opengl.Context.CONTEXTS;
Expand Down Expand Up @@ -703,12 +705,14 @@ public static MethodCall paramGlfwWindow(MethodCall mc, long window) {
}

public static MethodCall paramGlfwMonitor(MethodCall mc, long monitor) {
PointerBuffer monitors = org.lwjgl.glfw.GLFW.glfwGetMonitors();
for (int i = 0; i < monitors.remaining(); i++) {
long m = monitors.get(i);
if (m == monitor) {
mc.paramEnum("monitor[" + i + "]");
return mc;
PointerBuffer monitors = GLFW.glfwGetMonitors();
if (monitors != null) {
for (int i = 0; i < monitors.remaining(); i++) {
long m = monitors.get(i);
if (m == monitor) {
mc.paramEnum("monitor[" + i + "]");
return mc;
}
}
}
mc.param(monitor);
Expand Down Expand Up @@ -758,12 +762,14 @@ public static long returnValueGlfwWindow(long window, MethodCall mc) {
}

public static long returnValueGlfwMonitor(long monitor, MethodCall mc) {
PointerBuffer monitors = org.lwjgl.glfw.GLFW.glfwGetMonitors();
for (int i = 0; i < monitors.remaining(); i++) {
long m = monitors.get(i);
if (m == monitor) {
mc.returnValueEnum("monitor[" + i + "]");
return monitor;
PointerBuffer monitors = GLFW.glfwGetMonitors();
if (monitors != null) {
for (int i = 0; i < monitors.remaining(); i++) {
long m = monitors.get(i);
if (m == monitor) {
mc.returnValueEnum("monitor[" + i + "]");
return monitor;
}
}
}
mc.returnValue(monitor);
Expand Down Expand Up @@ -1250,10 +1256,12 @@ public static void checkGlfwInitialized(String methodName) {
public static void checkGlfwMonitor(long monitor) {
if (monitor == 0L)
return;
PointerBuffer pb = org.lwjgl.glfw.GLFW.glfwGetMonitors();
for (int i = 0; i < pb.remaining(); i++) {
if (pb.get(i) == monitor)
return;
PointerBuffer pb = GLFW.glfwGetMonitors();
if (pb != null) {
for (int i = 0; i < pb.remaining(); i++) {
if (pb.get(i) == monitor)
return;
}
}
throwIAEOrLogError("Provided 'monitor' argument is not a valid GLFW monitor handle: " + monitor);
}
Expand Down Expand Up @@ -1324,9 +1332,24 @@ public static long returnValueSdlGlContext(long context, MethodCall mc) {
return context;
}

public static void checkSdlInitialized(String methodName) {
if (!sdlInitialized) {
throwISEOrLogError("Method " + methodName + " was called before initializing SDL via SDL_Init().");
public static void checkSdlInitialized(String methodName, int requiredFlags) {
boolean initialized = false;
try {
if (requiredFlags == 0) {
initialized = sdlInitialized || SDL_WasInit(0) != 0;
} else {
initialized = (SDL_WasInit(requiredFlags) & requiredFlags) == requiredFlags;
}
} catch (Throwable t) {
t.printStackTrace();
initialized = sdlInitialized;
}
if (!initialized) {
if (requiredFlags == 0) {
throwISEOrLogError("Method " + methodName + " was called before initializing SDL.");
} else {
throwISEOrLogError("Method " + methodName + " was called before initializing the required SDL subsystem.");
}
}
}

Expand Down
24 changes: 10 additions & 14 deletions src/org/lwjglx/debug/org/lwjgl/sdl/SDLInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,23 @@
public class SDLInit {

public static boolean SDL_Init(int flags) {
boolean ret = org.lwjgl.sdl.SDLInit.SDL_Init(flags);
if (ret) {
boolean result = org.lwjgl.sdl.SDLInit.SDL_Init(flags);
if (result) {
RT.sdlInitialized = true;
} else {
if (Properties.VALIDATE.enabled) {
error("SDL_Init returned false");
}
} else if (Properties.VALIDATE.enabled) {
error("SDL_Init returned false");
}
return ret;
return result;
}

public static boolean SDL_InitSubSystem(int flags) {
boolean ret = org.lwjgl.sdl.SDLInit.SDL_InitSubSystem(flags);
if (ret) {
boolean result = org.lwjgl.sdl.SDLInit.SDL_InitSubSystem(flags);
if (result) {
RT.sdlInitialized = true;
} else {
if (Properties.VALIDATE.enabled) {
error("SDL_InitSubSystem returned false");
}
} else if (Properties.VALIDATE.enabled) {
error("SDL_InitSubSystem returned false");
}
return ret;
return result;
}

public static void SDL_Quit() {
Expand Down
56 changes: 56 additions & 0 deletions test/test/DebugSDLIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.lwjgl.sdl.SDLError;
import org.lwjgl.sdl.SDLHints;
import org.lwjgl.sdl.SDLInit;
import org.lwjgl.sdl.SDLStdinc;
import org.lwjgl.sdl.SDLTimer;
import org.lwjglx.debug.Properties;
import org.lwjglx.debug.org.lwjgl.opengl.Context;

import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -172,4 +178,54 @@ public void testGLError() {
createCapabilities();
assertThrowsWithMessage(IllegalStateException.class, () -> glEnable(GL_VERTEX_ARRAY_POINTER), Pattern.compile("glEnable produced error: 1280 \\(GL_INVALID_ENUM\\)"));
}

@Test
public void testPreInitAllowed() {
SDL_Quit();
alreadyTerminated = true;
assertTrue(SDLHints.SDL_SetHint("SDL_VIDEO_DRIVER", "dummy"));
assertEquals("dummy", SDLHints.SDL_GetHint("SDL_VIDEO_DRIVER"));
SDLError.SDL_GetError();
ByteBuffer buf = SDLStdinc.SDL_malloc(16);
if (buf != null) {
SDLStdinc.SDL_free(buf);
}
assertTrue(SDL_Init(SDL_INIT_VIDEO));
alreadyTerminated = false;
window = SDL_CreateWindow("Test PreInit Allowed", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
assertNotNull(window);
}

@Test
public void testSubsystemRefcounting() {
SDL_Quit();
alreadyTerminated = true;
assertTrue(SDL_Init(SDL_INIT_VIDEO));
assertTrue(SDL_Init(SDL_INIT_VIDEO));
alreadyTerminated = false;
window = SDL_CreateWindow("Test Refcount 1", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
assertNotNull(window);
SDLInit.SDL_QuitSubSystem(SDL_INIT_VIDEO);
window2 = SDL_CreateWindow("Test Refcount 2", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
assertNotNull(window2);
SDL_DestroyWindow(window);
window = 0L;
SDL_DestroyWindow(window2);
window2 = 0L;
SDLInit.SDL_QuitSubSystem(SDL_INIT_VIDEO);
alreadyTerminated = true;
assertThrows(IllegalStateException.class, () -> SDL_CreateWindow("Should Fail", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN));
}

@Test
public void testInitZero() {
SDL_Quit();
alreadyTerminated = true;
assertTrue(SDL_Init(0));
// core/timer method should succeed
long ticks = SDLTimer.SDL_GetTicks();
assertTrue(ticks >= 0L);
// video method should throw IllegalStateException because video subsystem is not initialized
assertThrows(IllegalStateException.class, () -> SDL_CreateWindow("Should Fail", 320, 240, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN));
}
}