Compare commits
3 Commits
mc_1215
...
inverted_n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8dd71defe | ||
|
|
e3329f210b | ||
|
|
5841b36469 |
@@ -188,9 +188,7 @@ processIncludeJars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remapJar {
|
remapJar {
|
||||||
doFirst {
|
delete getDestinationDirectory().get()
|
||||||
delete fileTree(getDestinationDirectory().get())
|
|
||||||
}
|
|
||||||
|
|
||||||
def hash = gitCommitHash();
|
def hash = gitCommitHash();
|
||||||
if (!hash.equals("<UnknownCommit>")) {
|
if (!hash.equals("<UnknownCommit>")) {
|
||||||
@@ -220,7 +218,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:10.2.1'))
|
include(implementation('org.rocksdb:rocksdbjni:8.10.0'))
|
||||||
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,7 +29,9 @@ 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() {
|
||||||
@@ -37,7 +39,9 @@ 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,11 +29,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,25 +75,26 @@ 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) {
|
||||||
Logger.error("Config deserialization null, reverting to default");
|
throw new IllegalStateException("Config deserialization null, reverting to default");
|
||||||
} else {
|
}
|
||||||
if (config.sectionStorageConfig == null) {
|
if (config.sectionStorageConfig == null) {
|
||||||
Logger.error("Config section storage null, reverting to default");
|
throw new IllegalStateException("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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config == null) {
|
|
||||||
config = DEFAULT_STORAGE_CONFIG;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Files.writeString(json, Serialization.GSON.toJson(config));
|
config = DEFAULT_STORAGE_CONFIG;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(json, Serialization.GSON.toJson(config));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed write the config, aborting!", e);
|
throw new RuntimeException("Failed to deserialize the default 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");
|
||||||
@@ -107,7 +103,6 @@ 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;
|
||||||
@@ -157,4 +152,96 @@ 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 = (int) Math.max(CpuLayout.CORES.length/1.5, 1);
|
public int serviceThreads = Math.max(CpuLayout.CORES.length/2, 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;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core;
|
|||||||
|
|
||||||
public interface IGetVoxyRenderSystem {
|
public interface IGetVoxyRenderSystem {
|
||||||
VoxyRenderSystem getVoxyRenderSystem();
|
VoxyRenderSystem getVoxyRenderSystem();
|
||||||
|
VoxyRenderSystem getVoxyOverworldRenderSystem();
|
||||||
void shutdownRenderer();
|
void shutdownRenderer();
|
||||||
void createRenderer();
|
void createRenderer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,43 +61,38 @@ public class VoxyRenderSystem {
|
|||||||
public final ChunkBoundRenderer chunkBoundRenderer;
|
public final ChunkBoundRenderer chunkBoundRenderer;
|
||||||
|
|
||||||
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
||||||
//Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more
|
this(world, threadPool, 1L<<32);
|
||||||
// than timeout, we keep the world acquired
|
}
|
||||||
world.acquireRef();
|
|
||||||
try {
|
|
||||||
//wait for opengl to be finished, this should hopefully ensure all memory allocations are free
|
|
||||||
glFinish();
|
|
||||||
glFinish();
|
|
||||||
|
|
||||||
//Trigger the shared index buffer loading
|
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool, long maxGeometryCapacity) {
|
||||||
SharedIndexBuffer.INSTANCE.id();
|
//Trigger the shared index buffer loading
|
||||||
Capabilities.init();//Ensure clinit is called
|
SharedIndexBuffer.INSTANCE.id();
|
||||||
|
Capabilities.init();//Ensure clinit is called
|
||||||
|
|
||||||
this.worldIn = world;
|
this.worldIn = world;
|
||||||
this.renderer = new RenderService(world, threadPool);
|
this.renderer = new RenderService(world, threadPool, maxGeometryCapacity);
|
||||||
this.postProcessing = new PostProcessing();
|
this.postProcessing = new PostProcessing();
|
||||||
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5;
|
int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord()>>5;
|
||||||
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5;
|
int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord()-1)>>5;
|
||||||
|
|
||||||
//Do some very cheeky stuff for MiB
|
//Do some very cheeky stuff for MiB
|
||||||
if (false) {
|
if (false) {
|
||||||
minSec = -8;
|
minSec = -8;
|
||||||
maxSec = 7;
|
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) {
|
||||||
@@ -105,6 +100,40 @@ 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
|
||||||
@@ -147,9 +176,6 @@ 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;
|
||||||
@@ -196,7 +222,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());
|
this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture(), startTime);
|
||||||
|
|
||||||
|
|
||||||
TimingStatistics.F.start();
|
TimingStatistics.F.start();
|
||||||
@@ -274,4 +300,129 @@ 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,7 +13,6 @@ 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;
|
||||||
@@ -29,8 +28,7 @@ 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.repFragTest = cap.GL_NV_representative_fragment_test;
|
this.meshShaders = cap.GL_NV_mesh_shader && 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()) {
|
||||||
Logger.warn(log);
|
System.err.println(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = GL20C.glGetShaderi(shader, GL20C.GL_COMPILE_STATUS);
|
int result = GL20C.glGetShaderi(shader, GL20C.GL_COMPILE_STATUS);
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ 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;
|
||||||
|
|
||||||
@@ -98,7 +100,7 @@ public class ModelBakerySubsystem {
|
|||||||
|
|
||||||
this.factory.tick();
|
this.factory.tick();
|
||||||
|
|
||||||
long start = System.nanoTime();
|
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,7 +8,6 @@ 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;
|
||||||
@@ -491,7 +490,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) {
|
||||||
Logger.error("Biome added was a duplicate");
|
System.err.println("Biome added was a duplicate");
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -221,12 +221,6 @@ 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
|
||||||
@@ -245,12 +239,6 @@ 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;
|
||||||
@@ -276,12 +264,6 @@ 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
|
||||||
@@ -320,25 +302,24 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//the face/direction is the face (e.g. down is the down face)
|
//TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!!
|
||||||
addView(0, -90,0, 0, 0);//Direction.DOWN
|
|
||||||
addView(1, 90,0, 0, 0b100);//Direction.UP
|
|
||||||
|
|
||||||
addView(2, 0,180, 0, 0b001);//Direction.NORTH
|
addView(0, -90,0, 0, false);//Direction.DOWN
|
||||||
addView(3, 0,0, 0, 0);//Direction.SOUTH
|
addView(1, 90,0, 0, false);//Direction.UP
|
||||||
|
addView(2, 0,180, 0, true);//Direction.NORTH
|
||||||
addView(4, 0,90, 270, 0b100);//Direction.WEST
|
addView(3, 0,0, 0, false);//Direction.SOUTH
|
||||||
addView(5, 0,270, 270, 0);//Direction.EAST
|
//TODO: check these arnt the wrong way round
|
||||||
|
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, int flip) {
|
private static void addView(int i, float pitch, float yaw, float rotation, boolean flipX) {
|
||||||
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,23 +50,24 @@ 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() - 1024*1024*1024;
|
long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 512*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<<28;
|
//geometryCapacity = 1<<24;
|
||||||
//geometryCapacity = 1<<30;//1GB test
|
//geometryCapacity = 1<<30;//1GB test
|
||||||
return geometryCapacity;
|
return geometryCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) {
|
public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool, long maxGeometryCapacity) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.modelService = new ModelBakerySubsystem(world.getMapper());
|
this.modelService = new ModelBakerySubsystem(world.getMapper());
|
||||||
|
|
||||||
long geometryCapacity = getGeometryBufferSize();
|
long geometryCapacity = getGeometryBufferSize();
|
||||||
|
geometryCapacity = Math.min(maxGeometryCapacity, geometryCapacity);
|
||||||
|
|
||||||
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
|
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
|
||||||
|
|
||||||
@@ -84,7 +85,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.renderGen);
|
this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner);
|
||||||
|
|
||||||
world.setDirtyCallback(this.nodeManager::worldEvent);
|
world.setDirtyCallback(this.nodeManager::worldEvent);
|
||||||
|
|
||||||
@@ -106,19 +107,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
|
|||||||
this.modelService.tick(budget);
|
this.modelService.tick(budget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean frexStillHasWork() {
|
public void renderFarAwayOpaque(J viewport, GlTexture depthBoundTexture, long frameStart) {
|
||||||
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
|
||||||
@@ -133,13 +122,6 @@ 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
|
||||||
@@ -176,11 +158,23 @@ 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);
|
this.traversal.doTraversal(viewport, depthBuffer);
|
||||||
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(final long[] rawSectionData) {
|
private int prepareSectionData() {
|
||||||
final var sectionData = this.sectionData;
|
final var sectionData = this.sectionData;
|
||||||
final var rawModelIds = this.modelMan._unsafeRawAccess();
|
final var rawModelIds = this.modelMan._unsafeRawAccess();
|
||||||
long opaque = 0;
|
int opaque = 0;
|
||||||
long notEmpty = 0;
|
int notEmpty = 0;
|
||||||
long pureFluid = 0;
|
int pureFluid = 0;
|
||||||
long partialFluid = 0;
|
int partialFluid = 0;
|
||||||
|
|
||||||
int neighborAcquireMsk = 0;//-+x, -+z, -+y
|
int neighborAcquireMsk = 0;//-+x, -+y, -+Z
|
||||||
for (int i = 0; i < 32*32*32;) {
|
for (int i = 0; i < 32*32*32;) {
|
||||||
long block = rawSectionData[i];//Get the block mapping
|
long block = sectionData[i + 32 * 32 * 32];//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;
|
||||||
|
|
||||||
long msk = 1L << (i & 63);
|
int msk = 1 << (i & 31);
|
||||||
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,28 +241,21 @@ public class RenderDataFactory {
|
|||||||
//Do increment here
|
//Do increment here
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if ((i & 63) == 0 && notEmpty != 0) {
|
if ((i & 31) == 0 && notEmpty != 0) {
|
||||||
long nonOpaque = (notEmpty^opaque)&~pureFluid;
|
this.opaqueMasks[(i >> 5) - 1] = opaque;
|
||||||
long fluid = pureFluid|partialFluid;
|
this.nonOpaqueMasks[(i >> 5) - 1] = (notEmpty^opaque)&~pureFluid;
|
||||||
this.opaqueMasks[(i >> 5) - 2] = (int) opaque;
|
this.fluidMasks[(i >> 5) - 1] = pureFluid|partialFluid;
|
||||||
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 |= packedEmpty&1;//-x
|
neighborMsk |= notEmpty&1;//-x
|
||||||
neighborMsk |= (packedEmpty>>>30)&0b10;//+x
|
neighborMsk |= (notEmpty>>>30)&0b10;//+x
|
||||||
|
|
||||||
//notEmpty = (notEmpty != 0)?1:0;
|
//notEmpty = (notEmpty != 0)?1:0;
|
||||||
neighborMsk |= ((((i - 1) >> 10) == 0) ? 0b100 : 0)*(packedEmpty!=0?1:0);//-y
|
neighborMsk |= (((i - 1) >> 10) == 0) ? 0b100 : 0;//-y
|
||||||
neighborMsk |= ((((i - 1) >> 10) == 31) ? 0b1000 : 0)*(packedEmpty!=0?1:0);//+y
|
neighborMsk |= (((i - 1) >> 10) == 31) ? 0b1000 : 0;//+y
|
||||||
neighborMsk |= (((((i - 33) >> 5) & 0x1F) == 0) ? 0b10000 : 0)*(((int)notEmpty)!=0?1:0);//-z
|
neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 0) ? 0b10000 : 0;//-z
|
||||||
neighborMsk |= (((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0)*((notEmpty>>>32)!=0?1:0);//+z
|
neighborMsk |= ((((i - 1) >> 5) & 0x1F) == 31) ? 0b100000 : 0;//+z
|
||||||
|
|
||||||
neighborAcquireMsk |= neighborMsk;
|
neighborAcquireMsk |= neighborMsk;
|
||||||
|
|
||||||
@@ -1549,7 +1542,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
|
||||||
@@ -1585,7 +1578,7 @@ public class RenderDataFactory {
|
|||||||
Arrays.fill(this.fluidMasks, 0);
|
Arrays.fill(this.fluidMasks, 0);
|
||||||
|
|
||||||
//Prepare everything
|
//Prepare everything
|
||||||
int neighborMsk = this.prepareSectionData(section._unsafeGetRawDataArray());
|
int neighborMsk = this.prepareSectionData();
|
||||||
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, 3))*2 + this.addin) <<32) + Integer.toUnsignedLong(unique);
|
this.priority = (((lvl*3L + Math.min(this.attempts, 5))*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?1:0;//Single time addin which gives the models time to bake before the task executes
|
task.addin = WorldEngine.getLevel(task.position)>2?3: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,7 +179,6 @@ 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;
|
||||||
@@ -246,7 +245,8 @@ 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,7 +280,6 @@ 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;
|
||||||
@@ -290,8 +289,9 @@ public class AsyncNodeManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == 0 && zeroCount++>0) {
|
if (pos == 0) {
|
||||||
Logger.error("Remove node pos is 0 " + zeroCount + " times, this is really bad, please report" );
|
//THIS SHOULD BE IMPOSSIBLE
|
||||||
|
//TODO: VVVVV MUCH MEGA FIX
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +314,6 @@ 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;
|
||||||
}
|
}
|
||||||
//=====================
|
//=====================
|
||||||
@@ -750,11 +749,10 @@ 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,7 +7,6 @@ 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;
|
||||||
@@ -31,7 +30,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 MAX_REQUEST_QUEUE_SIZE = 50;
|
public static final int REQUEST_QUEUE_SIZE = 50;
|
||||||
public static final int MAX_QUEUE_SIZE = 200_000;
|
public static final int MAX_QUEUE_SIZE = 200_000;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,7 +39,6 @@ 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;
|
||||||
|
|
||||||
@@ -75,7 +73,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("MAX_REQUEST_QUEUE_SIZE", MAX_REQUEST_QUEUE_SIZE)
|
.define("REQUEST_QUEUE_SIZE", REQUEST_QUEUE_SIZE)
|
||||||
|
|
||||||
.define("HIZ_BINDING", 0)
|
.define("HIZ_BINDING", 0)
|
||||||
|
|
||||||
@@ -98,18 +96,19 @@ public class HierarchicalOcclusionTraverser {
|
|||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
|
|
||||||
public HierarchicalOcclusionTraverser(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, RenderGenerationService meshGen) {
|
public HierarchicalOcclusionTraverser(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner) {
|
||||||
this.nodeCleaner = nodeCleaner;
|
this.nodeCleaner = nodeCleaner;
|
||||||
this.nodeManager = nodeManager;
|
this.nodeManager = nodeManager;
|
||||||
this.meshGen = meshGen;
|
this.requestBuffer = new GlBuffer(REQUEST_QUEUE_SIZE*8L+8).zero();
|
||||||
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_NEAREST_MIPMAP_NEAREST);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
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)
|
||||||
@@ -176,31 +175,23 @@ 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;
|
||||||
|
|
||||||
final float screenspaceAreaDecreasingSize = VoxyConfig.CONFIG.subDivisionSize*VoxyConfig.CONFIG.subDivisionSize;
|
|
||||||
//Screen space size for descending
|
|
||||||
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4;
|
|
||||||
|
|
||||||
setFrustum(viewport, ptr); ptr += 4*4*6;
|
setFrustum(viewport, ptr); ptr += 4*4*6;
|
||||||
|
|
||||||
MemoryUtil.memPutInt(ptr, (int) (viewport.getRenderList().size()/4-1)); ptr += 4;
|
MemoryUtil.memPutInt(ptr, (int) (viewport.getRenderList().size()/4-1)); ptr += 4;
|
||||||
|
|
||||||
|
|
||||||
|
final float screenspaceAreaDecreasingSize = VoxyConfig.CONFIG.subDivisionSize*VoxyConfig.CONFIG.subDivisionSize;
|
||||||
|
//Screen space size for descending
|
||||||
|
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); 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) {
|
||||||
@@ -212,7 +203,10 @@ 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) {
|
public void doTraversal(Viewport<?> viewport, int depthBuffer) {
|
||||||
|
//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,7 +8,6 @@ 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;
|
||||||
@@ -113,8 +112,7 @@ 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);
|
||||||
//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);
|
||||||
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);
|
||||||
@@ -167,22 +165,6 @@ 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();
|
||||||
|
|||||||
@@ -1213,8 +1213,7 @@ 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,6 +263,8 @@ 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,7 +2,6 @@ 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;
|
||||||
@@ -35,7 +34,6 @@ 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> {
|
||||||
@@ -138,7 +136,6 @@ 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);
|
||||||
@@ -172,8 +169,6 @@ 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);
|
||||||
@@ -206,9 +201,6 @@ 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);
|
||||||
@@ -224,9 +216,6 @@ 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 long GEOMETRY_ELEMENT_SIZE = 8;
|
private static final int 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 != Integer.highestOneBit(width) || this.height != Integer.highestOneBit(height)) {
|
if (this.width != width || this.height != height) {
|
||||||
if (this.texture != null) {
|
if (this.texture != null) {
|
||||||
this.texture.free();
|
this.texture.free();
|
||||||
this.texture = null;
|
this.texture = null;
|
||||||
}
|
}
|
||||||
this.alloc(Integer.highestOneBit(width), Integer.highestOneBit(height));
|
this.alloc(width, 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,22 +86,26 @@ public class HiZBuffer {
|
|||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
|
||||||
glBindTextureUnit(0, srcDepthTex);
|
//System.err.println("SRC: " + GlTexture.getRawTextureType(srcDepthTex) + " DST: " + this.texture.id);
|
||||||
|
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;
|
||||||
for (int i = 0; i < this.levels; i++) {
|
glViewport(0, 0, cw, ch);
|
||||||
this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, i);
|
for (int i = 0; i < this.levels-1; i++) {
|
||||||
glViewport(0, 0, cw, ch); cw = Math.max(cw/2, 1); ch = Math.max(ch/2, 1);
|
glTextureParameteri(this.texture.id, GL_TEXTURE_BASE_LEVEL, i);
|
||||||
|
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
|
||||||
@@ -126,8 +130,4 @@ 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import me.cortex.voxy.commonImpl.WorldIdentifier;
|
|||||||
import net.minecraft.client.render.*;
|
import net.minecraft.client.render.*;
|
||||||
import net.minecraft.client.util.ObjectAllocator;
|
import net.minecraft.client.util.ObjectAllocator;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.world.World;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -25,12 +26,26 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
|||||||
@Shadow private Frustum frustum;
|
@Shadow private Frustum frustum;
|
||||||
@Shadow private @Nullable ClientWorld world;
|
@Shadow private @Nullable ClientWorld world;
|
||||||
@Unique private VoxyRenderSystem renderer;
|
@Unique private VoxyRenderSystem renderer;
|
||||||
|
@Unique private VoxyRenderSystem overworldRenderer;
|
||||||
|
@Unique private WorldIdentifier overworldIdentifier;
|
||||||
|
|
||||||
|
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxyRenderSystem getVoxyOverworldRenderSystem() {
|
||||||
|
return this.overworldRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(method = "reload()V", at = @At("RETURN"), order = 900)//We want to inject before sodium
|
@Inject(method = "reload()V", at = @At("RETURN"), order = 900)//We want to inject before sodium
|
||||||
private void reloadVoxyRenderer(CallbackInfo ci) {
|
private void reloadVoxyRenderer(CallbackInfo ci) {
|
||||||
this.shutdownRenderer();
|
this.shutdownRenderer();
|
||||||
@@ -44,6 +59,9 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
|||||||
if (this.world != world) {
|
if (this.world != world) {
|
||||||
this.shutdownRenderer();
|
this.shutdownRenderer();
|
||||||
}
|
}
|
||||||
|
if (world!=null&&world.getRegistryKey()==World.OVERWORLD) {
|
||||||
|
this.overworldIdentifier = WorldIdentifier.of(world);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "close", at = @At("HEAD"))
|
@Inject(method = "close", at = @At("HEAD"))
|
||||||
@@ -57,6 +75,10 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
|||||||
this.renderer.shutdown();
|
this.renderer.shutdown();
|
||||||
this.renderer = null;
|
this.renderer = null;
|
||||||
}
|
}
|
||||||
|
if (this.overworldRenderer != null) {
|
||||||
|
this.overworldRenderer.shutdown();
|
||||||
|
this.overworldRenderer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,5 +103,11 @@ public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool());
|
this.renderer = new VoxyRenderSystem(world, instance.getThreadPool());
|
||||||
|
if (this.world.getRegistryKey()== World.NETHER && this.overworldIdentifier != null) {
|
||||||
|
var engine = this.overworldIdentifier.getOrCreateEngine();
|
||||||
|
if (engine != null) {
|
||||||
|
this.overworldRenderer = new VoxyRenderSystem(engine, instance.getThreadPool(), 1L<<31);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ public class MixinDefaultChunkRenderer {
|
|||||||
if (renderer != null) {
|
if (renderer != null) {
|
||||||
renderer.renderOpaque(matrices, camera.x, camera.y, camera.z);
|
renderer.renderOpaque(matrices, camera.x, camera.y, camera.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var overworldRenderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyOverworldRenderSystem();
|
||||||
|
if (overworldRenderer != null) {
|
||||||
|
overworldRenderer.renderOpaque(new ChunkRenderMatrices(matrices.projection(),
|
||||||
|
new Matrix4f(matrices.modelView()).scale(1/8f).scale(1,-1,1)),
|
||||||
|
camera.x*8, (300-camera.y)*8, (camera.z)*8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,30 +14,6 @@ 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;
|
||||||
@@ -48,8 +24,8 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
String error = (INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" "));
|
String error = (INSERT_CLASS?("["+stackEntry.getClassName()+"]: "):"") + 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();
|
||||||
@@ -72,7 +48,8 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOGGER.warn((INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
|
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) {
|
||||||
@@ -85,7 +62,8 @@ public class Logger {
|
|||||||
throwable = (Throwable) i;
|
throwable = (Throwable) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOGGER.info((INSERT_CLASS?("["+callClsName()+"]: "):"") + Stream.of(args).map(Logger::objToString).collect(Collectors.joining(" ")), throwable);
|
var stackEntry = new Throwable().getStackTrace()[1];
|
||||||
|
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,7 +3,6 @@ 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;
|
||||||
@@ -101,7 +100,7 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verification.size() != 1) {
|
if (verification.size() != 1) {
|
||||||
Logger.error("Error id mapping not matching across all fragments, attempting to recover");
|
System.err.println("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,39 +54,22 @@ 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(), cfWorldSecOpts),
|
new ColumnFamilyDescriptor("world_sections".getBytes(), cfOpts),
|
||||||
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*128);//128 mb max WAL size
|
.setMaxTotalWalSize(1024*1024*512);//512 mb max WAL size
|
||||||
|
|
||||||
List<ColumnFamilyHandle> handles = new ArrayList<>();
|
List<ColumnFamilyHandle> handles = new ArrayList<>();
|
||||||
|
|
||||||
@@ -102,11 +85,8 @@ 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);
|
||||||
@@ -213,7 +193,6 @@ 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,19 +46,15 @@ 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);
|
||||||
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(priority);
|
||||||
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;
|
||||||
@@ -278,7 +274,8 @@ public class ServiceThreadPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
||||||
Logger.error("Service worker thread has exploded unexpectedly! this is really not good very very bad.", throwable);
|
System.err.println("Service worker thread has exploded unexpectedly! this is really not good very very bad.");
|
||||||
|
throwable.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ 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;
|
||||||
@@ -116,7 +115,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: ");
|
||||||
Logger.error("Unable to load section " + section.key + " setting to air");
|
System.err.println("Unable to load section " + section.key + " setting to air");
|
||||||
status = 1;
|
status = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,9 +129,9 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.acquire();
|
section.acquire();
|
||||||
VarHandle.storeStoreFence();//Do not reorder setting this object
|
VarHandle.fullFence();//Do not reorder setting this object
|
||||||
holder.obj = section;
|
holder.obj = section;
|
||||||
VarHandle.releaseFence();
|
VarHandle.fullFence();
|
||||||
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,7 +1,6 @@
|
|||||||
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;
|
||||||
@@ -121,7 +120,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);
|
||||||
Logger.error("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);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +145,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);
|
||||||
Logger.error("Hash mismatch got: " + hash + " expected: " + expectedHash + " removing region");
|
System.err.println("Hash mismatch got: " + hash + " expected: " + expectedHash + " removing region");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ 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(Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
private record SerializationCache(long[] blockStateCache, Long2ShortOpenHashMap lutMapCache, MemoryBuffer memoryBuffer) {
|
||||||
public SerializationCache() {
|
public SerializationCache() {
|
||||||
this(new Long2ShortOpenHashMap(512), ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
this(new long[WorldSection.SECTION_VOLUME],
|
||||||
|
new Long2ShortOpenHashMap(512),
|
||||||
|
ThreadLocalMemoryBuffer.create(WorldSection.SECTION_VOLUME*2+WorldSection.SECTION_VOLUME*8+1024));
|
||||||
this.lutMapCache.defaultReturnValue((short) -1);
|
this.lutMapCache.defaultReturnValue((short) -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +39,8 @@ 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 = section.data;
|
var data = cache.blockStateCache;
|
||||||
|
section.copyDataTo(data);
|
||||||
|
|
||||||
Long2ShortOpenHashMap LUT = cache.lutMapCache; LUT.clear();
|
Long2ShortOpenHashMap LUT = cache.lutMapCache; LUT.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -163,18 +163,15 @@ 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,7 +1,6 @@
|
|||||||
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;
|
||||||
@@ -100,7 +99,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()) {
|
||||||
Logger.error("Deserialization was air, removed block");
|
System.err.println("Deserialization was air, removed block");
|
||||||
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,18 +53,12 @@ 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) {
|
||||||
//wait a bit
|
while (this.getTaskCount() > 5_000) {
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +69,7 @@ public class SectionSavingService {
|
|||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (this.threads.getJobCount() != 0) {
|
if (this.threads.getJobCount() != 0) {
|
||||||
Logger.error("Voxy section saving still in progress, estimated " + this.threads.getJobCount() + " sections remaining.");
|
System.err.println("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,9 +86,6 @@ 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");
|
||||||
}
|
}
|
||||||
@@ -120,12 +117,7 @@ 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));
|
||||||
try {
|
this.threads.execute();
|
||||||
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: " + identifier.getLongHash());
|
Logger.info("Creating new world engine");
|
||||||
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,7 +158,6 @@ 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(registryKeyHashCode(key))^mixStafford13(registryKeyHashCode(dimension))^mixStafford13(biomeSeed);
|
this.hashCode = mixStafford13(key.hashCode()^biomeSeed)^mixStafford13(dimension.hashCode()^biomeSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,16 +101,4 @@ 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) {
|
||||||
Logger.warn("Cannot access chunk sector as it goes out of bounds. start bytes: " + (sectorStart*4096) + " sector count: " + sectorCount + " fileSize: " + regionFile.size);
|
System.err.println("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)) {
|
||||||
Logger.warn("Chunk stream to small");
|
System.err.println("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.999999999f), depths);
|
bvec4 cv = lessThanEqual(vec4(0.999999f), 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,10 +1,3 @@
|
|||||||
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) {
|
||||||
@@ -25,13 +18,13 @@ uint faceHasAlphaCuttoutOverride(uint faceData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool modelHasBiomeLUT(BlockModel model) {
|
bool modelHasBiomeLUT(BlockModel model) {
|
||||||
return ((model.flagsA)&2u) != 0;
|
return ((model.flagsA)&2) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool modelIsTranslucent(BlockModel model) {
|
bool modelIsTranslucent(BlockModel model) {
|
||||||
return ((model.flagsA)&4u) != 0;
|
return ((model.flagsA)&4) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool modelHasMipmaps(BlockModel model) {
|
bool modelHasMipmaps(BlockModel model) {
|
||||||
return ((model.flagsA)&8u) != 0;
|
return ((model.flagsA)&8) != 0;
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,24 @@ 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/section.glsl>
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
#import <voxy:lod/section.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/section.glsl>
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
#import <voxy:lod/section.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/section.glsl>
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
#import <voxy:lod/section.glsl>
|
||||||
|
|
||||||
flat out uint id;
|
flat out uint id;
|
||||||
flat out uint value;
|
flat out uint value;
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
#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;
|
||||||
|
|
||||||
@@ -17,50 +13,22 @@ 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 = 7) in flat uint quadDebug;
|
layout(location = 6) 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
|
||||||
@@ -70,7 +38,14 @@ void main() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
outColour = computeColour(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
outColour = (colour * tinting) + addin;
|
||||||
|
|
||||||
|
//outColour = vec4(uv + baseUV, 0, 1);
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_RENDER
|
#ifdef DEBUG_RENDER
|
||||||
@@ -81,30 +56,4 @@ 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,6 +2,7 @@
|
|||||||
#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
|
||||||
@@ -9,8 +10,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#import <voxy:lod/quad_format.glsl>
|
#import <voxy:lod/quad_format.glsl>
|
||||||
#import <voxy:lod/block_model.glsl>
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
|
||||||
//#define DEBUG_RENDER
|
//#define DEBUG_RENDER
|
||||||
|
|
||||||
@@ -20,10 +21,9 @@ 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 = 7) out flat uint quadDebug;
|
layout(location = 6) 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.00005f;
|
float EPSILON = 0.0005f;
|
||||||
|
|
||||||
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,16 +104,13 @@ 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);
|
||||||
|
|
||||||
if (cornerIdx == 1) //Only if we are the provoking vertex
|
{ //Generate tinting and flag data
|
||||||
{
|
|
||||||
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/block_model.glsl>
|
|
||||||
#import <voxy:lod/gl46/bindings.glsl>
|
#import <voxy:lod/gl46/bindings.glsl>
|
||||||
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
|
||||||
layout(location = 6) out flat uint quadDebug;
|
layout(location = 6) out flat uint quadDebug;
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,10 @@ void bubbleSortInital(uint vis, uint id) {
|
|||||||
|
|
||||||
bool shouldSortId(uint id) {
|
bool shouldSortId(uint id) {
|
||||||
UnpackedNode node;
|
UnpackedNode node;
|
||||||
if (unpackNode(node, id)==uvec4(-1)) {
|
if (unpackNode(node, gl_GlobalInvocationID.x)==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;
|
||||||
}
|
}
|
||||||
@@ -104,9 +105,6 @@ 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
|
||||||
@@ -132,7 +130,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*OPS_PER_THREAD) + i;
|
uint id = gl_LocalInvocationID.x + (i*WORK_SIZE);
|
||||||
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();
|
||||||
@@ -160,7 +158,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*OPS_PER_THREAD)+i;
|
uint id = gl_LocalInvocationID.x+(i*WORK_SIZE);
|
||||||
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 sampler2D hizDepthSampler;
|
layout(binding = HIZ_BINDING) uniform sampler2DShadow 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,6 +39,7 @@ 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;
|
||||||
@@ -116,8 +117,7 @@ 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)));
|
||||||
|
|
||||||
minBB = clamp(minBB, vec3(0), vec3(1));
|
size = clamp(maxBB.xy - minBB.xy, vec2(0), vec2(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,31 +128,32 @@ bool outsideFrustum() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isCulledByHiz() {
|
bool isCulledByHiz() {
|
||||||
ivec2 ssize = ivec2(1)<<ivec2((packedHizSize>>16)&0xFFFF,packedHizSize&0xFFFF);
|
vec2 ssize = size * vec2(screenW, screenH);
|
||||||
vec2 size = (maxBB.xy-minBB.xy)*ssize;
|
float miplevel = log2(max(max(ssize.x, ssize.y),1));
|
||||||
float miplevel = log2(max(max(size.x, size.y),1));
|
|
||||||
|
|
||||||
miplevel = floor(miplevel)-1;
|
//TODO: make a path for if the miplevel would result in the textureSampler sampling a size of 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);
|
|
||||||
|
|
||||||
float pointSample = -1.0f;
|
miplevel = ceil(miplevel);
|
||||||
//float pointSample2 = 0.0f;
|
miplevel = clamp(miplevel, 0, 20);
|
||||||
for (int x = mnbb.x; x<=mxbb.x; x++) {
|
|
||||||
for (int y = mnbb.y; y<=mxbb.y; y++) {
|
if (miplevel >= 10.0f) {//Level 9 or 10// TODO: FIX THIS JANK SHIT
|
||||||
float sp = texelFetch(hizDepthSampler, ivec2(x, y), ml).r;
|
//return false;
|
||||||
//pointSample2 = max(sp, pointSample2);
|
|
||||||
//sp = mix(sp, pointSample, 0.9999999f<=sp);
|
|
||||||
pointSample = max(sp, pointSample);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//pointSample = mix(pointSample, pointSample2, pointSample<=0.000001f);
|
|
||||||
|
|
||||||
return pointSample<=minBB.z;
|
vec2 midpoint = (maxBB.xy + minBB.xy)*0.5f;
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
#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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//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;
|
||||||
int packedHizSize;
|
float screenW;
|
||||||
vec3 camSubSecPos;
|
vec3 camSubSecPos;
|
||||||
float minSSS;
|
float screenH;
|
||||||
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 < requestQueueSize) {//Soft limit
|
if (requestQueueIndex.x < REQUEST_QUEUE_SIZE) {
|
||||||
uint atomRes = atomicAdd(requestQueueIndex.x, 1);
|
uint atomRes = atomicAdd(requestQueueIndex.x, 1);
|
||||||
if (atomRes < MAX_REQUEST_QUEUE_SIZE) {//Hard limit
|
if (atomRes < REQUEST_QUEUE_SIZE) {
|
||||||
//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,14 +1,3 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -16,7 +5,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&((1u<<20)-1))<<4);
|
int z = int((section.posA&((1<<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