diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index af3136e5b..1c4133bbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -25,8 +25,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.objects.GLMessage; -import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream; +import com.seibel.distanthorizons.core.util.objects.GLMessages.*; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; @@ -38,28 +37,17 @@ import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLUtil; import java.io.PrintStream; -import java.lang.invoke.MethodHandles; import java.util.concurrent.ConcurrentLinkedQueue; /** * A singleton that holds references to different openGL contexts * and GPU capabilities. - * - *

- * Helpful OpenGL resources: - *

- * https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
- * https://learnopengl.com/Advanced-OpenGL/Advanced-Data
- * https://www.slideshare.net/CassEveritt/approaching-zero-driver-overhead

- * - * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one
- * https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer

*/ public class GLProxy { private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); public static final ConfigBasedLogger GL_LOGGER = new ConfigBasedLogger(LogManager.getLogger(GLProxy.class), () -> Config.Common.Logging.logRendererGLEvent.get()); @@ -79,7 +67,29 @@ public class GLProxy private final EDhApiGpuUploadMethod preferredUploadMethod; - public final GLMessage.Builder vanillaDebugMessageBuilder = GLMessage.Builder.DEFAULT_MESSAGE_BUILDER; + public final GLMessageBuilder vanillaDebugMessageBuilder = + new GLMessageBuilder( + (type) -> + { + if (type == EGLMessageType.POP_GROUP) + return false; + else if (type == EGLMessageType.PUSH_GROUP) + return false; + else if (type == EGLMessageType.MARKER) + return false; + else + return true; + }, + (severity) -> + { + // notifications can generally be ignored (if they are logged at all) + if (severity == EGLMessageSeverity.NOTIFICATION) + return false; + else + return true; + }, + null + ); @@ -268,11 +278,11 @@ public class GLProxy - if (msg.type == GLMessage.EType.ERROR || msg.type == GLMessage.EType.UNDEFINED_BEHAVIOR) + if (msg.type == EGLMessageType.ERROR || msg.type == EGLMessageType.UNDEFINED_BEHAVIOR) { // critical error - GL_LOGGER.error("GL ERROR " + msg.id + " from " + msg.source + ": " + msg.message); + GL_LOGGER.error("GL ERROR [" + msg.id + "] from [" + msg.source + "]: [" + msg.message + "]."); if (errorHandlingMode == EDhApiGLErrorHandlingMode.LOG_THROW) { @@ -284,28 +294,28 @@ public class GLProxy { // non-critical log - GLMessage.ESeverity severity = msg.severity; - RuntimeException ex = new RuntimeException("GL MESSAGE: " + msg); + EGLMessageSeverity severity = msg.severity; + RuntimeException exception = new RuntimeException("GL MESSAGE: " + msg); if (severity == null) { // just in case the message was malformed - severity = GLMessage.ESeverity.LOW; + severity = EGLMessageSeverity.LOW; } switch (severity) { case HIGH: - GL_LOGGER.error("{}", ex); + GL_LOGGER.error(exception.getMessage(), exception); break; case MEDIUM: - GL_LOGGER.warn("{}", ex); + GL_LOGGER.warn(exception.getMessage(), exception); break; case LOW: - GL_LOGGER.info("{}", ex); + GL_LOGGER.info(exception.getMessage(), exception); break; case NOTIFICATION: - GL_LOGGER.debug("{}", ex); + GL_LOGGER.debug(exception.getMessage(), exception); break; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessage.java deleted file mode 100644 index 8fc852339..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessage.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.util.objects; - -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.coreapi.ModInfo; -import org.apache.logging.log4j.Logger; - -import java.lang.invoke.MethodHandles; -import java.util.HashMap; -import java.util.function.Function; - -/** - * Handles parsing and creating string messages from OpenGL messages. - * - * @author Leetom - * @version 2022-10-1 - */ -public final class GLMessage -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); - - static final String HEADER = "[LWJGL] OpenGL debug message"; - public final EType type; - public final ESeverity severity; - public final ESource source; - public final String id; - public final String message; - - /** This is needed since gl callback will not have the correct class loader set, which causes issues. */ - static void initLoadClass() - { - Builder dummy = new Builder(); - dummy.add(GLMessage.HEADER); - dummy.add("ID"); - dummy.add(":"); - dummy.add("dummyId"); - dummy.add("Source"); - dummy.add(":"); - dummy.add(ESource.API.name); - dummy.add("Type"); - dummy.add(":"); - dummy.add(EType.OTHER.name); - dummy.add("Severity"); - dummy.add(":"); - dummy.add(ESeverity.LOW.name); - dummy.add("Message"); - dummy.add(":"); - dummy.add("dummyMessage"); - } - - static - { - initLoadClass(); - } - - - - GLMessage(EType type, ESeverity severity, ESource source, String id, String message) - { - this.type = type; - this.source = source; - this.severity = severity; - this.id = id; - this.message = message; - } - - @Override - public String toString() { return "[level:" + severity + ", type:" + type + ", source:" + source + ", id:" + id + ", msg:{" + message + "}]"; } - - - - //==============// - // helper enums // - //==============// - - public enum EType - { - ERROR, - DEPRECATED_BEHAVIOR, - UNDEFINED_BEHAVIOR, - PORTABILITY, - PERFORMANCE, - MARKER, - PUSH_GROUP, - POP_GROUP, - OTHER; - - - private static final HashMap ENUM_BY_NAME = new HashMap<>(); - - private final String name; - - - static - { - for (EType type : EType.values()) - { - ENUM_BY_NAME.put(type.name, type); - } - } - - EType() { name = super.toString().toUpperCase(); } - - - @Override - public final String toString() { return name; } - - public static EType get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } - - } - - public enum ESource - { - API, - WINDOW_SYSTEM, - SHADER_COMPILER, - THIRD_PARTY, - APPLICATION, - OTHER; - - - private static final HashMap ENUM_BY_NAME = new HashMap<>(); - - public final String name; - - - static - { - for (ESource source : ESource.values()) - { - ENUM_BY_NAME.put(source.name, source); - } - } - - ESource() { name = super.toString().toUpperCase(); } - - - @Override - public final String toString() { return name; } - - public static ESource get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } - - } - - public enum ESeverity - { - HIGH, - MEDIUM, - LOW, - NOTIFICATION; - - - public final String name; - - static final HashMap ENUM_BY_NAME = new HashMap<>(); - - - static - { - for (ESeverity severity : ESeverity.values()) - { - ENUM_BY_NAME.put(severity.name, severity); - } - } - - ESeverity() { name = super.toString().toUpperCase(); } - - - @Override - public final String toString() { return name; } - - public static ESeverity get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } - - } - - - - //================// - // helper classes // - //================// - - /** - * Expected message format:
- * - * [LWJGL] OpenGL debug message
- * ID: 0x20071
- * Source: API
- * Type: OTHER
- * Severity: NOTIFICATION
- * Message: Buffer detailed info: Buffer object 1014084 (bound to ... - *
- */ - public static class Builder - { - /** how many stages are present in the message parser */ - private static final int FINAL_PARSER_STAGE_INDEX = 15; - - public static final Builder DEFAULT_MESSAGE_BUILDER = - new Builder( - (type) -> - { // type filter - if (type == GLMessage.EType.POP_GROUP) - return false; - if (type == GLMessage.EType.PUSH_GROUP) - return false; - if (type == GLMessage.EType.MARKER) - return false; - // if (type == GLMessage.Type.PERFORMANCE) return false; - return true; - }, - (severity) -> - { // severity filter - if (severity == GLMessage.ESeverity.NOTIFICATION) - return false; - return true; - }, - null - ); - - - private final StringBuilder inProgressMessageBuilder = new StringBuilder(); - - private EType type; - private ESeverity severity; - private ESource source; - - /** if the function returns false the message will be allowed */ - private final Function typeFilter; - /** if the function returns false the message will be allowed */ - private final Function severityFilter; - /** if the function returns false the message will be allowed */ - private final Function sourceFilter; - - private String id; - private String message; - /** how far into the message parser this builder is */ - private int parserStage = 0; - - - - static - { - initLoadClass(); - } - - public Builder() { this(null, null, null); } - - public Builder( - Function typeFilter, - Function severityFilter, - Function sourceFilter) - { - this.typeFilter = typeFilter; - this.severityFilter = severityFilter; - this.sourceFilter = sourceFilter; - } - - - - /** - * Adds the given string to the message builder.

- * - * Will log a warning if the string given wasn't expected - * for the next stage of the OpenGL message format.

- * - * @return null if the message isn't complete - */ - public GLMessage add(String str) - { - // TODO fix implementation for MC 1.20.2 and newer - // please see the incomplete GLMessageTest for an example as to how the message formats differ - if (true) - return null; - - str = str.trim(); - if (str.isEmpty()) - return null; - - boolean parseSuccess = runNextParserStage(str); - if (parseSuccess && parserStage > FINAL_PARSER_STAGE_INDEX) - { - this.parserStage = 0; - GLMessage msg = new GLMessage(this.type, this.severity, this.source, this.id, this.message); - if (doesMessagePassFilters(msg)) - { - return msg; - } - } - else if (!parseSuccess) - { - LOGGER.warn("Failed to parse GLMessage line '{}' at stage {}", str, parserStage); - } - - // the message isn't finished yet - return null; - - // TODO implement a method that works for both MC 1.20.2+ and 1.20.1- - //if (str.equals(HEADER) && inProgressMessageBuilder.length() != 0) - //{ - // boolean parseSuccess = runNextParserStage(str); - // if (parseSuccess && parserStage > FINAL_PARSER_STAGE_INDEX) - // { - // this.parserStage = 0; - // GLMessage msg = new GLMessage(this.type, this.severity, this.source, this.id, this.message); - // if (doesMessagePassFilters(msg)) - // { - // return msg; - // } - // else - // { - // inProgressMessageBuilder.setLength(0); - // return null; - // } - // } - // else - // { - // if (!parseSuccess) - // { - // LOGGER.warn("Failed to parse GLMessage line '{}' at stage {}", str, parserStage); - // inProgressMessageBuilder.setLength(0); - // } - // - // return null; - // } - //} - //else - //{ - // inProgressMessageBuilder.append(str); - // return null; - //} - } - - private boolean doesMessagePassFilters(GLMessage msg) - { - if (this.sourceFilter != null && !this.sourceFilter.apply(msg.source)) - return false; - else if (this.typeFilter != null && !this.typeFilter.apply(msg.type)) - return false; - else if (this.severityFilter != null && !this.severityFilter.apply(msg.severity)) - return false; - else - return true; - } - - /** @return true if the given string was expected next for the OpenGL message format */ - private boolean runNextParserStage(String str) - { - switch (this.parserStage) - { - case 0: - return checkAndIncStage(str, GLMessage.HEADER); - case 1: - return checkAndIncStage(str, "ID"); - case 2: - return checkAndIncStage(str, ":"); - case 3: - this.id = str; - this.parserStage++; - return true; - case 4: - return checkAndIncStage(str, "Source"); - case 5: - return checkAndIncStage(str, ":"); - case 6: - this.source = ESource.get(str); - this.parserStage++; - return true; - case 7: - return checkAndIncStage(str, "Type"); - case 8: - return checkAndIncStage(str, ":"); - case 9: - this.type = EType.get(str); - this.parserStage++; - return true; - case 10: - return checkAndIncStage(str, "Severity"); - case 11: - return checkAndIncStage(str, ":"); - case 12: - this.severity = ESeverity.get(str); - this.parserStage++; - return true; - case 13: - return checkAndIncStage(str, "Message"); - case 14: - return checkAndIncStage(str, ":"); - case 15: - this.message = str; - this.parserStage++; - return true; - default: - return false; - } - } - - /** - * Returns true and increments the parserStage - * if the given and expected strings are the same. - */ - private boolean checkAndIncStage(String givenString, String expectedString) - { - boolean equal = givenString.equals(expectedString); - //boolean equal = givenString.contains(expectedString); - if (equal) - this.parserStage++; - return equal; - } - - } // builder class - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSeverity.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSeverity.java new file mode 100644 index 000000000..959e51223 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSeverity.java @@ -0,0 +1,54 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.objects.GLMessages; + +import java.util.HashMap; + +public enum EGLMessageSeverity +{ + HIGH, + MEDIUM, + LOW, + NOTIFICATION; + + + public final String name; + + static final HashMap ENUM_BY_NAME = new HashMap<>(); + + + static + { + for (EGLMessageSeverity severity : EGLMessageSeverity.values()) + { + ENUM_BY_NAME.put(severity.name, severity); + } + } + + EGLMessageSeverity() { this.name = super.toString().toUpperCase(); } + + + @Override + public final String toString() { return this.name; } + + public static EGLMessageSeverity get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } + +} + \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSource.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSource.java new file mode 100644 index 000000000..119204026 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageSource.java @@ -0,0 +1,55 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.objects.GLMessages; + +import java.util.HashMap; + +public enum EGLMessageSource +{ + API, + WINDOW_SYSTEM, + SHADER_COMPILER, + THIRD_PARTY, + APPLICATION, + OTHER; + + + private static final HashMap ENUM_BY_NAME = new HashMap<>(); + + public final String name; + + + static + { + for (EGLMessageSource source : EGLMessageSource.values()) + { + ENUM_BY_NAME.put(source.name, source); + } + } + + EGLMessageSource() { this.name = super.toString().toUpperCase(); } + + + @Override + public final String toString() { return this.name; } + + public static EGLMessageSource get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageType.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageType.java new file mode 100644 index 000000000..8ae4f9ef8 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/EGLMessageType.java @@ -0,0 +1,58 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.objects.GLMessages; + +import java.util.HashMap; + +public enum EGLMessageType +{ + ERROR, + DEPRECATED_BEHAVIOR, + UNDEFINED_BEHAVIOR, + PORTABILITY, + PERFORMANCE, + MARKER, + PUSH_GROUP, + POP_GROUP, + OTHER; + + + private static final HashMap ENUM_BY_NAME = new HashMap<>(); + + public final String name; + + + static + { + for (EGLMessageType type : EGLMessageType.values()) + { + ENUM_BY_NAME.put(type.name, type); + } + } + + EGLMessageType() { this.name = super.toString().toUpperCase(); } + + + @Override + public final String toString() { return this.name; } + + public static EGLMessageType get(String name) { return ENUM_BY_NAME.get(name.toUpperCase()); } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessage.java new file mode 100644 index 000000000..0e7afcb15 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessage.java @@ -0,0 +1,56 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.objects.GLMessages; + +public final class GLMessage +{ + static final String HEADER = "[LWJGL] OpenGL debug message"; + public final EGLMessageType type; + public final EGLMessageSeverity severity; + public final EGLMessageSource source; + public final String id; + public final String message; + + + + GLMessage(EGLMessageType type, EGLMessageSeverity severity, EGLMessageSource source, String id, String message) + { + this.type = type; + this.source = source; + this.severity = severity; + this.id = id; + this.message = message; + } + + + + @Override + public String toString() + { + return "level: [" + this.severity + "], " + + "type: [" + this.type + "], " + + "source: [" + this.source + "], " + + "id: [" + this.id + "], " + + "msg: [" + this.message + "]"; + } + + + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageBuilder.java new file mode 100644 index 000000000..8c775a818 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageBuilder.java @@ -0,0 +1,319 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.objects.GLMessages; + +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import org.apache.logging.log4j.Logger; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** Expected message formats can be found in GLMessageTest. */ +public class GLMessageBuilder +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + /** how many stages are present in the message parser */ + private static final int FINAL_LEGACY_PARSER_STAGE_INDEX = 15; + private static final int FINAL_NEW_PARSER_STAGE_INDEX = 5; + + + + private EGLMessageType type; + private EGLMessageSeverity severity; + private EGLMessageSource source; + + /** if the function returns false the message will be allowed */ + private final Function typeFilter; + /** if the function returns false the message will be allowed */ + private final Function severityFilter; + /** if the function returns false the message will be allowed */ + private final Function sourceFilter; + + private String id; + private String message; + /** how far into the message parser this builder is */ + private int parserStage = 0; + + private boolean legacyMessage = true; + + + + //==============// + // constructors // + //==============// + + public GLMessageBuilder() { this(null, null, null); } + + public GLMessageBuilder( + Function typeFilter, + Function severityFilter, + Function sourceFilter) + { + this.typeFilter = typeFilter; + this.severityFilter = severityFilter; + this.sourceFilter = sourceFilter; + } + + + + //=================// + // message parsing // + //=================// + + /** + * Adds the given string to the message builder.

+ * + * Will log a warning if the string given wasn't expected + * for the next stage of the OpenGL message format.

+ * + * @return null if the message isn't complete + */ + public GLMessage add(String str) + { + str = str.trim(); + if (str.isEmpty()) + { + return null; + } + + boolean messageFinished = false; + boolean parseSuccess = this.runNextParserStage(str); + + + if (this.legacyMessage + && this.parserStage > FINAL_LEGACY_PARSER_STAGE_INDEX) + { + messageFinished = true; + } + else if (!this.legacyMessage + && this.parserStage > FINAL_NEW_PARSER_STAGE_INDEX) + { + messageFinished = true; + } + + + if (parseSuccess && messageFinished) + { + this.parserStage = 0; + GLMessage msg = new GLMessage(this.type, this.severity, this.source, this.id, this.message); + if (this.doesMessagePassFilters(msg)) + { + return msg; + } + } + else if (!parseSuccess && messageFinished) + { + LOGGER.warn("Failed to parse GLMessage line [" + str + "] at stage [" + this.parserStage + "]"); + } + + // the message isn't finished yet + return null; + } + + private boolean doesMessagePassFilters(GLMessage msg) + { + if (this.sourceFilter != null && !this.sourceFilter.apply(msg.source)) + return false; + else if (this.typeFilter != null && !this.typeFilter.apply(msg.type)) + return false; + else if (this.severityFilter != null && !this.severityFilter.apply(msg.severity)) + return false; + else + return true; + } + + /** @return true if the given string was expected next for the OpenGL message format */ + private boolean runNextParserStage(String str) + { + if (this.parserStage == 0) + { + return this.checkExactAndIncStage(str, GLMessage.HEADER); + } + else if (this.parserStage == 1) + { + // legacy message only contains "ID" (not the colon) + this.legacyMessage = !str.contains("ID: "); + } + + + if (this.legacyMessage) + { + return this.runNextLegacyParserStage(str); + } + else + { + return this.runNextNewParserStage(str); + } + } + /** MC 1.20.2 and older */ + private boolean runNextLegacyParserStage(String str) + { + switch (this.parserStage) + { + case 0: + throw new IllegalStateException("Parser should be past stage ["+this.parserStage+"], next stage is [1]."); + case 1: + return this.checkExactAndIncStage(str, "ID"); + case 2: + return this.checkExactAndIncStage(str, ":"); + case 3: + this.id = str; + this.parserStage++; + return true; + case 4: + return this.checkExactAndIncStage(str, "Source"); + case 5: + return this.checkExactAndIncStage(str, ":"); + case 6: + this.source = EGLMessageSource.get(str); + this.parserStage++; + return true; + case 7: + return this.checkExactAndIncStage(str, "Type"); + case 8: + return this.checkExactAndIncStage(str, ":"); + case 9: + this.type = EGLMessageType.get(str); + this.parserStage++; + return true; + case 10: + return this.checkExactAndIncStage(str, "Severity"); + case 11: + return this.checkExactAndIncStage(str, ":"); + case 12: + this.severity = EGLMessageSeverity.get(str); + this.parserStage++; + return true; + case 13: + return this.checkExactAndIncStage(str, "Message"); + case 14: + return this.checkExactAndIncStage(str, ":"); + case 15: + this.message = str; + this.parserStage++; + return true; + default: + return false; + } + } + /** after MC 1.20.2 */ + private boolean runNextNewParserStage(String str) + { + switch (this.parserStage) + { + case 0: + throw new IllegalStateException("Parser should be past stage [" + this.parserStage + "], next stage is [1]."); + case 1: + String idPrefix = "ID: "; + return this.checkPrefixAndRun(str, idPrefix, + (line) -> + { + this.id = trySubstring(str, idPrefix.length()); + this.parserStage++; + }); + case 2: + String sourcePrefix = "Source: "; + return this.checkPrefixAndRun(str, sourcePrefix, + (line) -> + { + String sourceString = trySubstring(str, sourcePrefix.length()); + this.source = EGLMessageSource.get(sourceString); + this.parserStage++; + }); + case 3: + String typePrefix = "Type: "; + return this.checkPrefixAndRun(str, typePrefix, + (line) -> + { + String sourceString = trySubstring(str, typePrefix.length()); + this.type = EGLMessageType.get(sourceString); + this.parserStage++; + }); + case 4: + String severityPrefix = "Severity: "; + return this.checkPrefixAndRun(str, severityPrefix, + (line) -> + { + String sourceString = trySubstring(str, severityPrefix.length()); + this.severity = EGLMessageSeverity.get(sourceString); + this.parserStage++; + }); + case 5: + String messagePrefix = "Message: "; + return this.checkPrefixAndRun(str, messagePrefix, + (line) -> + { + this.message = trySubstring(str, messagePrefix.length()); + this.parserStage++; + }); + default: + return false; + } + } + + + + //================// + // helper methods // + //================// + + /** + * Returns true and increments the parserStage + * if the message and expected strings are the same. + */ + private boolean checkExactAndIncStage(String message, String expectedString) + { + boolean equal = message.equals(expectedString); + if (equal) + { + this.parserStage++; + } + return equal; + } + + /** + * Returns true and increments the parserStage + * if the message starts with the given prefix. + */ + private boolean checkPrefixAndRun(String message, String expectedPrefix, Consumer successConsumer) + { + boolean equal = message.startsWith(expectedPrefix); + if (equal) + { + successConsumer.accept(message); + } + return equal; + } + /** returns "" if the string isn't long enough */ + private static String trySubstring(String string, int beginIndex) + { + if (beginIndex > string.length()) + { + // prevent index-out-of-bounds errors + // if the message isn't what we expected + return ""; + } + + return string.substring(beginIndex); + } + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessageOutputStream.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageOutputStream.java similarity index 78% rename from core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessageOutputStream.java rename to core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageOutputStream.java index 70463061f..b29e5df22 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessageOutputStream.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/GLMessages/GLMessageOutputStream.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.util.objects; +package com.seibel.distanthorizons.core.util.objects.GLMessages; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -27,12 +27,12 @@ import java.util.function.Consumer; public final class GLMessageOutputStream extends OutputStream { final Consumer func; - final GLMessage.Builder builder; + final GLMessageBuilder builder; private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - public GLMessageOutputStream(Consumer func, GLMessage.Builder builder) + public GLMessageOutputStream(Consumer func, GLMessageBuilder builder) { this.func = func; this.builder = builder; @@ -41,24 +41,30 @@ public final class GLMessageOutputStream extends OutputStream @Override public void write(int b) { - buffer.write(b); - if (b == '\n') flush(); + this.buffer.write(b); + if (b == '\n') + { + this.flush(); + } } @Override public void flush() { - String str = buffer.toString(); - GLMessage msg = builder.add(str); - if (msg != null) func.accept(msg); - buffer.reset(); + String str = this.buffer.toString(); + GLMessage msg = this.builder.add(str); + if (msg != null) + { + this.func.accept(msg); + } + this.buffer.reset(); } @Override public void close() throws IOException { - flush(); - buffer.close(); + this.flush(); + this.buffer.close(); } } diff --git a/core/src/test/java/tests/GLMessageTest.java b/core/src/test/java/tests/GLMessageTest.java index 798e8534c..a96c8b354 100644 --- a/core/src/test/java/tests/GLMessageTest.java +++ b/core/src/test/java/tests/GLMessageTest.java @@ -19,7 +19,7 @@ package tests; -import com.seibel.distanthorizons.core.util.objects.GLMessage; +import com.seibel.distanthorizons.core.util.objects.GLMessages.*; import org.junit.Assert; import org.junit.Test; @@ -28,66 +28,88 @@ import java.util.ArrayList; public class GLMessageTest { public static final String MESSAGE_ID = "0x20071"; - public static final GLMessage.ESource MESSAGE_SOURCE = GLMessage.ESource.API; - public static final GLMessage.EType MESSAGE_TYPE = GLMessage.EType.OTHER; - public static final GLMessage.ESeverity MESSAGE_SEVERITY = GLMessage.ESeverity.NOTIFICATION; + public static final EGLMessageSource MESSAGE_SOURCE = EGLMessageSource.API; + public static final EGLMessageType MESSAGE_TYPE = EGLMessageType.OTHER; + public static final EGLMessageSeverity MESSAGE_SEVERITY = EGLMessageSeverity.NOTIFICATION; public static final String MESSAGE = "Buffer detailed info: Buffer object 1014084 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as\" \"the source for buffer object operations."; /** This is how debug messages were sent prior to Minecraft 1.20.2 */ - private static final String[] PRE_1_20_2_MESSAGE_ARRAY = + private static final String[] OLD_MESSAGE_ARRAY = { "[LWJGL] OpenGL debug message" - ,"ID", ":", "0x20071" - ,"Source", ":", "API" - ,"Type", ":", "OTHER" - ,"Severity", ":", "NOTIFICATION" - ,"Message", ":", "Buffer detailed info: Buffer object 1014084 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as\" \"the source for buffer object operations." + ,"ID", ":", MESSAGE_ID + ,"Source", ":", MESSAGE_SOURCE.name + ,"Type", ":", MESSAGE_TYPE.name + ,"Severity", ":", MESSAGE_SEVERITY.name + ,"Message", ":", MESSAGE // optional addition to force the builder into noticing the message ended, shouldn't be necessary //,"[LWJGL] OpenGL debug message" }; /** This is how debug messages were sent after (and including) Minecraft 1.20.2 */ - private static final String[] POST_1_20_2_MESSAGE_ARRAY = + private static final String[] NEW_MESSAGE_ARRAY = { "[LWJGL] OpenGL debug message" - ,"ID: 0x20071" - ,"Source: API" - ,"Type: OTHER" - ,"Severity: NOTIFICATION" - ,"Message: Buffer detailed info: Buffer object 1014084 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as\" \"the source for buffer object operations." - + ,"ID: " + MESSAGE_ID + ,"Source: " + MESSAGE_SOURCE.name + ,"Type: " + MESSAGE_TYPE.name + ,"Severity: " + MESSAGE_SEVERITY.name + ,"Message: " + MESSAGE + // optional addition to force the builder into noticing the message ended, shouldn't be necessary //,"[LWJGL] OpenGL debug message" }; + public final GLMessageBuilder messageBuilder = new GLMessageBuilder(null, null, null); + + //=======// + // tests // + //=======// + @Test public void preMc1_20_2() { ArrayList messageList = new ArrayList<>(); - for (String str : PRE_1_20_2_MESSAGE_ARRAY) + for (String str : OLD_MESSAGE_ARRAY) { - GLMessage message = GLMessage.Builder.DEFAULT_MESSAGE_BUILDER.add(str); + GLMessage message = this.messageBuilder.add(str); if (message != null) { messageList.add(message); } } - //Assert.assertEquals("Incorrect message parse count.", 1, messageList.size()); - //testMessage(messageList.get(0)); + Assert.assertEquals("Incorrect message parse count.", 1, messageList.size()); + messageMatchesExpected(messageList.get(0)); } @Test public void mc1_20_2() { - // TODO + ArrayList messageList = new ArrayList<>(); + for (String str : NEW_MESSAGE_ARRAY) + { + GLMessage message = this.messageBuilder.add(str); + if (message != null) + { + messageList.add(message); + } + } + + Assert.assertEquals("Incorrect message parse count.", 1, messageList.size()); + messageMatchesExpected(messageList.get(0)); } + + //================// + // helper methods // + //================// + private static void messageMatchesExpected(GLMessage testMessage) { Assert.assertEquals(MESSAGE_ID, testMessage.id); @@ -97,4 +119,6 @@ public class GLMessageTest Assert.assertEquals(MESSAGE, testMessage.message); } + + }