what if minecraft ran on the GPU exclusively? glad you asked! because now it can
This commit is contained in:
parent
fa7bf893e7
commit
bf9af81f38
41 changed files with 2433 additions and 34 deletions
|
|
@ -5,6 +5,8 @@ import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
|
|||
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.SkyBox;
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelChunkGenerator;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.GUI.GuiRenderer;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.GUI.GuiTexture;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.Lighting.LightRenderer;
|
||||
|
|
@ -28,6 +30,8 @@ import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.SwapChain.
|
|||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.SwapChain.SwapChainRender;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils;
|
||||
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.vulkan.VkCommandBufferSubmitInfo;
|
||||
import org.lwjgl.vulkan.VkExtent2D;
|
||||
|
|
@ -179,6 +183,8 @@ public class VulkanRenderer implements Renderer {
|
|||
spriteRenderer.CompileSprites(RendererContext,modelsCache, textureCache);
|
||||
spriteRenderer.LoadMaterials(RendererContext,materialsCache,textureCache);
|
||||
GuiRender.LoadTextures(RendererContext,initData.GuiTextures(),textureCache);
|
||||
VoxelWorldManager.Init(RendererContext);
|
||||
// VoxelWorldManager.GenerateChunks(new Vector3i(0,0,0), new Vector3i(16,0,16));
|
||||
}
|
||||
|
||||
public GuiRenderer GetGUIRenderer(){return GuiRender;}
|
||||
|
|
@ -196,6 +202,9 @@ public class VulkanRenderer implements Renderer {
|
|||
public void cleanup() {
|
||||
RendererContext.GetDevice().waitIdle();
|
||||
Logger.debug("Waiting Vulkan Context");
|
||||
|
||||
VoxelWorldManager.Cleanup(RendererContext);
|
||||
|
||||
sceneRender.cleanup(RendererContext);
|
||||
if(ssaoRenderer != null)ssaoRenderer.cleanup(RendererContext);
|
||||
if(ssrRender != null)ssrRender.cleanup(RendererContext);
|
||||
|
|
@ -239,7 +248,7 @@ public class VulkanRenderer implements Renderer {
|
|||
|
||||
public static volatile boolean Debug = false;
|
||||
public static volatile DebugRenderMode debugMode = DebugRenderMode.Albedo;
|
||||
|
||||
Vector3f CamPos = new Vector3f();
|
||||
public void DeferredRender(EngineInstance engineInstance){
|
||||
SwapChain swapChain = RendererContext.GetSwapChain();
|
||||
WaitForFence(CurrentFrame);
|
||||
|
|
@ -247,9 +256,8 @@ public class VulkanRenderer implements Renderer {
|
|||
|
||||
var CommandPool = CommandPools[CurrentFrame];
|
||||
var CommandBuffer = CommandBuffers[CurrentFrame];
|
||||
|
||||
RecordingStart(CommandPool, CommandBuffer);
|
||||
|
||||
VoxelWorldManager.RecordGeneration(RendererContext,CommandBuffer);
|
||||
sceneRender.Render(engineInstance,RendererContext,CommandBuffer, modelsCache,materialsCache,CurrentFrame);
|
||||
boolean RenderShadows = ShadowRenderer.AllowShadowRendering() && EngineConfig.getInstance().RenderShadows();
|
||||
if(RenderShadows) {shadowRender.render(engineInstance, RendererContext, CommandBuffer, modelsCache, materialsCache, CurrentFrame); }
|
||||
|
|
@ -289,9 +297,8 @@ public class VulkanRenderer implements Renderer {
|
|||
resize(engineInstance);
|
||||
return;
|
||||
}
|
||||
|
||||
RecordingStart(CommandPool, CommandBuffer);
|
||||
|
||||
VoxelWorldManager.RecordGeneration(RendererContext,CommandBuffer);
|
||||
sceneRender.Render(engineInstance,RendererContext,CommandBuffer, modelsCache,materialsCache,CurrentFrame);
|
||||
PostProcessor.Render(RendererContext,CommandBuffer,sceneRender.GetAttachmentColour());
|
||||
spriteRenderer.Render(engineInstance,RendererContext,CommandBuffer,PostProcessor.GetAttachment(),modelsCache,CurrentFrame);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.Noise2D;
|
|||
import net.halbear.Terrain4J.EngineCore.Main.Util.Maths.PermutationArray;
|
||||
import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor;
|
||||
import net.halbear.Terrain4J.EngineCore.Profiling.GPUProfiler;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelChunkGenerator;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager;
|
||||
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
|
||||
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils;
|
||||
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
|
||||
|
|
@ -36,13 +38,11 @@ import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPass
|
|||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialData;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.ModelData;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.Forward.ForwardSceneRender;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.*;
|
||||
import org.lwjgl.openal.AL11;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ import static org.lwjgl.glfw.GLFW.*;
|
|||
public class GameCore implements GameLogic {
|
||||
|
||||
private static final float MOUSE_SENSITIVITY = 0.1f;
|
||||
private static final float MOVEMENT_SPEED = 0.01f;
|
||||
private static final float MOVEMENT_SPEED = 0.1f;
|
||||
public static boolean LoadingLevel = false;
|
||||
|
||||
private Vector2f LastMousePos = new Vector2f(0,0);
|
||||
|
|
@ -161,6 +161,9 @@ public class GameCore implements GameLogic {
|
|||
}
|
||||
boolean createOfflinePlayer = !PrimaryRuntime.IsServer && !ClientSideNetworkUtils.Connected;
|
||||
|
||||
MaterialData VoxelMat = new MaterialData("VoxelTerrain","resources/EngineResources/Texture/test_cube.png","","",new Vector4f(1,1,1,1),
|
||||
0,0,"",new Vector4f(),"",0,"",1,new Vector4f(),1.0f,0);
|
||||
materials.add(VoxelMat);
|
||||
materials.addAll(MelonaMat);
|
||||
materials.addAll(CollisionVisualisationMat);
|
||||
materials.addAll(MelonaMaterial);
|
||||
|
|
@ -283,11 +286,11 @@ public class GameCore implements GameLogic {
|
|||
permutation.GeneratePermutationArray(5783904701859L);
|
||||
for(int x = -1; x < 2; x++){
|
||||
for(int y = -1; y < 2; y++){
|
||||
GenerateChunk(new Vector3i(x, 0, y), 16);
|
||||
//GenerateChunk(new Vector3i(x, 0, y), 16);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 20; i++){
|
||||
SpawnNewActor(engineInstance, new Vector3f(-5 + (float)Math.random() * 10,10 + (float)Math.random() * 10, -5 + (float)Math.random() * 10), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1));
|
||||
//SpawnNewActor(engineInstance, new Vector3f(-5 + (float)Math.random() * 10,10 + (float)Math.random() * 10, -5 + (float)Math.random() * 10), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1));
|
||||
}
|
||||
Settings.textBuffer.set(ClientSideNetworkUtils.ServerIP);
|
||||
Settings.UsernameBuffer.set("Player");
|
||||
|
|
@ -651,10 +654,20 @@ public class GameCore implements GameLogic {
|
|||
FetchingMetrics = false;
|
||||
return imGuiIO.getWantCaptureKeyboard();
|
||||
}
|
||||
|
||||
Vector3f CamPos = new Vector3f();
|
||||
@Override
|
||||
public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
|
||||
if(!PrimaryRuntime.IsServer && !RenderThread.Headless) {
|
||||
CamPos.set(engineInstance.scene().GetCamera().GetPosition());
|
||||
Vector3i ChunkPos = new Vector3i((int)Math.floor(CamPos.x/ (float) VoxelChunkGenerator.CHUNK_SIZE),(int)Math.floor(CamPos.y/ (float)VoxelChunkGenerator.CHUNK_SIZE),(int)Math.floor(CamPos.z/ (float)VoxelChunkGenerator.CHUNK_SIZE));
|
||||
if(ChunkPos.x != VoxelWorldManager.LastPosition.x || ChunkPos.y != VoxelWorldManager.LastPosition.y || ChunkPos.z != VoxelWorldManager.LastPosition.z) {
|
||||
VoxelWorldManager.GenerateChunks(ChunkPos, new Vector3i(5, 3, 5));
|
||||
VoxelWorldManager.UnloadFarChunks(ChunkPos, 10);
|
||||
} else {
|
||||
VoxelWorldManager.GenerateChunks(ChunkPos, new Vector3i(24,10,24));
|
||||
VoxelWorldManager.UnloadFarChunks(ChunkPos, 48);
|
||||
}
|
||||
|
||||
for (int i = 0; i < VisualisedCollisionActor3D.CollisionVisuals.size(); i++) {
|
||||
VisualisedCollisionActor3D.CollisionVisuals.get(i).Update();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,330 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil.ComputePipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.*;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialsCache;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.ModelsCache;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
import org.lwjgl.vulkan.*;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
|
||||
import static net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.BufferBarrier;
|
||||
import static org.lwjgl.system.MemoryUtil.memFree;
|
||||
import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
import static org.lwjgl.vulkan.VK10.*;
|
||||
import static org.lwjgl.vulkan.VK10.VK_PIPELINE_BIND_POINT_COMPUTE;
|
||||
import static org.lwjgl.vulkan.VK10.VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
import static org.lwjgl.vulkan.VK10.vkCmdDispatch;
|
||||
import static org.lwjgl.vulkan.VK10.vkCmdPushConstants;
|
||||
import static org.lwjgl.vulkan.VK13.*;
|
||||
|
||||
public class ComputeVoxelTerrain {
|
||||
public static final int CHUNK_SIZE = 16;
|
||||
public static final int LOCAL_SIZE = 4;
|
||||
public static final int DISPATCH_SIZE = CHUNK_SIZE / LOCAL_SIZE;
|
||||
|
||||
public static final int CHUNK_PUSH_CONSTANT_SIZE = Integer.BYTES * 4;
|
||||
|
||||
private static final int VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||
private static final int MAX_FACES = VOXEL_COUNT * 6;
|
||||
private static final int MAX_VISIBLE_FACES_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * 16;
|
||||
private static final int MAX_VERTICES = MAX_VISIBLE_FACES_PER_CHUNK * 4;
|
||||
private static final int MAX_INDICES = MAX_VISIBLE_FACES_PER_CHUNK * 6;
|
||||
|
||||
private static final int FLOATS_PER_VERTEX = 14;
|
||||
private static final int VERTEX_SIZE_BYTES = FLOATS_PER_VERTEX * Float.BYTES;
|
||||
private static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES;
|
||||
private static final int COUNTER_BUFFER_SIZE = 2 * Integer.BYTES;
|
||||
|
||||
private final String DESCRIPTOR_ID_MESH;
|
||||
private final String DESCRIPTOR_ID_RESET;
|
||||
private final String DESCRIPTOR_ID_VOXEL;
|
||||
|
||||
private static final String MESH_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/meshGeneration.glsl";
|
||||
private static final String VOXEL_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/voxelGeneration.glsl";
|
||||
private static final String VOXEL_RESET_GLSL = "resources/EngineResources/VoxelComputeShaders/resetVoxelDraw.glsl";
|
||||
|
||||
private static final String MESH_GENERATION_SPV = MESH_GENERATION_GLSL + ".spv";
|
||||
private static final String VOXEL_GENERATION_SPV = VOXEL_GENERATION_GLSL + ".spv";
|
||||
private static final String VOXEL_RESET_SPV = VOXEL_RESET_GLSL + ".spv";
|
||||
|
||||
private final VulkanBuffer VoxelData;
|
||||
private final VulkanBuffer VertexBuffer;
|
||||
private final VulkanBuffer IndexBuffer;
|
||||
private final VulkanBuffer DrawCommand;
|
||||
private final VulkanBuffer Counters;
|
||||
|
||||
private final DescriptorSetLayout voxelGenerationLayout;
|
||||
private final DescriptorSetLayout resetLayout;
|
||||
private final DescriptorSetLayout meshGenerationLayout;
|
||||
|
||||
private final Pipeline voxelGeneration;
|
||||
private final Pipeline voxelReset;
|
||||
private final Pipeline meshGeneration;
|
||||
|
||||
private final ByteBuffer graphicsPushConstants;
|
||||
|
||||
private final int chunkX;
|
||||
private final int chunkY;
|
||||
private final int chunkZ;
|
||||
|
||||
private boolean generated;
|
||||
|
||||
public ComputeVoxelTerrain(VulkanContext VkCtx, int chunkX, int chunkY, int chunkZ) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkY = chunkY;
|
||||
this.chunkZ = chunkZ;
|
||||
this.generated = false;
|
||||
DESCRIPTOR_ID_MESH = "DESC_ID_MESH_" + chunkX + "_" + chunkY + "_" + chunkZ;
|
||||
DESCRIPTOR_ID_RESET = "DESC_ID_RESET_" + chunkX + "_" + chunkY + "_" + chunkZ;
|
||||
DESCRIPTOR_ID_VOXEL = "DESC_ID_VOXEL_" + chunkX + "_" + chunkY + "_" + chunkZ;
|
||||
long voxelBufferSize = (long) VOXEL_COUNT * Integer.BYTES;
|
||||
long vertexBufferSize = (long) MAX_VERTICES * VERTEX_SIZE_BYTES;
|
||||
long indexBufferSize = (long) MAX_INDICES * Integer.BYTES;
|
||||
|
||||
VoxelData = new VulkanBuffer(VkCtx,voxelBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
VertexBuffer = new VulkanBuffer( VkCtx, vertexBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
IndexBuffer = new VulkanBuffer(VkCtx,indexBufferSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
DrawCommand = new VulkanBuffer(VkCtx,DRAW_INDEXED_INDIRECT_COMMAND_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
Counters = new VulkanBuffer(VkCtx,COUNTER_BUFFER_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
voxelGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT ) });
|
||||
resetLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT ),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT ) });
|
||||
meshGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,
|
||||
1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,
|
||||
1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,2,
|
||||
1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,3,
|
||||
1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,4,
|
||||
1,VK_SHADER_STAGE_COMPUTE_BIT),});
|
||||
createDescriptorSets(VkCtx);
|
||||
PushConstantsRange[] chunkPushConstants = new PushConstantsRange[]{
|
||||
new PushConstantsRange(
|
||||
VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
0,
|
||||
CHUNK_PUSH_CONSTANT_SIZE
|
||||
)
|
||||
};
|
||||
ShaderModule resetShader = createComputeShader(VkCtx, VOXEL_RESET_GLSL, VOXEL_RESET_SPV);
|
||||
ShaderModule voxelShader = createComputeShader(VkCtx, VOXEL_GENERATION_GLSL, VOXEL_GENERATION_SPV);
|
||||
ShaderModule meshShader = createComputeShader(VkCtx, MESH_GENERATION_GLSL, MESH_GENERATION_SPV);
|
||||
voxelReset = new ComputePipeline( VkCtx, resetShader,new DescriptorSetLayout[]{resetLayout},
|
||||
new PushConstantsRange[0]);
|
||||
voxelGeneration = new ComputePipeline( VkCtx,voxelShader, new DescriptorSetLayout[]{voxelGenerationLayout},
|
||||
chunkPushConstants );
|
||||
meshGeneration = new ComputePipeline(VkCtx, meshShader,new DescriptorSetLayout[]{meshGenerationLayout},
|
||||
chunkPushConstants );
|
||||
resetShader.CleanUp(VkCtx);
|
||||
voxelShader.CleanUp(VkCtx);
|
||||
meshShader.CleanUp(VkCtx);
|
||||
graphicsPushConstants = org.lwjgl.system.MemoryUtil.memAlloc(VulkanUtils.MATRIX4X4_SIZE + VulkanUtils.INT_SIZE);
|
||||
}
|
||||
|
||||
private static ShaderModule createComputeShader(VulkanContext VkCtx, String glslPath, String spvPath) {
|
||||
if (EngineConfig.getInstance().RecompileShaders()) {
|
||||
ShaderCompiler.CompileGLSLShaderOnChange(glslPath, Shaderc.shaderc_glsl_compute_shader);
|
||||
}
|
||||
|
||||
return new ShaderModule(VkCtx, VK_SHADER_STAGE_COMPUTE_BIT, spvPath, null);
|
||||
}
|
||||
|
||||
private void createDescriptorSets(VulkanContext VkCtx) {
|
||||
DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator();
|
||||
Device device = VkCtx.GetDevice();
|
||||
|
||||
DescriptorSet resetSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_RESET, resetLayout);
|
||||
resetSet.SetBuffer(device, DrawCommand, DrawCommand.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
resetSet.SetBuffer(device, Counters, Counters.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
|
||||
DescriptorSet voxelSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_VOXEL, voxelGenerationLayout);
|
||||
voxelSet.SetBuffer(device, VoxelData, VoxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
|
||||
DescriptorSet meshSet = allocator.AddDescriptorSet(device, DESCRIPTOR_ID_MESH, meshGenerationLayout);
|
||||
meshSet.SetBuffer(device, VoxelData, VoxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, VertexBuffer, VertexBuffer.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, IndexBuffer, IndexBuffer.GetRequestedSize(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, DrawCommand, DrawCommand.GetRequestedSize(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, Counters, Counters.GetRequestedSize(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
public void RecordGeneration(VulkanContext VkCtx, CommandBuffer commandBuffer) {
|
||||
if (generated) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (MemoryStack MemStack = MemoryStack.stackPush()) {
|
||||
VkCommandBuffer CommandBuffer = commandBuffer.GetVulkanCommandBuffer();
|
||||
DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator();
|
||||
|
||||
ByteBuffer chunkPushBuffer = MemStack.malloc(CHUNK_PUSH_CONSTANT_SIZE);
|
||||
chunkPushBuffer.putInt(0, chunkX);
|
||||
chunkPushBuffer.putInt(4, chunkY);
|
||||
chunkPushBuffer.putInt(8, chunkZ);
|
||||
chunkPushBuffer.putInt(12, 0);
|
||||
|
||||
LongBuffer resetDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_RESET).GetVkDescriptorSet());
|
||||
|
||||
LongBuffer voxelDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_VOXEL).GetVkDescriptorSet());
|
||||
|
||||
LongBuffer meshDescriptorSet = MemStack.longs(allocator.GetDescriptorSet(DESCRIPTOR_ID_MESH).GetVkDescriptorSet());
|
||||
|
||||
vkCmdBindPipeline(CommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,voxelReset.GetVulkanPipeline());
|
||||
|
||||
vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelReset.GetVulkanPipelineLayout(),0,resetDescriptorSet,null);
|
||||
|
||||
vkCmdDispatch(CommandBuffer, 1, 1, 1);
|
||||
|
||||
VulkanUtils.BufferBarriers(MemStack, CommandBuffer,
|
||||
new long[]{DrawCommand.GetBuffer(),Counters.GetBuffer()},
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT);
|
||||
|
||||
vkCmdBindPipeline(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelGeneration.GetVulkanPipeline());
|
||||
|
||||
vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,voxelGeneration.GetVulkanPipelineLayout(),0, voxelDescriptorSet,null);
|
||||
|
||||
vkCmdPushConstants(CommandBuffer,voxelGeneration.GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0,chunkPushBuffer);
|
||||
|
||||
vkCmdDispatch(CommandBuffer, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE);
|
||||
|
||||
BufferBarrier(MemStack, CommandBuffer, VoxelData.GetBuffer(),
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_SHADER_READ_BIT);
|
||||
|
||||
vkCmdBindPipeline(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,meshGeneration.GetVulkanPipeline());
|
||||
|
||||
vkCmdBindDescriptorSets(CommandBuffer,VK_PIPELINE_BIND_POINT_COMPUTE,meshGeneration.GetVulkanPipelineLayout(),0, meshDescriptorSet,null);
|
||||
|
||||
vkCmdPushConstants(CommandBuffer, meshGeneration.GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0,chunkPushBuffer);
|
||||
|
||||
vkCmdDispatch(CommandBuffer, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE);
|
||||
|
||||
VulkanUtils.BufferBarriers(MemStack, CommandBuffer,
|
||||
new long[]{VertexBuffer.GetBuffer(),
|
||||
IndexBuffer.GetBuffer(),
|
||||
DrawCommand.GetBuffer()},
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_2_INDEX_READ_BIT | VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT);
|
||||
|
||||
generated = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void RecordRender(VkCommandBuffer CommandBuffer, Pipeline graphicsPipeline,Matrix4f modelMatrix, int materialIndex) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
modelMatrix.get(0, graphicsPushConstants);
|
||||
graphicsPushConstants.putInt(VulkanUtils.MATRIX4X4_SIZE, materialIndex);
|
||||
// Logger.debug("Rendering Chunk {}:{}:{}",chunkX,chunkY,chunkZ);
|
||||
vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_VERTEX_BIT,0, graphicsPushConstants.slice(0, VulkanUtils.MATRIX4X4_SIZE));
|
||||
|
||||
vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_FRAGMENT_BIT,VulkanUtils.MATRIX4X4_SIZE, graphicsPushConstants.slice(VulkanUtils.MATRIX4X4_SIZE, VulkanUtils.INT_SIZE));
|
||||
|
||||
LongBuffer vertexBuffers = stack.longs(VertexBuffer.GetBuffer());
|
||||
LongBuffer offsets = stack.longs(0L);
|
||||
|
||||
vkCmdBindVertexBuffers(CommandBuffer, 0, vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(CommandBuffer, IndexBuffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
//vkCmdDrawIndexed(CommandBuffer,MAX_INDICES,1,0,0,0);
|
||||
vkCmdDrawIndexedIndirect(CommandBuffer,DrawCommand.GetBuffer(),0,1,DRAW_INDEXED_INDIRECT_COMMAND_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public VulkanBuffer GetVoxelDataBuffer() {
|
||||
return VoxelData;
|
||||
}
|
||||
|
||||
public VulkanBuffer GetVertexBuffer() {
|
||||
return VertexBuffer;
|
||||
}
|
||||
|
||||
public VulkanBuffer GetIndexBuffer() {
|
||||
return IndexBuffer;
|
||||
}
|
||||
|
||||
public VulkanBuffer GetDrawCommandBuffer() {
|
||||
return DrawCommand;
|
||||
}
|
||||
|
||||
public VulkanBuffer GetCountersBuffer() {
|
||||
return Counters;
|
||||
}
|
||||
|
||||
public boolean IsGenerated() {
|
||||
return generated;
|
||||
}
|
||||
|
||||
public void MarkDirty() {
|
||||
generated = false;
|
||||
}
|
||||
|
||||
public Vector3i GetPosition(){
|
||||
return new Vector3i(chunkX,chunkY,chunkZ);
|
||||
}
|
||||
|
||||
public void cleanup(VulkanContext VkCtx) {
|
||||
voxelGeneration.CleanUp(VkCtx);
|
||||
voxelReset.CleanUp(VkCtx);
|
||||
meshGeneration.CleanUp(VkCtx);
|
||||
|
||||
voxelGenerationLayout.CleanUp(VkCtx);
|
||||
resetLayout.CleanUp(VkCtx);
|
||||
meshGenerationLayout.CleanUp(VkCtx);
|
||||
|
||||
VoxelData.cleanup(VkCtx);
|
||||
VertexBuffer.cleanup(VkCtx);
|
||||
IndexBuffer.cleanup(VkCtx);
|
||||
DrawCommand.cleanup(VkCtx);
|
||||
Counters.cleanup(VkCtx);
|
||||
|
||||
org.lwjgl.system.MemoryUtil.memFree(graphicsPushConstants);
|
||||
}
|
||||
|
||||
public void recordComputeCommandBuffer(VkCommandBuffer cmd, long computePipeline, long pipelineLayout,
|
||||
long descriptorSet, int chunkX, int chunkY, int chunkZ) {
|
||||
try (MemoryStack MemStack = MemoryStack.stackPush()) {
|
||||
ByteBuffer pushConstants = MemStack.malloc(CHUNK_PUSH_CONSTANT_SIZE);
|
||||
pushConstants.putInt(0, chunkX);
|
||||
pushConstants.putInt(4, chunkY);
|
||||
pushConstants.putInt(8, chunkZ);
|
||||
pushConstants.putInt(12, 0);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline);
|
||||
vkCmdBindDescriptorSets(cmd,VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout,
|
||||
0,MemStack.longs(descriptorSet),null);
|
||||
|
||||
vkCmdPushConstants(cmd,pipelineLayout,VK_SHADER_STAGE_COMPUTE_BIT,0,pushConstants);
|
||||
|
||||
vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
public record RenderChunk(Vector3i position, int slot,long vertexOffsetBytes, long indexOffsetBytes,int maxIndexCount,long indirectCommandOffsetBytes) {
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil.ComputePipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.*;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
|
||||
import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
import static org.lwjgl.vulkan.VK13.*;
|
||||
|
||||
public class SharedVoxelTerrainResources {
|
||||
public static final int CHUNK_SIZE = 16;
|
||||
public static final int VOXEL_COUNT = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||
|
||||
public static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES;
|
||||
public static final int COUNTER_BUFFER_SIZE = 2 * Integer.BYTES;
|
||||
|
||||
|
||||
public static final int TERRAIN_GENERATION_PUSH_CONSTANT_SIZE = Integer.BYTES * 8;
|
||||
/*Push constants:
|
||||
int chunkX
|
||||
int chunkY
|
||||
int chunkZ
|
||||
int slot
|
||||
uint vertexFloatOffset
|
||||
uint indexOffset
|
||||
uint indirectCommandOffset
|
||||
uint padding */
|
||||
private static final String DESC_ID_VOXEL_GENERATION = "VOXEL_SHARED_DESC_GENERATION";
|
||||
private static final String DESC_ID_RESET = "VOXEL_SHARED_DESC_RESET";
|
||||
private static final String DESC_ID_MESH = "VOXEL_SHARED_DESC_MESH";
|
||||
|
||||
private static final String MESH_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/meshGenerationShared.glsl";
|
||||
private static final String VOXEL_GENERATION_GLSL = "resources/EngineResources/VoxelComputeShaders/voxelGenerationShared.glsl";
|
||||
private static final String VOXEL_RESET_GLSL = "resources/EngineResources/VoxelComputeShaders/resetVoxelDrawShared.glsl";
|
||||
|
||||
private static final String MESH_GENERATION_SPV = MESH_GENERATION_GLSL + ".spv";
|
||||
private static final String VOXEL_GENERATION_SPV = VOXEL_GENERATION_GLSL + ".spv";
|
||||
private static final String VOXEL_RESET_SPV = VOXEL_RESET_GLSL + ".spv";
|
||||
|
||||
private final VoxelMeshPool meshPool;
|
||||
|
||||
private final VulkanBuffer voxelData;
|
||||
private final VulkanBuffer counters;
|
||||
|
||||
private final DescriptorSetLayout voxelGenerationLayout;
|
||||
private final DescriptorSetLayout resetLayout;
|
||||
private final DescriptorSetLayout meshGenerationLayout;
|
||||
|
||||
private final Pipeline voxelGenerationPipeline;
|
||||
private final Pipeline resetPipeline;
|
||||
private final Pipeline meshGenerationPipeline;
|
||||
|
||||
public SharedVoxelTerrainResources(VulkanContext VkCtx, int maxResidentChunks) {
|
||||
meshPool = new VoxelMeshPool(VkCtx, maxResidentChunks);
|
||||
|
||||
voxelData = new VulkanBuffer(VkCtx, (long) VOXEL_COUNT * Integer.BYTES,
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
|
||||
counters = new VulkanBuffer( VkCtx,COUNTER_BUFFER_SIZE,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0 );
|
||||
|
||||
voxelGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
});
|
||||
|
||||
resetLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1,VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
});
|
||||
|
||||
meshGenerationLayout = new DescriptorSetLayout(VkCtx, new DescriptorSetLayout.LayoutInformation[]{
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,0,1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,1,1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,2,1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,3,1,VK_SHADER_STAGE_COMPUTE_BIT),
|
||||
new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,4,1,VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
});
|
||||
|
||||
createDescriptorSets(VkCtx);
|
||||
|
||||
PushConstantsRange[] generationPushConstants = new PushConstantsRange[]{
|
||||
new PushConstantsRange(VK_SHADER_STAGE_COMPUTE_BIT,0, TERRAIN_GENERATION_PUSH_CONSTANT_SIZE)
|
||||
};
|
||||
|
||||
ShaderModule resetShader = createComputeShader(VkCtx, VOXEL_RESET_GLSL, VOXEL_RESET_SPV);
|
||||
ShaderModule voxelShader = createComputeShader(VkCtx, VOXEL_GENERATION_GLSL, VOXEL_GENERATION_SPV);
|
||||
ShaderModule meshShader = createComputeShader(VkCtx, MESH_GENERATION_GLSL, MESH_GENERATION_SPV);
|
||||
|
||||
resetPipeline = new ComputePipeline(VkCtx,resetShader,new DescriptorSetLayout[]{resetLayout},generationPushConstants);
|
||||
|
||||
voxelGenerationPipeline = new ComputePipeline(VkCtx,voxelShader, new DescriptorSetLayout[]{voxelGenerationLayout},generationPushConstants);
|
||||
|
||||
meshGenerationPipeline = new ComputePipeline( VkCtx,meshShader, new DescriptorSetLayout[]{meshGenerationLayout},generationPushConstants );
|
||||
|
||||
resetShader.CleanUp(VkCtx);
|
||||
voxelShader.CleanUp(VkCtx);
|
||||
meshShader.CleanUp(VkCtx);
|
||||
}
|
||||
|
||||
private static ShaderModule createComputeShader(VulkanContext VkCtx, String glslPath, String spvPath) {
|
||||
if (EngineConfig.getInstance().RecompileShaders()) {
|
||||
ShaderCompiler.CompileGLSLShaderOnChange(glslPath, Shaderc.shaderc_glsl_compute_shader);
|
||||
}
|
||||
|
||||
return new ShaderModule(VkCtx, VK_SHADER_STAGE_COMPUTE_BIT, spvPath, null);
|
||||
}
|
||||
|
||||
private void createDescriptorSets(VulkanContext VkCtx) {
|
||||
DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator();
|
||||
Device device = VkCtx.GetDevice();
|
||||
|
||||
DescriptorSet resetSet = allocator.AddDescriptorSet(device, DESC_ID_RESET, resetLayout);
|
||||
resetSet.SetBuffer(device, meshPool.indirectPool(), meshPool.indirectPool().GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
resetSet.SetBuffer(device, counters, counters.GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
|
||||
DescriptorSet voxelSet = allocator.AddDescriptorSet(device, DESC_ID_VOXEL_GENERATION, voxelGenerationLayout);
|
||||
voxelSet.SetBuffer(device, voxelData, voxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
|
||||
DescriptorSet meshSet = allocator.AddDescriptorSet(device, DESC_ID_MESH, meshGenerationLayout);
|
||||
meshSet.SetBuffer(device, voxelData, voxelData.GetRequestedSize(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, meshPool.vertexPool(), meshPool.vertexPool().GetRequestedSize(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, meshPool.indexPool(), meshPool.indexPool().GetRequestedSize(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, meshPool.indirectPool(), meshPool.indirectPool().GetRequestedSize(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
meshSet.SetBuffer(device, counters, counters.GetRequestedSize(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
public VoxelMeshPool meshPool() {
|
||||
return meshPool;
|
||||
}
|
||||
|
||||
public VulkanBuffer voxelData() {
|
||||
return voxelData;
|
||||
}
|
||||
|
||||
public VulkanBuffer counters() {
|
||||
return counters;
|
||||
}
|
||||
|
||||
public Pipeline voxelGenerationPipeline() {
|
||||
return voxelGenerationPipeline;
|
||||
}
|
||||
|
||||
public Pipeline resetPipeline() {
|
||||
return resetPipeline;
|
||||
}
|
||||
|
||||
public Pipeline meshGenerationPipeline() {
|
||||
return meshGenerationPipeline;
|
||||
}
|
||||
|
||||
public String voxelGenerationDescriptorId() {
|
||||
return DESC_ID_VOXEL_GENERATION;
|
||||
}
|
||||
|
||||
public String resetDescriptorId() {
|
||||
return DESC_ID_RESET;
|
||||
}
|
||||
|
||||
public String meshDescriptorId() {
|
||||
return DESC_ID_MESH;
|
||||
}
|
||||
|
||||
public void cleanup(VulkanContext VkCtx) {
|
||||
resetPipeline.CleanUp(VkCtx);
|
||||
voxelGenerationPipeline.CleanUp(VkCtx);
|
||||
meshGenerationPipeline.CleanUp(VkCtx);
|
||||
|
||||
resetLayout.CleanUp(VkCtx);
|
||||
voxelGenerationLayout.CleanUp(VkCtx);
|
||||
meshGenerationLayout.CleanUp(VkCtx);
|
||||
|
||||
voxelData.cleanup(VkCtx);
|
||||
counters.cleanup(VkCtx);
|
||||
meshPool.cleanup(VkCtx);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.DescriptorAllocator;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.vulkan.VkCommandBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
|
||||
import static org.lwjgl.vulkan.VK13.*;
|
||||
|
||||
public class VoxelChunkGenerator {
|
||||
public static final int CHUNK_SIZE = 16;
|
||||
public static final int LOCAL_SIZE = 4;
|
||||
public static final int DISPATCH_SIZE = CHUNK_SIZE / LOCAL_SIZE;
|
||||
|
||||
private final SharedVoxelTerrainResources resources;
|
||||
|
||||
public VoxelChunkGenerator(SharedVoxelTerrainResources resources) {
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
public void recordGenerateChunk(VulkanContext VkCtx,CommandBuffer commandBuffer, RenderChunk chunk) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
VkCommandBuffer cmd = commandBuffer.GetVulkanCommandBuffer();
|
||||
DescriptorAllocator allocator = VkCtx.GetDescriptorAllocator();
|
||||
|
||||
ByteBuffer pushConstants = stack.malloc(SharedVoxelTerrainResources.TERRAIN_GENERATION_PUSH_CONSTANT_SIZE);
|
||||
|
||||
int vertexFloatOffset = (int) (chunk.vertexOffsetBytes() / Float.BYTES);
|
||||
int indexOffset = (int) (chunk.indexOffsetBytes() / Integer.BYTES);
|
||||
int indirectCommandIndex = (int) (chunk.indirectCommandOffsetBytes() / VoxelMeshPool.DRAW_INDEXED_INDIRECT_COMMAND_SIZE);
|
||||
|
||||
pushConstants.putInt(0, chunk.position().x);
|
||||
pushConstants.putInt(4, chunk.position().y);
|
||||
pushConstants.putInt(8, chunk.position().z);
|
||||
pushConstants.putInt(12, chunk.slot());
|
||||
pushConstants.putInt(16, vertexFloatOffset);
|
||||
pushConstants.putInt(20, indexOffset);
|
||||
pushConstants.putInt(24, indirectCommandIndex);
|
||||
pushConstants.putInt(28, 0);
|
||||
|
||||
LongBuffer resetDescriptorSet = stack.longs(
|
||||
allocator.GetDescriptorSet(resources.resetDescriptorId()).GetVkDescriptorSet()
|
||||
);
|
||||
|
||||
LongBuffer voxelDescriptorSet = stack.longs(
|
||||
allocator.GetDescriptorSet(resources.voxelGenerationDescriptorId()).GetVkDescriptorSet()
|
||||
);
|
||||
|
||||
LongBuffer meshDescriptorSet = stack.longs(
|
||||
allocator.GetDescriptorSet(resources.meshDescriptorId()).GetVkDescriptorSet()
|
||||
);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.resetPipeline().GetVulkanPipeline());
|
||||
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.resetPipeline().GetVulkanPipelineLayout(),0, resetDescriptorSet,null );
|
||||
|
||||
vkCmdPushConstants( cmd, resources.resetPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants );
|
||||
|
||||
vkCmdDispatch(cmd, 1, 1, 1);
|
||||
|
||||
VulkanUtils.BufferBarriers(stack, cmd,
|
||||
new long[]{
|
||||
resources.meshPool().indirectPoolHandle(),
|
||||
resources.counters().GetBuffer()
|
||||
},
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT
|
||||
);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.voxelGenerationPipeline().GetVulkanPipeline());
|
||||
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.voxelGenerationPipeline().GetVulkanPipelineLayout(),0, voxelDescriptorSet,null );
|
||||
|
||||
vkCmdPushConstants( cmd, resources.voxelGenerationPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants );
|
||||
|
||||
vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE);
|
||||
|
||||
VulkanUtils.BufferBarrier(stack, cmd,resources.voxelData().GetBuffer(),
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
VK_ACCESS_2_SHADER_READ_BIT
|
||||
);
|
||||
|
||||
vkCmdBindPipeline(cmd,VK_PIPELINE_BIND_POINT_COMPUTE,resources.meshGenerationPipeline().GetVulkanPipeline() );
|
||||
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,resources.meshGenerationPipeline().GetVulkanPipelineLayout(),0, meshDescriptorSet,null );
|
||||
|
||||
vkCmdPushConstants( cmd, resources.meshGenerationPipeline().GetVulkanPipelineLayout(),VK_SHADER_STAGE_COMPUTE_BIT,0, pushConstants );
|
||||
|
||||
vkCmdDispatch(cmd, DISPATCH_SIZE, DISPATCH_SIZE, DISPATCH_SIZE);
|
||||
|
||||
VulkanUtils.BufferBarriers(stack, cmd,
|
||||
new long[]{
|
||||
resources.voxelData().GetBuffer(),
|
||||
resources.counters().GetBuffer(),
|
||||
resources.meshPool().vertexPoolHandle(),
|
||||
resources.meshPool().indexPoolHandle(),
|
||||
resources.meshPool().indirectPoolHandle()
|
||||
},
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT |
|
||||
VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT,
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT,
|
||||
VK_ACCESS_2_SHADER_READ_BIT |
|
||||
VK_ACCESS_2_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_2_INDEX_READ_BIT |
|
||||
VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
public enum VoxelChunkVisibility {
|
||||
EMPTY_AIR,
|
||||
FULLY_SOLID_UNDERGROUND,
|
||||
SURFACE
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanBuffer;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
import static org.lwjgl.util.vma.Vma.VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
import static org.lwjgl.vulkan.VK13.*;
|
||||
|
||||
public class VoxelMeshPool{
|
||||
public static final int CHUNK_SIZE = 16;
|
||||
|
||||
public static final int FLOATS_PER_VERTEX = 14;
|
||||
public static final int VERTEX_SIZE_BYTES = FLOATS_PER_VERTEX * Float.BYTES;
|
||||
|
||||
public static final int MAX_VISIBLE_FACES_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * 12;
|
||||
public static final int MAX_VERTICES_PER_CHUNK = MAX_VISIBLE_FACES_PER_CHUNK * 4;
|
||||
public static final int MAX_INDICES_PER_CHUNK = MAX_VISIBLE_FACES_PER_CHUNK * 6;
|
||||
|
||||
public static final int INDEX_SIZE_BYTES = Integer.BYTES;
|
||||
public static final int DRAW_INDEXED_INDIRECT_COMMAND_SIZE = 5 * Integer.BYTES;
|
||||
|
||||
private final int maxChunks;
|
||||
|
||||
private final long vertexSlotSizeBytes;
|
||||
private final long indexSlotSizeBytes;
|
||||
private final long indirectSlotSizeBytes;
|
||||
|
||||
private final VulkanBuffer vertexPool;
|
||||
private final VulkanBuffer indexPool;
|
||||
private final VulkanBuffer indirectPool;
|
||||
|
||||
private final Queue<Integer> freeSlots = new ArrayDeque<>();
|
||||
|
||||
public VoxelMeshPool(VulkanContext VkCtx, int maxChunks) {
|
||||
this.maxChunks = maxChunks;
|
||||
|
||||
this.vertexSlotSizeBytes = (long) MAX_VERTICES_PER_CHUNK * VERTEX_SIZE_BYTES;
|
||||
this.indexSlotSizeBytes = (long) MAX_INDICES_PER_CHUNK * INDEX_SIZE_BYTES;
|
||||
this.indirectSlotSizeBytes = DRAW_INDEXED_INDIRECT_COMMAND_SIZE;
|
||||
|
||||
long vertexPoolSize = vertexSlotSizeBytes * maxChunks;
|
||||
long indexPoolSize = indexSlotSizeBytes * maxChunks;
|
||||
long indirectPoolSize = indirectSlotSizeBytes * maxChunks;
|
||||
|
||||
vertexPool = new VulkanBuffer(VkCtx,vertexPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0);
|
||||
|
||||
indexPool = new VulkanBuffer(VkCtx,indexPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0);
|
||||
|
||||
indirectPool = new VulkanBuffer(VkCtx,indirectPoolSize,VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,0,0);
|
||||
|
||||
for (int i = 0; i < maxChunks; i++) {
|
||||
freeSlots.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public RenderChunk allocate(Vector3i position) {
|
||||
Integer slot = freeSlots.poll();
|
||||
|
||||
if (slot == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RenderChunk(new Vector3i(position),slot, vertexOffsetBytes(slot),indexOffsetBytes(slot),MAX_INDICES_PER_CHUNK,indirectCommandOffsetBytes(slot));
|
||||
}
|
||||
|
||||
public void free(RenderChunk chunk) {
|
||||
freeSlots.add(chunk.slot());
|
||||
}
|
||||
|
||||
public long vertexOffsetBytes(int slot) {
|
||||
return vertexSlotSizeBytes * slot;
|
||||
}
|
||||
|
||||
public long indexOffsetBytes(int slot) {
|
||||
return indexSlotSizeBytes * slot;
|
||||
}
|
||||
|
||||
public long indirectCommandOffsetBytes(int slot) {
|
||||
return indirectSlotSizeBytes * slot;
|
||||
}
|
||||
|
||||
public VulkanBuffer vertexPool() {
|
||||
return vertexPool;
|
||||
}
|
||||
|
||||
public VulkanBuffer indexPool() {
|
||||
return indexPool;
|
||||
}
|
||||
|
||||
public VulkanBuffer indirectPool() {
|
||||
return indirectPool;
|
||||
}
|
||||
|
||||
public long vertexPoolHandle() {
|
||||
return vertexPool.GetBuffer();
|
||||
}
|
||||
|
||||
public long indexPoolHandle() {
|
||||
return indexPool.GetBuffer();
|
||||
}
|
||||
|
||||
public long indirectPoolHandle() {
|
||||
return indirectPool.GetBuffer();
|
||||
}
|
||||
|
||||
public int maxChunks() {
|
||||
return maxChunks;
|
||||
}
|
||||
|
||||
public int freeSlotCount() {
|
||||
return freeSlots.size();
|
||||
}
|
||||
|
||||
public void cleanup(VulkanContext VkCtx) {
|
||||
vertexPool.cleanup(VkCtx);
|
||||
indexPool.cleanup(VkCtx);
|
||||
indirectPool.cleanup(VkCtx);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
public final class VoxelTerrainHeightSampler {
|
||||
private VoxelTerrainHeightSampler() {
|
||||
}
|
||||
|
||||
private static double fract(double value) {
|
||||
return value - Math.floor(value);
|
||||
}
|
||||
|
||||
private static double mix(double a, double b, double t) {
|
||||
return a * (1.0 - t) + b * t;
|
||||
}
|
||||
|
||||
private static double rand(double x, double z) {
|
||||
return fract(Math.sin(x * 12.9898 + z * 78.233) * 43758.5453123);
|
||||
}
|
||||
|
||||
private static double valueNoise(double x, double z) {
|
||||
double ix = Math.floor(x);
|
||||
double iz = Math.floor(z);
|
||||
|
||||
double fx = fract(x);
|
||||
double fz = fract(z);
|
||||
|
||||
double a = rand(ix, iz);
|
||||
double b = rand(ix + 1.0, iz);
|
||||
double c = rand(ix, iz + 1.0);
|
||||
double d = rand(ix + 1.0, iz + 1.0);
|
||||
|
||||
double ux = fx * fx * (3.0 - 2.0 * fx);
|
||||
double uz = fz * fz * (3.0 - 2.0 * fz);
|
||||
|
||||
return mix(mix(a, b, ux), mix(c, d, ux), uz);
|
||||
}
|
||||
|
||||
public static double terrainHeight(double worldX, double worldZ) {
|
||||
return valueNoise(worldX * 0.005, worldZ * 0.005) * 100.0
|
||||
+ valueNoise(worldX * 0.01, worldZ * 0.01) * 10.0
|
||||
+ valueNoise(worldX * 0.1, worldZ * 0.1) * 5.0
|
||||
+ valueNoise(worldX, worldZ) * 0.5;
|
||||
}
|
||||
|
||||
public static double minHeightForChunkXZ(int chunkX, int chunkZ, int chunkSize) {
|
||||
double min = Double.POSITIVE_INFINITY;
|
||||
|
||||
int worldMinX = chunkX * chunkSize;
|
||||
int worldMinZ = chunkZ * chunkSize;
|
||||
int worldMaxX = worldMinX + chunkSize - 1;
|
||||
int worldMaxZ = worldMinZ + chunkSize - 1;
|
||||
|
||||
int samplesPerAxis = 5;
|
||||
|
||||
for (int sx = 0; sx < samplesPerAxis; sx++) {
|
||||
for (int sz = 0; sz < samplesPerAxis; sz++) {
|
||||
double tx = sx / (double) (samplesPerAxis - 1);
|
||||
double tz = sz / (double) (samplesPerAxis - 1);
|
||||
|
||||
double x = mix(worldMinX, worldMaxX, tx);
|
||||
double z = mix(worldMinZ, worldMaxZ, tz);
|
||||
|
||||
min = Math.min(min, terrainHeight(x, z));
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
public static double maxHeightForChunkXZ(int chunkX, int chunkZ, int chunkSize) {
|
||||
double max = Double.NEGATIVE_INFINITY;
|
||||
|
||||
int worldMinX = chunkX * chunkSize;
|
||||
int worldMinZ = chunkZ * chunkSize;
|
||||
int worldMaxX = worldMinX + chunkSize - 1;
|
||||
int worldMaxZ = worldMinZ + chunkSize - 1;
|
||||
|
||||
int samplesPerAxis = 5;
|
||||
|
||||
for (int sx = 0; sx < samplesPerAxis; sx++) {
|
||||
for (int sz = 0; sz < samplesPerAxis; sz++) {
|
||||
double tx = sx / (double) (samplesPerAxis - 1);
|
||||
double tz = sz / (double) (samplesPerAxis - 1);
|
||||
|
||||
double x = mix(worldMinX, worldMaxX, tx);
|
||||
double z = mix(worldMinZ, worldMaxZ, tz);
|
||||
|
||||
max = Math.max(max, terrainHeight(x, z));
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
public static VoxelChunkVisibility classifyChunk(int chunkX, int chunkY, int chunkZ, int chunkSize) {
|
||||
int chunkMinY = chunkY * chunkSize;
|
||||
int chunkMaxY = chunkMinY + chunkSize - 1;
|
||||
|
||||
double minHeight = minHeightForChunkXZ(chunkX, chunkZ, chunkSize);
|
||||
double maxHeight = maxHeightForChunkXZ(chunkX, chunkZ, chunkSize);
|
||||
|
||||
double safetyPadding = 32.0;
|
||||
|
||||
if (chunkMinY > maxHeight + safetyPadding) {
|
||||
return VoxelChunkVisibility.EMPTY_AIR;
|
||||
}
|
||||
|
||||
if (chunkMaxY < minHeight - safetyPadding) {
|
||||
return VoxelChunkVisibility.FULLY_SOLID_UNDERGROUND;
|
||||
}
|
||||
|
||||
return VoxelChunkVisibility.SURFACE;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialsCache;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.CommandBuffer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3i;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.vulkan.VkCommandBuffer;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import static org.lwjgl.vulkan.VK13.*;
|
||||
|
||||
public class VoxelWorldManager {
|
||||
private static final int MAX_RESIDENT_CHUNKS = 512 * 24;
|
||||
private static final int MAX_CHUNK_GENERATIONS_PER_FRAME = 32;
|
||||
|
||||
private static final ConcurrentLinkedQueue<Vector3i> RequestedChunks = new ConcurrentLinkedQueue<>();
|
||||
private static final ConcurrentHashMap<Vector3i, RenderChunk> RenderChunks = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<Vector3i, Boolean> KnownChunks = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<Vector3i, VoxelChunkVisibility> CulledChunks = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private static SharedVoxelTerrainResources Resources;
|
||||
private static VoxelChunkGenerator Generator;
|
||||
|
||||
private static ByteBuffer GraphicsPushConstants;
|
||||
|
||||
public static void Init(VulkanContext VkCtx) {
|
||||
if (Resources != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Resources = new SharedVoxelTerrainResources(VkCtx, MAX_RESIDENT_CHUNKS);
|
||||
Generator = new VoxelChunkGenerator(Resources);
|
||||
|
||||
GraphicsPushConstants = org.lwjgl.system.MemoryUtil.memAlloc(
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE +
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.INT_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
public static void GenerateChunk(Vector3i Position) {
|
||||
Vector3i key = new Vector3i(Position);
|
||||
|
||||
if (KnownChunks.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RequestedChunks.add(key);
|
||||
KnownChunks.put(key, true);
|
||||
}
|
||||
|
||||
public static int GenerationOffset = 0;
|
||||
public static Vector3i LastPosition = new Vector3i(0, 0, 0);
|
||||
|
||||
static int IterationX = 0;
|
||||
static Vector3i ChunkPos = new Vector3i(0, 0, 0);
|
||||
static Vector3i ChunkPos2 = new Vector3i(0, 0, 0);
|
||||
|
||||
public static void GenerateChunks(Vector3i Position, Vector3i Radius) {
|
||||
int ChunkX = Position.x;
|
||||
int ChunkY = Position.y;
|
||||
int ChunkY2 = Position.y;
|
||||
int ChunkZ = Position.z;
|
||||
for (int Ry = 0; Ry <= Radius.y; Ry++)
|
||||
{
|
||||
for (int Rx = -IterationX; Rx <= IterationX; Rx++)
|
||||
{
|
||||
for (int Rz = -IterationX; Rz <= IterationX; Rz++)
|
||||
{
|
||||
ChunkX = Position.x + Rx;
|
||||
ChunkY = Position.y + Ry;
|
||||
ChunkY2 = Position.y - Ry;
|
||||
ChunkZ = Position.z + Rz;
|
||||
if (Rx == -IterationX || Rz == -IterationX || Rx == IterationX || Rz == IterationX)
|
||||
{
|
||||
GenerateChunk(ChunkPos.set(ChunkX, ChunkY, ChunkZ));
|
||||
GenerateChunk(ChunkPos2.set(ChunkX, ChunkY2, ChunkZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
IterationX++;
|
||||
if (IterationX > Radius.x || Position.x != LastPosition.x || Position.y != LastPosition.y || Position.z != LastPosition.z)
|
||||
{
|
||||
IterationX = 0;
|
||||
}
|
||||
LastPosition.set(Position);
|
||||
}
|
||||
|
||||
public static void RecordGeneration(VulkanContext VkCtx, CommandBuffer commandBuffer) {
|
||||
if (Resources == null) {
|
||||
Init(VkCtx);
|
||||
}
|
||||
|
||||
int generatedThisFrame = 0;
|
||||
|
||||
while (!RequestedChunks.isEmpty() && generatedThisFrame < MAX_CHUNK_GENERATIONS_PER_FRAME) {
|
||||
Vector3i position = RequestedChunks.poll();
|
||||
|
||||
if (position == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// VoxelChunkVisibility visibility = VoxelTerrainHeightSampler.classifyChunk(
|
||||
// position.x,
|
||||
// position.y,
|
||||
// position.z,
|
||||
// VoxelChunkGenerator.CHUNK_SIZE
|
||||
// );
|
||||
//
|
||||
// if (visibility != VoxelChunkVisibility.SURFACE) {
|
||||
// CulledChunks.put(new Vector3i(position), visibility);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
RenderChunk renderChunk = Resources.meshPool().allocate(position);
|
||||
|
||||
if (renderChunk == null) {
|
||||
Logger.warn("Voxel mesh pool full. Could not allocate chunk {}", position);
|
||||
RequestedChunks.add(position);
|
||||
break;
|
||||
}
|
||||
|
||||
Generator.recordGenerateChunk(VkCtx, commandBuffer, renderChunk);
|
||||
RenderChunks.put(new Vector3i(position), renderChunk);
|
||||
|
||||
generatedThisFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RecordRender(VkCommandBuffer CommandBuffer,Pipeline graphicsPipeline, MaterialsCache materialsCache) {
|
||||
if (Resources == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int terrainMaterialIndex = materialsCache.GetPosition("VoxelTerrain");
|
||||
|
||||
if (terrainMaterialIndex < 0) {
|
||||
terrainMaterialIndex = 0;
|
||||
}
|
||||
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
Matrix4f identity = new Matrix4f().identity();
|
||||
|
||||
identity.get(0, GraphicsPushConstants);
|
||||
GraphicsPushConstants.putInt(
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE,
|
||||
terrainMaterialIndex
|
||||
);
|
||||
|
||||
vkCmdPushConstants(CommandBuffer, graphicsPipeline.GetVulkanPipelineLayout(),VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0,GraphicsPushConstants.slice(
|
||||
0,
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE));
|
||||
|
||||
vkCmdPushConstants(CommandBuffer,graphicsPipeline.GetVulkanPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE,
|
||||
GraphicsPushConstants.slice(
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.MATRIX4X4_SIZE,
|
||||
net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils.INT_SIZE ) );
|
||||
|
||||
LongBuffer vertexBuffers = stack.longs(Resources.meshPool().vertexPoolHandle());
|
||||
LongBuffer offsets = stack.longs(0L);
|
||||
|
||||
vkCmdBindVertexBuffers(CommandBuffer, 0, vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(CommandBuffer, Resources.meshPool().indexPoolHandle(), 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
RenderChunks.forEach((position, chunk) -> vkCmdDrawIndexedIndirect(CommandBuffer,Resources.meshPool().indirectPoolHandle(),
|
||||
chunk.indirectCommandOffsetBytes(), 1,VoxelMeshPool.DRAW_INDEXED_INDIRECT_COMMAND_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnloadFarChunks(Vector3i centerChunk, int unloadRadius) {
|
||||
int maxDistSq = unloadRadius * unloadRadius;
|
||||
|
||||
RenderChunks.entrySet().removeIf(entry -> {
|
||||
Vector3i pos = entry.getKey();
|
||||
|
||||
int dx = pos.x - centerChunk.x;
|
||||
int dy = pos.y - centerChunk.y;
|
||||
int dz = pos.z - centerChunk.z;
|
||||
|
||||
int distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq > maxDistSq) {
|
||||
RenderChunk chunk = entry.getValue();
|
||||
|
||||
Resources.meshPool().free(chunk);
|
||||
KnownChunks.remove(pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
CulledChunks.entrySet().removeIf(entry -> {
|
||||
Vector3i pos = entry.getKey();
|
||||
|
||||
int dx = pos.x - centerChunk.x;
|
||||
int dy = pos.y - centerChunk.y;
|
||||
int dz = pos.z - centerChunk.z;
|
||||
|
||||
int distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq > maxDistSq) {
|
||||
KnownChunks.remove(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
RequestedChunks.removeIf(pos -> {
|
||||
int dx = pos.x - centerChunk.x;
|
||||
int dy = pos.y - centerChunk.y;
|
||||
int dz = pos.z - centerChunk.z;
|
||||
|
||||
int distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq > maxDistSq) {
|
||||
KnownChunks.remove(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public static int GetRenderableChunkCount() {
|
||||
return RenderChunks.size();
|
||||
}
|
||||
|
||||
public static int GetCulledChunkCount() {
|
||||
return CulledChunks.size();
|
||||
}
|
||||
|
||||
public static int GetRequestedChunkCount() {
|
||||
return RequestedChunks.size();
|
||||
}
|
||||
|
||||
public static int GetFreeMeshSlots() {
|
||||
if (Resources == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Resources.meshPool().freeSlotCount();
|
||||
}
|
||||
|
||||
|
||||
public static void Cleanup(VulkanContext VkCtx) {
|
||||
if (Resources != null) {
|
||||
Resources.cleanup(VkCtx);
|
||||
Resources = null;
|
||||
}
|
||||
|
||||
if (GraphicsPushConstants != null) {
|
||||
org.lwjgl.system.MemoryUtil.memFree(GraphicsPushConstants);
|
||||
GraphicsPushConstants = null;
|
||||
}
|
||||
|
||||
RequestedChunks.clear();
|
||||
RenderChunks.clear();
|
||||
KnownChunks.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VulkanUtil;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Pipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.PushConstantsRange;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.DescriptorSetLayout;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Shader.ShaderModule;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DeviceLayers.Device;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Util.VulkanUtils;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.vulkan.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
|
||||
import static org.lwjgl.vulkan.VK10.*;
|
||||
|
||||
public class ComputePipeline implements Pipeline {
|
||||
|
||||
private final long VulkanPipeline;
|
||||
private final long VulkanPipelineLayout;
|
||||
|
||||
public ComputePipeline(VulkanContext VkCtx,ShaderModule shaderModule, DescriptorSetLayout[] descriptorSetLayouts,PushConstantsRange[] pushConstantsRanges) {
|
||||
Device device = VkCtx.GetDevice();
|
||||
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
LongBuffer pPipelineLayout = stack.mallocLong(1);
|
||||
LongBuffer pPipeline = stack.mallocLong(1);
|
||||
int layoutCount = descriptorSetLayouts != null ? descriptorSetLayouts.length : 0;
|
||||
LongBuffer setLayouts = stack.mallocLong(layoutCount);
|
||||
for (int i = 0; i < layoutCount; i++) {
|
||||
setLayouts.put(i, descriptorSetLayouts[i].GetVkDescriptorLayout());
|
||||
}
|
||||
|
||||
VkPushConstantRange.Buffer pushRanges = null;
|
||||
int pushRangeCount = pushConstantsRanges != null ? pushConstantsRanges.length : 0;
|
||||
if (pushRangeCount > 0) {
|
||||
pushRanges = VkPushConstantRange.calloc(pushRangeCount, stack);
|
||||
|
||||
for (int i = 0; i < pushRangeCount; i++) {
|
||||
PushConstantsRange range = pushConstantsRanges[i];
|
||||
|
||||
pushRanges.get(i)
|
||||
.stageFlags(range.Stage())
|
||||
.offset(range.Offset())
|
||||
.size(range.Size());
|
||||
}
|
||||
}
|
||||
VkPipelineLayoutCreateInfo layoutInfo = VkPipelineLayoutCreateInfo.calloc(stack)
|
||||
.sType$Default()
|
||||
.pSetLayouts(setLayouts)
|
||||
.pPushConstantRanges(pushRanges);
|
||||
|
||||
VulkanUtils.vkCheck(
|
||||
vkCreatePipelineLayout(device.FetchVulkanDevice(), layoutInfo, null, pPipelineLayout),
|
||||
"Unable to create compute pipeline layout"
|
||||
);
|
||||
VulkanPipelineLayout = pPipelineLayout.get(0);
|
||||
ByteBuffer main = stack.UTF8("main");
|
||||
VkPipelineShaderStageCreateInfo shaderStage = VkPipelineShaderStageCreateInfo.calloc(stack)
|
||||
.sType$Default()
|
||||
.stage(VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.module(shaderModule.GetHandle())
|
||||
.pName(main);
|
||||
|
||||
VkComputePipelineCreateInfo.Buffer pipelineInfo = VkComputePipelineCreateInfo.calloc(1, stack)
|
||||
.sType$Default()
|
||||
.stage(shaderStage)
|
||||
.layout(VulkanPipelineLayout);
|
||||
|
||||
VulkanUtils.vkCheck(vkCreateComputePipelines(device.FetchVulkanDevice(),VkCtx.GetVkPipelineCache().GetVkPipelineCache(),
|
||||
pipelineInfo,null,pPipeline),"Unable to create compute pipeline" );
|
||||
|
||||
VulkanPipeline = pPipeline.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long GetVulkanPipeline() {
|
||||
return VulkanPipeline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long GetVulkanPipelineLayout() {
|
||||
return VulkanPipelineLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void CleanUp(VulkanContext VkCtx) {
|
||||
VkDevice device = VkCtx.GetDevice().FetchVulkanDevice();
|
||||
vkDestroyPipeline(device, VulkanPipeline, null);
|
||||
vkDestroyPipelineLayout(device, VulkanPipelineLayout, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
|||
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3DElement;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ImageView;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.SceneRenderer;
|
||||
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
|
||||
|
|
@ -379,6 +380,8 @@ public class DeferredSceneRender implements SceneRenderer {//dynamic rendering
|
|||
|
||||
RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, false);
|
||||
|
||||
VoxelWorldManager.RecordRender(CommandHandle, DualPassRendering ? VkPipelineOpaque : VkPipeline,materialsCache);
|
||||
|
||||
Matrix4f skyboxViewMatrix = new Matrix4f().identity();
|
||||
skyboxViewMatrix.set(engineInstance.scene().GetCamera().GetViewMatrix());
|
||||
skyboxViewMatrix.m30(0.0f);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
|
|||
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3DElement;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.RenderPasses.DeferredRendering.DeferredSceneRender;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ImageView;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.SceneRenderer;
|
||||
|
|
@ -363,6 +364,7 @@ public class ForwardSceneRender implements SceneRenderer {//dynamic rendering
|
|||
.put(3,descriptorAllocator.GetDescriptorSet(DESCRIPTOR_ID_TEXT).GetVkDescriptorSet());
|
||||
vkCmdBindDescriptorSets(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, DualPassRendering ? VkPipelineOpaque.GetVulkanPipelineLayout() : VkPipeline.GetVulkanPipelineLayout(),0,DescriptorSets,null);
|
||||
RenderActors(engineInstance, CommandHandle, modelsCache, materialsCache, false);
|
||||
VoxelWorldManager.RecordRender(CommandHandle, DualPassRendering ? VkPipelineOpaque : VkPipeline,materialsCache);
|
||||
|
||||
Matrix4f skyboxViewMatrix = new Matrix4f().identity();
|
||||
skyboxViewMatrix.set(engineInstance.scene().GetCamera().GetViewMatrix());
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
|
|||
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor.Actor3D;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene.SceneLightingManager;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Computing.VoxelTerrainGeneration.VoxelWorldManager;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.Attachment;
|
||||
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.ITexture;
|
||||
|
|
@ -393,7 +394,7 @@ public class ShadowRenderer {
|
|||
vkCmdDrawIndexed(cmdHandle, vulkanMesh.IndicesCount(), 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
VoxelWorldManager.RecordRender(cmdHandle,pipeline,materialsCache);
|
||||
vkCmdEndRendering(cmdHandle);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public class DescriptorSet {
|
|||
.sType$Default()
|
||||
.dstSet(VkDescriptorSet)
|
||||
.dstBinding(Binding)
|
||||
.dstArrayElement(0)
|
||||
.descriptorType(Type)
|
||||
.descriptorCount(1)
|
||||
.pBufferInfo(BufferInfo);
|
||||
|
|
|
|||
|
|
@ -206,6 +206,50 @@ public class VulkanUtils {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static void BufferBarrier(MemoryStack MemStack, VkCommandBuffer CommandHandle, long BufferHandle,
|
||||
long SrcStage,long DstStage,long SrcAccess,long DstAccess){
|
||||
var BufferBarrier = VkBufferMemoryBarrier2.calloc(1, MemStack)
|
||||
.sType$Default()
|
||||
.srcStageMask(SrcStage)
|
||||
.dstStageMask(DstStage)
|
||||
.srcAccessMask(SrcAccess)
|
||||
.dstAccessMask(DstAccess)
|
||||
.srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.buffer(BufferHandle)
|
||||
.offset(0)
|
||||
.size(VK_WHOLE_SIZE);
|
||||
VkDependencyInfo DependencyInfo = VkDependencyInfo.calloc(MemStack)
|
||||
.sType$Default()
|
||||
.pBufferMemoryBarriers(BufferBarrier);
|
||||
vkCmdPipelineBarrier2(CommandHandle, DependencyInfo);
|
||||
}
|
||||
|
||||
public static void BufferBarriers(MemoryStack MemStack,VkCommandBuffer CommandHandle,long[] Buffers,
|
||||
long SrcStage,long DstStage,long SrcAccess,long DstAccess ){
|
||||
var BufferBarriers = VkBufferMemoryBarrier2.calloc(Buffers.length, MemStack);
|
||||
for (int i = 0; i < Buffers.length; i++) {
|
||||
BufferBarriers.get(i)
|
||||
.sType$Default()
|
||||
.srcStageMask(SrcStage)
|
||||
.dstStageMask(DstStage)
|
||||
.srcAccessMask(SrcAccess)
|
||||
.dstAccessMask(DstAccess)
|
||||
.srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.buffer(Buffers[i])
|
||||
.offset(0)
|
||||
.size(VK_WHOLE_SIZE);
|
||||
}
|
||||
VkDependencyInfo DependencyInfo = VkDependencyInfo.calloc(MemStack)
|
||||
.sType$Default()
|
||||
.pBufferMemoryBarriers(BufferBarriers);
|
||||
|
||||
vkCmdPipelineBarrier2(CommandHandle, DependencyInfo);
|
||||
}
|
||||
|
||||
public static void ImageBarrier(MemoryStack MemStack, VkCommandBuffer CommandHandle, long Image, int OldLayout,
|
||||
int NewLayout, long SrcStage, long DstStage, long SrcAccess, long DstAccess,
|
||||
int AspectMask){
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue