mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-08 12:35:01 +00:00
Harden SDL-GPU buffer mapping lifetime (#2217)
This commit is contained in:
1 parent
38af64f10c
commit
12857006c6
19 files changed
+931
-235
No files matched your search
@@ -6987,7 +6987,7 @@ public class GLStateManager {
|
||||
|
||||
private static void onBufferStorage(int target, int flags) {
|
||||
if ((flags & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
if (!RENDER_BACKEND.isBufferImmutable(bufferForTarget(ctx(), target))) markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferRespec(target);
|
||||
@@ -7070,14 +7070,27 @@ public class GLStateManager {
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { onBufferMapRange(target, access); return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access, length, old_buffer);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBufferRange(target, offset, length, access);
|
||||
if (buf != null) onBufferMapRange(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static long glMapBufferRangeAddress(int target, long offset, long length, int access) {
|
||||
onBufferMapRange(target, access);
|
||||
return RENDER_BACKEND.mapBufferRangeAddress(target, offset, length, access);
|
||||
final long address = RENDER_BACKEND.mapBufferRangeAddress(target, offset, length, access);
|
||||
if (address != 0L) onBufferMapRange(target, access);
|
||||
return address;
|
||||
}
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) { onBufferUnmap(target); return RENDER_BACKEND.unmapBuffer(target); }
|
||||
@@ -7466,8 +7479,8 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
public static long nglMapBuffer(int target, int access) {
|
||||
onBufferMap(target, access);
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
public static void nglMultiDrawElementsBaseVertex(int mode, long count, int type, long indices, int primcount, long basevertex) {
|
||||
|
||||
@@ -59,6 +59,7 @@ public abstract class RenderBackend {
|
||||
|
||||
public boolean framebufferCompletenessIsMeaningful() { return true; }
|
||||
public void onPersistentBufferWrite(int glId, long offset, long size) {}
|
||||
public boolean isBufferImmutable(int glId) { return false; }
|
||||
|
||||
public boolean isAnisotropicSupported() { return true; }
|
||||
|
||||
|
||||
+174
-119
@@ -41,6 +41,7 @@ import com.gtnewhorizons.angelica.sdlgpu.resource.FBOClearTracker;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FormatMap;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.Image3DClear;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.MappedRange;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentBufferSync;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentMapping;
|
||||
@@ -64,6 +65,7 @@ import com.gtnewhorizons.angelica.sdlgpu.util.ThreadRegistry;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -168,6 +170,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
private final FFPTrace ffpTrace = SystemProperties.FFP_TRACE ? new FFPTrace(resourceManager) : null;
|
||||
|
||||
private final IntOpenHashSet missingProgramWarned = new IntOpenHashSet();
|
||||
private final LongOpenHashSet warnedBufferOps = new LongOpenHashSet();
|
||||
|
||||
private boolean droppedCopyWarned;
|
||||
private boolean droppedSsboWriteWarned;
|
||||
@@ -248,7 +251,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
|
||||
@Override public void onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
persistentSync.onPersistentBufferWrite(glId, offset, size);
|
||||
if (!persistentSync.onPersistentBufferWrite(glId, offset, size)) {
|
||||
warnInvalidBufferOp("persistent write out of range", glId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPostWindowCreate(long window) {
|
||||
@@ -515,14 +520,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
st.appliedLogicOpKey = 0;
|
||||
st.fragSamplerBindings.free();
|
||||
st.vertSamplerBindings.free();
|
||||
if (st.mappedStagingBuffer != null) {
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
}
|
||||
clearMappedState(st);
|
||||
}
|
||||
registeredStates.clear();
|
||||
|
||||
drawDispatch.clearWarnedState();
|
||||
synchronized (warnedBufferOps) {
|
||||
warnedBufferOps.clear();
|
||||
}
|
||||
attachmentClear.shutdown();
|
||||
depthStencilReadback.shutdown();
|
||||
textureOps.shutdown();
|
||||
@@ -1333,6 +1337,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int srcGlId = getBoundBuffer(readTarget);
|
||||
final int dstGlId = getBoundBuffer(writeTarget);
|
||||
if (srcGlId == 0 || dstGlId == 0 || size <= 0) return;
|
||||
final long srcSize = resourceManager.getBufferSize(srcGlId);
|
||||
final long dstSize = resourceManager.getBufferSize(dstGlId);
|
||||
if (readOffset < 0 || writeOffset < 0 || (srcSize > 0 && (readOffset > srcSize || size > srcSize - readOffset)) || (dstSize > 0 && (writeOffset > dstSize || size > dstSize - writeOffset))) {
|
||||
warnInvalidBufferOp("glCopyBufferSubData out of range", dstGlId);
|
||||
return;
|
||||
}
|
||||
resourceManager.dropArrayShadow(dstGlId);
|
||||
resourceManager.dropEboShadow(dstGlId);
|
||||
resourceManager.markBufferContentsDefined(dstGlId);
|
||||
@@ -1347,7 +1357,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
deferredCopyOp.dstHandle = dstHandle;
|
||||
deferredCopyOp.writeOffset = writeOffset;
|
||||
deferredCopyOp.seq = seq;
|
||||
final boolean handled = resourceManager.enqueuePersistentCopyDeferred(srcGlId, dstGlId, seq, deferredCopyOp);
|
||||
final boolean handled = resourceManager.enqueuePersistentCopyDeferred(srcGlId, dstGlId, seq, readOffset, size, deferredCopyOp);
|
||||
if (handled) {
|
||||
wakeTransferThread();
|
||||
return;
|
||||
@@ -2761,8 +2771,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
private void deleteBuffer(int buffer) {
|
||||
dropMappingFor(buffer);
|
||||
if (buffer != 0) unbindDeletedBuffer(s(), buffer);
|
||||
if (buffer != 0) {
|
||||
freePlainMappingFor(buffer, false);
|
||||
unbindDeletedBuffer(s(), buffer);
|
||||
}
|
||||
readbackShadows.release(buffer);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
}
|
||||
@@ -2809,12 +2821,6 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (ssboChanged) st.ssboBindGen++;
|
||||
}
|
||||
|
||||
private static void dropMappingFor(int buffer) {
|
||||
final ContextState st = s();
|
||||
if (buffer == 0 || st.mappedBufferGlId != buffer || st.mappedStagingBuffer == null) return;
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
}
|
||||
@Override public void bindBuffer(int target, int buffer) {
|
||||
final ContextState st = s();
|
||||
switch (target) {
|
||||
@@ -2914,8 +2920,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override public void bufferData(int target, long size, int usage) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferData on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, size, usage);
|
||||
}
|
||||
|
||||
private void respecifyStore(ContextState cs, int target, int glId, long size, int usage) {
|
||||
freePlainMappingFor(glId, true);
|
||||
resourceManager.recordBufferGlParams(glId, usage, BufferParams.MUTABLE_STORE);
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
if (resourceManager.getBufferSize(glId) == size) return;
|
||||
@@ -2931,14 +2944,45 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
readbackShadows.release(glId);
|
||||
resourceManager.deleteBuffer(glId);
|
||||
resourceManager.createBuffer(glId, sdlUsage, size);
|
||||
if (target == GL15.GL_ARRAY_BUFFER) s().bumpAttribStateGen();
|
||||
if (target == GL15.GL_ARRAY_BUFFER) cs.bumpAttribStateGen();
|
||||
}
|
||||
|
||||
private void freePlainMappingFor(int glId, boolean warn) {
|
||||
final ByteBuffer staging = resourceManager.takePlainStaging(glId);
|
||||
if (staging == null) return;
|
||||
MemoryUtil.memFree(staging);
|
||||
if (warn) warnInvalidBufferOp("glBufferData on mapped buffer", glId);
|
||||
}
|
||||
|
||||
private boolean refuseImmutable(int glId, String op) {
|
||||
if (resourceManager.getBufferStorageFlags(glId) == BufferParams.MUTABLE_STORE) return false;
|
||||
warnInvalidBufferOp(op, glId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isBufferImmutable(int glId) {
|
||||
return resourceManager.getBufferStorageFlags(glId) != BufferParams.MUTABLE_STORE;
|
||||
}
|
||||
|
||||
private void warnInvalidBufferOp(String op, int glId) {
|
||||
synchronized (warnedBufferOps) {
|
||||
if (warnedBufferOps.add(((long) op.hashCode() << 32) | (glId & 0xFFFFFFFFL))) {
|
||||
LOG.warn("{} (buffer {})", op, glId, new Throwable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void bufferData(int target, ByteBuffer data, int usage) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || data == null) return;
|
||||
if (refuseImmutable(glId, "glBufferData on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, data, usage);
|
||||
}
|
||||
|
||||
private void respecifyStore(ContextState cs, int target, int glId, ByteBuffer data, int usage) {
|
||||
final long dataSize = data.remaining();
|
||||
freePlainMappingFor(glId, true);
|
||||
resourceManager.recordBufferGlParams(glId, usage, BufferParams.MUTABLE_STORE);
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
if (dataSize > 0) {
|
||||
@@ -3028,11 +3072,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void bufferSubData(int target, long offset, ByteBuffer data) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || data == null) return;
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow != null) {
|
||||
if (offset < 0 || offset + data.remaining() > shadow.capacity()) {
|
||||
warnInvalidBufferOp("glBufferSubData out of range on uniform buffer", glId);
|
||||
return;
|
||||
}
|
||||
shadow.position(0);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, (int) offset, data.remaining());
|
||||
}
|
||||
@@ -3101,133 +3149,132 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public boolean unmapBuffer(int target) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
|
||||
final PersistentMapping pm = resourceManager.removePersistentMapping(glId);
|
||||
if (pm != null) {
|
||||
resourceManager.releasePersistentStaging(pm);
|
||||
if (resourceManager.getPersistentMapping(glId) != null) {
|
||||
final PersistentMapping pm = resourceManager.removePersistentMapping(glId);
|
||||
if (pm != null) resourceManager.releasePersistentStaging(pm, glId, "unmap");
|
||||
return true;
|
||||
}
|
||||
|
||||
final ContextState st = cs;
|
||||
if (st.mappedStagingBuffer == null) return false;
|
||||
final MappedRange m = cs.mappingScratch;
|
||||
if (!resourceManager.takePlainMapping(glId, m)) {
|
||||
warnInvalidBufferOp("glUnmapBuffer on unmapped buffer", glId);
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean writeMap = (st.mappedAccessFlags & GL30.GL_MAP_WRITE_BIT) != 0;
|
||||
final ByteBuffer staging = m.staging;
|
||||
m.staging = null;
|
||||
final boolean writeMap = (m.accessFlags & GL30.GL_MAP_WRITE_BIT) != 0;
|
||||
|
||||
if (target == GL30.GL_PIXEL_UNPACK_BUFFER) {
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
final long gpuHandle = resourceManager.getBufferHandle(st.mappedBufferGlId);
|
||||
staging.position(0).limit((int) m.length);
|
||||
final long gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
if (writeMap && gpuHandle != 0) {
|
||||
if (st.deferUploads) {
|
||||
enqueuePreCopied(st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
if (cs.deferUploads) {
|
||||
enqueuePreCopied(staging, gpuHandle, m.offset, false);
|
||||
} else if (frameManager.getCommandBuffer() != 0) {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), staging, gpuHandle, m.offset, false);
|
||||
}
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
final ByteBuffer old = resourceManager.putPboStaging(glId, st.mappedStagingBuffer);
|
||||
final ByteBuffer old = resourceManager.putPboStaging(glId, staging);
|
||||
if (old != null) MemoryUtil.memFree(old);
|
||||
clearMappedState(st);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(st.mappedBufferGlId);
|
||||
if (writeMap && shadow != null && st.mappedOffset + st.mappedLength <= shadow.capacity()) {
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
shadow.position((int) st.mappedOffset).limit((int) (st.mappedOffset + st.mappedLength));
|
||||
shadow.put(st.mappedStagingBuffer);
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (writeMap && shadow != null && m.offset + m.length <= shadow.capacity()) {
|
||||
staging.position(0).limit((int) m.length);
|
||||
shadow.position((int) m.offset).limit((int) (m.offset + m.length));
|
||||
shadow.put(staging);
|
||||
shadow.position(0).limit(shadow.capacity());
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!writeMap) {
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
staging.position(0).limit((int) m.length);
|
||||
final long gpuHandle;
|
||||
if (st.mappedInvalidate) {
|
||||
if (m.invalidate) {
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(0);
|
||||
final long glSize = Math.max(resourceManager.getBufferSize(st.mappedBufferGlId), st.mappedLength);
|
||||
final int glUsage = resourceManager.getBufferGlUsage(st.mappedBufferGlId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(st.mappedBufferGlId);
|
||||
resourceManager.deleteBuffer(st.mappedBufferGlId);
|
||||
gpuHandle = resourceManager.createBuffer(st.mappedBufferGlId, sdlUsage, glSize);
|
||||
resourceManager.recordBufferGlParams(st.mappedBufferGlId, glUsage, storageFlags);
|
||||
st.bumpAttribStateGen();
|
||||
final long glSize = Math.max(resourceManager.getBufferSize(glId), m.length);
|
||||
final int glUsage = resourceManager.getBufferGlUsage(glId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(glId);
|
||||
resourceManager.deleteBuffer(glId);
|
||||
gpuHandle = resourceManager.createBuffer(glId, sdlUsage, glSize);
|
||||
resourceManager.recordBufferGlParams(glId, glUsage, storageFlags);
|
||||
cs.bumpAttribStateGen();
|
||||
} else {
|
||||
gpuHandle = resourceManager.getBufferHandle(st.mappedBufferGlId);
|
||||
gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
}
|
||||
if (target == GL15.GL_ELEMENT_ARRAY_BUFFER
|
||||
&& (cs.primitiveRestartEnabled || resourceManager.hasEboShadow(st.mappedBufferGlId))) {
|
||||
final int len = (int) st.mappedLength;
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
persistentSync.mirrorEboShadow(st.mappedBufferGlId, st.mappedStagingBuffer, (int) st.mappedOffset, len);
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
&& (cs.primitiveRestartEnabled || resourceManager.hasEboShadow(glId))) {
|
||||
final int len = (int) m.length;
|
||||
staging.position(0).limit(len);
|
||||
persistentSync.mirrorEboShadow(glId, staging, (int) m.offset, len);
|
||||
staging.position(0).limit(len);
|
||||
}
|
||||
if (target == GL15.GL_ARRAY_BUFFER && resourceManager.hasArrayShadow(st.mappedBufferGlId)) {
|
||||
final int len = (int) st.mappedLength;
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
resourceManager.mirrorArrayShadowRegion(st.mappedBufferGlId, st.mappedStagingBuffer, (int) st.mappedOffset);
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
if (target == GL15.GL_ARRAY_BUFFER && resourceManager.hasArrayShadow(glId)) {
|
||||
final int len = (int) m.length;
|
||||
staging.position(0).limit(len);
|
||||
resourceManager.mirrorArrayShadowRegion(glId, staging, (int) m.offset);
|
||||
staging.position(0).limit(len);
|
||||
}
|
||||
if (gpuHandle != 0) {
|
||||
if (st.deferUploads) {
|
||||
enqueuePreCopied(st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
if (cs.deferUploads) {
|
||||
enqueuePreCopied(staging, gpuHandle, m.offset, false);
|
||||
} else if (frameManager.getCommandBuffer() != 0) {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), staging, gpuHandle, m.offset, false);
|
||||
}
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void clearMappedState(ContextState st) {
|
||||
st.mappedStagingBuffer = null;
|
||||
st.mappedBufferGlId = 0;
|
||||
st.mappedOffset = 0;
|
||||
st.mappedLength = 0;
|
||||
st.mappedInvalidate = false;
|
||||
st.mappedAccessFlags = 0;
|
||||
}
|
||||
|
||||
@Override public ByteBuffer mapBufferRange(int target, long offset, long length, int access) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || length <= 0) return null;
|
||||
|
||||
final boolean persistent = (access & GL44.GL_MAP_PERSISTENT_BIT) != 0;
|
||||
|
||||
if (persistent) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) resourceManager.dropArrayShadow(glId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(glId);
|
||||
if (storageFlags < 0 || (storageFlags & GL44.GL_MAP_PERSISTENT_BIT) == 0) {
|
||||
warnInvalidBufferOp("glMapBufferRange persistent without persistent storage", glId);
|
||||
return null;
|
||||
}
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc((int) length);
|
||||
final PersistentMapping fresh = new PersistentMapping(staging, offset, length, access);
|
||||
final PersistentMapping prior = resourceManager.swapPersistentMapping(glId, fresh);
|
||||
if (prior != null) resourceManager.releasePersistentStaging(prior);
|
||||
if (!resourceManager.putPersistentMappingIfAbsent(glId, fresh)) {
|
||||
MemoryUtil.memFree(staging);
|
||||
warnInvalidBufferOp("glMapBufferRange on mapped buffer", glId);
|
||||
return null;
|
||||
}
|
||||
if (target == GL15.GL_ARRAY_BUFFER) resourceManager.dropArrayShadow(glId);
|
||||
return staging;
|
||||
}
|
||||
|
||||
final ContextState st = s();
|
||||
if (st.mappedStagingBuffer != null) MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
st.mappedStagingBuffer = MemoryUtil.memAlloc((int) length);
|
||||
st.mappedBufferGlId = glId;
|
||||
st.mappedOffset = offset;
|
||||
st.mappedLength = length;
|
||||
st.mappedInvalidate = (access & GL30.GL_MAP_INVALIDATE_BUFFER_BIT) != 0;
|
||||
st.mappedAccessFlags = access;
|
||||
final ByteBuffer staging = MemoryUtil.memAlloc((int) length);
|
||||
if ((access & (GL30.GL_MAP_INVALIDATE_BUFFER_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT)) == 0) {
|
||||
seedMappedStaging(target, glId, offset, length, st.mappedStagingBuffer);
|
||||
seedMappedStaging(target, glId, offset, length, staging);
|
||||
}
|
||||
return st.mappedStagingBuffer;
|
||||
if (!resourceManager.putPlainMappingIfAbsent(glId, staging, offset, length, (access & GL30.GL_MAP_INVALIDATE_BUFFER_BIT) != 0, access)) {
|
||||
MemoryUtil.memFree(staging);
|
||||
warnInvalidBufferOp("glMapBufferRange on mapped buffer", glId);
|
||||
return null;
|
||||
}
|
||||
return staging;
|
||||
}
|
||||
|
||||
private void seedMappedStaging(int target, int glId, long offset, long length, ByteBuffer staging) {
|
||||
@@ -3315,22 +3362,31 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void flushMappedBufferRange(int target, long offset, long length) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId == 0 || length <= 0) return;
|
||||
persistentSync.onPersistentBufferWrite(glId, offset, length);
|
||||
if (!persistentSync.onPersistentBufferWrite(glId, offset, length)) {
|
||||
warnInvalidBufferOp("glFlushMappedBufferRange out of range", glId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void bufferStorage(int target, ByteBuffer data, int flags) {
|
||||
bufferData(target, data, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(target, flags);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferStorage on immutable storage")) return;
|
||||
if (data != null) respecifyStore(cs, target, glId, data, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(glId, flags);
|
||||
}
|
||||
|
||||
@Override public void bufferStorage(int target, long size, int flags) {
|
||||
bufferData(target, size, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(target, flags);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferStorage on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, size, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(glId, flags);
|
||||
}
|
||||
|
||||
private void recordImmutableStorage(int target, int flags) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId != 0) resourceManager.recordBufferGlParams(glId, GL15.GL_DYNAMIC_DRAW, flags);
|
||||
private void recordImmutableStorage(int glId, int flags) {
|
||||
resourceManager.recordBufferGlParams(glId, GL15.GL_DYNAMIC_DRAW, flags);
|
||||
}
|
||||
@Override public void getBufferSubData(int target, long offset, ByteBuffer data) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
@@ -3346,23 +3402,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
private void downloadBufferBlocking(long handle, int offset, int size, ByteBuffer out) {
|
||||
flushDeferredUploadsForRead();
|
||||
frameManager.endCopyPassIfActive();
|
||||
frameManager.endRenderPassIfActive(FrameManager.PASS_END_COPY);
|
||||
flushAndSubmitMidFrame();
|
||||
final long cb = SDL_AcquireGPUCommandBuffer(device.getDevice());
|
||||
if (cb == 0) return;
|
||||
resourceManager.downloadFromBuffer(cb, handle, offset, size, out);
|
||||
}
|
||||
|
||||
private void flushDeferredUploadsForRead() {
|
||||
final ContextState st = s();
|
||||
drainDeferredPersistentRegions(st);
|
||||
resourceManager.flushUploadArena();
|
||||
if (transferThread != null && st.frameHighestEnqueuedSeq > transferThread.getSubmittedSeq()) {
|
||||
transferThread.awaitSubmittedUpTo(st.frameHighestEnqueuedSeq);
|
||||
}
|
||||
}
|
||||
|
||||
private static void serveFromShadow(ByteBuffer shadow, int srcOffset, ByteBuffer data, int rem) {
|
||||
final int dstPos = data.position();
|
||||
if (shadow.isDirect() && data.isDirect()) {
|
||||
@@ -3399,8 +3444,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
final ContextState st = s();
|
||||
final boolean plainMapped = st.mappedStagingBuffer != null && st.mappedBufferGlId == glId;
|
||||
final MappedRange plain = s().mappingScratch;
|
||||
final boolean plainMapped = resourceManager.peekPlainMapping(glId, plain);
|
||||
plain.staging = null;
|
||||
|
||||
final boolean mapped;
|
||||
final int accessFlags;
|
||||
@@ -3413,9 +3459,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
mapLength = pm.length;
|
||||
} else if (plainMapped) {
|
||||
mapped = true;
|
||||
accessFlags = st.mappedAccessFlags;
|
||||
mapOffset = st.mappedOffset;
|
||||
mapLength = st.mappedLength;
|
||||
accessFlags = plain.accessFlags;
|
||||
mapOffset = plain.offset;
|
||||
mapLength = plain.length;
|
||||
} else {
|
||||
mapped = false;
|
||||
accessFlags = 0;
|
||||
@@ -3853,14 +3899,18 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public int createBuffers() { return resourceManager.genBuffer(); }
|
||||
@Override public void namedBufferData(int buffer, long size, int usage) {
|
||||
if (buffer == 0 || size <= 0) return;
|
||||
if (refuseImmutable(buffer, "glNamedBufferData on immutable storage")) return;
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER);
|
||||
freePlainMappingFor(buffer, true);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
resourceManager.createBuffer(buffer, sdlUsage, size);
|
||||
resourceManager.recordBufferGlParams(buffer, usage, BufferParams.MUTABLE_STORE);
|
||||
}
|
||||
@Override public void namedBufferData(int buffer, ByteBuffer data, int usage) {
|
||||
if (buffer == 0 || data == null) return;
|
||||
if (refuseImmutable(buffer, "glNamedBufferData on immutable storage")) return;
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER);
|
||||
freePlainMappingFor(buffer, true);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
final long handle = resourceManager.createBuffer(buffer, sdlUsage, data.remaining());
|
||||
resourceManager.recordBufferGlParams(buffer, usage, BufferParams.MUTABLE_STORE);
|
||||
@@ -4121,6 +4171,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId == 0 || size <= 0) return;
|
||||
final long bufSize = resourceManager.getBufferSize(glId);
|
||||
if (offset < 0 || (bufSize > 0 && (offset > bufSize || size > bufSize - offset))) {
|
||||
warnInvalidBufferOp("glClearBufferSubData out of range", glId);
|
||||
return;
|
||||
}
|
||||
final long handle = resourceManager.getBufferHandle(glId);
|
||||
if (handle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||
|
||||
@@ -4484,8 +4539,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
long readOffset, size, dstHandle, writeOffset, seq;
|
||||
@Override
|
||||
public void accept(PersistentMapping src, PersistentMapping dst) {
|
||||
enqueueUpload(TransferThread.StagingReadUpload.acquire(src.staging, readOffset, size, dstHandle, writeOffset, seq, false));
|
||||
if (dst != null) PersistentBufferSync.mirrorPersistentCopy(src.staging, readOffset, dst.staging, writeOffset, size);
|
||||
enqueueUpload(TransferThread.StagingReadUpload.acquire(src.staging, src.stagingIndex(readOffset), size, dstHandle, writeOffset, seq, false));
|
||||
if (dst != null) PersistentBufferSync.mirrorPersistentCopy(src, readOffset, dst, writeOffset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.MappedRange;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
@@ -314,12 +315,7 @@ public final class ContextState {
|
||||
public final long[] computeBatchRwTexs = new long[MAX_IMAGE_UNITS];
|
||||
public final int[] computeBatchRwTexLevels = new int[MAX_IMAGE_UNITS];
|
||||
|
||||
public int mappedBufferGlId;
|
||||
public ByteBuffer mappedStagingBuffer;
|
||||
public long mappedOffset;
|
||||
public long mappedLength;
|
||||
public boolean mappedInvalidate;
|
||||
public int mappedAccessFlags;
|
||||
public final MappedRange mappingScratch = new MappedRange();
|
||||
|
||||
public final SDL_GPUTextureSamplerBinding.Buffer fragSamplerBindings = SDL_GPUTextureSamplerBinding.calloc(MAX_SAMPLERS);
|
||||
public final SDL_GPUTextureSamplerBinding.Buffer vertSamplerBindings = SDL_GPUTextureSamplerBinding.calloc(MAX_SAMPLERS);
|
||||
|
||||
@@ -173,8 +173,9 @@ public final class FFPTrace {
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" noStaging");
|
||||
return;
|
||||
}
|
||||
final int base = st.uboRangeOffset[binding];
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" base=").append(base);
|
||||
final int rangeOffset = st.uboRangeOffset[binding];
|
||||
final int base = pm != null ? (int) pm.stagingIndex(rangeOffset) : rangeOffset;
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" base=").append(rangeOffset);
|
||||
appendUboFloat(buf, base, FFPUniformBlock.ALPHA_REF, " u_AlphaRef=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.MVP_MATRIX, " u_MVPMatrix=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, " u_LightmapTextureMatrix=");
|
||||
|
||||
+2
-2
@@ -698,8 +698,8 @@ public final class PipelineApplier {
|
||||
final int offset = st.uboRangeOffset[binding];
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
if (pm != null) {
|
||||
if (offset < 0 || offset + size > pm.staging.capacity()) return 0L;
|
||||
return MemoryUtil.memAddress0(pm.staging) + offset;
|
||||
if (!pm.covers(offset, size)) return 0L;
|
||||
return MemoryUtil.memAddress0(pm.staging) + pm.stagingIndex(offset);
|
||||
}
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow != null) {
|
||||
|
||||
+4
-7
@@ -8,16 +8,13 @@ public final class ByteRegionCopy {
|
||||
private ByteRegionCopy() {}
|
||||
|
||||
public static void copyByteRegion(ByteBuffer src, int srcOff, ByteBuffer dst, int dstOff, int len) {
|
||||
if ((srcOff | dstOff | len) < 0 || srcOff + (long) len > src.capacity() || dstOff + (long) len > dst.capacity()) {
|
||||
throw new IllegalStateException("copyByteRegion out of bounds: len=" + len + " srcOff=" + srcOff + " src.capacity=" + src.capacity() + " dstOff=" + dstOff + " dst.capacity=" + dst.capacity());
|
||||
}
|
||||
if (src.isDirect() && dst.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress(src, srcOff), MemoryUtil.memAddress(dst, dstOff), len);
|
||||
return;
|
||||
}
|
||||
if (src.hasArray() && dst.hasArray()) {
|
||||
System.arraycopy(src.array(), src.arrayOffset() + srcOff, dst.array(), dst.arrayOffset() + dstOff, len);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
dst.put(dstOff + i, src.get(srcOff + i));
|
||||
}
|
||||
dst.put(dstOff, src, srcOff, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class MappedRange {
|
||||
public int glId;
|
||||
public ByteBuffer staging;
|
||||
public long offset;
|
||||
public long length;
|
||||
public boolean invalidate;
|
||||
public int accessFlags;
|
||||
}
|
||||
+35
-21
@@ -33,11 +33,14 @@ public final class PersistentBufferSync {
|
||||
this.sink = sink;
|
||||
}
|
||||
|
||||
public void onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
public boolean onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
if (pm != null && pm.markDirty(offset, size)) {
|
||||
if (pm == null || size == 0) return true;
|
||||
if (offset < 0 || size < 0 || offset > pm.length || size > pm.length - offset) return false;
|
||||
if (pm.markDirty(offset, size)) {
|
||||
resourceManager.trackPersistentDirty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void uploadDirtyPersistentRegion() { processDirtyPersistentRegions(false); }
|
||||
@@ -59,30 +62,41 @@ public final class PersistentBufferSync {
|
||||
for (int i = 0; i < n; i++) {
|
||||
final PersistentMapping pm = vals.get(i);
|
||||
if (!pm.isDirty()) continue;
|
||||
final long gpuHandle = resourceManager.getBufferHandle(keys.getInt(i));
|
||||
if (gpuHandle == 0) continue;
|
||||
final long claimed = pm.claimDirty();
|
||||
if (PersistentMapping.isClean(claimed)) continue;
|
||||
final long off = PersistentMapping.rangeOffset(claimed);
|
||||
final long size = PersistentMapping.rangeSize(claimed);
|
||||
if (Tracy.ENABLED) frameManager.notePersistentDrain();
|
||||
if (defer) {
|
||||
final long seq = sink.nextSeq();
|
||||
pm.lastEnqueuedSeq = seq;
|
||||
sink.enqueue(TransferThread.StagingReadUpload.acquire(pm.staging, off, size, gpuHandle, off, seq, false));
|
||||
enqueued++;
|
||||
} else {
|
||||
pm.staging.position((int) off).limit((int) (off + size));
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), pm.staging, gpuHandle, off, false);
|
||||
pm.staging.clear();
|
||||
final int glId = keys.getInt(i);
|
||||
long copyPass = 0L;
|
||||
if (!defer) {
|
||||
if (resourceManager.getBufferHandle(glId) == 0) continue;
|
||||
copyPass = frameManager.ensureCopyPass();
|
||||
}
|
||||
resourceManager.beginPersistentDrain();
|
||||
try {
|
||||
if (resourceManager.getPersistentMapping(glId) != pm) continue;
|
||||
final long gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
if (gpuHandle == 0) continue;
|
||||
final long claimed = pm.claimDirty();
|
||||
if (PersistentMapping.isClean(claimed)) continue;
|
||||
resourceManager.clearPersistentDirty();
|
||||
final long off = PersistentMapping.rangeOffset(claimed);
|
||||
final long size = PersistentMapping.rangeSize(claimed);
|
||||
if (Tracy.ENABLED) frameManager.notePersistentDrain();
|
||||
if (defer) {
|
||||
final long seq = sink.nextSeq();
|
||||
pm.lastEnqueuedSeq = seq;
|
||||
sink.enqueue(TransferThread.StagingReadUpload.acquire(pm.staging, off, size, gpuHandle, pm.offset + off, seq, false));
|
||||
enqueued++;
|
||||
} else {
|
||||
resourceManager.uploadRangeToBuffer(copyPass, pm.staging, off, size, gpuHandle, pm.offset + off, false);
|
||||
}
|
||||
} finally {
|
||||
resourceManager.endPersistentDrain();
|
||||
}
|
||||
resourceManager.clearPersistentDirty();
|
||||
}
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
public static void mirrorPersistentCopy(ByteBuffer srcStaging, long srcOffset, ByteBuffer dstStaging, long dstOffset, long size) {
|
||||
ByteRegionCopy.copyByteRegion(srcStaging, (int) srcOffset, dstStaging, (int) dstOffset, (int) size);
|
||||
public static void mirrorPersistentCopy(PersistentMapping src, long readOffset, PersistentMapping dst, long writeOffset, long size) {
|
||||
if (!src.covers(readOffset, size) || !dst.covers(writeOffset, size)) return;
|
||||
ByteRegionCopy.copyByteRegion(src.staging, (int) src.stagingIndex(readOffset), dst.staging, (int) dst.stagingIndex(writeOffset), (int) size);
|
||||
}
|
||||
|
||||
public void mirrorEboShadow(int glId, ByteBuffer src, int dstOffset, int len) {
|
||||
|
||||
+6
@@ -24,6 +24,12 @@ public final class PersistentMapping {
|
||||
public static long rangeSize(long packed) { return (packed & 0xFFFFFFFFL) - (packed >>> 32); }
|
||||
static long packRange(long min, long max) { return (min << 32) | (max & 0xFFFFFFFFL); }
|
||||
|
||||
public long stagingIndex(long bufferOffset) { return bufferOffset - offset; }
|
||||
|
||||
public boolean covers(long bufferOffset, long size) {
|
||||
return size >= 0 && bufferOffset >= offset && bufferOffset - offset <= length && size <= length - (bufferOffset - offset);
|
||||
}
|
||||
|
||||
public boolean isDirty() { return dirtyRange.get() != CLEAN; }
|
||||
|
||||
public long claimDirty() { return dirtyRange.getAndSet(CLEAN); }
|
||||
|
||||
+124
-45
@@ -31,6 +31,7 @@ import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -96,6 +97,9 @@ public final class ResourceManager {
|
||||
|
||||
private final Int2ObjectOpenHashMap<ContextState.VAOState> vaoStates = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<PersistentMapping> persistentMappings = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<MappedRange> plainMappings = new Int2ObjectOpenHashMap<>();
|
||||
private MappedRange[] plainMappingPool = new MappedRange[4];
|
||||
private int plainMappingPoolCount;
|
||||
private final Int2ObjectOpenHashMap<ByteBuffer> uboShadows = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<ByteBuffer> eboShadows = new Int2ObjectOpenHashMap<>();
|
||||
private final IntOpenHashSet eboShadowWanted = new IntOpenHashSet();
|
||||
@@ -752,6 +756,7 @@ public final class ResourceManager {
|
||||
|
||||
public void deleteBuffer(int glId) {
|
||||
final ByteBuffer droppedPboStaging;
|
||||
final ByteBuffer droppedPlainStaging;
|
||||
final PersistentMapping droppedPm;
|
||||
wLock.lock();
|
||||
try {
|
||||
@@ -763,6 +768,7 @@ public final class ResourceManager {
|
||||
undefinedContentBuffers.remove(glId);
|
||||
droppedPm = persistentMappings.remove(glId);
|
||||
if (droppedPm != null) mappingsVersion++;
|
||||
droppedPlainStaging = removePlainMappingLocked(glId);
|
||||
droppedPboStaging = pboStagingData.remove(glId);
|
||||
deleteUboShadow(glId);
|
||||
deleteEboShadow(glId);
|
||||
@@ -777,8 +783,11 @@ public final class ResourceManager {
|
||||
if (droppedPboStaging != null) {
|
||||
memFree(droppedPboStaging);
|
||||
}
|
||||
if (droppedPlainStaging != null) {
|
||||
memFree(droppedPlainStaging);
|
||||
}
|
||||
untrackPersistentDirty(droppedPm);
|
||||
releasePersistentStaging(droppedPm);
|
||||
releasePersistentStaging(droppedPm, glId, "delete");
|
||||
}
|
||||
|
||||
public void flushDeferredReleases() {
|
||||
@@ -953,10 +962,81 @@ public final class ResourceManager {
|
||||
try { vaoStates.remove(glId); } finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public void putPersistentMapping(int bufferGlId, PersistentMapping m) {
|
||||
public boolean putPersistentMappingIfAbsent(int glId, PersistentMapping pm) {
|
||||
wLock.lock();
|
||||
try { persistentMappings.put(bufferGlId, m); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
try {
|
||||
if (plainMappings.containsKey(glId) || persistentMappings.putIfAbsent(glId, pm) != null) return false;
|
||||
mappingsVersion++;
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean putPlainMappingIfAbsent(int glId, ByteBuffer staging, long offset, long length, boolean invalidate, int accessFlags) {
|
||||
wLock.lock();
|
||||
try {
|
||||
if (plainMappings.containsKey(glId) || persistentMappings.containsKey(glId)) return false;
|
||||
final MappedRange m;
|
||||
if (plainMappingPoolCount > 0) {
|
||||
m = plainMappingPool[--plainMappingPoolCount];
|
||||
plainMappingPool[plainMappingPoolCount] = null;
|
||||
} else {
|
||||
m = new MappedRange();
|
||||
}
|
||||
m.glId = glId;
|
||||
m.staging = staging;
|
||||
m.offset = offset;
|
||||
m.length = length;
|
||||
m.invalidate = invalidate;
|
||||
m.accessFlags = accessFlags;
|
||||
plainMappings.put(glId, m);
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean takePlainMapping(int glId, MappedRange out) {
|
||||
wLock.lock();
|
||||
try {
|
||||
final MappedRange m = plainMappings.get(glId);
|
||||
if (m == null) return false;
|
||||
copyMappedRange(m, out);
|
||||
removePlainMappingLocked(glId);
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public ByteBuffer takePlainStaging(int glId) {
|
||||
wLock.lock();
|
||||
try { return removePlainMappingLocked(glId); } finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean peekPlainMapping(int glId, MappedRange out) {
|
||||
if (fastRead()) return copyMappedRange(plainMappings.get(glId), out);
|
||||
rLock.lock();
|
||||
try { return copyMappedRange(plainMappings.get(glId), out); } finally { rLock.unlock(); }
|
||||
}
|
||||
|
||||
private static boolean copyMappedRange(MappedRange m, MappedRange out) {
|
||||
if (m == null) return false;
|
||||
out.glId = m.glId;
|
||||
out.staging = m.staging;
|
||||
out.offset = m.offset;
|
||||
out.length = m.length;
|
||||
out.invalidate = m.invalidate;
|
||||
out.accessFlags = m.accessFlags;
|
||||
return true;
|
||||
}
|
||||
|
||||
private ByteBuffer removePlainMappingLocked(int glId) {
|
||||
final MappedRange m = plainMappings.remove(glId);
|
||||
if (m == null) return null;
|
||||
final ByteBuffer staging = m.staging;
|
||||
m.staging = null;
|
||||
if (plainMappingPoolCount == plainMappingPool.length) plainMappingPool = Arrays.copyOf(plainMappingPool, plainMappingPoolCount * 2);
|
||||
plainMappingPool[plainMappingPoolCount++] = m;
|
||||
return staging;
|
||||
}
|
||||
public void beginPersistentDrain() { rLock.lock(); }
|
||||
public void endPersistentDrain() { rLock.unlock(); }
|
||||
public PersistentMapping getPersistentMapping(int bufferGlId) {
|
||||
if (fastRead()) return persistentMappings.get(bufferGlId);
|
||||
rLock.lock();
|
||||
@@ -965,17 +1045,13 @@ public final class ResourceManager {
|
||||
public PersistentMapping removePersistentMapping(int bufferGlId) {
|
||||
final PersistentMapping dropped;
|
||||
wLock.lock();
|
||||
try { dropped = persistentMappings.remove(bufferGlId); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
try {
|
||||
dropped = persistentMappings.remove(bufferGlId);
|
||||
if (dropped != null) mappingsVersion++;
|
||||
} finally { wLock.unlock(); }
|
||||
untrackPersistentDirty(dropped);
|
||||
return dropped;
|
||||
}
|
||||
public PersistentMapping swapPersistentMapping(int bufferGlId, PersistentMapping fresh) {
|
||||
final PersistentMapping prior;
|
||||
wLock.lock();
|
||||
try { prior = persistentMappings.put(bufferGlId, fresh); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
untrackPersistentDirty(prior);
|
||||
return prior;
|
||||
}
|
||||
|
||||
private volatile int mappingsVersion;
|
||||
|
||||
@@ -989,8 +1065,9 @@ public final class ResourceManager {
|
||||
if (pm != null && !PersistentMapping.isClean(pm.claimDirty())) persistentDirtyCount.decrementAndGet();
|
||||
}
|
||||
|
||||
public void releasePersistentStaging(PersistentMapping pm) {
|
||||
public void releasePersistentStaging(PersistentMapping pm, int glId, String cause) {
|
||||
if (pm == null || pm.staging == null) return;
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Persistent mapping released: buffer {} ({} KiB) via {}", glId, pm.length >> 10, cause);
|
||||
final TransferThread tt = transferThread;
|
||||
final long seq = pm.lastEnqueuedSeq;
|
||||
if (tt != null && seq > tt.getSubmittedSeq()) tt.freeAfterSeq(pm.staging, seq);
|
||||
@@ -1001,24 +1078,20 @@ public final class ResourceManager {
|
||||
rLock.lock();
|
||||
try {
|
||||
final PersistentMapping srcPm = persistentMappings.get(srcGlId);
|
||||
if (srcPm == null) return false;
|
||||
final ByteBuffer slice = srcPm.staging.duplicate();
|
||||
slice.position((int) readOffset).limit((int) (readOffset + size));
|
||||
uploadToBuffer(copyPass, slice, dstHandle, writeOffset, false);
|
||||
if (srcPm == null || !srcPm.covers(readOffset, size)) return false;
|
||||
uploadRangeToBuffer(copyPass, srcPm.staging, srcPm.stagingIndex(readOffset), size, dstHandle, writeOffset, false);
|
||||
final PersistentMapping dstPm = persistentMappings.get(dstGlId);
|
||||
if (dstPm != null) {
|
||||
PersistentBufferSync.mirrorPersistentCopy(srcPm.staging, readOffset, dstPm.staging, writeOffset, size);
|
||||
}
|
||||
if (dstPm != null) PersistentBufferSync.mirrorPersistentCopy(srcPm, readOffset, dstPm, writeOffset, size);
|
||||
return true;
|
||||
} finally { rLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean enqueuePersistentCopyDeferred(int srcGlId, int dstGlId, long seq,
|
||||
public boolean enqueuePersistentCopyDeferred(int srcGlId, int dstGlId, long seq, long readOffset, long size,
|
||||
BiConsumer<PersistentMapping, PersistentMapping> inLock) {
|
||||
rLock.lock();
|
||||
try {
|
||||
final PersistentMapping srcPm = persistentMappings.get(srcGlId);
|
||||
if (srcPm == null) return false;
|
||||
if (srcPm == null || !srcPm.covers(readOffset, size)) return false;
|
||||
final PersistentMapping dstPm = persistentMappings.get(dstGlId);
|
||||
srcPm.lastEnqueuedSeq = seq;
|
||||
inLock.accept(srcPm, dstPm);
|
||||
@@ -1192,7 +1265,7 @@ public final class ResourceManager {
|
||||
public void mirrorArrayShadowFull(int glId, ByteBuffer data) {
|
||||
final int len = data.remaining();
|
||||
final ByteBuffer shadow = getOrAllocArrayShadow(glId, len);
|
||||
copyShadowRegion(data, data.position(), shadow, 0, len);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, 0, len);
|
||||
shadow.position(0).limit(len);
|
||||
}
|
||||
|
||||
@@ -1206,7 +1279,7 @@ public final class ResourceManager {
|
||||
removeArrayShadowLocked(glId);
|
||||
return;
|
||||
}
|
||||
copyShadowRegion(data, data.position(), shadow, dstOffset, len);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, dstOffset, len);
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
@@ -1226,18 +1299,6 @@ public final class ResourceManager {
|
||||
arrayShadowWanted.remove(glId);
|
||||
}
|
||||
|
||||
private static void copyShadowRegion(ByteBuffer src, int srcOff, ByteBuffer dst, int dstOff, int len) {
|
||||
if (src.isDirect() && dst.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress(src) + srcOff, MemoryUtil.memAddress(dst) + dstOff, len);
|
||||
return;
|
||||
}
|
||||
final ByteBuffer s = src.duplicate();
|
||||
s.position(srcOff).limit(srcOff + len);
|
||||
final ByteBuffer d = dst.duplicate();
|
||||
d.position(dstOff);
|
||||
d.put(s);
|
||||
}
|
||||
|
||||
public EBOSplitScanner.EboSplit[] getOrScanSplits(int glId, int indexType, int sentinel) {
|
||||
if (!hasEboShadow(glId)) return null;
|
||||
final long version = getEboShadowVersion(glId);
|
||||
@@ -1502,10 +1563,10 @@ public final class ResourceManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static final int COPY_CALLSITE_UPLOAD_BUFFER = 1;
|
||||
public static final int COPY_CALLSITE_UPLOAD_TEX_BATCH = 2;
|
||||
public static final int COPY_CALLSITE_UPLOAD_TEX_DIRECT = 3;
|
||||
public static final int COPY_CALLSITE_ARENA = 4;
|
||||
public static final int COPY_CALLSITE_FILL_BUFFER = 5;
|
||||
|
||||
public boolean arenaUpload(ByteBuffer data, long dstHandle, long dstOffset, boolean cycle) {
|
||||
final FrameManager.FrameState f = frameManager.frame();
|
||||
@@ -1600,7 +1661,13 @@ public final class ResourceManager {
|
||||
}
|
||||
|
||||
public void uploadToBuffer(long copyPass, ByteBuffer data, long gpuBuffer, long offset, boolean cycle) {
|
||||
final long size = data.remaining();
|
||||
uploadRangeToBuffer(copyPass, data, data.position(), data.remaining(), gpuBuffer, offset, cycle);
|
||||
}
|
||||
|
||||
public void uploadRangeToBuffer(long copyPass, ByteBuffer src, long srcOff, long size, long gpuBuffer, long dstOff, boolean cycle) {
|
||||
if (srcOff < 0 || size < 0 || srcOff + size > src.capacity()) {
|
||||
throw new IllegalStateException("uploadRangeToBuffer: bad source range srcOff=" + srcOff + " size=" + size + " src.capacity=" + src.capacity());
|
||||
}
|
||||
final long xfer = acquireTransferBuffer(size);
|
||||
if (xfer == 0) return;
|
||||
|
||||
@@ -1609,22 +1676,26 @@ public final class ResourceManager {
|
||||
if (mapped == null || mapped.capacity() < size) {
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
returnTransferBuffer(xfer, size);
|
||||
throw new IllegalStateException("uploadToBuffer: bad mapping size=" + size + " mapSize=" + mapSize + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
throw new IllegalStateException("uploadRangeToBuffer: bad mapping size=" + size + " mapSize=" + mapSize + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
}
|
||||
if (src.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress0(src) + srcOff, MemoryUtil.memAddress0(mapped), size);
|
||||
} else {
|
||||
mapped.put(0, src, (int) srcOff, (int) size);
|
||||
}
|
||||
copyMappedFromData(mapped, data, (int) size, COPY_CALLSITE_UPLOAD_BUFFER);
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTransferBufferLocation src = SDL_GPUTransferBufferLocation.calloc(stack)
|
||||
final SDL_GPUTransferBufferLocation srcLoc = SDL_GPUTransferBufferLocation.calloc(stack)
|
||||
.transfer_buffer(xfer)
|
||||
.offset(0);
|
||||
|
||||
final SDL_GPUBufferRegion dst = SDL_GPUBufferRegion.calloc(stack)
|
||||
.buffer(gpuBuffer)
|
||||
.offset((int) offset)
|
||||
.offset((int) dstOff)
|
||||
.size((int) size);
|
||||
|
||||
SDL_UploadToGPUBuffer(copyPass, src, dst, cycle);
|
||||
SDL_UploadToGPUBuffer(copyPass, srcLoc, dst, cycle);
|
||||
}
|
||||
|
||||
frameManager.recordUploadCommands(size, 1);
|
||||
@@ -1647,7 +1718,7 @@ public final class ResourceManager {
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
throw new IllegalStateException("fillBuffer: bad mapping size=" + chunk + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
}
|
||||
copyMappedFromData(mapped, pattern, chunk, COPY_CALLSITE_UPLOAD_BUFFER);
|
||||
copyMappedFromData(mapped, pattern, chunk, COPY_CALLSITE_FILL_BUFFER);
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
|
||||
int uploads = 0;
|
||||
@@ -2576,12 +2647,20 @@ public final class ResourceManager {
|
||||
arrayShadowWanted.clear();
|
||||
for (ByteBuffer bb : pboStagingData.values()) memFree(bb);
|
||||
pboStagingData.clear();
|
||||
for (PersistentMapping pm : persistentMappings.values()) {
|
||||
for (var e : persistentMappings.int2ObjectEntrySet()) {
|
||||
final PersistentMapping pm = e.getValue();
|
||||
untrackPersistentDirty(pm);
|
||||
releasePersistentStaging(pm);
|
||||
releasePersistentStaging(pm, e.getIntKey(), "shutdown");
|
||||
}
|
||||
persistentMappings.clear();
|
||||
mappingsVersion++;
|
||||
for (MappedRange m : plainMappings.values()) {
|
||||
if (m.staging != null) memFree(m.staging);
|
||||
m.staging = null;
|
||||
}
|
||||
plainMappings.clear();
|
||||
Arrays.fill(plainMappingPool, null);
|
||||
plainMappingPoolCount = 0;
|
||||
}
|
||||
|
||||
public record TextureMeta(int glTarget, int glFormat, int sdlFormat, int width, int height, int depth, int levels, int usage) {}
|
||||
|
||||
+9
-5
@@ -329,11 +329,15 @@ public final class TransferThread {
|
||||
final ByteBuffer mapped = SDL_MapGPUTransferBuffer(device.getDevice(), xfer, true, mapSize);
|
||||
if (mapped != null) {
|
||||
final ByteBuffer src = sr.stagingBuffer;
|
||||
final int prevPos = src.position();
|
||||
final int prevLim = src.limit();
|
||||
src.limit((int) (sr.srcOffset + size)).position((int) sr.srcOffset);
|
||||
mapped.put(src);
|
||||
src.limit(prevLim).position(prevPos);
|
||||
if (sr.srcOffset < 0 || sr.srcOffset + size > src.capacity() || size > mapped.capacity()) {
|
||||
LOG.error("StagingReadUpload out of bounds: srcOffset={} size={} src.capacity={} mapped.capacity={}; upload dropped", sr.srcOffset, size, src.capacity(), mapped.capacity());
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
resourceManager.returnTransferBufferThreadSafe(xfer, size);
|
||||
retireSeq(sr.seq);
|
||||
StagingReadUpload.release(sr);
|
||||
return;
|
||||
}
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress0(src) + sr.srcOffset, MemoryUtil.memAddress0(mapped), size);
|
||||
}
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
recordBufferUpload(xfer, size, sr.dstGpuBuffer, sr.dstOffset, sr.seq, sr.cycle);
|
||||
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlBufferMappingLifetimeTest {
|
||||
|
||||
private static final int SIZE = 4096;
|
||||
private static final int PERSISTENT_STORAGE = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT | GL44.GL_CLIENT_STORAGE_BIT;
|
||||
private static final int PERSISTENT_MAP = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
|
||||
private static final int PLAIN_MAP = GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
GlsmSdlHeadlessRig.boot();
|
||||
}
|
||||
|
||||
private static void fill(ByteBuffer buf, int seed) {
|
||||
for (int i = 0; i < buf.capacity(); i++) buf.put(i, (byte) (seed * 37 + i * 7 + 1));
|
||||
}
|
||||
|
||||
private static void assertPattern(ByteBuffer actual, int seed) {
|
||||
for (int i = 0; i < actual.capacity(); i++) {
|
||||
assertEquals((byte) (seed * 37 + i * 7 + 1), actual.get(i), "byte " + i + " of pattern " + seed);
|
||||
}
|
||||
}
|
||||
|
||||
private static ByteBuffer readBack(int target, int size) {
|
||||
final ByteBuffer out = MemoryUtil.memAlloc(size);
|
||||
GLStateManager.glGetBufferSubData(target, 0, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static ByteBuffer mapPersistent(int target, int id) {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferStorage(target, SIZE, PERSISTENT_STORAGE);
|
||||
return GLStateManager.glMapBufferRange(target, 0, SIZE, PERSISTENT_MAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferDataOnPersistentlyMappedBufferIsRefusedAndMappingSurvives() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
final ByteBuffer mapped = mapPersistent(target, id);
|
||||
assertNotNull(mapped);
|
||||
GLStateManager.glBufferData(target, SIZE * 2L, GL15.GL_STATIC_DRAW);
|
||||
fill(mapped, 1);
|
||||
GLStateManager.glFlushMappedBufferRange(target, 0, SIZE);
|
||||
out = readBack(target, SIZE);
|
||||
assertPattern(out, 1);
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistentMapWithoutPersistentStorageReturnsNull() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferData(target, (long) SIZE, GL15.GL_STATIC_DRAW);
|
||||
assertNull(GLStateManager.glMapBufferRange(target, 0, SIZE, PERSISTENT_MAP));
|
||||
} finally {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondPersistentMapReturnsNullAndFirstStillRoundTrips() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
final ByteBuffer first = mapPersistent(target, id);
|
||||
assertNotNull(first);
|
||||
assertNull(GLStateManager.glMapBufferRange(target, 0, SIZE, PERSISTENT_MAP));
|
||||
fill(first, 2);
|
||||
GLStateManager.glFlushMappedBufferRange(target, 0, SIZE);
|
||||
out = readBack(target, SIZE);
|
||||
assertPattern(out, 2);
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoBuffersMappedNonPersistentlyKeepTheirOwnData() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int a = GLStateManager.glGenBuffers();
|
||||
final int b = GLStateManager.glGenBuffers();
|
||||
ByteBuffer outA = null;
|
||||
ByteBuffer outB = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, a);
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glBindBuffer(target, b);
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
|
||||
GLStateManager.glBindBuffer(target, a);
|
||||
final ByteBuffer mapA = GLStateManager.glMapBufferRange(target, 0, SIZE, PLAIN_MAP);
|
||||
assertNotNull(mapA);
|
||||
GLStateManager.glBindBuffer(target, b);
|
||||
final ByteBuffer mapB = GLStateManager.glMapBufferRange(target, 0, SIZE, PLAIN_MAP);
|
||||
assertNotNull(mapB);
|
||||
fill(mapA, 3);
|
||||
fill(mapB, 4);
|
||||
|
||||
GLStateManager.glBindBuffer(target, a);
|
||||
assertTrue(GLStateManager.glUnmapBuffer(target));
|
||||
GLStateManager.glBindBuffer(target, b);
|
||||
assertTrue(GLStateManager.glUnmapBuffer(target));
|
||||
|
||||
GLStateManager.glBindBuffer(target, a);
|
||||
outA = readBack(target, SIZE);
|
||||
assertPattern(outA, 3);
|
||||
GLStateManager.glBindBuffer(target, b);
|
||||
outB = readBack(target, SIZE);
|
||||
assertPattern(outB, 4);
|
||||
} finally {
|
||||
if (outA != null) MemoryUtil.memFree(outA);
|
||||
if (outB != null) MemoryUtil.memFree(outB);
|
||||
GLStateManager.glDeleteBuffers(a);
|
||||
GLStateManager.glDeleteBuffers(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmapOfUnmappedBufferReturnsFalseAndLeavesOtherMappingIntact() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int mappedId = GLStateManager.glGenBuffers();
|
||||
final int idle = GLStateManager.glGenBuffers();
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, mappedId);
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
final ByteBuffer map = GLStateManager.glMapBufferRange(target, 0, SIZE, PLAIN_MAP);
|
||||
assertNotNull(map);
|
||||
fill(map, 5);
|
||||
|
||||
GLStateManager.glBindBuffer(target, idle);
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
assertFalse(GLStateManager.glUnmapBuffer(target));
|
||||
|
||||
GLStateManager.glBindBuffer(target, mappedId);
|
||||
assertTrue(GLStateManager.glUnmapBuffer(target));
|
||||
out = readBack(target, SIZE);
|
||||
assertPattern(out, 5);
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
GLStateManager.glDeleteBuffers(mappedId);
|
||||
GLStateManager.glDeleteBuffers(idle);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeUniformSubDataIsIgnored() {
|
||||
final int ubo = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
final ByteBuffer big = MemoryUtil.memAlloc(64);
|
||||
try {
|
||||
fill(data, 6);
|
||||
fill(big, 7);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, ubo);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, data, GL15.GL_DYNAMIC_DRAW);
|
||||
assertDoesNotThrow(() -> {
|
||||
big.clear();
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 1024, big);
|
||||
big.clear();
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 224, big);
|
||||
big.clear();
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, -8, big);
|
||||
});
|
||||
} finally {
|
||||
MemoryUtil.memFree(data);
|
||||
MemoryUtil.memFree(big);
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeCopyBufferSubDataIsIgnored() {
|
||||
final int src = GLStateManager.glGenBuffers();
|
||||
final int dst = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
ByteBuffer outDst = null;
|
||||
try {
|
||||
fill(data, 6);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, src);
|
||||
GLStateManager.glBufferData(GL31.GL_COPY_READ_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, dst);
|
||||
data.clear();
|
||||
GLStateManager.glBufferData(GL31.GL_COPY_WRITE_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 200, 0, 128);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0, 200, 128);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, -4, 0, 16);
|
||||
outDst = readBack(GL31.GL_COPY_WRITE_BUFFER, 256);
|
||||
assertPattern(outDst, 6);
|
||||
} finally {
|
||||
if (outDst != null) MemoryUtil.memFree(outDst);
|
||||
MemoryUtil.memFree(data);
|
||||
GLStateManager.glDeleteBuffers(src);
|
||||
GLStateManager.glDeleteBuffers(dst);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeClearBufferSubDataIsIgnored() {
|
||||
final int clearStart = 64;
|
||||
final int clearEnd = 96;
|
||||
final int ssbo = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
fill(data, 8);
|
||||
GLStateManager.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, ssbo);
|
||||
GLStateManager.glBufferData(GL43.GL_SHADER_STORAGE_BUFFER, data, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, clearStart, clearEnd - clearStart, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 200, 128, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, -4, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 1024, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
GlsmSdlHeadlessRig.beginFrame();
|
||||
out = readBack(GL43.GL_SHADER_STORAGE_BUFFER, 256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final boolean cleared = i >= clearStart && i < clearEnd;
|
||||
assertEquals(cleared ? 0 : data.get(i), out.get(i), cleared ? "in-range clear did not zero byte " + i : "out-of-range clear touched byte " + i);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
MemoryUtil.memFree(data);
|
||||
GLStateManager.glDeleteBuffers(ssbo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondBufferStorageIsRefused() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferStorage(target, SIZE, PERSISTENT_STORAGE);
|
||||
GLStateManager.glBufferStorage(target, SIZE * 2L, PERSISTENT_STORAGE);
|
||||
assertEquals(GL11.GL_TRUE, GLStateManager.glGetBufferParameteri(target, GL44.GL_BUFFER_IMMUTABLE_STORAGE));
|
||||
assertEquals(SIZE, GLStateManager.glGetBufferParameteri(target, GL15.GL_BUFFER_SIZE));
|
||||
} finally {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void namedBufferDataOnImmutableStorageIsRefused() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferStorage(target, SIZE, PERSISTENT_STORAGE);
|
||||
GLStateManager.glNamedBufferData(id, SIZE * 2L, GL15.GL_STATIC_DRAW);
|
||||
assertEquals(SIZE, GLStateManager.glGetBufferParameteri(target, GL15.GL_BUFFER_SIZE));
|
||||
} finally {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferStorageWithNullDataMarksBufferImmutable() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferStorage(target, (ByteBuffer) null, PERSISTENT_STORAGE);
|
||||
assertEquals(GL11.GL_TRUE, GLStateManager.glGetBufferParameteri(target, GL44.GL_BUFFER_IMMUTABLE_STORAGE));
|
||||
} finally {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertBufferDataUnmapsPlainMapping(int target) {
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
assertNotNull(GLStateManager.glMapBufferRange(target, 0, SIZE, PLAIN_MAP));
|
||||
GLStateManager.glBufferData(target, SIZE, GL15.GL_DYNAMIC_DRAW);
|
||||
assertNotNull(GLStateManager.glMapBufferRange(target, 0, SIZE, PLAIN_MAP));
|
||||
assertTrue(GLStateManager.glUnmapBuffer(target));
|
||||
} finally {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferDataOnMappedArrayBufferUnmapsIt() {
|
||||
assertBufferDataUnmapsPlainMapping(GL15.GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bufferDataOnMappedUniformBufferUnmapsIt() {
|
||||
assertBufferDataUnmapsPlainMapping(GL31.GL_UNIFORM_BUFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeFlushOfPersistentMappingIsIgnored() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
final ByteBuffer mapped = mapPersistent(target, id);
|
||||
assertNotNull(mapped);
|
||||
GLStateManager.glFlushMappedBufferRange(target, SIZE, 64);
|
||||
GLStateManager.glFlushMappedBufferRange(target, SIZE - 16, 64);
|
||||
GLStateManager.glFlushMappedBufferRange(target, -8, 16);
|
||||
fill(mapped, 9);
|
||||
GLStateManager.glFlushMappedBufferRange(target, 0, SIZE);
|
||||
out = readBack(target, SIZE);
|
||||
assertPattern(out, 9);
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistentMappingAtNonZeroOffsetRoundTrips() {
|
||||
final int target = GL15.GL_ARRAY_BUFFER;
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer out = MemoryUtil.memAlloc(SIZE);
|
||||
try {
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
GLStateManager.glBufferStorage(target, SIZE * 2L, PERSISTENT_STORAGE);
|
||||
final ByteBuffer mapped = GLStateManager.glMapBufferRange(target, SIZE, SIZE, PERSISTENT_MAP);
|
||||
assertNotNull(mapped);
|
||||
fill(mapped, 10);
|
||||
GLStateManager.glFlushMappedBufferRange(target, 0, SIZE);
|
||||
GLStateManager.glGetBufferSubData(target, SIZE, out);
|
||||
assertPattern(out, 10);
|
||||
} finally {
|
||||
MemoryUtil.memFree(out);
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyFromPersistentMappingShorterThanBufferReadsGpuContents() {
|
||||
final int src = GLStateManager.glGenBuffers();
|
||||
final int dst = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(SIZE * 2);
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
fill(data, 11);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, src);
|
||||
GLStateManager.glBufferStorage(GL31.GL_COPY_READ_BUFFER, data, PERSISTENT_STORAGE);
|
||||
assertNotNull(GLStateManager.glMapBufferRange(GL31.GL_COPY_READ_BUFFER, 0, SIZE, PERSISTENT_MAP));
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, dst);
|
||||
GLStateManager.glBufferData(GL31.GL_COPY_WRITE_BUFFER, SIZE, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, SIZE, 0, 256);
|
||||
out = readBack(GL31.GL_COPY_WRITE_BUFFER, 256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
assertEquals(data.get(SIZE + i), out.get(i), "byte " + i);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
MemoryUtil.memFree(data);
|
||||
GLStateManager.glDeleteBuffers(src);
|
||||
GLStateManager.glDeleteBuffers(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -45,6 +47,30 @@ class GlsmSdlReadbackTest {
|
||||
GLStateManager.glDeleteTextures(texture);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBufferSubDataSeesSameFrameStorageBufferWrite() {
|
||||
final int size = 64;
|
||||
final int ssbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, ssbo);
|
||||
|
||||
final ByteBuffer initial = BufferUtils.createByteBuffer(size);
|
||||
final ByteBuffer update = BufferUtils.createByteBuffer(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
initial.put(i, (byte) 0x11);
|
||||
update.put(i, (byte) (i + 1));
|
||||
}
|
||||
GLStateManager.glBufferData(GL43.GL_SHADER_STORAGE_BUFFER, initial, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, 0, update);
|
||||
|
||||
final ByteBuffer dst = BufferUtils.createByteBuffer(size);
|
||||
GLStateManager.glGetBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, 0, dst);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertEquals((byte) (i + 1), dst.get(i), "byte " + i + " should hold the same-frame sub-data write");
|
||||
}
|
||||
GLStateManager.glDeleteBuffers(ssbo);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readPixelsSeesPriorDraw() {
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ class AbsoluteCopyRegionTest {
|
||||
for (int i = 0; i < 32; i++) src.put(i, (byte) i);
|
||||
src.position(7).limit(24);
|
||||
dst.position(9).limit(28);
|
||||
PersistentBufferSync.mirrorPersistentCopy(src, 4, dst, 16, 8);
|
||||
PersistentBufferSync.mirrorPersistentCopy(new PersistentMapping(src, 0L, 32L, 0), 4, new PersistentMapping(dst, 0L, 32L, 0), 16, 8);
|
||||
for (int i = 0; i < 8; i++) assertEquals((byte) (4 + i), dst.get(16 + i));
|
||||
assertEquals(7, src.position());
|
||||
assertEquals(24, src.limit());
|
||||
|
||||
+13
-11
@@ -40,7 +40,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(64);
|
||||
try {
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 64, 0);
|
||||
|
||||
assertFalse(rm.hasDirtyPersistentRegions(), "a mapping with no writes is not dirty");
|
||||
@@ -70,7 +70,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(64);
|
||||
try {
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 64, 0);
|
||||
|
||||
sync.onPersistentBufferWrite(1, 0, 16);
|
||||
@@ -95,7 +95,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
try {
|
||||
final int atStart = rm.getMappingsVersion();
|
||||
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
final int afterPut = rm.getMappingsVersion();
|
||||
assertNotEquals(atStart, afterPut, "adding a mapping must move the version");
|
||||
|
||||
@@ -107,7 +107,8 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
sync.enqueueDirtyPersistentRegions();
|
||||
assertEquals(afterPut, rm.getMappingsVersion(), "dirtying and draining is not a mapping change");
|
||||
|
||||
rm.swapPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.removePersistentMapping(1);
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
final int afterSwap = rm.getMappingsVersion();
|
||||
assertNotEquals(afterPut, afterSwap, "swapping a mapping must move the version");
|
||||
|
||||
@@ -129,7 +130,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(256);
|
||||
try {
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 256, 0);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@@ -156,13 +157,14 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
final ByteBuffer first = MemoryUtil.memCalloc(64);
|
||||
final ByteBuffer second = MemoryUtil.memCalloc(64);
|
||||
try {
|
||||
rm.putPersistentMapping(1, new PersistentMapping(first, 0L, first.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(first, 0L, first.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 64, 0);
|
||||
sync.onPersistentBufferWrite(1, 0, 16);
|
||||
sync.enqueueDirtyPersistentRegions();
|
||||
assertEquals(1, sink.enqueued);
|
||||
|
||||
rm.swapPersistentMapping(1, new PersistentMapping(second, 0L, second.capacity(), 0));
|
||||
rm.removePersistentMapping(1);
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(second, 0L, second.capacity(), 0));
|
||||
sync.onPersistentBufferWrite(1, 0, 16);
|
||||
sync.enqueueDirtyPersistentRegions();
|
||||
assertEquals(2, sink.enqueued, "the drain must see the replacement, not a stale snapshot");
|
||||
@@ -184,7 +186,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(4096);
|
||||
try {
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 4096, 0);
|
||||
|
||||
final int writers = 6;
|
||||
@@ -235,7 +237,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(64);
|
||||
try {
|
||||
final PersistentMapping pm = new PersistentMapping(staging, 0L, staging.capacity(), 0);
|
||||
rm.putPersistentMapping(1, pm);
|
||||
rm.putPersistentMappingIfAbsent(1, pm);
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, 64, 0);
|
||||
|
||||
final long[] stampAtEnqueue = {-1L};
|
||||
@@ -267,7 +269,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
final int seedCount = 100;
|
||||
for (int i = 0; i < seedCount; i++) {
|
||||
final ByteBuffer b = MemoryUtil.memAlloc(64);
|
||||
rm.putPersistentMapping(i, new PersistentMapping(b, 0L, b.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(i, new PersistentMapping(b, 0L, b.capacity(), 0));
|
||||
}
|
||||
|
||||
final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
@@ -279,7 +281,7 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
int next = seedCount;
|
||||
while (!stop.get()) {
|
||||
final ByteBuffer b = MemoryUtil.memAlloc(64);
|
||||
rm.putPersistentMapping(next, new PersistentMapping(b, 0L, b.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(next, new PersistentMapping(b, 0L, b.capacity(), 0));
|
||||
rm.removePersistentMapping(next - 50);
|
||||
next++;
|
||||
if (next > seedCount + 10000) next = seedCount;
|
||||
|
||||
+2
-2
@@ -37,13 +37,13 @@ class PersistentDrainBoundaryTest {
|
||||
final ResourceManager rm;
|
||||
final PersistentBufferSync sync;
|
||||
final RecordingSink sink = new RecordingSink();
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(4096);
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc(16384);
|
||||
|
||||
Fixture() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
rm = rig.resourceManager;
|
||||
sync = new PersistentBufferSync(rig.frameManager, rm, sink);
|
||||
rm.putPersistentMapping(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
rm.putPersistentMappingIfAbsent(1, new PersistentMapping(staging, 0L, staging.capacity(), 0));
|
||||
SdlReflect.recordBuffer(rm, 1, 0xCAFEBABEL, staging.capacity(), 0);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -18,14 +18,14 @@ class ReleasePersistentStagingTest {
|
||||
@Test
|
||||
void nullPmAndNullStagingNoOp() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
rm.releasePersistentStaging(null);
|
||||
rm.releasePersistentStaging(new PersistentMapping(null, 0L, 0L, 0));
|
||||
rm.releasePersistentStaging(null, 7, "unmap");
|
||||
rm.releasePersistentStaging(new PersistentMapping(null, 0L, 0L, 0), 7, "unmap");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTransferThreadFreesInline() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
rm.releasePersistentStaging(new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0));
|
||||
rm.releasePersistentStaging(new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0), 7, "unmap");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -39,7 +39,7 @@ class ReleasePersistentStagingTest {
|
||||
try {
|
||||
final PersistentMapping pm = new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0);
|
||||
pm.lastEnqueuedSeq = 0L;
|
||||
rm.releasePersistentStaging(pm);
|
||||
rm.releasePersistentStaging(pm, 7, "unmap");
|
||||
assertEquals(0, SdlReflect.pendingFreeCount(tt));
|
||||
} finally { shutdown(tt); }
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class ReleasePersistentStagingTest {
|
||||
try {
|
||||
final PersistentMapping pm = new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0);
|
||||
pm.lastEnqueuedSeq = 50L;
|
||||
rm.releasePersistentStaging(pm);
|
||||
rm.releasePersistentStaging(pm, 7, "unmap");
|
||||
assertEquals(1, SdlReflect.pendingFreeCount(tt));
|
||||
} finally { shutdown(tt); }
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class ReleasePersistentStagingTest {
|
||||
void deleteBufferReleasesPersistentStaging() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
SdlReflect.recordBuffer(rm, 7, 0xDEADBEEFL, 64, 0);
|
||||
rm.putPersistentMapping(7, new PersistentMapping(MemoryUtil.memCalloc(64), 0L, 64L, 0));
|
||||
rm.putPersistentMappingIfAbsent(7, new PersistentMapping(MemoryUtil.memCalloc(64), 0L, 64L, 0));
|
||||
|
||||
rm.deleteBuffer(7);
|
||||
|
||||
@@ -84,7 +84,7 @@ class ReleasePersistentStagingTest {
|
||||
final PersistentMapping pm = new PersistentMapping(staging, 0L, staging.capacity(), 0);
|
||||
pm.lastEnqueuedSeq = 99L;
|
||||
SdlReflect.recordBuffer(rm, 7, 0xDEADBEEFL, 64, 0);
|
||||
rm.putPersistentMapping(7, pm);
|
||||
rm.putPersistentMappingIfAbsent(7, pm);
|
||||
|
||||
rm.deleteBuffer(7);
|
||||
|
||||
|
||||
+97
@@ -3,7 +3,9 @@ package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -159,4 +161,99 @@ class ResourceManagerConcurrencyTest {
|
||||
|
||||
assertTrue(created.get() >= threads, "each thread should have created at least one FBO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void putPlainMappingIfAbsent_oneWinnerAcrossThreads() throws Exception {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
final int glId = 9001;
|
||||
final int n = 8;
|
||||
final AtomicInteger winners = new AtomicInteger();
|
||||
|
||||
race(n, slot -> {
|
||||
final ByteBuffer staging = MemoryUtil.memAlloc(16);
|
||||
if (rm.putPlainMappingIfAbsent(glId, staging, 0L, 16L, false, slot)) winners.incrementAndGet();
|
||||
else MemoryUtil.memFree(staging);
|
||||
});
|
||||
|
||||
assertEquals(1, winners.get(), "exactly one thread may map the buffer");
|
||||
final ByteBuffer staging = rm.takePlainStaging(glId);
|
||||
assertTrue(staging != null, "the winning mapping must still be registered");
|
||||
MemoryUtil.memFree(staging);
|
||||
assertNull(rm.takePlainStaging(glId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void plainMappingTakenFromAnotherThread() throws Exception {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
final int glId = 9002;
|
||||
final ByteBuffer staging = MemoryUtil.memAlloc(32);
|
||||
try {
|
||||
assertTrue(rm.putPlainMappingIfAbsent(glId, staging, 8L, 32L, true, 0x2A));
|
||||
final MappedRange peeked = new MappedRange();
|
||||
final MappedRange taken = new MappedRange();
|
||||
final AtomicBoolean peekedOk = new AtomicBoolean();
|
||||
final AtomicBoolean takenOk = new AtomicBoolean();
|
||||
final AtomicBoolean secondTake = new AtomicBoolean(true);
|
||||
|
||||
race(1, slot -> {
|
||||
peekedOk.set(rm.peekPlainMapping(glId, peeked));
|
||||
takenOk.set(rm.takePlainMapping(glId, taken));
|
||||
secondTake.set(rm.takePlainMapping(glId, new MappedRange()));
|
||||
});
|
||||
|
||||
assertTrue(peekedOk.get(), "peek from another thread must see the mapping");
|
||||
assertSame(staging, peeked.staging);
|
||||
assertTrue(takenOk.get(), "take from another thread must succeed");
|
||||
assertSame(staging, taken.staging);
|
||||
assertEquals(glId, taken.glId);
|
||||
assertEquals(8L, taken.offset);
|
||||
assertEquals(32L, taken.length);
|
||||
assertTrue(taken.invalidate);
|
||||
assertEquals(0x2A, taken.accessFlags);
|
||||
assertFalse(secondTake.get(), "a taken mapping must be gone");
|
||||
assertFalse(rm.peekPlainMapping(glId, new MappedRange()));
|
||||
} finally {
|
||||
MemoryUtil.memFree(staging);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteBufferFromAnotherThreadDropsPlainMapping() throws Exception {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
final int glId = 9003;
|
||||
assertTrue(rm.putPlainMappingIfAbsent(glId, MemoryUtil.memAlloc(16), 0L, 16L, false, 0));
|
||||
|
||||
race(1, slot -> rm.deleteBuffer(glId));
|
||||
|
||||
assertFalse(rm.peekPlainMapping(glId, new MappedRange()));
|
||||
assertFalse(rm.takePlainMapping(glId, new MappedRange()));
|
||||
final ByteBuffer again = MemoryUtil.memAlloc(16);
|
||||
assertTrue(rm.putPlainMappingIfAbsent(glId, again, 0L, 16L, false, 0), "a deleted buffer's id must be mappable again");
|
||||
MemoryUtil.memFree(rm.takePlainStaging(glId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistentAndPlainMappingsExcludeEachOther() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
final int plainFirst = 9004;
|
||||
final int persistentFirst = 9005;
|
||||
final ByteBuffer plainStaging = MemoryUtil.memAlloc(16);
|
||||
final ByteBuffer persistentStaging = MemoryUtil.memAlloc(16);
|
||||
final ByteBuffer refused = MemoryUtil.memAlloc(16);
|
||||
try {
|
||||
assertTrue(rm.putPlainMappingIfAbsent(plainFirst, plainStaging, 0L, 16L, false, 0));
|
||||
assertFalse(rm.putPersistentMappingIfAbsent(plainFirst, new PersistentMapping(refused, 0L, 16L, 0)), "persistent map of a plain-mapped buffer must be refused");
|
||||
assertNull(rm.getPersistentMapping(plainFirst));
|
||||
|
||||
assertTrue(rm.putPersistentMappingIfAbsent(persistentFirst, new PersistentMapping(persistentStaging, 0L, 16L, 0)));
|
||||
assertFalse(rm.putPlainMappingIfAbsent(persistentFirst, refused, 0L, 16L, false, 0), "plain map of a persistently mapped buffer must be refused");
|
||||
assertFalse(rm.peekPlainMapping(persistentFirst, new MappedRange()));
|
||||
} finally {
|
||||
rm.takePlainStaging(plainFirst);
|
||||
rm.removePersistentMapping(persistentFirst);
|
||||
MemoryUtil.memFree(plainStaging);
|
||||
MemoryUtil.memFree(persistentStaging);
|
||||
MemoryUtil.memFree(refused);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user