Compare commits
50 Commits
inverted_n
...
mc_1215
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2327c6baf8 | ||
|
|
144faf5b21 | ||
|
|
dc6dd4bb11 | ||
|
|
84482e8998 | ||
|
|
072ece7a3d | ||
|
|
22d557ed01 | ||
|
|
b454d54a99 | ||
|
|
341119386a | ||
|
|
1575d7319c | ||
|
|
950e92d7c7 | ||
|
|
0e98f52580 | ||
|
|
caf2703102 | ||
|
|
b79923de3d | ||
|
|
ee6d171ef6 | ||
|
|
1c30198347 | ||
|
|
4ed9199e1c | ||
|
|
2027cb064c | ||
|
|
a00eec69b7 | ||
|
|
84c07c4115 | ||
|
|
6398164d42 | ||
|
|
fa42ad5a03 | ||
|
|
22553eb1f9 | ||
|
|
cb599eea0b | ||
|
|
f73413e7c0 | ||
|
|
5b752d3f87 | ||
|
|
4f37d3b597 | ||
|
|
21b497d2d4 | ||
|
|
3bfc0c266d | ||
|
|
f252fa3a7a | ||
|
|
66266fb426 | ||
|
|
225e2d9d1a | ||
|
|
3b4aa75890 | ||
|
|
0c1917d56e | ||
|
|
35850082d5 | ||
|
|
d24b719a93 | ||
|
|
a0c33a439b | ||
|
|
6bbd2c521a | ||
|
|
7575c35b02 | ||
|
|
f78a8df275 | ||
|
|
8462dde374 | ||
|
|
075e8f2897 | ||
|
|
204989b909 | ||
|
|
c023e3b4f2 | ||
|
|
e7c4d6f132 | ||
|
|
9d0cf33a45 | ||
|
|
34c5c71d77 | ||
|
|
03bede4067 | ||
|
|
f624f85698 | ||
|
|
985fa4b53c | ||
|
|
6c6c08d188 |
@@ -188,7 +188,9 @@ processIncludeJars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remapJar {
|
remapJar {
|
||||||
delete getDestinationDirectory().get()
|
doFirst {
|
||||||
|
delete fileTree(getDestinationDirectory().get())
|
||||||
|
}
|
||||||
|
|
||||||
def hash = gitCommitHash();
|
def hash = gitCommitHash();
|
||||||
if (!hash.equals("<UnknownCommit>")) {
|
if (!hash.equals("<UnknownCommit>")) {
|
||||||
@@ -218,7 +220,7 @@ dependencies {
|
|||||||
include(runtimeOnly "org.lwjgl:lwjgl-zstd:$lwjglVersion:natives-linux")
|
include(runtimeOnly "org.lwjgl:lwjgl-zstd:$lwjglVersion:natives-linux")
|
||||||
|
|
||||||
include(implementation 'redis.clients:jedis:5.1.0')
|
include(implementation 'redis.clients:jedis:5.1.0')
|
||||||
include(implementation('org.rocksdb:rocksdbjni:8.10.0'))
|
include(implementation('org.rocksdb:rocksdbjni:10.2.1'))
|
||||||
include(implementation 'org.apache.commons:commons-pool2:2.12.0')
|
include(implementation 'org.apache.commons:commons-pool2:2.12.0')
|
||||||
include(implementation 'org.lz4:lz4-java:1.8.0')
|
include(implementation 'org.lz4:lz4-java:1.8.0')
|
||||||
include(implementation('org.tukaani:xz:1.10'))
|
include(implementation('org.tukaani:xz:1.10'))
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ public class TimingStatistics {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
this.running = true;
|
this.running = true;
|
||||||
VarHandle.fullFence();
|
|
||||||
this.timestamp = System.nanoTime();
|
this.timestamp = System.nanoTime();
|
||||||
VarHandle.fullFence();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
@@ -39,9 +37,7 @@ public class TimingStatistics {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
this.running = false;
|
this.running = false;
|
||||||
VarHandle.fullFence();
|
|
||||||
this.runtime += System.nanoTime() - this.timestamp;
|
this.runtime += System.nanoTime() - this.timestamp;
|
||||||
VarHandle.fullFence();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subtract(TimeSampler sampler) {
|
public void subtract(TimeSampler sampler) {
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ public class VoxyClientInstance extends VoxyInstance {
|
|||||||
private final Path basePath = getBasePath();
|
private final Path basePath = getBasePath();
|
||||||
public VoxyClientInstance() {
|
public VoxyClientInstance() {
|
||||||
super(VoxyConfig.CONFIG.serviceThreads);
|
super(VoxyConfig.CONFIG.serviceThreads);
|
||||||
|
try {
|
||||||
|
Files.createDirectories(this.basePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
this.storageConfig = getCreateStorageConfig(this.basePath);
|
this.storageConfig = getCreateStorageConfig(this.basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,26 +80,25 @@ public class VoxyClientInstance extends VoxyInstance {
|
|||||||
try {
|
try {
|
||||||
config = Serialization.GSON.fromJson(Files.readString(json), Config.class);
|
config = Serialization.GSON.fromJson(Files.readString(json), Config.class);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
throw new IllegalStateException("Config deserialization null, reverting to default");
|
Logger.error("Config deserialization null, reverting to default");
|
||||||
}
|
} else {
|
||||||
if (config.sectionStorageConfig == null) {
|
if (config.sectionStorageConfig == null) {
|
||||||
throw new IllegalStateException("Config section storage null, reverting to default");
|
Logger.error("Config section storage null, reverting to default");
|
||||||
|
config = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.error("Failed to load the storage configuration file, resetting it to default, this will probably break your save if you used a custom storage config", e);
|
Logger.error("Failed to load the storage configuration file, resetting it to default, this will probably break your save if you used a custom storage config", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (config == null) {
|
||||||
config = DEFAULT_STORAGE_CONFIG;
|
config = DEFAULT_STORAGE_CONFIG;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Files.writeString(json, Serialization.GSON.toJson(config));
|
Files.writeString(json, Serialization.GSON.toJson(config));
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to deserialize the default config, aborting!", e);
|
throw new RuntimeException("Failed write the config, aborting!", e);
|
||||||
}
|
}
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
throw new IllegalStateException("Config is still null\n");
|
throw new IllegalStateException("Config is still null\n");
|
||||||
@@ -103,6 +107,7 @@ public class VoxyClientInstance extends VoxyInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class Config {
|
private static class Config {
|
||||||
|
public int version = 1;
|
||||||
public SectionStorageConfig sectionStorageConfig;
|
public SectionStorageConfig sectionStorageConfig;
|
||||||
}
|
}
|
||||||
private static final Config DEFAULT_STORAGE_CONFIG;
|
private static final Config DEFAULT_STORAGE_CONFIG;
|
||||||
@@ -152,96 +157,4 @@ public class VoxyClientInstance extends VoxyInstance {
|
|||||||
}
|
}
|
||||||
return basePath.toAbsolutePath();
|
return basePath.toAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
private static void testDbPerformance(WorldEngine engine) {
|
|
||||||
Random r = new Random(123456);
|
|
||||||
r.nextLong();
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
int c = 0;
|
|
||||||
long tA = 0;
|
|
||||||
long tR = 0;
|
|
||||||
for (int i = 0; i < 1_000_000; i++) {
|
|
||||||
if (i == 20_000) {
|
|
||||||
c = 0;
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
c++;
|
|
||||||
int x = (r.nextInt(256*2+2)-256);//-32
|
|
||||||
int z = (r.nextInt(256*2+2)-256);//-32
|
|
||||||
int y = r.nextInt(2)-1;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
long t = System.nanoTime();
|
|
||||||
var sec = engine.acquire(WorldEngine.getWorldSectionId(lvl, x>>lvl, y>>lvl, z>>lvl));
|
|
||||||
tA += System.nanoTime()-t;
|
|
||||||
t = System.nanoTime();
|
|
||||||
sec.release();
|
|
||||||
tR += System.nanoTime()-t;
|
|
||||||
}
|
|
||||||
long delta = System.currentTimeMillis() - start;
|
|
||||||
System.out.println("Total "+delta+"ms " + ((double)delta/c) + "ms average tA: " + tA + " tR: " + tR);
|
|
||||||
}
|
|
||||||
private static void testDbPerformance2(WorldEngine engine) {
|
|
||||||
Random r = new Random(123456);
|
|
||||||
r.nextLong();
|
|
||||||
ConcurrentLinkedDeque<Long> queue = new ConcurrentLinkedDeque<>();
|
|
||||||
var ser = engine.instanceIn.getThreadPool().createServiceNoCleanup("aa", 1, ()-> () ->{
|
|
||||||
var sec = engine.acquire(queue.poll());
|
|
||||||
sec.release();
|
|
||||||
});
|
|
||||||
int priming = 1_000_000;
|
|
||||||
for (int i = 0; i < 2_000_000+priming; i++) {
|
|
||||||
int x = (r.nextInt(256*2+2)-256)>>2;//-32
|
|
||||||
int z = (r.nextInt(256*2+2)-256)>>2;//-32
|
|
||||||
int y = r.nextInt(2)-1;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
queue.add(WorldEngine.getWorldSectionId(lvl, x>>lvl, y>>lvl, z>>lvl));
|
|
||||||
}
|
|
||||||
for (int i = 0; i < priming; i++) {
|
|
||||||
ser.execute();
|
|
||||||
}
|
|
||||||
ser.blockTillEmpty();
|
|
||||||
int c = queue.size();
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (int i = 0; i < c; i++) {
|
|
||||||
ser.execute();
|
|
||||||
}
|
|
||||||
ser.blockTillEmpty();
|
|
||||||
long delta = System.currentTimeMillis() - start;
|
|
||||||
ser.shutdown();
|
|
||||||
System.out.println("Total "+delta+"ms " + ((double)delta/c) + "ms average total, avg wrt threads: " + (((double)delta/c)*engine.instanceIn.getThreadPool().getThreadCount()) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void verifyTopNodeChildren(int X, int Y, int Z) {
|
|
||||||
var world = this.getOrMakeRenderWorld(MinecraftClient.getInstance().world);
|
|
||||||
for (int lvl = 0; lvl < 5; lvl++) {
|
|
||||||
for (int y = (Y<<5)>>lvl; y < ((Y+1)<<5)>>lvl; y++) {
|
|
||||||
for (int x = (X<<5)>>lvl; x < ((X+1)<<5)>>lvl; x++) {
|
|
||||||
for (int z = (Z<<5)>>lvl; z < ((Z+1)<<5)>>lvl; z++) {
|
|
||||||
if (lvl == 0) {
|
|
||||||
var own = world.acquire(lvl, x, y, z);
|
|
||||||
if ((own.getNonEmptyChildren() != 0) ^ (own.getNonEmptyBlockCount() != 0)) {
|
|
||||||
Logger.error("Lvl 0 node not marked correctly " + WorldEngine.pprintPos(own.key));
|
|
||||||
}
|
|
||||||
own.release();
|
|
||||||
} else {
|
|
||||||
byte msk = 0;
|
|
||||||
for (int child = 0; child < 8; child++) {
|
|
||||||
var section = world.acquire(lvl-1, (child&1)+(x<<1), ((child>>2)&1)+(y<<1), ((child>>1)&1)+(z<<1));
|
|
||||||
msk |= (byte) (section.getNonEmptyBlockCount()!=0?(1<<child):0);
|
|
||||||
section.release();
|
|
||||||
}
|
|
||||||
var own = world.acquire(lvl, x, y, z);
|
|
||||||
if (own.getNonEmptyChildren() != msk) {
|
|
||||||
Logger.error("Section empty child mask not correct " + WorldEngine.pprintPos(own.key) + " got: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(own.getNonEmptyChildren()))).replace(' ', '0') + " expected: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(msk))).replace(' ', '0'));
|
|
||||||
}
|
|
||||||
own.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class VoxyConfig implements OptionStorage<VoxyConfig> {
|
|||||||
public boolean enableRendering = true;
|
public boolean enableRendering = true;
|
||||||
public boolean ingestEnabled = true;
|
public boolean ingestEnabled = true;
|
||||||
public int sectionRenderDistance = 16;
|
public int sectionRenderDistance = 16;
|
||||||
public int serviceThreads = Math.max(CpuLayout.CORES.length/2, 1);
|
public int serviceThreads = (int) Math.max(CpuLayout.CORES.length/1.5, 1);
|
||||||
public float subDivisionSize = 64;
|
public float subDivisionSize = 64;
|
||||||
public boolean renderVanillaFog = false;
|
public boolean renderVanillaFog = false;
|
||||||
public boolean renderStatistics = false;
|
public boolean renderStatistics = false;
|
||||||
|
|||||||
@@ -61,34 +61,43 @@ public class VoxyRenderSystem {
|
|||||||
public final ChunkBoundRenderer chunkBoundRenderer;
|
public final ChunkBoundRenderer chunkBoundRenderer;
|
||||||
|
|
||||||
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
||||||
//Trigger the shared index buffer loading
|
//Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more
|
||||||
SharedIndexBuffer.INSTANCE.id();
|
// than timeout, we keep the world acquired
|
||||||
Capabilities.init();//Ensure clinit is called
|
world.acquireRef();
|
||||||
|
try {
|
||||||
|
//wait for opengl to be finished, this should hopefully ensure all memory allocations are free
|
||||||
|
glFinish();
|
||||||
|
glFinish();
|
||||||
|
|
||||||
this.worldIn = world;
|
//Trigger the shared index buffer loading
|
||||||
this.renderer = new RenderService(world, threadPool);
|
SharedIndexBuffer.INSTANCE.id();
|
||||||
this.postProcessing = new PostProcessing();
|
Capabilities.init();//Ensure clinit is called
|
||||||
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord()>>5;
|
|
||||||
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord()-1)>>5;
|
|
||||||
|
|
||||||
//Do some very cheeky stuff for MiB
|
this.worldIn = world;
|
||||||
if (false) {
|
this.renderer = new RenderService(world, threadPool);
|
||||||
minSec = -8;
|
this.postProcessing = new PostProcessing();
|
||||||
maxSec = 7;
|
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5;
|
||||||
|
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5;
|
||||||
|
|
||||||
|
//Do some very cheeky stuff for MiB
|
||||||
|
if (false) {
|
||||||
|
minSec = -8;
|
||||||
|
maxSec = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderDistanceTracker = new RenderDistanceTracker(20,
|
||||||
|
minSec,
|
||||||
|
maxSec,
|
||||||
|
this.renderer::addTopLevelNode,
|
||||||
|
this.renderer::removeTopLevelNode);
|
||||||
|
|
||||||
|
this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance);
|
||||||
|
|
||||||
|
this.chunkBoundRenderer = new ChunkBoundRenderer();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
world.releaseRef();//If something goes wrong, we must release the world first
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderDistanceTracker = new RenderDistanceTracker(20,
|
|
||||||
minSec,
|
|
||||||
maxSec,
|
|
||||||
this.renderer::addTopLevelNode,
|
|
||||||
this.renderer::removeTopLevelNode);
|
|
||||||
|
|
||||||
this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance);
|
|
||||||
|
|
||||||
this.chunkBoundRenderer = new ChunkBoundRenderer();
|
|
||||||
|
|
||||||
//Keep the world loaded
|
|
||||||
this.worldIn.acquireRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRenderDistance(int renderDistance) {
|
public void setRenderDistance(int renderDistance) {
|
||||||
@@ -96,40 +105,6 @@ public class VoxyRenderSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//private static final ModelTextureBakery mtb = new ModelTextureBakery(16, 16);
|
|
||||||
//private static final RawDownloadStream downstream = new RawDownloadStream(1<<20);
|
|
||||||
public void renderSetup(Frustum frustum, Camera camera) {
|
|
||||||
TimingStatistics.resetSamplers();
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (false) {
|
|
||||||
int allocation = downstream.download(2 * 4 * 6 * 16 * 16, ptr -> {
|
|
||||||
ColourDepthTextureData[] textureData = new ColourDepthTextureData[6];
|
|
||||||
final int FACE_SIZE = 16 * 16;
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
long faceDataPtr = ptr + (FACE_SIZE * 4) * face * 2;
|
|
||||||
int[] colour = new int[FACE_SIZE];
|
|
||||||
int[] depth = new int[FACE_SIZE];
|
|
||||||
|
|
||||||
//Copy out colour
|
|
||||||
for (int i = 0; i < FACE_SIZE; i++) {
|
|
||||||
//De-interpolate results
|
|
||||||
colour[i] = MemoryUtil.memGetInt(faceDataPtr + (i * 4 * 2));
|
|
||||||
depth[i] = MemoryUtil.memGetInt(faceDataPtr + (i * 4 * 2) + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
textureData[face] = new ColourDepthTextureData(colour, depth, 16, 16);
|
|
||||||
}
|
|
||||||
if (textureData[0].colour()[0] == 0) {
|
|
||||||
int a = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mtb.renderFacesToStream(Blocks.AIR.getDefaultState(), 123456, false, downstream.getBufferId(), allocation);
|
|
||||||
downstream.submit();
|
|
||||||
downstream.tick();
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private void autoBalanceSubDivSize() {
|
private void autoBalanceSubDivSize() {
|
||||||
//only increase quality while there are very few mesh queues, this stops,
|
//only increase quality while there are very few mesh queues, this stops,
|
||||||
// e.g. while flying and is rendering alot of low quality chunks
|
// e.g. while flying and is rendering alot of low quality chunks
|
||||||
@@ -172,6 +147,9 @@ public class VoxyRenderSystem {
|
|||||||
if (IrisUtil.irisShadowActive()) {
|
if (IrisUtil.irisShadowActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
TimingStatistics.resetSamplers();
|
||||||
|
|
||||||
|
|
||||||
//Do some very cheeky stuff for MiB
|
//Do some very cheeky stuff for MiB
|
||||||
if (false) {
|
if (false) {
|
||||||
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10;
|
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10;
|
||||||
@@ -218,7 +196,7 @@ public class VoxyRenderSystem {
|
|||||||
this.postProcessing.setup(viewport.width, viewport.height, boundFB);
|
this.postProcessing.setup(viewport.width, viewport.height, boundFB);
|
||||||
TimingStatistics.F.stop();
|
TimingStatistics.F.stop();
|
||||||
|
|
||||||
this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture(), startTime);
|
this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture());
|
||||||
|
|
||||||
|
|
||||||
TimingStatistics.F.start();
|
TimingStatistics.F.start();
|
||||||
@@ -296,129 +274,4 @@ public class VoxyRenderSystem {
|
|||||||
//Release hold on the world
|
//Release hold on the world
|
||||||
this.worldIn.releaseRef();
|
this.worldIn.releaseRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void testMeshingPerformance() {
|
|
||||||
var modelService = new ModelBakerySubsystem(this.worldIn.getMapper());
|
|
||||||
var factory = new RenderDataFactory(this.worldIn, modelService.factory, false);
|
|
||||||
|
|
||||||
List<WorldSection> sections = new ArrayList<>();
|
|
||||||
|
|
||||||
System.out.println("Loading sections");
|
|
||||||
for (int x = -17; x <= 17; x++) {
|
|
||||||
for (int z = -17; z <= 17; z++) {
|
|
||||||
for (int y = -1; y <= 4; y++) {
|
|
||||||
var section = this.worldIn.acquire(0, x, y, z);
|
|
||||||
|
|
||||||
int nonAir = 0;
|
|
||||||
for (long state : section.copyData()) {
|
|
||||||
nonAir += Mapper.isAir(state)?0:1;
|
|
||||||
modelService.requestBlockBake(Mapper.getBlockId(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonAir > 500 && Math.abs(x) <= 16 && Math.abs(z) <= 16) {
|
|
||||||
sections.add(section);
|
|
||||||
} else {
|
|
||||||
section.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Baking models");
|
|
||||||
{
|
|
||||||
//Bake everything
|
|
||||||
while (!modelService.areQueuesEmpty()) {
|
|
||||||
modelService.tick(5_000_000);
|
|
||||||
glFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Ready!");
|
|
||||||
|
|
||||||
{
|
|
||||||
int iteration = 0;
|
|
||||||
while (true) {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (var section : sections) {
|
|
||||||
var mesh = factory.generateMesh(section);
|
|
||||||
|
|
||||||
mesh.free();
|
|
||||||
}
|
|
||||||
long delta = System.currentTimeMillis() - start;
|
|
||||||
System.out.println("Iteration: " + (iteration++) + " took " + delta + "ms, for an average of " + ((float)delta/sections.size()) + "ms per section");
|
|
||||||
//System.out.println("Quad count: " + factory.quadCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testFullMesh() {
|
|
||||||
var modelService = new ModelBakerySubsystem(this.worldIn.getMapper());
|
|
||||||
var completedCounter = new AtomicInteger();
|
|
||||||
var generationService = new RenderGenerationService(this.worldIn, modelService, VoxyCommon.getInstance().getThreadPool(), false);
|
|
||||||
generationService.setResultConsumer(a-> {completedCounter.incrementAndGet(); a.free();});
|
|
||||||
|
|
||||||
var r = new Random(12345);
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 10_000; i++) {
|
|
||||||
int x = (r.nextInt(256*2+2)-256)>>1;//-32
|
|
||||||
int z = (r.nextInt(256*2+2)-256)>>1;//-32
|
|
||||||
int y = r.nextInt(10)-2;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
long key = WorldEngine.getWorldSectionId(lvl, x>>lvl, y>>lvl, z>>lvl);
|
|
||||||
generationService.enqueueTask(key);
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
modelService.tick(5_000_000);
|
|
||||||
if (i++%5000==0)
|
|
||||||
System.out.println(completedCounter.get());
|
|
||||||
glFinish();
|
|
||||||
List<String> a = new ArrayList<>();
|
|
||||||
generationService.addDebugData(a);
|
|
||||||
if (a.getFirst().endsWith(" 0")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Running benchmark");
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
completedCounter.set(0);
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
int C = 200_000;
|
|
||||||
for (int i = 0; i < C; i++) {
|
|
||||||
int x = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
|
||||||
int z = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
|
||||||
int y = r.nextInt(10) - 2;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
long key = WorldEngine.getWorldSectionId(lvl, x >> lvl, y >> lvl, z >> lvl);
|
|
||||||
generationService.enqueueTask(key);
|
|
||||||
}
|
|
||||||
//int i = 0;
|
|
||||||
while (true) {
|
|
||||||
//if (i++%5000==0)
|
|
||||||
// System.out.println(completedCounter.get());
|
|
||||||
modelService.tick(5_000_000);
|
|
||||||
glFinish();
|
|
||||||
List<String> a = new ArrayList<>();
|
|
||||||
generationService.addDebugData(a);
|
|
||||||
if (a.getFirst().endsWith(" 0")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long delta = (System.currentTimeMillis()-start);
|
|
||||||
System.out.println("Time "+delta+"ms count: " + completedCounter.get() + " avg per mesh: " + ((double)delta/completedCounter.get()) + "ms");
|
|
||||||
if (false)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
generationService.shutdown();
|
|
||||||
modelService.shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class Capabilities {
|
|||||||
|
|
||||||
public static final Capabilities INSTANCE = new Capabilities();
|
public static final Capabilities INSTANCE = new Capabilities();
|
||||||
|
|
||||||
|
public final boolean repFragTest;
|
||||||
public final boolean meshShaders;
|
public final boolean meshShaders;
|
||||||
public final boolean INT64_t;
|
public final boolean INT64_t;
|
||||||
public final long ssboMaxSize;
|
public final long ssboMaxSize;
|
||||||
@@ -28,7 +29,8 @@ public class Capabilities {
|
|||||||
var cap = GL.getCapabilities();
|
var cap = GL.getCapabilities();
|
||||||
this.compute = cap.glDispatchComputeIndirect != 0;
|
this.compute = cap.glDispatchComputeIndirect != 0;
|
||||||
this.indirectParameters = cap.glMultiDrawElementsIndirectCountARB != 0;
|
this.indirectParameters = cap.glMultiDrawElementsIndirectCountARB != 0;
|
||||||
this.meshShaders = cap.GL_NV_mesh_shader && cap.GL_NV_representative_fragment_test;
|
this.repFragTest = cap.GL_NV_representative_fragment_test;
|
||||||
|
this.meshShaders = cap.GL_NV_mesh_shader;
|
||||||
this.canQueryGpuMemory = cap.GL_NVX_gpu_memory_info;
|
this.canQueryGpuMemory = cap.GL_NVX_gpu_memory_info;
|
||||||
//this.INT64_t = cap.GL_ARB_gpu_shader_int64 || cap.GL_AMD_gpu_shader_int64;
|
//this.INT64_t = cap.GL_ARB_gpu_shader_int64 || cap.GL_AMD_gpu_shader_int64;
|
||||||
//The only reliable way to test for int64 support is to try compile a shader
|
//The only reliable way to test for int64 support is to try compile a shader
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ public class Shader extends TrackedObject {
|
|||||||
String log = GL20C.glGetShaderInfoLog(shader);
|
String log = GL20C.glGetShaderInfoLog(shader);
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
if (!log.isEmpty()) {
|
||||||
System.err.println(log);
|
Logger.warn(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = GL20C.glGetShaderi(shader, GL20C.GL_COMPILE_STATUS);
|
int result = GL20C.glGetShaderi(shader, GL20C.GL_COMPILE_STATUS);
|
||||||
|
|||||||
@@ -74,8 +74,6 @@ public class ModelBakerySubsystem {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
//TimingStatistics.modelProcess.start();
|
//TimingStatistics.modelProcess.start();
|
||||||
long start = System.nanoTime();
|
|
||||||
VarHandle.fullFence();
|
|
||||||
if (this.blockIdCount.get() != 0) {
|
if (this.blockIdCount.get() != 0) {
|
||||||
long budget = Math.min(totalBudget-150_000, totalBudget-(this.factory.resultJobs.size()*10_000L))-150_000;
|
long budget = Math.min(totalBudget-150_000, totalBudget-(this.factory.resultJobs.size()*10_000L))-150_000;
|
||||||
|
|
||||||
@@ -100,7 +98,7 @@ public class ModelBakerySubsystem {
|
|||||||
|
|
||||||
this.factory.tick();
|
this.factory.tick();
|
||||||
|
|
||||||
start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
while (!this.factory.resultJobs.isEmpty()) {
|
while (!this.factory.resultJobs.isEmpty()) {
|
||||||
this.factory.resultJobs.poll().run();
|
this.factory.resultJobs.poll().run();
|
||||||
if (totalBudget<(System.nanoTime()-start))
|
if (totalBudget<(System.nanoTime()-start))
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import me.cortex.voxy.client.core.gl.Capabilities;
|
|||||||
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery;
|
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery;
|
||||||
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
|
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
|
||||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
@@ -490,7 +491,7 @@ public class ModelFactory {
|
|||||||
throw new IllegalStateException("Biome was put in an id that was not null");
|
throw new IllegalStateException("Biome was put in an id that was not null");
|
||||||
}
|
}
|
||||||
if (oldBiome == biome) {
|
if (oldBiome == biome) {
|
||||||
System.err.println("Biome added was a duplicate");
|
Logger.error("Biome added was a duplicate");
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -221,6 +221,12 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
var mat = new Matrix4f();
|
var mat = new Matrix4f();
|
||||||
for (int i = 0; i < VIEWS.length; i++) {
|
for (int i = 0; i < VIEWS.length; i++) {
|
||||||
|
if (i==1||i==2||i==4) {
|
||||||
|
glCullFace(GL_FRONT);
|
||||||
|
} else {
|
||||||
|
glCullFace(GL_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
||||||
|
|
||||||
//The projection matrix
|
//The projection matrix
|
||||||
@@ -239,6 +245,12 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
var mat = new Matrix4f();
|
var mat = new Matrix4f();
|
||||||
for (int i = 0; i < VIEWS.length; i++) {
|
for (int i = 0; i < VIEWS.length; i++) {
|
||||||
|
if (i==1||i==2||i==4) {
|
||||||
|
glCullFace(GL_FRONT);
|
||||||
|
} else {
|
||||||
|
glCullFace(GL_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
this.vc.reset();
|
this.vc.reset();
|
||||||
this.bakeFluidState(state, layer, i);
|
this.bakeFluidState(state, layer, i);
|
||||||
if (this.vc.isEmpty()) continue;
|
if (this.vc.isEmpty()) continue;
|
||||||
@@ -264,6 +276,12 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
var mat = new Matrix4f();
|
var mat = new Matrix4f();
|
||||||
for (int i = 0; i < VIEWS.length; i++) {
|
for (int i = 0; i < VIEWS.length; i++) {
|
||||||
|
if (i==1||i==2||i==4) {
|
||||||
|
glCullFace(GL_FRONT);
|
||||||
|
} else {
|
||||||
|
glCullFace(GL_BACK);
|
||||||
|
}
|
||||||
|
|
||||||
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
|
||||||
|
|
||||||
//The projection matrix
|
//The projection matrix
|
||||||
@@ -302,24 +320,25 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!!
|
//the face/direction is the face (e.g. down is the down face)
|
||||||
|
addView(0, -90,0, 0, 0);//Direction.DOWN
|
||||||
|
addView(1, 90,0, 0, 0b100);//Direction.UP
|
||||||
|
|
||||||
addView(0, -90,0, 0, false);//Direction.DOWN
|
addView(2, 0,180, 0, 0b001);//Direction.NORTH
|
||||||
addView(1, 90,0, 0, false);//Direction.UP
|
addView(3, 0,0, 0, 0);//Direction.SOUTH
|
||||||
addView(2, 0,180, 0, true);//Direction.NORTH
|
|
||||||
addView(3, 0,0, 0, false);//Direction.SOUTH
|
addView(4, 0,90, 270, 0b100);//Direction.WEST
|
||||||
//TODO: check these arnt the wrong way round
|
addView(5, 0,270, 270, 0);//Direction.EAST
|
||||||
addView(4, 0,90, 270, false);//Direction.EAST
|
|
||||||
addView(5, 0,270, 270, false);//Direction.WEST
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addView(int i, float pitch, float yaw, float rotation, boolean flipX) {
|
private static void addView(int i, float pitch, float yaw, float rotation, int flip) {
|
||||||
var stack = new MatrixStack();
|
var stack = new MatrixStack();
|
||||||
stack.translate(0.5f,0.5f,0.5f);
|
stack.translate(0.5f,0.5f,0.5f);
|
||||||
stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation));
|
stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation));
|
||||||
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch));
|
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch));
|
||||||
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
|
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
|
||||||
|
stack.multiplyPositionMatrix(new Matrix4f().scale(1-2*(flip&1), 1-(flip&2), 1-((flip>>1)&2)));
|
||||||
stack.translate(-0.5f,-0.5f,-0.5f);
|
stack.translate(-0.5f,-0.5f,-0.5f);
|
||||||
VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix());
|
VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
//Limit to available dedicated memory if possible
|
//Limit to available dedicated memory if possible
|
||||||
if (Capabilities.INSTANCE.canQueryGpuMemory) {
|
if (Capabilities.INSTANCE.canQueryGpuMemory) {
|
||||||
//512mb less than avalible,
|
//512mb less than avalible,
|
||||||
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 512*1024*1024;
|
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 1024*1024*1024;
|
||||||
// Give a minimum of 512 mb requirement
|
// Give a minimum of 512 mb requirement
|
||||||
limit = Math.max(512*1024*1024, limit);
|
limit = Math.max(512*1024*1024, limit);
|
||||||
|
|
||||||
geometryCapacity = Math.min(geometryCapacity, limit);
|
geometryCapacity = Math.min(geometryCapacity, limit);
|
||||||
}
|
}
|
||||||
//geometryCapacity = 1<<24;
|
//geometryCapacity = 1<<28;
|
||||||
//geometryCapacity = 1<<30;//1GB test
|
//geometryCapacity = 1<<30;//1GB test
|
||||||
return geometryCapacity;
|
return geometryCapacity;
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
this.nodeManager = new AsyncNodeManager(1<<21, this.geometryData, this.renderGen);
|
this.nodeManager = new AsyncNodeManager(1<<21, this.geometryData, this.renderGen);
|
||||||
this.nodeCleaner = new NodeCleaner(this.nodeManager);
|
this.nodeCleaner = new NodeCleaner(this.nodeManager);
|
||||||
|
|
||||||
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner);
|
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner, this.renderGen);
|
||||||
|
|
||||||
world.setDirtyCallback(this.nodeManager::worldEvent);
|
world.setDirtyCallback(this.nodeManager::worldEvent);
|
||||||
|
|
||||||
@@ -106,7 +106,19 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
this.modelService.tick(budget);
|
this.modelService.tick(budget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderFarAwayOpaque(J viewport, GlTexture depthBoundTexture, long frameStart) {
|
private boolean frexStillHasWork() {
|
||||||
|
if (!VoxyClient.isFrexActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//If frex is running we must tick everything to ensure correctness
|
||||||
|
UploadStream.INSTANCE.tick();
|
||||||
|
//Done here as is allows less gl state resetup
|
||||||
|
this.modelService.tick(100_000_000);
|
||||||
|
glFinish();
|
||||||
|
return this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderFarAwayOpaque(J viewport, GlTexture depthBoundTexture) {
|
||||||
//LightMapHelper.tickLightmap();
|
//LightMapHelper.tickLightmap();
|
||||||
|
|
||||||
//Render previous geometry with the abstract renderer
|
//Render previous geometry with the abstract renderer
|
||||||
@@ -121,6 +133,13 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
this.sectionRenderer.renderOpaque(viewport, depthBoundTexture);
|
this.sectionRenderer.renderOpaque(viewport, depthBoundTexture);
|
||||||
TimingStatistics.G.stop();
|
TimingStatistics.G.stop();
|
||||||
|
|
||||||
|
{
|
||||||
|
int depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
|
||||||
|
|
||||||
|
//Compute the mip chain
|
||||||
|
viewport.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
//NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable"
|
//NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable"
|
||||||
// sections
|
// sections
|
||||||
@@ -157,23 +176,11 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
|
|
||||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT);
|
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT);
|
||||||
|
|
||||||
int depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
|
|
||||||
//if (depthBuffer == 0) {
|
|
||||||
// depthBuffer = glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
|
|
||||||
//}
|
|
||||||
|
|
||||||
TimingStatistics.I.start();
|
TimingStatistics.I.start();
|
||||||
this.traversal.doTraversal(viewport, depthBuffer);
|
this.traversal.doTraversal(viewport);
|
||||||
TimingStatistics.I.stop();
|
TimingStatistics.I.stop();
|
||||||
|
|
||||||
|
} while (this.frexStillHasWork());
|
||||||
if (VoxyClient.isFrexActive()) {//If frex is running we must tick everything to ensure correctness
|
|
||||||
UploadStream.INSTANCE.tick();
|
|
||||||
//Done here as is allows less gl state resetup
|
|
||||||
this.tickModelService(100_000_000);
|
|
||||||
glFinish();
|
|
||||||
}
|
|
||||||
} while (VoxyClient.isFrexActive() && (this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty()));
|
|
||||||
|
|
||||||
|
|
||||||
TimingStatistics.H.start();
|
TimingStatistics.H.start();
|
||||||
|
|||||||
@@ -207,17 +207,17 @@ public class RenderDataFactory {
|
|||||||
return quadData;
|
return quadData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int prepareSectionData() {
|
private int prepareSectionData(final long[] rawSectionData) {
|
||||||
final var sectionData = this.sectionData;
|
final var sectionData = this.sectionData;
|
||||||
final var rawModelIds = this.modelMan._unsafeRawAccess();
|
final var rawModelIds = this.modelMan._unsafeRawAccess();
|
||||||
int opaque = 0;
|
long opaque = 0;
|
||||||
int notEmpty = 0;
|
long notEmpty = 0;
|
||||||
int pureFluid = 0;
|
long pureFluid = 0;
|
||||||
int partialFluid = 0;
|
long partialFluid = 0;
|
||||||
|
|
||||||
int neighborAcquireMsk = 0;//-+x, -+y, -+Z
|
int neighborAcquireMsk = 0;//-+x, -+z, -+y
|
||||||
for (int i = 0; i < 32*32*32;) {
|
for (int i = 0; i < 32*32*32;) {
|
||||||
long block = sectionData[i + 32 * 32 * 32];//Get the block mapping
|
long block = rawSectionData[i];//Get the block mapping
|
||||||
if (Mapper.isAir(block)) {//If it is air, just emit lighting
|
if (Mapper.isAir(block)) {//If it is air, just emit lighting
|
||||||
sectionData[i * 2] = (block&(0xFFL<<56))>>1;
|
sectionData[i * 2] = (block&(0xFFL<<56))>>1;
|
||||||
sectionData[i * 2 + 1] = 0;
|
sectionData[i * 2 + 1] = 0;
|
||||||
@@ -231,7 +231,7 @@ public class RenderDataFactory {
|
|||||||
sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata);
|
sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata);
|
||||||
sectionData[i * 2 + 1] = modelMetadata;
|
sectionData[i * 2 + 1] = modelMetadata;
|
||||||
|
|
||||||
int msk = 1 << (i & 31);
|
long msk = 1L << (i & 63);
|
||||||
opaque |= ModelQueries.isFullyOpaque(modelMetadata) ? msk : 0;
|
opaque |= ModelQueries.isFullyOpaque(modelMetadata) ? msk : 0;
|
||||||
notEmpty |= modelId != 0 ? msk : 0;
|
notEmpty |= modelId != 0 ? msk : 0;
|
||||||
pureFluid |= ModelQueries.isFluid(modelMetadata) ? msk : 0;
|
pureFluid |= ModelQueries.isFluid(modelMetadata) ? msk : 0;
|
||||||
@@ -241,21 +241,28 @@ public class RenderDataFactory {
|
|||||||
//Do increment here
|
//Do increment here
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if ((i & 31) == 0 && notEmpty != 0) {
|
if ((i & 63) == 0 && notEmpty != 0) {
|
||||||
this.opaqueMasks[(i >> 5) - 1] = opaque;
|
long nonOpaque = (notEmpty^opaque)&~pureFluid;
|
||||||
this.nonOpaqueMasks[(i >> 5) - 1] = (notEmpty^opaque)&~pureFluid;
|
long fluid = pureFluid|partialFluid;
|
||||||
this.fluidMasks[(i >> 5) - 1] = pureFluid|partialFluid;
|
this.opaqueMasks[(i >> 5) - 2] = (int) opaque;
|
||||||
|
this.opaqueMasks[(i >> 5) - 1] = (int) (opaque>>>32);
|
||||||
|
this.nonOpaqueMasks[(i >> 5) - 2] = (int) nonOpaque;
|
||||||
|
this.nonOpaqueMasks[(i >> 5) - 1] = (int) (nonOpaque>>>32);
|
||||||
|
this.fluidMasks[(i >> 5) - 2] = (int) fluid;
|
||||||
|
this.fluidMasks[(i >> 5) - 1] = (int) (fluid>>>32);
|
||||||
|
|
||||||
|
int packedEmpty = (int) ((notEmpty>>>32)|notEmpty);
|
||||||
|
|
||||||
int neighborMsk = 0;
|
int neighborMsk = 0;
|
||||||
//-+x
|
//-+x
|
||||||
neighborMsk |= notEmpty&1;//-x
|
neighborMsk |= packedEmpty&1;//-x
|
||||||
neighborMsk |= (notEmpty>>>30)&0b10;//+x
|
neighborMsk |= (packedEmpty>>>30)&0b10;//+x
|
||||||
|
|
||||||
//notEmpty = (notEmpty != 0)?1:0;
|
//notEmpty = (notEmpty != 0)?1:0;
|
||||||
neighborMsk |= (((i - 1) >> 10) == 0) ? 0b100 : 0;//-y
|
neighborMsk |= ((((i - 1) >> 10) == 0) ? 0b100 : 0)*(packedEmpty!=0?1:0);//-y
|
||||||
neighborMsk |= (((i - 1) >> 10) == 31) ? 0b1000 : 0;//+y
|
neighborMsk |= ((((i - 1) >> 10) == 31) ? 0b1000 : 0)*(packedEmpty!=0?1:0);//+y
|
||||||
neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 0) ? 0b10000 : 0;//-z
|
neighborMsk |= (((((i - 33) >> 5) & 0x1F) == 0) ? 0b10000 : 0)*(((int)notEmpty)!=0?1:0);//-z
|
||||||
neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0;//+z
|
neighborMsk |= (((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0)*((notEmpty>>>32)!=0?1:0);//+z
|
||||||
|
|
||||||
neighborAcquireMsk |= neighborMsk;
|
neighborAcquireMsk |= neighborMsk;
|
||||||
|
|
||||||
@@ -1542,7 +1549,7 @@ public class RenderDataFactory {
|
|||||||
//THE EXCEPTION THAT THIS THROWS CAUSES MAJOR ISSUES
|
//THE EXCEPTION THAT THIS THROWS CAUSES MAJOR ISSUES
|
||||||
|
|
||||||
//Copy section data to end of array so that can mutate array while reading safely
|
//Copy section data to end of array so that can mutate array while reading safely
|
||||||
section.copyDataTo(this.sectionData, 32*32*32);
|
//section.copyDataTo(this.sectionData, 32*32*32);
|
||||||
|
|
||||||
//We must reset _everything_ that could have changed as we dont exactly know the state due to how the model id exception
|
//We must reset _everything_ that could have changed as we dont exactly know the state due to how the model id exception
|
||||||
// throwing system works
|
// throwing system works
|
||||||
@@ -1578,7 +1585,7 @@ public class RenderDataFactory {
|
|||||||
Arrays.fill(this.fluidMasks, 0);
|
Arrays.fill(this.fluidMasks, 0);
|
||||||
|
|
||||||
//Prepare everything
|
//Prepare everything
|
||||||
int neighborMsk = this.prepareSectionData();
|
int neighborMsk = this.prepareSectionData(section._unsafeGetRawDataArray());
|
||||||
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
|
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
|
||||||
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
|
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class RenderGenerationService {
|
|||||||
int unique = COUNTER.incrementAndGet();
|
int unique = COUNTER.incrementAndGet();
|
||||||
int lvl = WorldEngine.MAX_LOD_LAYER-WorldEngine.getLevel(this.position);
|
int lvl = WorldEngine.MAX_LOD_LAYER-WorldEngine.getLevel(this.position);
|
||||||
lvl = Math.min(lvl, 3);//Make the 2 highest quality have equal priority
|
lvl = Math.min(lvl, 3);//Make the 2 highest quality have equal priority
|
||||||
this.priority = (((lvl*3L + Math.min(this.attempts, 5))*2 + this.addin) <<32) + Integer.toUnsignedLong(unique);
|
this.priority = (((lvl*3L + Math.min(this.attempts, 3))*2 + this.addin) <<32) + Integer.toUnsignedLong(unique);
|
||||||
this.addin = 0;
|
this.addin = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ public class RenderGenerationService {
|
|||||||
task.hasDoneModelRequestOuter = true;
|
task.hasDoneModelRequestOuter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
task.addin = WorldEngine.getLevel(task.position)>2?3:0;//Single time addin which gives the models time to bake before the task executes
|
task.addin = WorldEngine.getLevel(task.position)>2?1:0;//Single time addin which gives the models time to bake before the task executes
|
||||||
}
|
}
|
||||||
|
|
||||||
//Keep the lock on the section, and attach it to the task, this prevents needing to re-aquire it later
|
//Keep the lock on the section, and attach it to the task, this prevents needing to re-aquire it later
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ public class AsyncNodeManager {
|
|||||||
|
|
||||||
private void run() {
|
private void run() {
|
||||||
if (this.workCounter.get() <= 0) {
|
if (this.workCounter.get() <= 0) {
|
||||||
|
//TODO: here, instead of parking, we can do more work on other sub-tasks such as filtering the mesh build queue
|
||||||
LockSupport.park();
|
LockSupport.park();
|
||||||
if (this.workCounter.get() <= 0 || !this.running) {//No work
|
if (this.workCounter.get() <= 0 || !this.running) {//No work
|
||||||
return;
|
return;
|
||||||
@@ -245,8 +246,7 @@ public class AsyncNodeManager {
|
|||||||
|
|
||||||
//Limit uploading as well as by geometry capacity being available
|
//Limit uploading as well as by geometry capacity being available
|
||||||
// must have 50 mb of free geometry space to upload
|
// must have 50 mb of free geometry space to upload
|
||||||
for (int limit = 0; limit < 200 && (this.geometryCapacity-this.geometryManager.getGeometryUsedBytes())>50_000_000; limit++)
|
for (int limit = 0; limit < 200 && ((this.geometryCapacity-this.geometryManager.getGeometryUsedBytes())>50_000_000); limit++) {
|
||||||
{
|
|
||||||
var job = this.geometryUpdateQueue.poll();
|
var job = this.geometryUpdateQueue.poll();
|
||||||
if (job == null)
|
if (job == null)
|
||||||
break;
|
break;
|
||||||
@@ -280,6 +280,7 @@ public class AsyncNodeManager {
|
|||||||
break;
|
break;
|
||||||
workDone++;
|
workDone++;
|
||||||
long ptr = job.address;
|
long ptr = job.address;
|
||||||
|
int zeroCount = 0;
|
||||||
for (int i = 0; i < NodeCleaner.OUTPUT_COUNT; i++) {
|
for (int i = 0; i < NodeCleaner.OUTPUT_COUNT; i++) {
|
||||||
long pos = ((long) MemoryUtil.memGetInt(ptr)) << 32; ptr += 4;
|
long pos = ((long) MemoryUtil.memGetInt(ptr)) << 32; ptr += 4;
|
||||||
pos |= Integer.toUnsignedLong(MemoryUtil.memGetInt(ptr)); ptr += 4;
|
pos |= Integer.toUnsignedLong(MemoryUtil.memGetInt(ptr)); ptr += 4;
|
||||||
@@ -289,9 +290,8 @@ public class AsyncNodeManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == 0) {
|
if (pos == 0 && zeroCount++>0) {
|
||||||
//THIS SHOULD BE IMPOSSIBLE
|
Logger.error("Remove node pos is 0 " + zeroCount + " times, this is really bad, please report" );
|
||||||
//TODO: VVVVV MUCH MEGA FIX
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +314,7 @@ public class AsyncNodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (workDone == 0) {//Nothing happened, which is odd, but just return
|
if (workDone == 0) {//Nothing happened, which is odd, but just return
|
||||||
|
//Should probably log that nothing happened, at least once
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//=====================
|
//=====================
|
||||||
@@ -648,8 +649,12 @@ public class AsyncNodeManager {
|
|||||||
public void addTopLevel(long section) {//Only called from render thread
|
public void addTopLevel(long section) {//Only called from render thread
|
||||||
if (!this.running) throw new IllegalStateException("Not running");
|
if (!this.running) throw new IllegalStateException("Not running");
|
||||||
long stamp = this.tlnLock.writeLock();
|
long stamp = this.tlnLock.writeLock();
|
||||||
int state = this.tlnAdd.add(section)?1:0;
|
int state = 0;
|
||||||
state -= this.tlnRem.remove(section)?1:0;
|
if (!this.tlnRem.remove(section)) {
|
||||||
|
state += this.tlnAdd.add(section)?1:0;
|
||||||
|
} else {
|
||||||
|
state -= 1;
|
||||||
|
}
|
||||||
if (state != 0) {
|
if (state != 0) {
|
||||||
if (this.workCounter.getAndAdd(state) == 0) {
|
if (this.workCounter.getAndAdd(state) == 0) {
|
||||||
LockSupport.unpark(this.thread);
|
LockSupport.unpark(this.thread);
|
||||||
@@ -661,8 +666,12 @@ public class AsyncNodeManager {
|
|||||||
public void removeTopLevel(long section) {//Only called from render thread
|
public void removeTopLevel(long section) {//Only called from render thread
|
||||||
if (!this.running) throw new IllegalStateException("Not running");
|
if (!this.running) throw new IllegalStateException("Not running");
|
||||||
long stamp = this.tlnLock.writeLock();
|
long stamp = this.tlnLock.writeLock();
|
||||||
int state = this.tlnRem.add(section)?1:0;
|
int state = 0;
|
||||||
state -= this.tlnAdd.remove(section)?1:0;
|
if (!this.tlnAdd.remove(section)) {
|
||||||
|
state += this.tlnRem.add(section)?1:0;
|
||||||
|
} else {
|
||||||
|
state -= 1;
|
||||||
|
}
|
||||||
if (state != 0) {
|
if (state != 0) {
|
||||||
if (this.workCounter.getAndAdd(state) == 0) {
|
if (this.workCounter.getAndAdd(state) == 0) {
|
||||||
LockSupport.unpark(this.thread);
|
LockSupport.unpark(this.thread);
|
||||||
@@ -741,10 +750,11 @@ public class AsyncNodeManager {
|
|||||||
|
|
||||||
public void addDebug(List<String> debug) {
|
public void addDebug(List<String> debug) {
|
||||||
debug.add("UC/GC: " + (this.getUsedGeometryCapacity()/(1<<20))+"/"+(this.getGeometryCapacity()/(1<<20)));
|
debug.add("UC/GC: " + (this.getUsedGeometryCapacity()/(1<<20))+"/"+(this.getGeometryCapacity()/(1<<20)));
|
||||||
|
//debug.add("GUQ/NRC: " + this.geometryUpdateQueue.size()+"/"+this.removeBatchQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasWork() {
|
public boolean hasWork() {
|
||||||
return this.workCounter.get()!=0 && RESULT_HANDLE.get(this) != null;
|
return this.workCounter.get()!=0 || RESULT_HANDLE.get(this) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void worldEvent(WorldSection section, int flags) {
|
public void worldEvent(WorldSection section, int flags) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
|
|||||||
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
|
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
|
||||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||||
|
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||||
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
|
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
|
||||||
import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
|
import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
|
||||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||||
@@ -30,7 +31,7 @@ import static org.lwjgl.opengl.GL45.*;
|
|||||||
public class HierarchicalOcclusionTraverser {
|
public class HierarchicalOcclusionTraverser {
|
||||||
public static final boolean HIERARCHICAL_SHADER_DEBUG = System.getProperty("voxy.hierarchicalShaderDebug", "false").equals("true");
|
public static final boolean HIERARCHICAL_SHADER_DEBUG = System.getProperty("voxy.hierarchicalShaderDebug", "false").equals("true");
|
||||||
|
|
||||||
public static final int REQUEST_QUEUE_SIZE = 50;
|
public static final int MAX_REQUEST_QUEUE_SIZE = 50;
|
||||||
public static final int MAX_QUEUE_SIZE = 200_000;
|
public static final int MAX_QUEUE_SIZE = 200_000;
|
||||||
|
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
|
|
||||||
private final AsyncNodeManager nodeManager;
|
private final AsyncNodeManager nodeManager;
|
||||||
private final NodeCleaner nodeCleaner;
|
private final NodeCleaner nodeCleaner;
|
||||||
|
private final RenderGenerationService meshGen;
|
||||||
|
|
||||||
private final GlBuffer requestBuffer;
|
private final GlBuffer requestBuffer;
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
.defineIf("DEBUG", HIERARCHICAL_SHADER_DEBUG)
|
.defineIf("DEBUG", HIERARCHICAL_SHADER_DEBUG)
|
||||||
.define("MAX_ITERATIONS", MAX_ITERATIONS)
|
.define("MAX_ITERATIONS", MAX_ITERATIONS)
|
||||||
.define("LOCAL_SIZE_BITS", LOCAL_WORK_SIZE_BITS)
|
.define("LOCAL_SIZE_BITS", LOCAL_WORK_SIZE_BITS)
|
||||||
.define("REQUEST_QUEUE_SIZE", REQUEST_QUEUE_SIZE)
|
.define("MAX_REQUEST_QUEUE_SIZE", MAX_REQUEST_QUEUE_SIZE)
|
||||||
|
|
||||||
.define("HIZ_BINDING", 0)
|
.define("HIZ_BINDING", 0)
|
||||||
|
|
||||||
@@ -96,19 +98,18 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
|
|
||||||
public HierarchicalOcclusionTraverser(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner) {
|
public HierarchicalOcclusionTraverser(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, RenderGenerationService meshGen) {
|
||||||
this.nodeCleaner = nodeCleaner;
|
this.nodeCleaner = nodeCleaner;
|
||||||
this.nodeManager = nodeManager;
|
this.nodeManager = nodeManager;
|
||||||
this.requestBuffer = new GlBuffer(REQUEST_QUEUE_SIZE*8L+8).zero();
|
this.meshGen = meshGen;
|
||||||
|
this.requestBuffer = new GlBuffer(MAX_REQUEST_QUEUE_SIZE*8L+8).zero();
|
||||||
this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).fill(-1);
|
this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).fill(-1);
|
||||||
|
|
||||||
|
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
||||||
|
|
||||||
this.traversal
|
this.traversal
|
||||||
.ubo("SCENE_UNIFORM_BINDING", this.uniformBuffer)
|
.ubo("SCENE_UNIFORM_BINDING", this.uniformBuffer)
|
||||||
@@ -175,23 +176,31 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
|
|
||||||
viewport.section.getToAddress(ptr); ptr += 4*3;
|
viewport.section.getToAddress(ptr); ptr += 4*3;
|
||||||
|
|
||||||
MemoryUtil.memPutFloat(ptr, viewport.width); ptr += 4;
|
//MemoryUtil.memPutFloat(ptr, viewport.width); ptr += 4;
|
||||||
|
MemoryUtil.memPutInt(ptr, viewport.hiZBuffer.getPackedLevels()); ptr += 4;
|
||||||
|
|
||||||
viewport.innerTranslation.getToAddress(ptr); ptr += 4*3;
|
viewport.innerTranslation.getToAddress(ptr); ptr += 4*3;
|
||||||
|
|
||||||
MemoryUtil.memPutFloat(ptr, viewport.height); ptr += 4;
|
//MemoryUtil.memPutFloat(ptr, viewport.height); ptr += 4;
|
||||||
|
|
||||||
setFrustum(viewport, ptr); ptr += 4*4*6;
|
|
||||||
|
|
||||||
MemoryUtil.memPutInt(ptr, (int) (viewport.getRenderList().size()/4-1)); ptr += 4;
|
|
||||||
|
|
||||||
|
|
||||||
final float screenspaceAreaDecreasingSize = VoxyConfig.CONFIG.subDivisionSize*VoxyConfig.CONFIG.subDivisionSize;
|
final float screenspaceAreaDecreasingSize = VoxyConfig.CONFIG.subDivisionSize*VoxyConfig.CONFIG.subDivisionSize;
|
||||||
//Screen space size for descending
|
//Screen space size for descending
|
||||||
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4;
|
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4;
|
||||||
|
|
||||||
|
setFrustum(viewport, ptr); ptr += 4*4*6;
|
||||||
|
|
||||||
|
MemoryUtil.memPutInt(ptr, (int) (viewport.getRenderList().size()/4-1)); ptr += 4;
|
||||||
|
|
||||||
//VisibilityId
|
//VisibilityId
|
||||||
MemoryUtil.memPutInt(ptr, this.nodeCleaner.visibilityId); ptr += 4;
|
MemoryUtil.memPutInt(ptr, this.nodeCleaner.visibilityId); ptr += 4;
|
||||||
|
|
||||||
|
{
|
||||||
|
final double TARGET_COUNT = 4000;//TODO: make this configurable, or at least dynamically computed based on throughput rate of mesh gen
|
||||||
|
double iFillness = Math.max(0, (TARGET_COUNT - this.meshGen.getTaskCount()) / TARGET_COUNT);
|
||||||
|
iFillness = Math.pow(iFillness, 2);
|
||||||
|
final int requestSize = (int) Math.ceil(iFillness * MAX_REQUEST_QUEUE_SIZE);
|
||||||
|
MemoryUtil.memPutInt(ptr, Math.max(0, Math.min(MAX_REQUEST_QUEUE_SIZE, requestSize)));ptr += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindings(Viewport<?> viewport) {
|
private void bindings(Viewport<?> viewport) {
|
||||||
@@ -203,10 +212,7 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_QUEUE_BINDING, viewport.getRenderList().id);
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_QUEUE_BINDING, viewport.getRenderList().id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doTraversal(Viewport<?> viewport, int depthBuffer) {
|
public void doTraversal(Viewport<?> viewport) {
|
||||||
//Compute the mip chain
|
|
||||||
viewport.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height);
|
|
||||||
|
|
||||||
this.uploadUniform(viewport);
|
this.uploadUniform(viewport);
|
||||||
//UploadStream.INSTANCE.commit(); //Done inside traversal
|
//UploadStream.INSTANCE.commit(); //Done inside traversal
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
|||||||
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
|
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
|
||||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||||
|
import org.lwjgl.opengl.ARBDirectStateAccess;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData;
|
import static org.lwjgl.opengl.ARBDirectStateAccess.glCopyNamedBufferSubData;
|
||||||
@@ -112,7 +113,8 @@ public class NodeCleaner {
|
|||||||
//TODO: choose whether this is in nodeSpace or section/geometryId space
|
//TODO: choose whether this is in nodeSpace or section/geometryId space
|
||||||
//
|
//
|
||||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||||
glDispatchCompute((this.nodeManager.getCurrentMaxNodeId() + (SORTING_WORKER_SIZE+WORK_PER_THREAD) - 1) / (SORTING_WORKER_SIZE+WORK_PER_THREAD), 1, 1);
|
//This should (IN THEORY naturally align its self to the pow2 max boarder, if not... well undefined behavior is ok right?)
|
||||||
|
glDispatchCompute((this.nodeManager.getCurrentMaxNodeId() + (SORTING_WORKER_SIZE*WORK_PER_THREAD) - 1) / (SORTING_WORKER_SIZE*WORK_PER_THREAD), 1, 1);
|
||||||
|
|
||||||
this.resultTransformer.bind();
|
this.resultTransformer.bind();
|
||||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, this.outputBuffer.id, 0, 4 * OUTPUT_COUNT);
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, this.outputBuffer.id, 0, 4 * OUTPUT_COUNT);
|
||||||
@@ -165,6 +167,22 @@ public class NodeCleaner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dumpDebugData() {
|
||||||
|
int[] outData = new int[OUTPUT_COUNT*3];
|
||||||
|
ARBDirectStateAccess.glGetNamedBufferSubData(this.outputBuffer.id, 0, outData);
|
||||||
|
for(int i =0;i < OUTPUT_COUNT; i++) {
|
||||||
|
System.out.println(outData[i]);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
System.out.println("---------------\n");
|
||||||
|
for(int i =0;i < OUTPUT_COUNT; i++) {
|
||||||
|
System.out.println(data[i*2+OUTPUT_COUNT]+", "+data[i*2+OUTPUT_COUNT+1]);
|
||||||
|
}*/
|
||||||
|
int[] visData = new int[(int) (this.visibilityBuffer.size()/4)];
|
||||||
|
ARBDirectStateAccess.glGetNamedBufferSubData(this.visibilityBuffer.id, 0, visData);
|
||||||
|
int a = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void free() {
|
public void free() {
|
||||||
this.sorter.free();
|
this.sorter.free();
|
||||||
this.visibilityBuffer.free();
|
this.visibilityBuffer.free();
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ public class NodeManager {
|
|||||||
|
|
||||||
public void removeTopLevelNode(long pos) {
|
public void removeTopLevelNode(long pos) {
|
||||||
if (!this.topLevelNodes.remove(pos)) {
|
if (!this.topLevelNodes.remove(pos)) {
|
||||||
throw new IllegalStateException("Position not in top level map");
|
throw new IllegalStateException("Position not in top level map: " + WorldEngine.pprintPos(pos));
|
||||||
}
|
}
|
||||||
int nodeId = this.activeSectionMap.get(pos);
|
int nodeId = this.activeSectionMap.get(pos);
|
||||||
if (nodeId == -1) {
|
if (nodeId == -1) {
|
||||||
@@ -1213,7 +1213,8 @@ public class NodeManager {
|
|||||||
|
|
||||||
if (!this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
if (!this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
||||||
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
//Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
||||||
|
this.invalidateNode(nodeId);//Who knows why but just invalidate the data just to keep in sync
|
||||||
} else {
|
} else {
|
||||||
this.nodeData.markNodeGeometryInFlight(nodeId);
|
this.nodeData.markNodeGeometryInFlight(nodeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,8 +263,6 @@ public final class NodeStore {
|
|||||||
if (!this.nodeExists(nodeId)) {
|
if (!this.nodeExists(nodeId)) {
|
||||||
MemoryUtil.memPutLong(ptr, -1);
|
MemoryUtil.memPutLong(ptr, -1);
|
||||||
MemoryUtil.memPutLong(ptr + 8, -1);
|
MemoryUtil.memPutLong(ptr + 8, -1);
|
||||||
MemoryUtil.memPutLong(ptr + 16, -1);
|
|
||||||
MemoryUtil.memPutLong(ptr + 24, -1);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long pos = this.nodePosition(nodeId);
|
long pos = this.nodePosition(nodeId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.rendering.section;
|
|||||||
|
|
||||||
|
|
||||||
import me.cortex.voxy.client.RenderStatistics;
|
import me.cortex.voxy.client.RenderStatistics;
|
||||||
|
import me.cortex.voxy.client.core.gl.Capabilities;
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
import me.cortex.voxy.client.core.gl.GlTexture;
|
import me.cortex.voxy.client.core.gl.GlTexture;
|
||||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||||
@@ -34,6 +35,7 @@ import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
|||||||
import static org.lwjgl.opengl.GL43.*;
|
import static org.lwjgl.opengl.GL43.*;
|
||||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||||
import static org.lwjgl.opengl.GL45.glClearNamedBufferData;
|
import static org.lwjgl.opengl.GL45.glClearNamedBufferData;
|
||||||
|
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
|
||||||
|
|
||||||
//Uses MDIC to render the sections
|
//Uses MDIC to render the sections
|
||||||
public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, BasicSectionGeometryData> {
|
public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, BasicSectionGeometryData> {
|
||||||
@@ -136,6 +138,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
|||||||
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
||||||
|
|
||||||
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
|
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
|
||||||
|
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||||
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, indirectOffset, drawCountOffset, maxDrawCount, 0);
|
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, indirectOffset, drawCountOffset, maxDrawCount, 0);
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
@@ -169,6 +172,8 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
|||||||
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
|
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
|
||||||
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
this.bindRenderingBuffers(viewport, depthBoundTexture);
|
||||||
|
|
||||||
|
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
|
||||||
|
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||||
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, TRANSLUCENT_OFFSET*5*4, 4*4, Math.min(this.geometryManager.getSectionCount(), 100_000), 0);
|
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, TRANSLUCENT_OFFSET*5*4, 4*4, Math.min(this.geometryManager.getSectionCount(), 100_000), 0);
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
@@ -201,6 +206,9 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
|||||||
|
|
||||||
{//Test occlusion
|
{//Test occlusion
|
||||||
this.cullShader.bind();
|
this.cullShader.bind();
|
||||||
|
if (Capabilities.INSTANCE.repFragTest) {
|
||||||
|
glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||||
|
}
|
||||||
glBindVertexArray(RenderService.STATIC_VAO);
|
glBindVertexArray(RenderService.STATIC_VAO);
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
|
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id);
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id);
|
||||||
@@ -216,6 +224,9 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
|||||||
glDepthMask(true);
|
glDepthMask(true);
|
||||||
glColorMask(true, true, true, true);
|
glColorMask(true, true, true, true);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
if (Capabilities.INSTANCE.repFragTest) {
|
||||||
|
glDisable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import static me.cortex.voxy.client.core.rendering.section.geometry.BasicSection
|
|||||||
//Is basicly the manager for an "undefined" data store, the underlying store is irrelevant
|
//Is basicly the manager for an "undefined" data store, the underlying store is irrelevant
|
||||||
// this manager serves as an overlay, that is, it allows an implementation to do "async management" of the data store
|
// this manager serves as an overlay, that is, it allows an implementation to do "async management" of the data store
|
||||||
public class BasicAsyncGeometryManager implements IGeometryManager {
|
public class BasicAsyncGeometryManager implements IGeometryManager {
|
||||||
private static final int GEOMETRY_ELEMENT_SIZE = 8;
|
private static final long GEOMETRY_ELEMENT_SIZE = 8;
|
||||||
private final HierarchicalBitSet allocationSet;
|
private final HierarchicalBitSet allocationSet;
|
||||||
private final AllocationArena allocationHeap = new AllocationArena();
|
private final AllocationArena allocationHeap = new AllocationArena();
|
||||||
private final ObjectArrayList<SectionMeta> sectionMetadata = new ObjectArrayList<>(1<<15);
|
private final ObjectArrayList<SectionMeta> sectionMetadata = new ObjectArrayList<>(1<<15);
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ public class HiZBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void buildMipChain(int srcDepthTex, int width, int height) {
|
public void buildMipChain(int srcDepthTex, int width, int height) {
|
||||||
if (this.width != width || this.height != height) {
|
if (this.width != Integer.highestOneBit(width) || this.height != Integer.highestOneBit(height)) {
|
||||||
if (this.texture != null) {
|
if (this.texture != null) {
|
||||||
this.texture.free();
|
this.texture.free();
|
||||||
this.texture = null;
|
this.texture = null;
|
||||||
}
|
}
|
||||||
this.alloc(width, height);
|
this.alloc(Integer.highestOneBit(width), Integer.highestOneBit(height));
|
||||||
}
|
}
|
||||||
glBindVertexArray(RenderService.STATIC_VAO);
|
glBindVertexArray(RenderService.STATIC_VAO);
|
||||||
int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
|
int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
|
||||||
@@ -86,26 +86,22 @@ public class HiZBuffer {
|
|||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
|
||||||
//System.err.println("SRC: " + GlTexture.getRawTextureType(srcDepthTex) + " DST: " + this.texture.id);
|
glBindTextureUnit(0, srcDepthTex);
|
||||||
glCopyImageSubData(srcDepthTex, GL_TEXTURE_2D, 0,0,0,0,
|
|
||||||
this.texture.id, GL_TEXTURE_2D, 0,0,0,0,
|
|
||||||
width, height, 1);
|
|
||||||
|
|
||||||
|
|
||||||
glBindTextureUnit(0, this.texture.id);
|
|
||||||
glBindSampler(0, this.sampler);
|
glBindSampler(0, this.sampler);
|
||||||
glUniform1i(0, 0);
|
glUniform1i(0, 0);
|
||||||
int cw = this.width;
|
int cw = this.width;
|
||||||
int ch = this.height;
|
int ch = this.height;
|
||||||
glViewport(0, 0, cw, ch);
|
for (int i = 0; i < this.levels; i++) {
|
||||||
for (int i = 0; i < this.levels-1; i++) {
|
this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, i);
|
||||||
glTextureParameteri(this.texture.id, GL_TEXTURE_BASE_LEVEL, i);
|
glViewport(0, 0, cw, ch); cw = Math.max(cw/2, 1); ch = Math.max(ch/2, 1);
|
||||||
glTextureParameteri(this.texture.id, GL_TEXTURE_MAX_LEVEL, i);
|
|
||||||
this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, i+1);
|
|
||||||
cw = Math.max(cw/2, 1); ch = Math.max(ch/2, 1); glViewport(0, 0, cw, ch);
|
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
glTextureBarrier();
|
glTextureBarrier();
|
||||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_FETCH_BARRIER_BIT);
|
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_FETCH_BARRIER_BIT);
|
||||||
|
glTextureParameteri(this.texture.id, GL_TEXTURE_BASE_LEVEL, i);
|
||||||
|
glTextureParameteri(this.texture.id, GL_TEXTURE_MAX_LEVEL, i);
|
||||||
|
if (i==0) {
|
||||||
|
glBindTextureUnit(0, this.texture.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
glTextureParameteri(this.texture.id, GL_TEXTURE_BASE_LEVEL, 0);
|
glTextureParameteri(this.texture.id, GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
glTextureParameteri(this.texture.id, GL_TEXTURE_MAX_LEVEL, 1000);//TODO: CHECK IF ITS -1 or -0
|
glTextureParameteri(this.texture.id, GL_TEXTURE_MAX_LEVEL, 1000);//TODO: CHECK IF ITS -1 or -0
|
||||||
@@ -130,4 +126,8 @@ public class HiZBuffer {
|
|||||||
public int getHizTextureId() {
|
public int getHizTextureId() {
|
||||||
return this.texture.id;
|
return this.texture.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPackedLevels() {
|
||||||
|
return ((Integer.numberOfTrailingZeros(this.width))<<16)|(Integer.numberOfTrailingZeros(this.height));//+1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,6 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
|||||||
@Shadow private @Nullable ClientWorld world;
|
@Shadow private @Nullable ClientWorld world;
|
||||||
@Unique private VoxyRenderSystem renderer;
|
@Unique private VoxyRenderSystem renderer;
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V", shift = At.Shift.AFTER))
|
|
||||||
private void injectSetup(ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, CallbackInfo ci) {
|
|
||||||
if (this.renderer != null) {
|
|
||||||
this.renderer.renderSetup(this.frustum, camera);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VoxyRenderSystem getVoxyRenderSystem() {
|
public VoxyRenderSystem getVoxyRenderSystem() {
|
||||||
return this.renderer;
|
return this.renderer;
|
||||||
|
|||||||
@@ -14,6 +14,30 @@ public class Logger {
|
|||||||
public static boolean SHUTUP = false;
|
public static boolean SHUTUP = false;
|
||||||
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy");
|
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy");
|
||||||
|
|
||||||
|
|
||||||
|
private static String callClsName() {
|
||||||
|
String className = "";
|
||||||
|
if (INSERT_CLASS) {
|
||||||
|
var stackEntry = new Throwable().getStackTrace()[2];
|
||||||
|
className = stackEntry.getClassName();
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
var parts = className.split("\\.");
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
var part = parts[i];
|
||||||
|
if (i < parts.length-1) {//-2
|
||||||
|
builder.append(part.charAt(0)).append(part.charAt(part.length()-1));
|
||||||
|
} else {
|
||||||
|
builder.append(part);
|
||||||
|
}
|
||||||
|
if (i!=parts.length-1) {
|
||||||
|
builder.append(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
className = builder.toString();
|
||||||
|
}
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
public static void error(Object... args) {
|
public static void error(Object... args) {
|
||||||
if (SHUTUP) {
|
if (SHUTUP) {
|
||||||
return;
|
return;
|
||||||
@@ -24,8 +48,8 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
|
||||||
String error = (INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" "));
|
String error = (INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" "));
|
||||||
LOGGER.error(error, throwable);
|
LOGGER.error(error, throwable);
|
||||||
if (VoxyCommon.IS_IN_MINECRAFT && !VoxyCommon.IS_DEDICATED_SERVER) {
|
if (VoxyCommon.IS_IN_MINECRAFT && !VoxyCommon.IS_DEDICATED_SERVER) {
|
||||||
var instance = MinecraftClient.getInstance();
|
var instance = MinecraftClient.getInstance();
|
||||||
@@ -48,8 +72,7 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
LOGGER.warn((INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
||||||
LOGGER.warn((INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void info(Object... args) {
|
public static void info(Object... args) {
|
||||||
@@ -62,8 +85,7 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stackEntry = new Throwable().getStackTrace()[1];
|
LOGGER.info((INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
||||||
LOGGER.info((INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String objToString(Object obj) {
|
private static String objToString(Object obj) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package me.cortex.voxy.common.config.storage.other;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.config.storage.StorageBackend;
|
import me.cortex.voxy.common.config.storage.StorageBackend;
|
||||||
import me.cortex.voxy.common.config.ConfigBuildCtx;
|
import me.cortex.voxy.common.config.ConfigBuildCtx;
|
||||||
import me.cortex.voxy.common.config.storage.StorageConfig;
|
import me.cortex.voxy.common.config.storage.StorageConfig;
|
||||||
@@ -100,7 +101,7 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verification.size() != 1) {
|
if (verification.size() != 1) {
|
||||||
System.err.println("Error id mapping not matching across all fragments, attempting to recover");
|
Logger.error("Error id mapping not matching across all fragments, attempting to recover");
|
||||||
Object2IntMap.Entry<Int2ObjectOpenHashMap<EqualingArray>> maxEntry = null;
|
Object2IntMap.Entry<Int2ObjectOpenHashMap<EqualingArray>> maxEntry = null;
|
||||||
for (var entry : verification.object2IntEntrySet()) {
|
for (var entry : verification.object2IntEntrySet()) {
|
||||||
if (maxEntry == null) { maxEntry = entry; }
|
if (maxEntry == null) { maxEntry = entry; }
|
||||||
|
|||||||
@@ -54,22 +54,39 @@ public class RocksDBStorageBackend extends StorageBackend {
|
|||||||
|
|
||||||
//TODO: FIXME: DONT USE THE SAME options PER COLUMN FAMILY
|
//TODO: FIXME: DONT USE THE SAME options PER COLUMN FAMILY
|
||||||
final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()
|
final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions()
|
||||||
|
.setCompressionType(CompressionType.ZSTD_COMPRESSION)
|
||||||
|
.optimizeForSmallDb();
|
||||||
|
|
||||||
|
final ColumnFamilyOptions cfWorldSecOpts = new ColumnFamilyOptions()
|
||||||
.setCompressionType(CompressionType.NO_COMPRESSION)
|
.setCompressionType(CompressionType.NO_COMPRESSION)
|
||||||
|
.setCompactionPriority(CompactionPriority.MinOverlappingRatio)
|
||||||
|
.setLevelCompactionDynamicLevelBytes(true)
|
||||||
.optimizeForPointLookup(128);
|
.optimizeForPointLookup(128);
|
||||||
|
|
||||||
|
var bCache = new HyperClockCache(128*1024L*1024L,0, 4, false);
|
||||||
|
var filter = new BloomFilter(10);
|
||||||
|
cfWorldSecOpts.setTableFormatConfig(new BlockBasedTableConfig()
|
||||||
|
.setCacheIndexAndFilterBlocksWithHighPriority(true)
|
||||||
|
.setBlockCache(bCache)
|
||||||
|
.setDataBlockHashTableUtilRatio(0.75)
|
||||||
|
//.setIndexType(IndexType.kHashSearch)//Maybe?
|
||||||
|
.setDataBlockIndexType(DataBlockIndexType.kDataBlockBinaryAndHash)
|
||||||
|
.setFilterPolicy(filter)
|
||||||
|
);
|
||||||
|
|
||||||
final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
|
final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
|
||||||
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
|
new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
|
||||||
new ColumnFamilyDescriptor("world_sections".getBytes(), cfOpts),
|
new ColumnFamilyDescriptor("world_sections".getBytes(), cfWorldSecOpts),
|
||||||
new ColumnFamilyDescriptor("id_mappings".getBytes(), cfOpts)
|
new ColumnFamilyDescriptor("id_mappings".getBytes(), cfOpts)
|
||||||
);
|
);
|
||||||
|
|
||||||
final DBOptions options = new DBOptions()
|
final DBOptions options = new DBOptions()
|
||||||
|
//.setUnorderedWrite(true)
|
||||||
.setAvoidUnnecessaryBlockingIO(true)
|
.setAvoidUnnecessaryBlockingIO(true)
|
||||||
.setIncreaseParallelism(2)
|
.setIncreaseParallelism(2)
|
||||||
.setCreateIfMissing(true)
|
.setCreateIfMissing(true)
|
||||||
.setCreateMissingColumnFamilies(true)
|
.setCreateMissingColumnFamilies(true)
|
||||||
.setMaxTotalWalSize(1024*1024*512);//512 mb max WAL size
|
.setMaxTotalWalSize(1024*1024*128);//128 mb max WAL size
|
||||||
|
|
||||||
List<ColumnFamilyHandle> handles = new ArrayList<>();
|
List<ColumnFamilyHandle> handles = new ArrayList<>();
|
||||||
|
|
||||||
@@ -85,8 +102,11 @@ public class RocksDBStorageBackend extends StorageBackend {
|
|||||||
this.closeList.add(this.db);
|
this.closeList.add(this.db);
|
||||||
this.closeList.add(options);
|
this.closeList.add(options);
|
||||||
this.closeList.add(cfOpts);
|
this.closeList.add(cfOpts);
|
||||||
|
this.closeList.add(cfWorldSecOpts);
|
||||||
this.closeList.add(this.sectionReadOps);
|
this.closeList.add(this.sectionReadOps);
|
||||||
this.closeList.add(this.sectionWriteOps);
|
this.closeList.add(this.sectionWriteOps);
|
||||||
|
this.closeList.add(filter);
|
||||||
|
this.closeList.add(bCache);
|
||||||
|
|
||||||
this.worldSections = handles.get(1);
|
this.worldSections = handles.get(1);
|
||||||
this.idMappings = handles.get(2);
|
this.idMappings = handles.get(2);
|
||||||
@@ -193,6 +213,7 @@ public class RocksDBStorageBackend extends StorageBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
this.flush();
|
||||||
this.closeList.forEach(AbstractImmutableNativeReference::close);
|
this.closeList.forEach(AbstractImmutableNativeReference::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,15 +46,19 @@ public class ServiceThreadPool {
|
|||||||
//Set worker affinity if possible
|
//Set worker affinity if possible
|
||||||
CpuLayout.setThreadAffinity(CpuLayout.CORES[2 + (threadId % (CpuLayout.CORES.length - 2))]);
|
CpuLayout.setThreadAffinity(CpuLayout.CORES[2 + (threadId % (CpuLayout.CORES.length - 2))]);
|
||||||
}
|
}
|
||||||
|
if (threadId != 0) {
|
||||||
ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_PRIORITY_LOWEST);
|
ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_PRIORITY_LOWEST);
|
||||||
//ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_MODE_BACKGROUND_BEGIN);
|
//ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_MODE_BACKGROUND_BEGIN);
|
||||||
|
}
|
||||||
this.worker(threadId);
|
this.worker(threadId);
|
||||||
});
|
});
|
||||||
worker.setDaemon(false);
|
worker.setDaemon(false);
|
||||||
worker.setName("Service worker #" + i);
|
worker.setName("Service worker #" + i);
|
||||||
worker.setPriority(priority);
|
if (i == 0) {//Give the first thread normal priority, this helps if the system is under huge load for voxy to get some work done
|
||||||
|
worker.setPriority(Thread.NORM_PRIORITY);
|
||||||
|
} else {
|
||||||
|
worker.setPriority(priority);
|
||||||
|
}
|
||||||
worker.start();
|
worker.start();
|
||||||
worker.setUncaughtExceptionHandler(this::handleUncaughtException);
|
worker.setUncaughtExceptionHandler(this::handleUncaughtException);
|
||||||
this.workers[i] = worker;
|
this.workers[i] = worker;
|
||||||
@@ -274,8 +278,7 @@ public class ServiceThreadPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
||||||
System.err.println("Service worker thread has exploded unexpectedly! this is really not good very very bad.");
|
Logger.error("Service worker thread has exploded unexpectedly! this is really not good very very bad.", throwable);
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.common.world;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.VolatileHolder;
|
import me.cortex.voxy.common.util.VolatileHolder;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -115,7 +116,7 @@ public class ActiveSectionTracker {
|
|||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
//TODO: Instead if throwing an exception do something better, like attempting to regen
|
//TODO: Instead if throwing an exception do something better, like attempting to regen
|
||||||
//throw new IllegalStateException("Unable to load section: ");
|
//throw new IllegalStateException("Unable to load section: ");
|
||||||
System.err.println("Unable to load section " + section.key + " setting to air");
|
Logger.error("Unable to load section " + section.key + " setting to air");
|
||||||
status = 1;
|
status = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +130,9 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.acquire();
|
section.acquire();
|
||||||
VarHandle.fullFence();//Do not reorder setting this object
|
VarHandle.storeStoreFence();//Do not reorder setting this object
|
||||||
holder.obj = section;
|
holder.obj = section;
|
||||||
VarHandle.fullFence();
|
VarHandle.releaseFence();
|
||||||
if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell
|
if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell
|
||||||
section.release();
|
section.release();
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxy.common.world;
|
package me.cortex.voxy.common.world;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ShortOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ShortOpenHashMap;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
import me.cortex.voxy.common.util.UnsafeUtil;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
@@ -120,7 +121,7 @@ public class SaveLoadSystem {
|
|||||||
|
|
||||||
if (section.key != key) {
|
if (section.key != key) {
|
||||||
//throw new IllegalStateException("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
//throw new IllegalStateException("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||||
System.err.println("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
Logger.error("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ public class SaveLoadSystem {
|
|||||||
long expectedHash = MemoryUtil.memGetLong(ptr); ptr += 8; if (VERIFY_MEMORY_ACCESS && data.size<(ptr-data.address)) throw new IllegalStateException("Memory access OOB");
|
long expectedHash = MemoryUtil.memGetLong(ptr); ptr += 8; if (VERIFY_MEMORY_ACCESS && data.size<(ptr-data.address)) throw new IllegalStateException("Memory access OOB");
|
||||||
if (expectedHash != hash) {
|
if (expectedHash != hash) {
|
||||||
//throw new IllegalStateException("Hash mismatch got: " + hash + " expected: " + expectedHash);
|
//throw new IllegalStateException("Hash mismatch got: " + hash + " expected: " + expectedHash);
|
||||||
System.err.println("Hash mismatch got: " + hash + " expected: " + expectedHash + " removing region");
|
Logger.error("Hash mismatch got: " + hash + " expected: " + expectedHash + " removing region");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ import me.cortex.voxy.commonImpl.VoxyCommon;
|
|||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
public class SaveLoadSystem3 {
|
public class SaveLoadSystem3 {
|
||||||
private record SerializationCache(long[] blockStateCache, Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
private record SerializationCache(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
||||||
public SerializationCache() {
|
public SerializationCache() {
|
||||||
this(new long[WorldSection.SECTION_VOLUME],
|
this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
||||||
new Long2ShortOpenHashMap(512),
|
|
||||||
ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
|
||||||
this.lutMapCache.defaultReturnValue((short) -1);
|
this.lutMapCache.defaultReturnValue((short) -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,8 +37,7 @@ public class SaveLoadSystem3 {
|
|||||||
//TODO: Cache like long2short and the short and other data to stop allocs
|
//TODO: Cache like long2short and the short and other data to stop allocs
|
||||||
public static MemoryBuffer serialize(WorldSection section) {
|
public static MemoryBuffer serialize(WorldSection section) {
|
||||||
var cache = CACHE.get();
|
var cache = CACHE.get();
|
||||||
var data = cache.blockStateCache;
|
var data = section.data;
|
||||||
section.copyDataTo(data);
|
|
||||||
|
|
||||||
Long2ShortOpenHashMap LUT = cache.lutMapCache; LUT.clear();
|
Long2ShortOpenHashMap LUT = cache.lutMapCache; LUT.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -163,15 +163,18 @@ public class WorldEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void markActive() {
|
public void markActive() {
|
||||||
|
if (!this.isLive) throw new IllegalStateException();
|
||||||
this.lastActiveTime = System.currentTimeMillis();
|
this.lastActiveTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acquireRef() {
|
public void acquireRef() {
|
||||||
|
if (!this.isLive) throw new IllegalStateException();
|
||||||
this.refCount.incrementAndGet();
|
this.refCount.incrementAndGet();
|
||||||
this.lastActiveTime = System.currentTimeMillis();
|
this.lastActiveTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseRef() {
|
public void releaseRef() {
|
||||||
|
if (!this.isLive) throw new IllegalStateException();
|
||||||
if (this.refCount.decrementAndGet()<0) {
|
if (this.refCount.decrementAndGet()<0) {
|
||||||
throw new IllegalStateException("ref count less than 0");
|
throw new IllegalStateException("ref count less than 0");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxy.common.world.other;
|
package me.cortex.voxy.common.world.other;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.config.IMappingStorage;
|
import me.cortex.voxy.common.config.IMappingStorage;
|
||||||
import me.cortex.voxy.common.config.section.SectionStorage;
|
import me.cortex.voxy.common.config.section.SectionStorage;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
@@ -99,7 +100,7 @@ public class Mapper {
|
|||||||
if (entryType == BLOCK_STATE_TYPE) {
|
if (entryType == BLOCK_STATE_TYPE) {
|
||||||
var sentry = StateEntry.deserialize(id, entry.getValue());
|
var sentry = StateEntry.deserialize(id, entry.getValue());
|
||||||
if (sentry.state.isAir()) {
|
if (sentry.state.isAir()) {
|
||||||
System.err.println("Deserialization was air, removed block");
|
Logger.error("Deserialization was air, removed block");
|
||||||
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,12 +53,18 @@ public class SectionSavingService {
|
|||||||
|
|
||||||
//Hard limit the save count to prevent OOM
|
//Hard limit the save count to prevent OOM
|
||||||
if (this.getTaskCount() > 5_000) {
|
if (this.getTaskCount() > 5_000) {
|
||||||
while (this.getTaskCount() > 5_000) {
|
//wait a bit
|
||||||
try {
|
try {
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
//If we are still full, process entries in the queue ourselves instead of waiting for the service
|
||||||
|
while (this.getTaskCount() > 5_000 && this.threads.isAlive()) {
|
||||||
|
if (!this.threads.steal()) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
this.processJob();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +75,7 @@ public class SectionSavingService {
|
|||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (this.threads.getJobCount() != 0) {
|
if (this.threads.getJobCount() != 0) {
|
||||||
System.err.println("Voxy section saving still in progress, estimated " + this.threads.getJobCount() + " sections remaining.");
|
Logger.error("Voxy section saving still in progress, estimated " + this.threads.getJobCount() + " sections remaining.");
|
||||||
while (this.threads.getJobCount() != 0) {
|
while (this.threads.getJobCount() != 0) {
|
||||||
Thread.onSpinWait();
|
Thread.onSpinWait();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ public class VoxelIngestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueIngest(WorldEngine engine, WorldChunk chunk) {
|
public void enqueueIngest(WorldEngine engine, WorldChunk chunk) {
|
||||||
|
if (!this.threads.isAlive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!engine.isLive()) {
|
if (!engine.isLive()) {
|
||||||
throw new IllegalStateException("Tried inserting chunk into WorldEngine that was not alive");
|
throw new IllegalStateException("Tried inserting chunk into WorldEngine that was not alive");
|
||||||
}
|
}
|
||||||
@@ -117,7 +120,12 @@ public class VoxelIngestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, engine, section, bl, sl));
|
this.ingestQueue.add(new IngestSection(chunk.getPos().x, i, chunk.getPos().z, engine, section, bl, sl));
|
||||||
this.threads.execute();
|
try {
|
||||||
|
this.threads.execute();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.error("Executing had an error: assume shutting down, aborting",e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public abstract class VoxyInstance {
|
|||||||
if (this.activeWorlds.containsKey(identifier)) {
|
if (this.activeWorlds.containsKey(identifier)) {
|
||||||
throw new IllegalStateException("Existing world with identifier");
|
throw new IllegalStateException("Existing world with identifier");
|
||||||
}
|
}
|
||||||
Logger.info("Creating new world engine");
|
Logger.info("Creating new world engine: " + identifier.getLongHash());
|
||||||
var world = new WorldEngine(this.createStorage(identifier), this);
|
var world = new WorldEngine(this.createStorage(identifier), this);
|
||||||
world.setSaveCallback(this.savingService::enqueueSave);
|
world.setSaveCallback(this.savingService::enqueueSave);
|
||||||
this.activeWorlds.put(identifier, world);
|
this.activeWorlds.put(identifier, world);
|
||||||
@@ -158,6 +158,7 @@ public abstract class VoxyInstance {
|
|||||||
var world = this.activeWorlds.remove(id);
|
var world = this.activeWorlds.remove(id);
|
||||||
if (world == null) continue;//Race condition between unlock read and acquire write
|
if (world == null) continue;//Race condition between unlock read and acquire write
|
||||||
if (!world.isWorldIdle()) {this.activeWorlds.put(id, world); continue;}//No longer idle
|
if (!world.isWorldIdle()) {this.activeWorlds.put(id, world); continue;}//No longer idle
|
||||||
|
Logger.info("Shutting down idle world: " + id.getLongHash());
|
||||||
//If is here close and free the world
|
//If is here close and free the world
|
||||||
world.free();
|
world.free();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class WorldIdentifier {
|
|||||||
this.key = key;
|
this.key = key;
|
||||||
this.biomeSeed = biomeSeed;
|
this.biomeSeed = biomeSeed;
|
||||||
this.dimension = dimension;
|
this.dimension = dimension;
|
||||||
this.hashCode = mixStafford13(key.hashCode()^biomeSeed)^mixStafford13(dimension.hashCode()^biomeSeed);
|
this.hashCode = mixStafford13(registryKeyHashCode(key))^mixStafford13(registryKeyHashCode(dimension))^mixStafford13(biomeSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,4 +101,16 @@ public class WorldIdentifier {
|
|||||||
seed = (seed ^ seed >>> 27) * -7723592293110705685L;
|
seed = (seed ^ seed >>> 27) * -7723592293110705685L;
|
||||||
return seed ^ seed >>> 31;
|
return seed ^ seed >>> 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLongHash() {
|
||||||
|
return this.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long registryKeyHashCode(RegistryKey<?> key) {
|
||||||
|
var A = key.getRegistry();
|
||||||
|
var B = key.getValue();
|
||||||
|
int a = A==null?0:A.hashCode();
|
||||||
|
int b = B==null?0:B.hashCode();
|
||||||
|
return (Integer.toUnsignedLong(a)<<32)|Integer.toUnsignedLong(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ public class WorldImporter implements IDataImporter {
|
|||||||
|
|
||||||
//TODO: create memory copy for each section
|
//TODO: create memory copy for each section
|
||||||
if (regionFile.size < ((sectorCount-1) + sectorStart) * 4096L) {
|
if (regionFile.size < ((sectorCount-1) + sectorStart) * 4096L) {
|
||||||
System.err.println("Cannot access chunk sector as it goes out of bounds. start bytes: " + (sectorStart*4096) + " sector count: " + sectorCount + " fileSize: " + regionFile.size);
|
Logger.warn("Cannot access chunk sector as it goes out of bounds. start bytes: " + (sectorStart*4096) + " sector count: " + sectorCount + " fileSize: " + regionFile.size);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ public class WorldImporter implements IDataImporter {
|
|||||||
} else {
|
} else {
|
||||||
int n = m - 1;
|
int n = m - 1;
|
||||||
if (regionFile.size < (n + sectorStart*4096L)) {
|
if (regionFile.size < (n + sectorStart*4096L)) {
|
||||||
System.err.println("Chunk stream to small");
|
Logger.warn("Chunk stream to small");
|
||||||
} else if ((b & 128) != 0) {
|
} else if ((b & 128) != 0) {
|
||||||
if (n != 0) {
|
if (n != 0) {
|
||||||
Logger.error("Chunk has both internal and external streams");
|
Logger.error("Chunk has both internal and external streams");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ layout(binding = 0) uniform sampler2D depthTex;
|
|||||||
void main() {
|
void main() {
|
||||||
vec4 depths = textureGather(depthTex, uv, 0); // Get depth values from all surrounding texels.
|
vec4 depths = textureGather(depthTex, uv, 0); // Get depth values from all surrounding texels.
|
||||||
|
|
||||||
bvec4 cv = lessThanEqual(vec4(0.999999f), depths);
|
bvec4 cv = lessThanEqual(vec4(0.999999999f), depths);
|
||||||
if (any(cv)) {//Patch holes (its very dodgy but should work :tm:, should clamp it to the first 3 levels)
|
if (any(cv)) {//Patch holes (its very dodgy but should work :tm:, should clamp it to the first 3 levels)
|
||||||
depths = mix(vec4(0.0f), depths, cv);
|
depths = mix(vec4(0.0f), depths, cv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
struct BlockModel {
|
||||||
|
uint faceData[6];
|
||||||
|
uint flagsA;
|
||||||
|
uint colourTint;
|
||||||
|
uint _pad[8];
|
||||||
|
};
|
||||||
|
|
||||||
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off
|
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off
|
||||||
// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much
|
// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much
|
||||||
float extractFaceIndentation(uint faceData) {
|
float extractFaceIndentation(uint faceData) {
|
||||||
@@ -18,13 +25,13 @@ uint faceHasAlphaCuttoutOverride(uint faceData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool modelHasBiomeLUT(BlockModel model) {
|
bool modelHasBiomeLUT(BlockModel model) {
|
||||||
return ((model.flagsA)&2) != 0;
|
return ((model.flagsA)&2u) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool modelIsTranslucent(BlockModel model) {
|
bool modelIsTranslucent(BlockModel model) {
|
||||||
return ((model.flagsA)&4) != 0;
|
return ((model.flagsA)&4u) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool modelHasMipmaps(BlockModel model) {
|
bool modelHasMipmaps(BlockModel model) {
|
||||||
return ((model.flagsA)&8) != 0;
|
return ((model.flagsA)&8u) != 0;
|
||||||
}
|
}
|
||||||
@@ -7,24 +7,6 @@ layout(binding = 0, std140) uniform SceneUniform {
|
|||||||
vec3 cameraSubPos;
|
vec3 cameraSubPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockModel {
|
|
||||||
uint faceData[6];
|
|
||||||
uint flagsA;
|
|
||||||
uint colourTint;
|
|
||||||
uint _pad[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SectionMeta {
|
|
||||||
uint posA;
|
|
||||||
uint posB;
|
|
||||||
uint AABB;
|
|
||||||
uint ptr;
|
|
||||||
uint cntA;
|
|
||||||
uint cntB;
|
|
||||||
uint cntC;
|
|
||||||
uint cntD;
|
|
||||||
};
|
|
||||||
|
|
||||||
//TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write
|
//TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write
|
||||||
struct DrawCommand {
|
struct DrawCommand {
|
||||||
uint count;
|
uint count;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ layout(local_size_x = 128) in;
|
|||||||
#define SECTION_METADATA_BUFFER_BINDING 3
|
#define SECTION_METADATA_BUFFER_BINDING 3
|
||||||
#define INDIRECT_SECTION_LOOKUP_BINDING 4
|
#define INDIRECT_SECTION_LOOKUP_BINDING 4
|
||||||
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
|
||||||
#import <voxy:lod/section.glsl>
|
#import <voxy:lod/section.glsl>
|
||||||
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
uint count;
|
uint count;
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ layout(local_size_x = 128) in;
|
|||||||
#define POSITION_SCRATCH_BINDING 6
|
#define POSITION_SCRATCH_BINDING 6
|
||||||
#define POSITION_SCRATCH_ACCESS writeonly
|
#define POSITION_SCRATCH_ACCESS writeonly
|
||||||
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
|
||||||
#import <voxy:lod/section.glsl>
|
#import <voxy:lod/section.glsl>
|
||||||
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
|
||||||
//https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_shader_16bit_storage.txt
|
//https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_shader_16bit_storage.txt
|
||||||
// adds support for uint8_t which can use for compact visibility buffer
|
// adds support for uint8_t which can use for compact visibility buffer
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
#define VISIBILITY_BUFFER_BINDING 2
|
#define VISIBILITY_BUFFER_BINDING 2
|
||||||
#define INDIRECT_SECTION_LOOKUP_BINDING 3
|
#define INDIRECT_SECTION_LOOKUP_BINDING 3
|
||||||
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
|
||||||
#import <voxy:lod/section.glsl>
|
#import <voxy:lod/section.glsl>
|
||||||
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
|
||||||
flat out uint id;
|
flat out uint id;
|
||||||
flat out uint value;
|
flat out uint value;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
#version 460 core
|
#version 460 core
|
||||||
|
//Use quad shuffling to compute fragment mip
|
||||||
|
//#extension GL_KHR_shader_subgroup_quad: enable
|
||||||
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
||||||
layout(binding = 2) uniform sampler2D depthTex;
|
layout(binding = 2) uniform sampler2D depthTex;
|
||||||
|
|
||||||
@@ -13,22 +17,50 @@ layout(location = 2) in flat vec4 tinting;
|
|||||||
layout(location = 3) in flat vec4 addin;
|
layout(location = 3) in flat vec4 addin;
|
||||||
layout(location = 4) in flat uint flags;
|
layout(location = 4) in flat uint flags;
|
||||||
layout(location = 5) in flat vec4 conditionalTinting;
|
layout(location = 5) in flat vec4 conditionalTinting;
|
||||||
|
layout(location = 6) in flat vec2 quadSize;
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
layout(location = 6) in flat uint quadDebug;
|
layout(location = 7) in flat uint quadDebug;
|
||||||
#endif
|
#endif
|
||||||
layout(location = 0) out vec4 outColour;
|
layout(location = 0) out vec4 outColour;
|
||||||
|
|
||||||
|
vec4 computeColour(vec4 colour) {
|
||||||
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if ((flags&(1u<<2)) != 0 && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
||||||
|
colour *= conditionalTinting;
|
||||||
|
}
|
||||||
|
return (colour * tinting) + addin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useMipmaps() {
|
||||||
|
return ((flags>>1)&1u)==0u;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
//Tile is the tile we are in
|
||||||
|
vec2 tile;
|
||||||
|
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec4 colour = vec4(1);
|
||||||
|
vec2 texPos = uv2 + baseUV;
|
||||||
|
if (useMipmaps()) {
|
||||||
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
||||||
|
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
||||||
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
|
} else {
|
||||||
|
colour = texture(blockModelAtlas, texPos, -5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(notEqual(clamp(tile, vec2(0), quadSize), tile))) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
//Check the minimum bounding texture and ensure we are greater than it
|
//Check the minimum bounding texture and ensure we are greater than it
|
||||||
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
vec2 uv = mod(uv, vec2(1.0))*(1.0/(vec2(3.0,2.0)*256.0));
|
|
||||||
vec2 texPos = uv + baseUV;
|
|
||||||
//vec4 colour = solidColour;
|
|
||||||
//TODO: FIXME, need to manually compute the mip colour
|
|
||||||
vec4 colour = texture(blockModelAtlas, texPos, ((flags>>1)&1u)*-5.0);//TODO: FIXME mipping needs to be fixed so that it doesnt go cross model bounds
|
|
||||||
//Also, small quad is really fking over the mipping level somehow
|
//Also, small quad is really fking over the mipping level somehow
|
||||||
if ((flags&1u) == 1 && (texture(blockModelAtlas, texPos, -16.0).a <= 0.1f)) {
|
if ((flags&1u) == 1 && (texture(blockModelAtlas, texPos, -16.0).a <= 0.1f)) {
|
||||||
//This is stupidly stupidly bad for divergence
|
//This is stupidly stupidly bad for divergence
|
||||||
@@ -38,14 +70,7 @@ void main() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
outColour = computeColour(colour);
|
||||||
if ((flags&(1u<<2)) != 0 && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
|
||||||
colour *= conditionalTinting;
|
|
||||||
}
|
|
||||||
|
|
||||||
outColour = (colour * tinting) + addin;
|
|
||||||
|
|
||||||
//outColour = vec4(uv + baseUV, 0, 1);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
@@ -56,4 +81,30 @@ void main() {
|
|||||||
hash = hash * 1827364925 + 123325621;
|
hash = hash * 1827364925 + 123325621;
|
||||||
outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1);
|
outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//#ifdef GL_KHR_shader_subgroup_quad
|
||||||
|
/*
|
||||||
|
uint hash = (uint(tile.x)*(1<<16))^uint(tile.y);
|
||||||
|
uint horiz = subgroupQuadSwapHorizontal(hash);
|
||||||
|
bool sameTile = horiz==hash;
|
||||||
|
uint sv = mix(uint(-1), hash, sameTile);
|
||||||
|
uint vert = subgroupQuadSwapVertical(sv);
|
||||||
|
sameTile = sameTile&&vert==hash;
|
||||||
|
mipBias = sameTile?0:-5.0;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
float lDx = subgroupQuadSwapHorizontal(uvSmol.x)-uvSmol.x;
|
||||||
|
float lDy = subgroupQuadSwapVertical(uvSmol.y)-uvSmol.y;
|
||||||
|
float dDx = subgroupQuadSwapDiagonal(lDx);
|
||||||
|
float dDy = subgroupQuadSwapDiagonal(lDy);
|
||||||
|
vec2 dx = vec2(lDx, dDx);
|
||||||
|
vec2 dy = vec2(lDy, dDy);
|
||||||
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
|
*/
|
||||||
|
//#else
|
||||||
|
//colour = texture(blockModelAtlas, texPos);
|
||||||
|
//#endif
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
#extension GL_ARB_gpu_shader_int64 : enable
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
|
||||||
#define QUAD_BUFFER_BINDING 1
|
#define QUAD_BUFFER_BINDING 1
|
||||||
#define SECTION_METADATA_BUFFER_BINDING 2
|
|
||||||
#define MODEL_BUFFER_BINDING 3
|
#define MODEL_BUFFER_BINDING 3
|
||||||
#define MODEL_COLOUR_BUFFER_BINDING 4
|
#define MODEL_COLOUR_BUFFER_BINDING 4
|
||||||
#define POSITION_SCRATCH_BINDING 5
|
#define POSITION_SCRATCH_BINDING 5
|
||||||
@@ -10,8 +9,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#import <voxy:lod/quad_format.glsl>
|
#import <voxy:lod/quad_format.glsl>
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
|
||||||
#import <voxy:lod/block_model.glsl>
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
|
||||||
//#define DEBUG_RENDER
|
//#define DEBUG_RENDER
|
||||||
|
|
||||||
@@ -21,9 +20,10 @@ layout(location = 2) out flat vec4 tinting;
|
|||||||
layout(location = 3) out flat vec4 addin;
|
layout(location = 3) out flat vec4 addin;
|
||||||
layout(location = 4) out flat uint flags;
|
layout(location = 4) out flat uint flags;
|
||||||
layout(location = 5) out flat vec4 conditionalTinting;
|
layout(location = 5) out flat vec4 conditionalTinting;
|
||||||
|
layout(location = 6) out flat vec2 size;
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
layout(location = 6) out flat uint quadDebug;
|
layout(location = 7) out flat uint quadDebug;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -42,17 +42,17 @@ vec4 uint2vec4RGBA(uint colour) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec4 getFaceSize(uint faceData) {
|
vec4 getFaceSize(uint faceData) {
|
||||||
float EPSILON = 0.0005f;
|
float EPSILON = 0.00005f;
|
||||||
|
|
||||||
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
||||||
|
|
||||||
|
//Make the end relative to the start
|
||||||
|
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
||||||
|
|
||||||
//Expand the quads by a very small amount
|
//Expand the quads by a very small amount
|
||||||
faceOffsetsSizes.xz -= vec2(EPSILON);
|
faceOffsetsSizes.xz -= vec2(EPSILON);
|
||||||
faceOffsetsSizes.yw += vec2(EPSILON);
|
faceOffsetsSizes.yw += vec2(EPSILON);
|
||||||
|
|
||||||
//Make the end relative to the start
|
|
||||||
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
|
||||||
|
|
||||||
return faceOffsetsSizes;
|
return faceOffsetsSizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +104,16 @@ void main() {
|
|||||||
uvec2 encPos = positionBuffer[gl_BaseInstance];
|
uvec2 encPos = positionBuffer[gl_BaseInstance];
|
||||||
uint lodLevel = extractDetail(encPos);
|
uint lodLevel = extractDetail(encPos);
|
||||||
|
|
||||||
|
|
||||||
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
|
||||||
baseUV = modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
|
||||||
|
|
||||||
ivec2 quadSize = extractSize(quad);
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
|
||||||
{ //Generate tinting and flag data
|
if (cornerIdx == 1) //Only if we are the provoking vertex
|
||||||
|
{
|
||||||
|
size = vec2(quadSize-1);
|
||||||
|
|
||||||
|
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
||||||
|
baseUV = modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
||||||
|
|
||||||
|
//Generate tinting and flag data
|
||||||
flags = faceHasAlphaCuttout(faceData);
|
flags = faceHasAlphaCuttout(faceData);
|
||||||
|
|
||||||
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#import <voxy:lod/quad_format.glsl>
|
#import <voxy:lod/quad_format.glsl>
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
|
||||||
#import <voxy:lod/block_model.glsl>
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
|
||||||
layout(location = 6) out flat uint quadDebug;
|
layout(location = 6) out flat uint quadDebug;
|
||||||
|
|
||||||
|
|||||||
@@ -93,10 +93,9 @@ void bubbleSortInital(uint vis, uint id) {
|
|||||||
|
|
||||||
bool shouldSortId(uint id) {
|
bool shouldSortId(uint id) {
|
||||||
UnpackedNode node;
|
UnpackedNode node;
|
||||||
if (unpackNode(node, gl_GlobalInvocationID.x)==uvec4(-1)) {
|
if (unpackNode(node, id)==uvec4(-1)) {
|
||||||
return false;//Unallocated node
|
return false;//Unallocated node
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmptyMesh(node) || (!hasMesh(node))) {//|| (!hasChildren(node))
|
if (isEmptyMesh(node) || (!hasMesh(node))) {//|| (!hasChildren(node))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -105,6 +104,9 @@ bool shouldSortId(uint id) {
|
|||||||
return false;//Cannot remove geometry from top level node
|
return false;//Cannot remove geometry from top level node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasRequested(node)) {//If a node has a request its not valid to remove
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*THIS IS COMPLETLY WRONG, we need to check if all the children of the parent of the child are leaf nodes
|
/*THIS IS COMPLETLY WRONG, we need to check if all the children of the parent of the child are leaf nodes
|
||||||
// not this node
|
// not this node
|
||||||
@@ -130,7 +132,7 @@ void main() {
|
|||||||
// this means that insertion into the local buffer can be accelerated W.R.T global
|
// this means that insertion into the local buffer can be accelerated W.R.T global
|
||||||
for (uint i = 0; i < OPS_PER_THREAD; i++) {
|
for (uint i = 0; i < OPS_PER_THREAD; i++) {
|
||||||
//Copy in with warp size batch fetch
|
//Copy in with warp size batch fetch
|
||||||
uint id = gl_LocalInvocationID.x + (i*WORK_SIZE);
|
uint id = (gl_LocalInvocationID.x*OPS_PER_THREAD) + i;
|
||||||
initalSort[id] = minVisIds[id]|(1u<<31);//Flag the id as being external
|
initalSort[id] = minVisIds[id]|(1u<<31);//Flag the id as being external
|
||||||
}
|
}
|
||||||
barrier();
|
barrier();
|
||||||
@@ -158,7 +160,7 @@ void main() {
|
|||||||
//Work size batching
|
//Work size batching
|
||||||
for (uint i = 0; i < OPS_PER_THREAD; i++) {
|
for (uint i = 0; i < OPS_PER_THREAD; i++) {
|
||||||
barrier();//Probably unneeded, was just to keep warp coheriancy
|
barrier();//Probably unneeded, was just to keep warp coheriancy
|
||||||
uint id = gl_LocalInvocationID.x+(i*WORK_SIZE);
|
uint id = (gl_LocalInvocationID.x*OPS_PER_THREAD)+i;
|
||||||
uint sid = initalSort[id];
|
uint sid = initalSort[id];
|
||||||
if ((sid&(1u<<31)) != 0) {
|
if ((sid&(1u<<31)) != 0) {
|
||||||
//The flag being external was set, meaning we should NOT insert this element
|
//The flag being external was set, meaning we should NOT insert this element
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// substantually for performance (for both persistent threads and incremental)
|
// substantually for performance (for both persistent threads and incremental)
|
||||||
|
|
||||||
|
|
||||||
layout(binding = HIZ_BINDING) uniform sampler2DShadow hizDepthSampler;
|
layout(binding = HIZ_BINDING) uniform sampler2D hizDepthSampler;
|
||||||
|
|
||||||
//TODO: maybe do spher bounds aswell? cause they have different accuracies but are both over estimates (liberals (non conservative xD))
|
//TODO: maybe do spher bounds aswell? cause they have different accuracies but are both over estimates (liberals (non conservative xD))
|
||||||
// so can do &&
|
// so can do &&
|
||||||
@@ -39,7 +39,6 @@ bool checkPointInView(vec4 point) {
|
|||||||
|
|
||||||
vec3 minBB = vec3(0.0f);
|
vec3 minBB = vec3(0.0f);
|
||||||
vec3 maxBB = vec3(0.0f);
|
vec3 maxBB = vec3(0.0f);
|
||||||
vec2 size = vec2(0.0f);
|
|
||||||
bool insideFrustum = false;
|
bool insideFrustum = false;
|
||||||
|
|
||||||
float screenSize = 0.0f;
|
float screenSize = 0.0f;
|
||||||
@@ -117,7 +116,8 @@ void setupScreenspace(in UnpackedNode node) {
|
|||||||
minBB = min(min(min(p000, p100), min(p001, p101)), min(min(p010, p110), min(p011, p111)));
|
minBB = min(min(min(p000, p100), min(p001, p101)), min(min(p010, p110), min(p011, p111)));
|
||||||
maxBB = max(max(max(p000, p100), max(p001, p101)), max(max(p010, p110), max(p011, p111)));
|
maxBB = max(max(max(p000, p100), max(p001, p101)), max(max(p010, p110), max(p011, p111)));
|
||||||
|
|
||||||
size = clamp(maxBB.xy - minBB.xy, vec2(0), vec2(1));
|
minBB = clamp(minBB, vec3(0), vec3(1));
|
||||||
|
maxBB = clamp(maxBB, vec3(0), vec3(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Checks if the node is implicitly culled (outside frustum)
|
//Checks if the node is implicitly culled (outside frustum)
|
||||||
@@ -128,32 +128,31 @@ bool outsideFrustum() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isCulledByHiz() {
|
bool isCulledByHiz() {
|
||||||
vec2 ssize = size * vec2(screenW, screenH);
|
ivec2 ssize = ivec2(1)<<ivec2((packedHizSize>>16)&0xFFFF,packedHizSize&0xFFFF);
|
||||||
float miplevel = log2(max(max(ssize.x, ssize.y),1));
|
vec2 size = (maxBB.xy-minBB.xy)*ssize;
|
||||||
|
float miplevel = log2(max(max(size.x, size.y),1));
|
||||||
|
|
||||||
//TODO: make a path for if the miplevel would result in the textureSampler sampling a size of 1
|
miplevel = floor(miplevel)-1;
|
||||||
|
miplevel = clamp(miplevel, 0, textureQueryLevels(hizDepthSampler)-1);
|
||||||
|
|
||||||
|
int ml = int(miplevel);
|
||||||
|
ssize = max(ivec2(1), ssize>>ml);
|
||||||
|
ivec2 mxbb = ivec2(maxBB.xy*ssize);
|
||||||
|
ivec2 mnbb = ivec2(minBB.xy*ssize);
|
||||||
|
|
||||||
miplevel = ceil(miplevel);
|
float pointSample = -1.0f;
|
||||||
miplevel = clamp(miplevel, 0, 20);
|
//float pointSample2 = 0.0f;
|
||||||
|
for (int x = mnbb.x; x<=mxbb.x; x++) {
|
||||||
if (miplevel >= 10.0f) {//Level 9 or 10// TODO: FIX THIS JANK SHIT
|
for (int y = mnbb.y; y<=mxbb.y; y++) {
|
||||||
//return false;
|
float sp = texelFetch(hizDepthSampler, ivec2(x, y), ml).r;
|
||||||
|
//pointSample2 = max(sp, pointSample2);
|
||||||
|
//sp = mix(sp, pointSample, 0.9999999f<=sp);
|
||||||
|
pointSample = max(sp, pointSample);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//pointSample = mix(pointSample, pointSample2, pointSample<=0.000001f);
|
||||||
|
|
||||||
vec2 midpoint = (maxBB.xy + minBB.xy)*0.5f;
|
return pointSample<=minBB.z;
|
||||||
|
|
||||||
float testAgainst = minBB.z;
|
|
||||||
//the *2.0f-1.0f converts from the 0->1 range to -1->1 range that depth is in (not having this causes tighter bounds, but causes culling issues in caves)
|
|
||||||
testAgainst = testAgainst*2.0f-1.0f;
|
|
||||||
|
|
||||||
bool culled = textureLod(hizDepthSampler, clamp(vec3(midpoint, testAgainst), vec3(0), vec3(1)), miplevel) < 0.0001f;
|
|
||||||
|
|
||||||
//printf("HiZ sample point: (%f,%f)@%f against %f", midpoint.x, midpoint.y, miplevel, minBB.z);
|
|
||||||
//if ((culled) && node22.lodLevel == 0) {
|
|
||||||
// printf("HiZ sample point: (%f,%f)@%f against %f, value %f", midpoint.x, midpoint.y, miplevel, minBB.z, textureLod(hizDepthSampler, vec3(0.5f,0.5f, 0.000000001f), 9.0f));
|
|
||||||
//}
|
|
||||||
return culled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
|
|
||||||
layout(binding = HIZ_BINDING_INDEX) uniform sampler2DShadow hizDepthSampler;
|
|
||||||
|
|
||||||
vec3 minBB;
|
|
||||||
vec3 maxBB;
|
|
||||||
vec2 size;
|
|
||||||
|
|
||||||
|
|
||||||
//Sets up screenspace with the given node id, returns true on success false on failure/should not continue
|
|
||||||
//Accesses data that is setup in the main traversal and is just shared to here
|
|
||||||
void setupScreenspace(in UnpackedNode node) {
|
|
||||||
//TODO: implment transform support
|
|
||||||
Transform transform = transforms[getTransformIndex(node)];
|
|
||||||
|
|
||||||
|
|
||||||
vec4 base = VP*vec4(vec3(((node.pos<<node.lodLevel)-camSecPos)<<5)-camSubSecPos, 1);
|
|
||||||
|
|
||||||
//TODO: AABB SIZES not just a max cube
|
|
||||||
|
|
||||||
//vec3 minPos = minSize + basePos;
|
|
||||||
//vec3 maxPos = maxSize + basePos;
|
|
||||||
|
|
||||||
minBB = base.xyz/base.w;
|
|
||||||
maxBB = minBB;
|
|
||||||
|
|
||||||
for (int i = 1; i < 8; i++) {
|
|
||||||
//NOTE!: cant this be precomputed and put in an array?? in the scene uniform??
|
|
||||||
vec4 pPoint = (VP*vec4(vec3((i&1)!=0,(i&2)!=0,(i&4)!=0),1))*(32<<node.lodLevel);//Size of section is 32x32x32 (need to change it to a bounding box in the future)
|
|
||||||
pPoint += base;
|
|
||||||
vec3 point = pPoint.xyz/pPoint.w;
|
|
||||||
//TODO: CLIP TO VIEWPORT
|
|
||||||
minBB = min(minBB, point);
|
|
||||||
maxBB = max(maxBB, point);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: MORE ACCURATLY DETERMIN SCREENSPACE AREA, this can be done by computing and adding
|
|
||||||
// the projected surface area of each face/quad which winding order faces the camera
|
|
||||||
// (this is just the dot product of 2 projected vectors)
|
|
||||||
|
|
||||||
//can do a funny by not doing the perspective divide except on the output of the area
|
|
||||||
|
|
||||||
//printf("Screenspace MIN: %f, %f, %f MAX: %f, %f, %f", minBB.x,minBB.y,minBB.z, maxBB.x,maxBB.y,maxBB.z);
|
|
||||||
|
|
||||||
size = maxBB.xy - minBB.xy;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Checks if the node is implicitly culled (outside frustum)
|
|
||||||
bool outsideFrustum() {
|
|
||||||
return any(lessThanEqual(maxBB, vec3(-1f, -1f, 0f))) || any(lessThanEqual(vec3(1f, 1f, 1f), minBB));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isCulledByHiz() {
|
|
||||||
if (minBB.z < 0) {//Minpoint is behind the camera, its always going to pass
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
vec2 ssize = size.xy * vec2(ivec2(screenW, screenH));
|
|
||||||
float miplevel = ceil(log2(max(max(ssize.x, ssize.y),1)));
|
|
||||||
vec2 midpoint = (maxBB.xy + minBB.xy)*0.5;
|
|
||||||
return textureLod(hizDepthSampler, vec3(midpoint, minBB.z), miplevel) > 0.0001;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns if we should decend into its children or not
|
|
||||||
bool shouldDecend() {
|
|
||||||
//printf("Screen area %f: %f, %f", (size.x*size.y*float(screenW)*float(screenH)), float(screenW), float(screenH));
|
|
||||||
return (size.x*size.y*screenW*screenH) > decendSSS;
|
|
||||||
}
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
#version 460 core
|
|
||||||
|
|
||||||
#define WORKGROUP 4
|
|
||||||
#define MINI_BATCH_SIZE 32
|
|
||||||
//The entire uint is a minibatch (each idx is one)
|
|
||||||
#define MINI_BATCH_MSK (uint(-1))
|
|
||||||
|
|
||||||
//Each y dim is a quadrent in the octree
|
|
||||||
// multiple x dims to fill up workgroups
|
|
||||||
layout(local_size_x=WORKGROUP, local_size_y=8) in;
|
|
||||||
|
|
||||||
layout(binding = 1, std430) restrict buffer RequestSectionLoadQueue {
|
|
||||||
uint counter;
|
|
||||||
uint[] queue;
|
|
||||||
} requestQueue;
|
|
||||||
|
|
||||||
//SectionNodeData is a uvec4 that contains the position + flags + ptr to own render section data + ptr to children
|
|
||||||
layout(binding = 2, std430) restrict readonly buffer SectionNodeData {
|
|
||||||
uvec4[] sectionNodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(binding = 3, std430) restrict buffer ActiveWorkingNodeQueue {
|
|
||||||
uint feedbackStatus;
|
|
||||||
uint batchIndex;
|
|
||||||
uint end;
|
|
||||||
uint start;
|
|
||||||
uint maxSize;//Needs to be a multiple of local_size_x
|
|
||||||
uint[] queue;
|
|
||||||
} nodeQueue;
|
|
||||||
|
|
||||||
|
|
||||||
struct UnpackedNode {
|
|
||||||
ivec4 position;//x,y,z,detail
|
|
||||||
uint flags;//16 bits
|
|
||||||
uint self;
|
|
||||||
uint children;
|
|
||||||
};
|
|
||||||
|
|
||||||
UnpackedNode unpackNode(uvec4 data) {
|
|
||||||
UnpackedNode node;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
//NOTE: this is different to nanite in the fact that if a node is not loaded, too bad dont render
|
|
||||||
|
|
||||||
shared UnpackedNode workingNodes[WORKGROUP];
|
|
||||||
shared uint miniBatchMsk;
|
|
||||||
void loadNode() {
|
|
||||||
if (gl_LocalInvocationIndex == 0) {//Check if we need to
|
|
||||||
batchMsk = 0;//Reset the minibatch
|
|
||||||
if (miniBatchMsk == MINI_BATCH_SIZE) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
barrier();
|
|
||||||
if (gl_LocalInvocationID.y == 0) {
|
|
||||||
|
|
||||||
|
|
||||||
//Need to make it work in y size 8, but only gl_LocalInvocationId.x == 0
|
|
||||||
workingNodes[gl_LocalInvocationID.x] = unpackNode(sectionNodes[id]);
|
|
||||||
}
|
|
||||||
barrier();//Synchonize, also acts as memory barrier
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Computes screensize of the node and whether it should render itself or its children
|
|
||||||
bool shouldRenderChildren(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Process a single node and enqueue child nodes if needed into work queue, enqueue self to render and/or request children to load
|
|
||||||
void processNode(uint id) {//Called even if it doesnt have any work (id==-1) to ensure uniform control flow for barriers
|
|
||||||
|
|
||||||
//Bottom 2 bits are status flags, is air and children loaded
|
|
||||||
// node.flags
|
|
||||||
|
|
||||||
//If the childrenloaded flag is not set, send a request for the children of the node to be loaded
|
|
||||||
// if all the children are loaded but we are not and we need to render, render the children and dispatch
|
|
||||||
// a request to load self
|
|
||||||
|
|
||||||
if (shouldRenderChildren(node)) {
|
|
||||||
//Dont care about
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//The activly schedualed/acquired work slot for this group
|
|
||||||
shared uint workingBatchIndex;
|
|
||||||
shared uint workingBatchOffset;
|
|
||||||
void process() {
|
|
||||||
if (gl_LocalInvocationIndex == 0) {//This includes both x and y
|
|
||||||
workingBatchIndex = atomicAdd(nodeQueue.batchIndex, BATCH_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
while (true) {
|
|
||||||
barrier();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//when a node is processed,
|
|
||||||
// compute its screen bounding box is computed using fast trick (e.g. if your viewing it from a quadrent you already know its bounding points (min/max))
|
|
||||||
// frustum cull, check hiz
|
|
||||||
// if it passes culling, use the screensize to check wether it must render itself
|
|
||||||
// or dispatch its children to render
|
|
||||||
// IF its error is small enough, then render itself, its mesh should always be loaded, if not its a critical error (except maybe if its a top level node or something)
|
|
||||||
// if its error is too large,
|
|
||||||
// check that all children are loaded (or empty), if they are not all loaded, enqueu a request for the cpu to load
|
|
||||||
// that nodes children
|
|
||||||
// if the load queue is full, dont enqueue it to the queue
|
|
||||||
// then instead of rendering children, render its own mesh since it should always be loaded
|
|
||||||
|
|
||||||
//Can also reverse the above slightly and make it so that it checks the children before enqueuing them
|
|
||||||
|
|
||||||
|
|
||||||
//the main thing to worry about is if there is enough work to fill the inital few rounds of this
|
|
||||||
// before amplification takes effect
|
|
||||||
// can do a thing where it initally just blasts child nodes out until the size is small enough
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: since matrix multiplication distributes over addition
|
|
||||||
// can precompute the AABB corners with respect to the matrix
|
|
||||||
// then you can just add a translation vector
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: can do in another way
|
|
||||||
// first compute the sections that should either render self or childs
|
|
||||||
// then in as a seperate job queue work though it
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint getChildCount(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Checks whether a node should be culled based on hiz/frustum
|
|
||||||
bool cullNode(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Should render this node, or recurse to children
|
|
||||||
bool shouldRenderChildrenInstead(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Does the node have its own mesh loaded
|
|
||||||
bool nodeHasSelfMesh(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Does the node its children loaded (note! not child meshes)
|
|
||||||
bool nodeHasChildrenLoaded(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Are all the childrens meshes loaded
|
|
||||||
bool nodeHasChildMeshesLoaded(UnpackedNode node) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void request(uint type, uint idx) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderMesh(uint idx) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void enqueueChildren(uint arg, UnpackedNode node) {
|
|
||||||
uint cnt = getChildCount(node);
|
|
||||||
//TODO: the queue needs 2 counters, the pre and post atomic,
|
|
||||||
// pre is incremented to get index
|
|
||||||
// queue is written to
|
|
||||||
// post is then incremented to signal
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportCritical(uint type) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void processNode(uint idx) {
|
|
||||||
UnpackedNode node = unpackNode(sectionNodes[idx]);
|
|
||||||
if (!cullNode(node)) {
|
|
||||||
//Should we render children instead of ourselves with respect to screenspace error
|
|
||||||
if (shouldRenderChildrenInstead(node)) {
|
|
||||||
if (nodeHasChildrenLoaded(node)) {
|
|
||||||
//Dispatch nodes to queue
|
|
||||||
enqueueChildren(0, node);
|
|
||||||
} else {
|
|
||||||
//Children arnt loaded so either render self mesh or if we cant
|
|
||||||
// abort basicly must request nodes
|
|
||||||
if (nodeHasSelfMesh(node)) {
|
|
||||||
//Render self and dispatch request to load children
|
|
||||||
renderMesh(node.self);
|
|
||||||
request(1, idx);
|
|
||||||
} else {
|
|
||||||
//Critical issue, no are loaded and self has no mesh
|
|
||||||
reportCritical(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (nodeHasSelfMesh(node)) {
|
|
||||||
//render self
|
|
||||||
renderMesh(node.self);
|
|
||||||
} else {
|
|
||||||
//Request that self mesh is loaded
|
|
||||||
request(0, idx);
|
|
||||||
|
|
||||||
//render children instead
|
|
||||||
if (nodeHasChildrenLoaded(node)) {//Might need to be node nodeHasChildMeshesLoaded
|
|
||||||
enqueueChildren(1, node);
|
|
||||||
} else {
|
|
||||||
//This is very bad, it means cant render anything
|
|
||||||
reportCritical(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Psudo code, one thread, one load
|
|
||||||
void main() {
|
|
||||||
while (true) {
|
|
||||||
//Try to process a node queue entry
|
|
||||||
uint work = atomicAdd(workingNodeQueuePos, 1);
|
|
||||||
uint idx = work&0xFFFFFFu;
|
|
||||||
uint arg = work>>24;
|
|
||||||
if (idx < workingNodeQueueEnd) {
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Do other queue work however we still have the work slot allocated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//This provides per scene/viewport/transfrom access, that is, a node can be attached to a specific scene/viewport/transfrom, this is so that
|
|
||||||
// different nodes/models can have different viewports/scenes/transfrom which enables some very cool things like
|
|
||||||
// absolutly massive VS2 structures should... just work :tm: - todd howard
|
|
||||||
|
|
||||||
struct Transform {
|
|
||||||
mat4 transform;
|
|
||||||
ivec4 originPos;
|
|
||||||
ivec4 worldPos;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
layout(binding = TRANSFORM_ARRAY_INDEX, std140) uniform TransformArray {
|
|
||||||
Transform transforms[32];
|
|
||||||
};
|
|
||||||
@@ -10,13 +10,13 @@ layout(local_size_x=LOCAL_SIZE) in;//, local_size_y=1
|
|||||||
layout(binding = SCENE_UNIFORM_BINDING, std140) uniform SceneUniform {
|
layout(binding = SCENE_UNIFORM_BINDING, std140) uniform SceneUniform {
|
||||||
mat4 VP;
|
mat4 VP;
|
||||||
ivec3 camSecPos;
|
ivec3 camSecPos;
|
||||||
float screenW;
|
int packedHizSize;
|
||||||
vec3 camSubSecPos;
|
vec3 camSubSecPos;
|
||||||
float screenH;
|
float minSSS;
|
||||||
Frustum frustum;
|
Frustum frustum;
|
||||||
uint renderQueueMaxSize;
|
uint renderQueueMaxSize;
|
||||||
float minSSS;
|
|
||||||
uint frameId;
|
uint frameId;
|
||||||
|
uint requestQueueSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
#import <voxy:lod/hierarchical/queue.glsl>
|
#import <voxy:lod/hierarchical/queue.glsl>
|
||||||
@@ -49,9 +49,9 @@ layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBu
|
|||||||
void addRequest(inout UnpackedNode node) {
|
void addRequest(inout UnpackedNode node) {
|
||||||
//printf("Put node decend request");
|
//printf("Put node decend request");
|
||||||
if (!hasRequested(node)) {
|
if (!hasRequested(node)) {
|
||||||
if (requestQueueIndex.x < REQUEST_QUEUE_SIZE) {
|
if (requestQueueIndex.x < requestQueueSize) {//Soft limit
|
||||||
uint atomRes = atomicAdd(requestQueueIndex.x, 1);
|
uint atomRes = atomicAdd(requestQueueIndex.x, 1);
|
||||||
if (atomRes < REQUEST_QUEUE_SIZE) {
|
if (atomRes < MAX_REQUEST_QUEUE_SIZE) {//Hard limit
|
||||||
//Mark node as having a request submitted to prevent duplicate submissions
|
//Mark node as having a request submitted to prevent duplicate submissions
|
||||||
requestQueue[atomRes] = getRawPos(node);
|
requestQueue[atomRes] = getRawPos(node);
|
||||||
markRequested(node);
|
markRequested(node);
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
struct SectionMeta {
|
||||||
|
uint posA;
|
||||||
|
uint posB;
|
||||||
|
uint AABB;
|
||||||
|
uint ptr;
|
||||||
|
uint cntA;
|
||||||
|
uint cntB;
|
||||||
|
uint cntC;
|
||||||
|
uint cntD;
|
||||||
|
};
|
||||||
|
|
||||||
uint extractDetail(SectionMeta section) {
|
uint extractDetail(SectionMeta section) {
|
||||||
return section.posA>>28;
|
return section.posA>>28;
|
||||||
}
|
}
|
||||||
@@ -5,7 +16,7 @@ uint extractDetail(SectionMeta section) {
|
|||||||
ivec3 extractPosition(SectionMeta section) {
|
ivec3 extractPosition(SectionMeta section) {
|
||||||
int y = ((int(section.posA)<<4)>>24);
|
int y = ((int(section.posA)<<4)>>24);
|
||||||
int x = (int(section.posB)<<4)>>8;
|
int x = (int(section.posB)<<4)>>8;
|
||||||
int z = int((section.posA&((1<<20)-1))<<4);
|
int z = int((section.posA&((1u<<20)-1))<<4);
|
||||||
z |= int(section.posB>>28);
|
z |= int(section.posB>>28);
|
||||||
z <<= 8;
|
z <<= 8;
|
||||||
z >>= 8;
|
z >>= 8;
|
||||||
|
|||||||
Reference in New Issue
Block a user