Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package me.jellysquid.mods.sodium.client.compatibility.workarounds.amd;

import me.jellysquid.mods.sodium.client.platform.windows.WindowsCommandLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.Util;

public class AmdWorkarounds {
private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-AmdWorkarounds");

public static void undoEnvironmentChanges() {
if (Util.getPlatform() == Util.OS.WINDOWS) {
undoEnvironmentChanges$Windows();
}
}

private static void undoEnvironmentChanges$Windows() {
WindowsCommandLine.resetCommandLine();
}

public static void applyEnvironmentChanges() {
// We can't know if the OpenGL context will actually be initialized using the AMD ICD, but we need to
// modify the process environment *now* otherwise the driver will initialize with bad settings. For non-AMD
// drivers, these workarounds are not likely to cause issues.
LOGGER.info("Modifying process environment to apply workarounds for the AMD graphics driver...");

try {
if (Util.getPlatform() == Util.OS.WINDOWS) {
applyEnvironmentChanges$Windows();
}
} catch (Throwable t) {
LOGGER.error("Failed to modify the process environment", t);
logWarning();
}
}


private static void applyEnvironmentChanges$Windows() {
// The new AMD drivers rely on parsing the command line arguments to detect Minecraft.
// When they do they apply an optimization that is broken with sodium present
// This stops AMD drivers from detecting the game
WindowsCommandLine.setCommandLine("net.caffeinemc.sodium / net.minecraft.client.main.Main /");
}

private static void logWarning() {
LOGGER.error("READ ME!");
LOGGER.error("READ ME! The workarounds for the AMD Graphics Driver did not apply correctly!");
LOGGER.error("READ ME! You may run into unexplained graphical issues.");
LOGGER.error("READ ME! More information about what went wrong can be found above this message.");
LOGGER.error("READ ME!");
LOGGER.error("READ ME! Please help us understand why this problem occurred by opening a bug report on our issue tracker:");
LOGGER.error("READ ME! https://github.com/CaffeineMC/sodium/issues");
LOGGER.error("READ ME!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,16 @@ public ImmediateDrawCommandList() {
@Override
public void multiDrawElementsBaseVertex(MultiDrawBatch batch, GlIndexType indexType) {
GlPrimitiveType primitiveType = GLRenderDevice.this.activeTessellation.getPrimitiveType();
int mode = primitiveType.getId();
int type = indexType.getFormatId();

GL32C.nglMultiDrawElementsBaseVertex(primitiveType.getId(),
batch.pElementCount,
indexType.getFormatId(),
batch.pElementPointer,
batch.size(),
batch.pBaseVertex);
for (int i = 0; i < batch.size(); i++) {
int count = org.lwjgl.system.MemoryUtil.memGetInt(batch.pElementCount + ((long) i * Integer.BYTES));
long indices = org.lwjgl.system.MemoryUtil.memGetAddress(batch.pElementPointer + ((long) i * org.lwjgl.system.Pointer.POINTER_SIZE));
int baseVertex = org.lwjgl.system.MemoryUtil.memGetInt(batch.pBaseVertex + ((long) i * Integer.BYTES));

GL32C.nglDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ public static class PerformanceSettings {
public boolean animateOnlyVisibleTextures = true;
public boolean useEntityCulling = true;
public boolean useFogOcclusion = true;
public boolean useBlockFaceCulling = true;
public boolean useCompactVertexFormat = true;
public boolean useBlockFaceCulling = false;
public boolean useCompactVertexFormat = false;
@SerializedName("use_translucent_face_sorting_v2")
public boolean useTranslucentFaceSorting = true;
public boolean useRenderPassOptimization = true;
public boolean useNoErrorGLContext = true;
public boolean useNoErrorGLContext = false;
}

public static class AdvancedSettings {
public boolean enableMemoryTracing = false;
public boolean useAdvancedStagingBuffers = true;
public boolean useAdvancedStagingBuffers = false;
public boolean disableIncompatibleModWarnings = false;

public int cpuRenderAheadLimit = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import me.jellysquid.mods.sodium.client.platform.windows.api.Kernel32;
import org.lwjgl.system.MemoryUtil;

import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Objects;
Expand All @@ -17,11 +18,15 @@ public static void setCommandLine(String modifiedCmdline) {
// Pointer into the command-line arguments stored within the Windows process structure
// We do not own this memory, and it should not be freed.
var pCmdline = Kernel32.getCommandLine();
var pCmdlineA = Kernel32.getCommandLineA();

// The original command-line the process was started with.
var cmdline = MemoryUtil.memUTF16(pCmdline);
var cmdlineLen = MemoryUtil.memLengthUTF16(cmdline, true);

var cmdlineA = MemoryUtil.memASCII(pCmdlineA);
var cmdLineLenA = MemoryUtil.memLengthASCII(cmdlineA, true);

if (MemoryUtil.memLengthUTF16(modifiedCmdline, true) > cmdlineLen) {
// We can never write a string which is larger than what we were given, as there
// may not be enough space remaining. Realistically, this should never happen, since
Expand All @@ -30,21 +35,30 @@ public static void setCommandLine(String modifiedCmdline) {
throw new BufferOverflowException();
}

if (MemoryUtil.memLengthASCII(modifiedCmdline, true) > cmdLineLenA) {
throw new BufferOverflowException();
}

ByteBuffer buffer = MemoryUtil.memByteBuffer(pCmdline, cmdlineLen);
ByteBuffer bufferA = MemoryUtil.memByteBuffer(pCmdlineA, cmdLineLenA);

// Write the new command line arguments into the process structure.
// The Windows API documentation explicitly says this is forbidden, but it *does* give us a pointer
// directly into the PEB structure, so...
MemoryUtil.memUTF16(modifiedCmdline, true, buffer);
MemoryUtil.memASCII(modifiedCmdline, true, bufferA);

// Make sure we can actually see our changes in the process structure
// We don't know if this could ever actually happen, but since we're doing something pretty hacky
// it's not out of the question that Windows might try to prevent it in a newer version.
if (!Objects.equals(modifiedCmdline, MemoryUtil.memUTF16(pCmdline))) {
throw new RuntimeException("Sanity check failed, the command line arguments did not appear to change");
}
if (!Objects.equals(modifiedCmdline, MemoryUtil.memASCII(pCmdlineA))) {
throw new RuntimeException("Sanity check failed, the command line arguments did not appear to change");
}

ACTIVE_COMMAND_LINE_HOOK = new CommandLineHook(cmdline, buffer);
ACTIVE_COMMAND_LINE_HOOK = new CommandLineHook(cmdline, cmdlineA, buffer, bufferA);
}

public static void resetCommandLine() {
Expand All @@ -56,13 +70,17 @@ public static void resetCommandLine() {

private static class CommandLineHook {
private final String cmdline;
private final String cmdlineA;
private final ByteBuffer cmdlineBuf;
private final ByteBuffer cmdlineBufA;

private boolean active = true;

private CommandLineHook(String cmdline, ByteBuffer cmdlineBuf) {
private CommandLineHook(String cmdline, String cmdlineA, ByteBuffer cmdlineBuf, ByteBuffer cmdlineBufA) {
this.cmdline = cmdline;
this.cmdlineA = cmdlineA;
this.cmdlineBuf = cmdlineBuf;
this.cmdlineBufA = cmdlineBufA;
}

public void uninstall() {
Expand All @@ -73,6 +91,7 @@ public void uninstall() {
// Restore the original value of the command line arguments
// Must be null-terminated (as it was given to us)
MemoryUtil.memUTF16(this.cmdline, true, this.cmdlineBuf);
MemoryUtil.memASCII(this.cmdlineA, true, this.cmdlineBufA);

this.active = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class Kernel32 {
private static final int GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 1 << 2;

private static final long PFN_GetCommandLineW;
private static final long PFN_GetCommandLineA;
private static final long PFN_SetEnvironmentVariableW;

private static final long PFN_GetModuleHandleExW;
Expand All @@ -26,6 +27,7 @@ public class Kernel32 {

static {
PFN_GetCommandLineW = APIUtil.apiGetFunctionAddress(LIBRARY, "GetCommandLineW");
PFN_GetCommandLineA = APIUtil.apiGetFunctionAddress(LIBRARY, "GetCommandLineA");
PFN_SetEnvironmentVariableW = APIUtil.apiGetFunctionAddress(LIBRARY, "SetEnvironmentVariableW");
PFN_GetModuleHandleExW = APIUtil.apiGetFunctionAddress(LIBRARY, "GetModuleHandleExW");
PFN_GetLastError = APIUtil.apiGetFunctionAddress(LIBRARY, "GetLastError");
Expand All @@ -52,6 +54,10 @@ public static long getCommandLine() {
return JNI.callP(PFN_GetCommandLineW);
}

public static long getCommandLineA() {
return JNI.callP(PFN_GetCommandLineA);
}

public static long getModuleHandleByNames(String[] names) {
for (String name : names) {
var handle = getModuleHandleByName(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,19 @@ private int getNextSize(int primitiveCount) {
private void grow(CommandList commandList, int primitiveCount) {
var bufferSize = primitiveCount * this.indexType.getBytesPerElement() * ELEMENTS_PER_PRIMITIVE;

commandList.allocateStorage(this.buffer, bufferSize, GlBufferUsage.STATIC_DRAW);

var mapped = commandList.mapBuffer(this.buffer, 0, bufferSize, EnumBitField.of(GlBufferMapFlags.INVALIDATE_BUFFER, GlBufferMapFlags.WRITE, GlBufferMapFlags.UNSYNCHRONIZED));
this.indexType.createIndexBuffer(mapped.getMemoryBuffer(), primitiveCount);

commandList.unmap(mapped);
// [AMD RDNA WORKAROUND]: Use safe RAM allocations and bulk copy (glBufferData)
// instead of mapping (glMapBufferRange). AMD drivers frequently fail mapped
// synchronizations for index buffers, causing severe geometry corruption.
java.nio.ByteBuffer tempBuffer = org.lwjgl.system.MemoryUtil.memAlloc(bufferSize);
try {
this.indexType.createIndexBuffer(tempBuffer, primitiveCount);
// TempBuffer is already at position 0 with limit=bufferSize.
// The writer array modifies it locally with absolute indices,
// so we don't need to flip() here.
commandList.uploadData(this.buffer, tempBuffer, GlBufferUsage.STATIC_DRAW);
} finally {
org.lwjgl.system.MemoryUtil.memFree(tempBuffer);
}

this.maxPrimitives = primitiveCount;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,8 @@ private record PendingResortUpload(RenderSection section, BuiltSectionMeshParts


private static StagingBuffer createStagingBuffer(CommandList commandList) {
if (SodiumClientMod.options().advanced.useAdvancedStagingBuffers && MappedStagingBuffer.isSupported(RenderDevice.INSTANCE)) {
return new MappedStagingBuffer(commandList);
}

// [AMD RDNA WORKAROUND]: Force FallbackStagingBuffer to prevent glMapBufferRange crashes on Windows Drivers.
// This ensures compatibility with Adrenalin drivers even if the JSON option is left enabled.
return new FallbackStagingBuffer(commandList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import me.jellysquid.mods.sodium.client.compatibility.checks.LateDriverScanner;
import me.jellysquid.mods.sodium.client.compatibility.workarounds.Workarounds;
import me.jellysquid.mods.sodium.client.compatibility.workarounds.nvidia.NvidiaWorkarounds;
import me.jellysquid.mods.sodium.client.compatibility.workarounds.amd.AmdWorkarounds;
import net.minecraft.Util;
import net.minecraftforge.fml.loading.FMLConfig;
import net.minecraftforge.fml.loading.ImmediateWindowHandler;
Expand Down Expand Up @@ -51,6 +52,8 @@ private long wrapGlfwCreateWindow(IntSupplier width, IntSupplier height, Supplie
if (applyNvidiaWorkarounds) {
NvidiaWorkarounds.install();
}

AmdWorkarounds.applyEnvironmentChanges();

/**
* @author Asek3
Expand All @@ -73,6 +76,7 @@ private long wrapGlfwCreateWindow(IntSupplier width, IntSupplier height, Supplie
if (applyNvidiaWorkarounds) {
NvidiaWorkarounds.uninstall();
}
AmdWorkarounds.undoEnvironmentChanges();
}
}

Expand Down
Loading