Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
fe72612
Refactor of CommandBuffer to break it out into a more maintainable & …
RuffledPlume Oct 20, 2025
91637d0
Fixes, Clean up & better validation
RuffledPlume Oct 20, 2025
cb50ae9
Disable Validation by default
RuffledPlume Oct 20, 2025
e940ddf
Merge remote-tracking branch 'aHooder/zone-renderer' into CommandBuffer
RuffledPlume Oct 20, 2025
184e748
Optimizations, Tighter Packing, Validation improvements & Fixes :)
RuffledPlume Oct 20, 2025
4998e00
Removed old workaround
RuffledPlume Oct 20, 2025
1370047
Optimisations
RuffledPlume Oct 21, 2025
64be837
Refactor of CommandBuffer to break it out into a more maintainable & …
RuffledPlume Oct 20, 2025
45265b9
Fixes, Clean up & better validation
RuffledPlume Oct 20, 2025
ae3fe7c
Disable Validation by default
RuffledPlume Oct 20, 2025
b86505d
Optimizations, Tighter Packing, Validation improvements & Fixes :)
RuffledPlume Oct 20, 2025
cbc1024
Removed old workaround
RuffledPlume Oct 20, 2025
b262b4f
Optimisations
RuffledPlume Oct 21, 2025
758bb22
Merge remote-tracking branch 'origin/CommandBuffer' into CommandBuffer
RuffledPlume Oct 21, 2025
0e6d7ec
More Improvements to perf
RuffledPlume Oct 21, 2025
9b67b2b
Even more Optimizations
RuffledPlume Oct 21, 2025
80aca4d
Make Commands Local to the CommandBuffer so we don't need to pass a r…
RuffledPlume Oct 21, 2025
b168b12
More optimizations
RuffledPlume Oct 21, 2025
c8c7b33
Big Command Buffer Update
RuffledPlume Oct 21, 2025
c2598db
Fix Alpha Shadows
RuffledPlume Oct 21, 2025
fe9aaf6
Added BlitFrameBufferCommand with MSAA support
RuffledPlume Oct 21, 2025
fc350ab
Replaced UpdateCMDUBOCommand with SetUniformBufferPropertyCommand
RuffledPlume Oct 21, 2025
aff6bf0
UBOs are now tracked and uploaded before the next drawcall generically
RuffledPlume Oct 21, 2025
4a52669
Tweaks
RuffledPlume Oct 21, 2025
4a8a3bc
UI & Overlays now render using CommandBuffers
RuffledPlume Oct 21, 2025
d1d0502
Fixed Legacy
RuffledPlume Oct 21, 2025
9184bd6
Added FrameTimer Support for CommandBuffers
RuffledPlume Oct 21, 2025
022f04a
Finer grain timings
RuffledPlume Oct 21, 2025
969ff6f
RenderThread Implemention
RuffledPlume Oct 22, 2025
694add2
CommandBuffers now create two Commands per type
RuffledPlume Oct 22, 2025
f6c1cfd
Flickering UI Fixes
RuffledPlume Oct 22, 2025
25e986c
More Tweaks
RuffledPlume Oct 22, 2025
b1bef32
Improved Context Swapping Reliability
RuffledPlume Oct 23, 2025
4eefb73
Fixed edge case where MapLoader thread could call Completion callback…
RuffledPlume Oct 23, 2025
4c495f4
Cleanup
RuffledPlume Oct 23, 2025
e0c920c
Fixed Init & Shutdown issues with RenderThread
RuffledPlume Oct 23, 2025
cdd45b1
Tweaks
RuffledPlume Oct 23, 2025
512546b
Fixed Deadlock that occurs when Client releases, then reobtains the A…
RuffledPlume Oct 23, 2025
fcb77f0
Added Config option to disable RenderThread
RuffledPlume Oct 23, 2025
5975a17
preSceneDrawTopLevel now uses the RenderThread, meaning it doesn't ne…
RuffledPlume Oct 24, 2025
383b2f8
Zone Draws dont need to wait on the RenderThread, currently only Dyna…
RuffledPlume Oct 24, 2025
2d201fb
The Big Synchronisation update... RenderThread now maintains ownershi…
RuffledPlume Oct 25, 2025
638de15
Optimizations
RuffledPlume Oct 25, 2025
2e54905
Tweaks and improvements
RuffledPlume Oct 25, 2025
fad6de1
Removed Redundant CheckGLErrors
RuffledPlume Oct 25, 2025
b525327
FrameSync should spinlock to avoid making the RenderThread unpark the…
RuffledPlume Oct 25, 2025
d3a1f8b
Tweaks & Fixes
RuffledPlume Oct 25, 2025
ba06f2e
Double buffer the pixel unpack buffer
RuffledPlume Oct 25, 2025
72d2750
Tweaks
RuffledPlume Oct 25, 2025
7cd2863
Merge remote-tracking branch 'aHooder/zone-renderer' into CommandBuffer
RuffledPlume Oct 26, 2025
9d34f44
Fixed FrameTimer
RuffledPlume Oct 26, 2025
083c6de
Optimizations
RuffledPlume Oct 26, 2025
1d3ddf1
Optimizations
RuffledPlume Oct 26, 2025
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
233 changes: 118 additions & 115 deletions src/main/java/rs117/hd/HdPlugin.java

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions src/main/java/rs117/hd/HdPluginConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ default SyncMode syncMode()
)
@Range(
min = 0,
max = 999
max = 2000
)
default int fpsTarget()
{
Expand Down Expand Up @@ -1098,17 +1098,6 @@ default boolean wireframe() {
return false;
}

String KEY_ASYNC_UI_COPY = "experimentalAsyncUICopy";
@ConfigItem(
keyName = KEY_ASYNC_UI_COPY,
name = "Perform UI copy asynchronously",
description = "Slightly improves performance by delaying the UI by one frame.",
section = experimentalSettings
)
default boolean asyncUICopy() {
return false;
}

String KEY_TILED_LIGHTING_IMAGE_STORE = "experimentalTiledLightingImageStore";
@ConfigItem(
keyName = KEY_TILED_LIGHTING_IMAGE_STORE,
Expand All @@ -1120,6 +1109,17 @@ default boolean tiledLightingImageLoadStore() {
return true;
}

String KEY_RENDER_THREAD = "experimentalRenderThread";
@ConfigItem(
keyName = KEY_RENDER_THREAD,
name = "Render Thread",
description = "Its a Render Thread...",
section = experimentalSettings
)
default boolean renderThread() {
return true;
}


/*====== Internal settings ======*/

Expand Down
108 changes: 0 additions & 108 deletions src/main/java/rs117/hd/opengl/AsyncUICopy.java

This file was deleted.

149 changes: 149 additions & 0 deletions src/main/java/rs117/hd/opengl/commandbuffer/AWTContextWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package rs117.hd.opengl.commandbuffer;

import java.awt.Canvas;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.*;
import net.runelite.rlawt.AWTContext;
import org.lwjgl.opengl.*;
import rs117.hd.HdPlugin;

@Slf4j
@Singleton
public final class AWTContextWrapper {
private static final boolean VALIDATE = log.isDebugEnabled();

public enum Owner { CLIENT, RENDER_THREAD, NONE }

private final Object ownershipLock = new Object();
private volatile Owner currentOwner = Owner.CLIENT;

private static final int MAX_OWNERSHIP_LOG = 8;
private final ConcurrentLinkedQueue<String> ownershipLog = new ConcurrentLinkedQueue<>();

private final StringBuilder sb = new StringBuilder();

@Inject
private Client client;

@Getter
private AWTContext context;

@Getter
private Canvas canvas;

public boolean initialize() {
AWTContext.loadNatives();
canvas = client.getCanvas();

synchronized (canvas.getTreeLock()) {
// Delay plugin startup until the client's canvas is valid
if (!canvas.isValid())
return false;

context = new AWTContext(canvas);
context.configurePixelFormat(0, 0, 0);
}

context.createGLContext();
canvas.setIgnoreRepaint(true);
return true;
}

public void shutdown() {
if (context != null)
context.destroy();
context = null;

// Validate the canvas so it becomes valid without having to manually resize the client
if(canvas != null)
canvas.validate();
canvas = null;
}

public int setSwapInterval(int interval) { return context.setSwapInterval(interval); }

public int getBufferMode() { return context.getBufferMode(); }

public int getBackBuffer() { return context.getFramebuffer(false); }

public Owner getOwner() { return currentOwner; }

public void makeCurrent(Owner newOwner) {
if (currentOwner == newOwner)
return;

synchronized (ownershipLock) {
while (currentOwner != Owner.NONE) {
try {
ownershipLock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}

try {
context.makeCurrent();
if(newOwner == Owner.RENDER_THREAD && HdPlugin.GL_RENDER_THREAD_CAPS == null){
HdPlugin.GL_RENDER_THREAD_CAPS = GL.createCapabilities();
}
updateOwner(newOwner, "makeCurrent");
} catch (Exception e) {
printOwnershipLog();
log.error("Failed to make OpenGL context current for {}", newOwner, e);
throw new RuntimeException(e);
}
}
}

public void detachCurrent(String reason) {
try {
context.detachCurrent();
updateOwner(Owner.NONE, reason);
} catch (Exception e) {
printOwnershipLog();
log.error("Failed to detach OpenGL context: " + reason, e);
throw new RuntimeException(e);
}
}

private void updateOwner(Owner newOwner, String action) {
if (currentOwner != newOwner && VALIDATE) {
if (ownershipLog.size() >= MAX_OWNERSHIP_LOG)
ownershipLog.poll();

sb.append(currentOwner)
.append(" -> ")
.append(newOwner)
.append(" via ")
.append(action);

ownershipLog.add(sb.toString());

sb.setLength(0);
}

currentOwner = newOwner;

synchronized (ownershipLock) {
ownershipLock.notifyAll();
}
}

private void printOwnershipLog() {
if (!VALIDATE) return;

if (ownershipLog.isEmpty()) {
log.error("No recent ownership transitions recorded.");
} else {
log.error("Recent ownership transitions:");
for (String entry : ownershipLog) {
log.error(" - {}", entry);
}
}
}
}
67 changes: 67 additions & 0 deletions src/main/java/rs117/hd/opengl/commandbuffer/BaseCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package rs117.hd.opengl.commandbuffer;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.lwjgl.system.MemoryStack;
import rs117.hd.opengl.uniforms.UniformBuffer;

@Slf4j
public abstract class BaseCommand {

protected int id;

@Getter
private final String name;

@Getter
private final boolean isDrawCall;

@Getter
private final boolean isGLCommand;

protected CommandBuffer owner;

protected BaseCommand(boolean isDrawCall, boolean isGLCommand) {
this.name = getClass().getSimpleName();
this.isDrawCall = isDrawCall;
this.isGLCommand = isDrawCall || isGLCommand;
}

protected BaseCommand(boolean isDrawCall) { this(isDrawCall, false); }

protected BaseCommand() { this(false, false); }

protected abstract void execute(MemoryStack stack);
protected abstract void print(StringBuilder sb);

protected final void write() {
write8(id);
doWrite();
}

protected abstract void doWrite();
protected abstract void doRead(MemoryStack stack);

protected final void markUniformBufferDirty(UniformBuffer buffer) { owner.markUniformBufferDirty(buffer); }

protected final void write1(int value) { owner.writeBits(value & 0x1L, 1); }
protected final void write8(int value) { owner.writeBits(value & 0xFFL, 8); }
protected final void write16(int value) { owner.writeBits(value & 0xFFFFL, 16); }
protected final void write32(int value) { owner.writeBits(value & 0xFFFFFFFFL, 32); }
protected final void write64(long value) { owner.writeBits(value, 64); }

protected final int read1() { return (int) owner.readBits(1); }
protected final int read8() { return (int) owner.readBits(8); }
protected final int read16() { return (int) owner.readBits(16); }
protected final int read32() { return (int) owner.readBits(32); }
protected final long read64() { return owner.readBits(64); }

protected final void writeObject(Object value) {owner.writeObject(value);}
protected final <T> T readObject() { return owner.readObject(); }

protected final void write32F(float value) { write32(Float.floatToIntBits(value)); }
protected final float read32F() { return Float.intBitsToFloat(read32()); }

protected final void writeFlag(boolean value) { write1(value ? 1 : 0); }
protected final boolean readFlag() { return read1() != 0; }
}
Loading