Repository navigation
Queued PBO Regression Draft
NativePixelBufferQueuedRemapProbe.java is an original draft for the existing mvhpackbench test package. It is not a production mod patch and has NOT been compiled or executed against Hari. The cloud has no usable Vulkan device. Do not report it as passing.
Integrate after reconciling the local API with published commit 98a85a89cb792d1d335b095ed774cf71773126fc. In NativePixelBufferSmoke, invoke it for each already hash-verified/transformed original Xaero class before that fixture binds its own buffers. The supplied class must be the qualified original caller, not untransformed LWJGL code. Run on the existing render thread during a recording frame and start with no pack/unpack PBO bound.
It allocates two 1x1 destinations before the critical sequence, uses the original map/unmap methods, queues red upload A, remaps and writes blue, queues upload B, and only then flushes/reads both destinations. It directly calls the existing native upload primitive with explicit row layout, avoiding accidental CPU-conversion fallback. Expected A=red, B=blue. The old mapped ByteBuffer argument is exercised without requiring identity after a legitimate copy-on-write replacement.
Review error cleanup in the actual harness, including device-loss handling, so a failed flush cannot mask the original failure or leave an unsafe cleanup path. Capture native validation output, both pixel results and allocation retirement after frame completion. Extend beyond this minimum case with partial writes, unchanged prefix/suffix, two frames in flight and deletion/reuse. The draft does not cover those extensions.
A synchronous implementation may pass by correctly waiting; passing alone does not establish a performance improvement. Measure its stalls separately. Do not execute the test in a live user save merely to obtain a green result.
package mvhpackbench;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import net.vulkanmod.gl.NativePixelBufferApi;
import net.vulkanmod.vulkan.Renderer;
import net.vulkanmod.vulkan.texture.ImageUtil;
import net.vulkanmod.vulkan.texture.VulkanImage;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.vulkan.VK10.*;
/** DRAFT: intended for the existing hash-qualified original-caller GPU suite.
* Not compiled or run against Hari in the cloud. No FPS acceptance claim.
* Call only on the render thread during a recording frame, with no PBO bound.
*/
final class NativePixelBufferQueuedRemapProbe {
private static Object call(Class<?> original, String method, Class<?>[] types, Object... args) throws Exception {
try { return original.getMethod(method, types).invoke(null, args); }
catch (InvocationTargetException e) { throw new IllegalStateException(method, e.getCause()); }
}
private static void bind(Class<?> original, int name) throws Exception {
call(original, "glBindBuffer", new Class<?>[]{int.class, int.class}, NativePixelBufferApi.UNPACK, name);
}
private static ByteBuffer map(Class<?> original, ByteBuffer previous) throws Exception {
ByteBuffer b = (ByteBuffer) call(original, "glMapBuffer",
new Class<?>[]{int.class, int.class, long.class, ByteBuffer.class},
NativePixelBufferApi.UNPACK, 0x88B9, 4L, previous);
if (b == null || b.capacity() < 4) throw new IllegalStateException("Writable map failed");
return b;
}
private static void unmap(Class<?> original) throws Exception {
if (!(boolean) call(original, "glUnmapBuffer", new Class<?>[]{int.class}, NativePixelBufferApi.UNPACK))
throw new IllegalStateException("Unmap failed");
}
private static void color(ByteBuffer b, int r, int g, int blue) {
b.put(0, (byte)r).put(1, (byte)g).put(2, (byte)blue).put(3, (byte)255);
}
private static VulkanImage image() {
return VulkanImage.builder(1, 1).setMipLevels(1).setFormat(VK_FORMAT_R8G8B8A8_UNORM)
.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT)
.setLinearFiltering(false).createVulkanImage();
}
private static void upload(VulkanImage image) {
if (!NativePixelBufferApi.upload(image, 0, 0, 0, 1, 1, 0x1908, 0x1401, 0L, 1, 4, 0, 0))
throw new IllegalStateException("Direct native PBO upload was not used");
}
private static void check(VulkanImage image, int r, int g, int blue, String name, JsonArray cases) {
ByteBuffer data = MemoryUtil.memAlloc(4);
try {
ImageUtil.downloadTexture(image, MemoryUtil.memAddress(data));
boolean ok = Byte.toUnsignedInt(data.get(0)) == r && Byte.toUnsignedInt(data.get(1)) == g
&& Byte.toUnsignedInt(data.get(2)) == blue && Byte.toUnsignedInt(data.get(3)) == 255;
JsonObject row = new JsonObject();
row.addProperty("case", name); row.addProperty("matched", ok);
row.addProperty("compared_pixels", 1); row.addProperty("mismatched_pixels", ok ? 0 : 1);
row.addProperty("fps_accepted", false); cases.add(row);
if (!ok) throw new IllegalStateException("Queued remap pixel mismatch: " + name);
} finally { MemoryUtil.memFree(data); }
}
static void run(Class<?> original, JsonArray cases, String tag) throws Exception {
if (!Renderer.isRecording()) throw new IllegalStateException("Recording frame required");
int pbo = 0;
VulkanImage a = null, b = null;
boolean recorded = false;
try {
// Allocate both destinations BEFORE the critical queued-write window.
a = image(); b = image();
pbo = (int) call(original, "glGenBuffers", new Class<?>[]{});
bind(original, pbo);
call(original, "glBufferData", new Class<?>[]{int.class, long.class, int.class},
NativePixelBufferApi.UNPACK, 4L, 0x88E0);
ByteBuffer first = map(original, null);
color(first, 255, 0, 0); unmap(original);
upload(a); recorded = true;
// NO readback, mipmap, glFinish, fence wait or frame retirement here.
ByteBuffer second = map(original, first);
// A new view/backing allocation is allowed; object identity is not required.
color(second, 0, 0, 255); unmap(original);
upload(b);
bind(original, 0);
Renderer.getInstance().flushForReadback();
recorded = false;
check(a, 255, 0, 0, tag + "-queued-red-before-remap", cases);
check(b, 0, 0, 255, tag + "-queued-blue-after-remap", cases);
} finally {
// Failure cleanup must also retire queued use before freeing destinations.
if (recorded) Renderer.getInstance().flushForReadback();
bind(original, 0);
if (pbo != 0) call(original, "glDeleteBuffers", new Class<?>[]{int.class}, pbo);
if (a != null) a.free();
if (b != null) b.free();
}
}
}Related investigation: https://github.com/Herbertofury/Minecraft-Vulkan-Hybrid/issues/3