diff --git a/resources/shaders/scene_fragment.glsl b/resources/shaders/scene_fragment.glsl index a89e0ab..01a67a4 100644 --- a/resources/shaders/scene_fragment.glsl +++ b/resources/shaders/scene_fragment.glsl @@ -1,9 +1,33 @@ #version 450 +const int MAX_TEXTURES = 100; + layout(location = 0) in vec2 inTextCoords; layout(location = 0) out vec4 outFragColor; +struct Material{ + vec4 diffuseColor; + uint hasTexture; + uint textureIdx; + uint padding[2]; +}; + +layout(set = 1, binding = 0) readonly buffer MaterialUniform{ + Material materials[]; +} matUniform; + +layout(set = 2, binding = 0) uniform sampler2D textSampler[MAX_TEXTURES]; + +layout(push_constant) uniform pc{ + layout(offset = 64) uint materialIdx; +} push_constants; + void main() { - outFragColor = vec4(inTextCoords.x, inTextCoords.y, 0, 1); + Material material = matUniform.materials[push_constants.materialIdx]; + if(material.hasTexture == 1){ + outFragColor = texture(textSampler[material.textureIdx],inTextCoords); + } else{ + outFragColor = material.diffuseColor; + } } \ No newline at end of file diff --git a/resources/shaders/scene_vertex.glsl b/resources/shaders/scene_vertex.glsl index 88901d0..610c74d 100644 --- a/resources/shaders/scene_vertex.glsl +++ b/resources/shaders/scene_vertex.glsl @@ -4,13 +4,17 @@ layout(location = 0) in vec3 inPos; layout(location = 1) in vec2 intTextCoords; layout(location = 0) out vec2 outTextCoords; -layout(push_constant) uniform matrices{ - mat4 projectionMatrix; + +layout(set = 0, binding = 0) uniform ProjUniform{ + mat4 matrix; +} projUniform; + +layout(push_constant) uniform pc{ mat4 modelMatrix; } push_constants; void main() { - gl_Position = push_constants.projectionMatrix * push_constants.modelMatrix * vec4(inPos,1); + gl_Position = projUniform.matrix * push_constants.modelMatrix * vec4(inPos,1); outTextCoords = intTextCoords; } \ No newline at end of file diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/EngineConfig.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/EngineConfig.java index e8c4a38..aa19cfe 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/EngineConfig.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/EngineConfig.java @@ -44,6 +44,7 @@ public class EngineConfig { private String PhysicalDeviceName; private int RequestedImages; private String IconPath = "/WindowResources/Icon/"; + private String DefaultTexturePath = "/EngineResources/Texture/DefaultTexture.png"; private String IconName = "ProgramIcon.png"; private float FOV = 60f; private float zFarPlane; @@ -62,7 +63,6 @@ public class EngineConfig { boolean SuccessfulLoad = false; try(InputStream stream = new FileInputStream(path.toAbsolutePath().toString() + "/" + FILENAME)){ try { - EngineConfigVar.load(stream); SuccessfulLoad = true; Logger.debug("File [{}] read", FILENAME); @@ -116,6 +116,7 @@ public class EngineConfig { FOV = (float)(DegToRad * Float.parseFloat(EngineConfigVar.getOrDefault("field_of_view", 60.0f).toString())); zNearPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_near_plane", 1.0f).toString())); zFarPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_far_plane", 100.0f).toString())); + DefaultTexturePath = EngineConfigVar.getOrDefault("DefaultTexturePath","/EngineResources/Texture/DefaultTexture.png").toString(); Logger.debug("\n\nsuccessfully loaded configuration: \n{}\n\n",EngineConfigVar.toString()); } else if(!ConfigFile.exists()){ @@ -142,6 +143,7 @@ public class EngineConfig { EngineConfigVar.setProperty("field_of_view","60"); EngineConfigVar.setProperty("z_near_plane","1"); EngineConfigVar.setProperty("z_far_plane","100"); + EngineConfigVar.setProperty("DefaultTexturePath",DefaultTexturePath); EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file"); Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath()); } catch (IOException excp2) { diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/GameLogic.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/GameLogic.java index 7d97549..7acccd2 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/GameLogic.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/GameLogic.java @@ -1,6 +1,6 @@ package net.halbear.Terrain4J.EngineCore.Logic; -public interface GameLogic { +public interface GameLogic { void cleanup(); InitData Initialise(EngineInstance engineInstance); void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/Rendering/VulkanContext.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/Rendering/VulkanContext.java index 6fbd9da..f432681 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/Rendering/VulkanContext.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Logic/Rendering/VulkanContext.java @@ -3,6 +3,7 @@ package net.halbear.Terrain4J.EngineCore.Logic.Rendering; import net.halbear.Terrain4J.EngineCore.Display.Window; import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineCache; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.DescriptorAllocator; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.Surface; @@ -18,6 +19,7 @@ public class VulkanContext { private final PhysicalDevice PhysDevice; private SwapChain swapChain; private final PipelineCache VkPipelineCache; + private final DescriptorAllocator descriptorAllocator; public VulkanContext(Window window){ var EngineGlobals = EngineConfig.getInstance(); @@ -28,6 +30,7 @@ public class VulkanContext { swapChain = new SwapChain(window, device, surface, EngineGlobals.GetRequestedImages(), EngineGlobals.GetVSYNC()); VulkanLayersOpen+=5; VkPipelineCache = new PipelineCache(device); + descriptorAllocator = new DescriptorAllocator(PhysDevice, device); } public void Resize(Window window){ swapChain.cleanup(device); @@ -37,6 +40,7 @@ public class VulkanContext { swapChain = new SwapChain(window, device, surface, EngCfg.GetRequestedImages(),EngCfg.GetVSYNC()); } public void cleanup(){ + descriptorAllocator.CleanUp(device); swapChain.cleanup(device); surface.cleanup(Instance); VkPipelineCache.CleanUp(device); @@ -57,4 +61,5 @@ public class VulkanContext { public Surface GetSurface(){ return surface; } + public DescriptorAllocator GetDescriptorAllocator(){return descriptorAllocator;} } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Attachment.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Attachment.java index ebeb57c..8da0e31 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Attachment.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Attachment.java @@ -24,7 +24,7 @@ public class Attachment { DepthAttachment = true; } var ImageViewData = new ImageView.ImageViewData().Format(VkImage.GetFormat()).AspectMask(AspectMask); - VkImageView = new ImageView(VkCtx.GetDevice(), VkImage.getVulkanImage(), ImageViewData); + VkImageView = new ImageView(VkCtx.GetDevice(), VkImage.getVulkanImage(), ImageViewData,false); } public Image GetVkImage(){return VkImage;} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Texture.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Texture.java index 3211126..22a2714 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Texture.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/Texture.java @@ -32,11 +32,11 @@ public class Texture { CreateStgBuffer(VkCtx, imageSrc.data()); var ImageData = new Image.ImageData().Width(Width).Height(Height) - .usage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT) - .format(ImageFormat); + .Usage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT) + .Format(ImageFormat); image = new Image(VkCtx, ImageData); - var ImageViewData = new ImageView.ImageViewData().format(image.GetFormat()) - .aspectMask(VK_IMAGE_ASPECT_COLOR_BIT); + var ImageViewData = new ImageView.ImageViewData().Format(image.GetFormat()) + .AspectMask(VK_IMAGE_ASPECT_COLOR_BIT); imageView = new ImageView(VkCtx.GetDevice(), image.getVulkanImage(), ImageViewData, false); } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/TextureCache.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/TextureCache.java new file mode 100644 index 0000000..be6b8f8 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Images/TextureCache.java @@ -0,0 +1,86 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images; + +import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.GraphUtils; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.IndexedLinkedHashMap; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandPool; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue; +import org.tinylog.Logger; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.lwjgl.vulkan.VK10.VK_FORMAT_R8G8B8A8_SRGB; + +public class TextureCache { + public static final int MAX_TEXTURES = 100; + private final IndexedLinkedHashMap TextureMap; + + public TextureCache(){ + TextureMap = new IndexedLinkedHashMap<>(); + } + + public Texture AddTexture(VulkanContext VkCtx, String ID, ImageSrc imageSrc, int Format){ + if(TextureMap.size() > MAX_TEXTURES){ + throw new IllegalArgumentException("Texture Cache Is Full"); + } + Texture newTexture = TextureMap.get(ID); + if(newTexture == null){ + newTexture = new Texture(VkCtx, ID, imageSrc, Format); + TextureMap.put(ID, newTexture); + } + return newTexture; + } + public Texture AddTexture(VulkanContext VkCtx, String ID, String TexturePath, int Format) { + ImageSrc imageSrc = null; + Texture result = null; + try{ + imageSrc = GraphUtils.LoadImage(TexturePath); + result = AddTexture(VkCtx, ID, imageSrc, Format); + } catch (IOException exception){ + Logger.error("Could not load texture from patch [{}], exception: {}",TexturePath, exception); + } finally{ + if(imageSrc != null){ + GraphUtils.CleanImageData(imageSrc); + } + } + return result; + } + + public void TransitionTexts(VulkanContext VkCtx, CommandPool CmdPool, Queue queue){ + Logger.debug("Recording Texture Transition"); + int TextureCount = TextureMap.size(); + if(TextureCount < MAX_TEXTURES){ + int PaddingTextCount = MAX_TEXTURES - TextureCount; + String DefaultTexturePath = EngineConfig.getInstance().GetDefaultTexturePath(); + for(int i = 0; i < PaddingTextCount; i++){ + AddTexture(VkCtx, UUID.randomUUID().toString(), DefaultTexturePath, VK_FORMAT_R8G8B8A8_SRGB); + } + } + var CommandBuffer = new CommandBuffer(VkCtx, CmdPool, true, true); + CommandBuffer.BeginRecording(); + TextureMap.forEach((key,value)->value.RecordTextureTransition(CommandBuffer)); + CommandBuffer.EndRecording(); + CommandBuffer.SubmitAndWait(VkCtx, queue); + CommandBuffer.cleanup(VkCtx, CmdPool); + TextureMap.forEach((key,value)->value.CleanUpStgBuffer(VkCtx)); + Logger.debug("Recorded Texture Transition"); + } + + public List GetTextureList(){return new ArrayList<>(TextureMap.values());} + public int GetPosition(String ID){ + int result = -1; + if(ID != null){ + result = TextureMap.GetIndexOf(ID); + } + return result; + } + public void CleanUp(VulkanContext VkCtx){ + TextureMap.forEach((k,t)->t.CleanUp(VkCtx)); + TextureMap.clear(); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/IndexedLinkedHashMap.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/IndexedLinkedHashMap.java new file mode 100644 index 0000000..b02c82c --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/IndexedLinkedHashMap.java @@ -0,0 +1,23 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +public class IndexedLinkedHashMap extends LinkedHashMap { + private final List IndexList = new ArrayList<>(); + + public int GetIndexOf(K key){ + return IndexList.indexOf(key); + } + + public V GetValueAtIndex(int i){ + return super.get(IndexList.get(i)); + } + + @Override + public V put(K key, V value){ + if(!super.containsKey(key)) IndexList.add(key); + return super.put(key,value); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Pipeline.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Pipeline.java index dc5debb..b7c01f0 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Pipeline.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/Pipeline.java @@ -1,6 +1,7 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline; import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.DescriptorSetLayout; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; @@ -99,8 +100,17 @@ public class Pipeline { .size(pushConstantsRange.Size()); } } + + DescriptorSetLayout[] descriptorSetLayouts = BuildInfo.GetDescriptorSetLayouts(); + int LayoutCount = descriptorSetLayouts != null ? descriptorSetLayouts.length : 0; + LongBuffer ppLayout = MemStack.mallocLong(LayoutCount); + for(int i = 0; i < LayoutCount; i++){ + ppLayout.put(i, descriptorSetLayouts[i].GetVkDescriptorLayout()); + } + var PipelineLayoutCreateInfoPtr = VkPipelineLayoutCreateInfo.calloc(MemStack) .sType$Default() + .pSetLayouts(ppLayout) .pPushConstantRanges(VkPushConstRangeBuffer); VulkanUtils.vkCheck(vkCreatePipelineLayout(device.FetchVulkanDevice(), PipelineLayoutCreateInfoPtr,null,longPtr) diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/PipelineBuildInfo.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/PipelineBuildInfo.java index 382171e..5ce3a30 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/PipelineBuildInfo.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Pipeline/PipelineBuildInfo.java @@ -1,5 +1,6 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.DescriptorSetLayout; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule; import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo; import org.lwjgl.vulkan.VkPushConstantRange; @@ -12,6 +13,7 @@ public class PipelineBuildInfo { private final VkPipelineVertexInputStateCreateInfo VertexInput; private int DepthFormat; private PushConstantsRange[] PushConstRanges; + private DescriptorSetLayout[] DescriptorSetLayouts; public PipelineBuildInfo(ShaderModule[] shaderModules, VkPipelineVertexInputStateCreateInfo VertexInput, int ColourFormat){ this.ColourFormat = ColourFormat; @@ -20,6 +22,13 @@ public class PipelineBuildInfo { DepthFormat = VK_FORMAT_UNDEFINED; } + public DescriptorSetLayout[] GetDescriptorSetLayouts(){return DescriptorSetLayouts;} + + public PipelineBuildInfo SetDescriptorSetLayouts(DescriptorSetLayout[] descriptorSetLayouts){ + this.DescriptorSetLayouts = descriptorSetLayouts; + return this; + } + public PipelineBuildInfo SetDepthFormat(int DepthFormat){ this.DepthFormat = DepthFormat; return this; diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorAllocator.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorAllocator.java new file mode 100644 index 0000000..e4aa909 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorAllocator.java @@ -0,0 +1,132 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice; +import org.lwjgl.vulkan.VkPhysicalDeviceFeatures2; +import org.lwjgl.vulkan.VkPhysicalDeviceLimits; +import org.tinylog.Logger; + +import java.util.*; + +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorAllocator { + private final Map DescriptorLimits; + private final List DescriptorPoolList; + private final Map DescriptorSetInfoMap; + + public DescriptorAllocator(PhysicalDevice physicalDevice, Device device){ + Logger.debug("Creating Descriptor Allocator"); + DescriptorPoolList = new ArrayList<>(); + DescriptorLimits = CreateDescriptorLimits(physicalDevice); + DescriptorPoolList.add(CreateDescriptorPoolInformation(device, DescriptorLimits)); + DescriptorSetInfoMap = new HashMap<>(); + } + + public synchronized DescriptorSet AddDescriptorSet(Device device, String ID, DescriptorSetLayout descriptorSetLayout){ + return AddDescriptorSets(device, ID, 1, descriptorSetLayout)[0]; + } + public synchronized DescriptorSet[] AddDescriptorSets(Device device, String ID, int count, DescriptorSetLayout descriptorSetLayout){ + DescriptorPoolInfo TargetPool = null; + int PoolPosition = 0; + for(DescriptorPoolInfo descriptorPoolInfo : DescriptorPoolList){ + for(DescriptorSetLayout.LayoutInformation LayoutInfo : descriptorSetLayout.GetLayoutInfos()){ + int DescriptorType = LayoutInfo.DescriptorType(); + Integer Available = descriptorPoolInfo.DescriptorCount.get(DescriptorType); + if(Available == null){ + throw new RuntimeException("Unknown Type [" + DescriptorType + "]"); + } + Integer MaxTotal = DescriptorLimits.get(DescriptorType); + if(count > MaxTotal){ + throw new RuntimeException("Cannot Create More than ["+MaxTotal+"] for descriptor type [" + DescriptorType + "]"); + } + if(Available < count){ + TargetPool = null; + break; + } else{ + TargetPool = descriptorPoolInfo; + } + + } + PoolPosition++; + } + if(TargetPool == null){ + TargetPool = CreateDescriptorPoolInformation(device, DescriptorLimits); + DescriptorPoolList.add(TargetPool); + PoolPosition++; + } + var Result = new DescriptorSet[count]; + for(int i = 0; i < count; i++){ + DescriptorSet descriptorSet = new DescriptorSet(device, TargetPool.descriptorPool(), descriptorSetLayout); + Result[i] = descriptorSet; + } + DescriptorSetInfoMap.put(ID, new DescriptorSetInfo(Result, PoolPosition)); + + for(DescriptorSetLayout.LayoutInformation LayoutInfo : descriptorSetLayout.GetLayoutInfos()){ + int DescriptorType = LayoutInfo.DescriptorType(); + TargetPool.DescriptorCount.put(DescriptorType,TargetPool.DescriptorCount.get(DescriptorType) - count); + } + return Result; + } + + private static DescriptorPoolInfo CreateDescriptorPoolInformation(Device device, MapDescriptorLimits){ + Map DescriptorCount = new HashMap<>(); + List DescriptorTypeCounts = new ArrayList<>(); + DescriptorLimits.forEach((key,value)->{ + DescriptorCount.put(key,value); + DescriptorTypeCounts.add(new DescriptorPool.DescriptorTypeCount(key,value)); + }); + var DescriptorPool = new DescriptorPool(device, DescriptorTypeCounts); + return new DescriptorPoolInfo(DescriptorCount, DescriptorPool); + } + + private static Map CreateDescriptorLimits(PhysicalDevice physicalDevice){ + var EngConfig = EngineConfig.getInstance(); + int MaxDescriptors = EngConfig.GetMaxDescriptors(); + VkPhysicalDeviceLimits limits = physicalDevice.GetPhysicalDeviceProperties().properties().limits(); + Map DescriptorLimits = new HashMap<>(); + DescriptorLimits.put(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (int)Math.min(MaxDescriptors, Integer.toUnsignedLong(limits.maxDescriptorSetUniformBuffers()))); + DescriptorLimits.put(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, (int)Math.min(MaxDescriptors, Integer.toUnsignedLong(limits.maxDescriptorSetSamplers()))); + DescriptorLimits.put(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, (int)Math.min(MaxDescriptors, Integer.toUnsignedLong(limits.maxDescriptorSetStorageBuffers()))); + return DescriptorLimits; + } + + + public synchronized void FreeDescriptorSet(Device device, String ID){ + DescriptorSetInfo descriptorSetInfo = DescriptorSetInfoMap.get(ID); + if(descriptorSetInfo == null){ + Logger.info("Descriptor Set of ID [{}} does not exist",ID); + return; + } + if(descriptorSetInfo.PoolPosition >= DescriptorPoolList.size()){ + Logger.info("Could not locate a pool associated with Descriptor Set ID [{}]",ID); + return; + } + DescriptorPoolInfo descriptorPoolInfo = DescriptorPoolList.get(descriptorSetInfo.PoolPosition); + Arrays.asList(descriptorSetInfo.DescriptorSets()).forEach(descriptorSet -> + descriptorPoolInfo.descriptorPool.FreeDescriptorSet(device, descriptorSet.GetVkDescriptorSet())); + } + + public synchronized DescriptorSet GetDescriptorSet(String ID, int Position){ + DescriptorSet Result = null; + DescriptorSetInfo descriptorSetInfo = DescriptorSetInfoMap.get(ID); + if(descriptorSetInfo != null){ + Result = descriptorSetInfo.DescriptorSets()[Position]; + } + return Result; + } + + public synchronized DescriptorSet GetDescriptorSet(String ID){ + return GetDescriptorSet(ID, 0); + } + + record DescriptorPoolInfo(MapDescriptorCount, DescriptorPool descriptorPool){} + record DescriptorSetInfo(DescriptorSet[] DescriptorSets, int PoolPosition){} + + public synchronized void CleanUp(Device device){ + Logger.debug("Destroying Descriptor Allocator"); + DescriptorSetInfoMap.clear(); + DescriptorPoolList.forEach(descriptor -> descriptor.descriptorPool.CleanUp(device)); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorPool.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorPool.java new file mode 100644 index 0000000..c229427 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorPool.java @@ -0,0 +1,60 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorPoolCreateInfo; +import org.lwjgl.vulkan.VkDescriptorPoolSize; +import org.tinylog.Logger; + +import java.nio.LongBuffer; +import java.util.List; + +import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorPool { + private final long VkDescriptorPool; + private List DescriptorTypeCounts; + + public DescriptorPool(Device device, List DescriptorTypeCounts){ + Logger.debug("Creating Descriptor pool"); + this.DescriptorTypeCounts = DescriptorTypeCounts; + try(var MemStack = MemoryStack.stackPush()){ + int MaxSets = 0; + int TypeNum = DescriptorTypeCounts.size(); + var TypeCounts = VkDescriptorPoolSize.calloc(TypeNum,MemStack); + for(int i = 0; i < TypeNum; i++){ + MaxSets+= DescriptorTypeCounts.get(i).Count(); + TypeCounts.get(i) + .type(DescriptorTypeCounts.get(i).DescriptorType()) + .descriptorCount(DescriptorTypeCounts.get(i).Count()); + } + var DescriptorPoolInfo = VkDescriptorPoolCreateInfo.calloc(MemStack) + .sType$Default() + .flags(VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT) + .pPoolSizes(TypeCounts) + .maxSets(MaxSets); + LongBuffer DescriptorPoolPointer = MemStack.mallocLong(1); + vkCheck(vkCreateDescriptorPool(device.FetchVulkanDevice(), DescriptorPoolInfo, null, DescriptorPoolPointer) + ,"Failed to create descriptor pool"); + VkDescriptorPool = DescriptorPoolPointer.get(0); + } + } + + public void FreeDescriptorSet(Device device, Long VkDescriptorSet){ + try(var MemStack = MemoryStack.stackPush()){ + LongBuffer longBuffer = MemStack.mallocLong(1); + longBuffer.put(0,VkDescriptorSet); + vkCheck(vkFreeDescriptorSets(device.FetchVulkanDevice(), this.VkDescriptorPool, longBuffer),"Failed to free Descriptor Set"); + } + } + + public List GetDescriptorTypeCounts(){return DescriptorTypeCounts;} + public long GetVkDescriptorPool(){return VkDescriptorPool;} + public void CleanUp(Device device){ + Logger.debug("Destroying Descriptor Pool"); + vkDestroyDescriptorPool(device.FetchVulkanDevice(), VkDescriptorPool, null); + } + + public record DescriptorTypeCount(int DescriptorType, int Count){} +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSet.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSet.java new file mode 100644 index 0000000..b08208b --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSet.java @@ -0,0 +1,109 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.ImageView; +import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorBufferInfo; +import org.lwjgl.vulkan.VkDescriptorImageInfo; +import org.lwjgl.vulkan.VkDescriptorSetAllocateInfo; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +import java.nio.LongBuffer; +import java.util.List; + +import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorSet { + protected long VkDescriptorSet; + + public DescriptorSet(Device device,DescriptorPool descriptorPool, DescriptorSetLayout descriptorSetLayout){ + try(var MemStack = MemoryStack.stackPush()){ + LongBuffer DescriptorSetLayoutPointer = MemStack.mallocLong(1); + DescriptorSetLayoutPointer.put(0,descriptorSetLayout.GetVkDescriptorLayout()); + var AllocInfo = VkDescriptorSetAllocateInfo.calloc(MemStack) + .sType$Default() + .descriptorPool(descriptorPool.GetVkDescriptorPool()) + .pSetLayouts(DescriptorSetLayoutPointer); + LongBuffer DescriptorSetPointer = MemStack.mallocLong(1); + vkCheck(vkAllocateDescriptorSets(device.FetchVulkanDevice(), AllocInfo, DescriptorSetPointer) + ,"Failed to create descriptor sets"); + VkDescriptorSet = DescriptorSetPointer.get(0); + } + } + + public void SetBuffer(Device device, VulkanBuffer VkBuffer, long Range, int Binding, int Type){ + try(var MemStack = MemoryStack.stackPush()){ + var BufferInfo = VkDescriptorBufferInfo.calloc(1,MemStack) + .buffer(VkBuffer.GetBuffer()) + .offset(0) + .range(Range); + var DescriptorBuffer = VkWriteDescriptorSet.calloc(1,MemStack); + DescriptorBuffer.get(0) + .sType$Default() + .dstSet(VkDescriptorSet) + .dstBinding(Binding) + .descriptorCount(1) + .pBufferInfo(BufferInfo); + vkUpdateDescriptorSets(device.FetchVulkanDevice(),DescriptorBuffer,null); + } + } + + public void SetImage(Device device, List imageViews, TextureSampler textureSampler, int BaseBinding){ + try(var MemStack = MemoryStack.stackPush()){ + int ImageCount = imageViews.size(); + var DescriptorBuffer = VkWriteDescriptorSet.calloc(ImageCount,MemStack); + for(int i = 0; i < ImageCount; i++){ + ImageView imageView = imageViews.get(i); + var ImageInfo = VkDescriptorImageInfo.calloc(1,MemStack) + .imageView(imageView.GetVulkanImageView()) + .sampler(textureSampler.GetVkSampler()); + if(imageView.IsDepthImage()){ + ImageInfo.imageLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + } else{ + ImageInfo.imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + DescriptorBuffer.get(i) + .sType$Default() + .dstSet(VkDescriptorSet) + .dstBinding(BaseBinding + i) + .descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + .descriptorCount(1) + .pImageInfo(ImageInfo); + } + vkUpdateDescriptorSets(device.FetchVulkanDevice(),DescriptorBuffer,null); + } + } + + public void SetImageArray(Device device, List imageViews, TextureSampler textureSampler, int BaseBinding){ + try(var MemStack = MemoryStack.stackPush()){ + int ImageCount = imageViews.size(); + VkDescriptorImageInfo.Buffer imageInfos = VkDescriptorImageInfo.calloc(ImageCount,MemStack); + for(int i = 0; i < ImageCount; i++){ + ImageView imageView = imageViews.get(i); + VkDescriptorImageInfo imageInfo = imageInfos.get(i); + imageInfo.imageView(imageView.GetVulkanImageView()).sampler(textureSampler.GetVkSampler()); + + if(imageView.IsDepthImage()){ + imageInfo.imageLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + } else{ + imageInfo.imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + } + + var DescriptorBuffer = VkWriteDescriptorSet.calloc(1,MemStack); + DescriptorBuffer.get(0) + .sType$Default() + .dstSet(VkDescriptorSet) + .dstBinding(BaseBinding) + .dstArrayElement(0) + .descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + .descriptorCount(ImageCount) + .pImageInfo(imageInfos); + vkUpdateDescriptorSets(device.FetchVulkanDevice(),DescriptorBuffer,null); + } + } + + public long GetVkDescriptorSet(){return VkDescriptorSet;} +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSetLayout.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSetLayout.java new file mode 100644 index 0000000..927c448 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/DescriptorSetLayout.java @@ -0,0 +1,53 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorSetLayoutBinding; +import org.lwjgl.vulkan.VkDescriptorSetLayoutCreateInfo; +import org.tinylog.Logger; + +import java.nio.LongBuffer; + +import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.VK10.vkCreateDescriptorSetLayout; +import static org.lwjgl.vulkan.VK10.vkDestroyDescriptorSetLayout; + +public class DescriptorSetLayout { + private final LayoutInformation[] LayoutInfos; + protected long VkDescriptorLayout; + public DescriptorSetLayout(VulkanContext VkCtx, LayoutInformation LayoutInfo){ + this(VkCtx, new DescriptorSetLayout.LayoutInformation[]{LayoutInfo}); + } + public DescriptorSetLayout(VulkanContext VkCtx, LayoutInformation[] LayoutInfos){ + this.LayoutInfos = LayoutInfos; + try(var MemStack = MemoryStack.stackPush()){ + int LayoutCount = LayoutInfos.length; + var LayoutBindings = VkDescriptorSetLayoutBinding.calloc(LayoutCount,MemStack); + for(int i = 0; i < LayoutCount; i++){ + LayoutInformation LayoutInfo = LayoutInfos[i]; + LayoutBindings.get(i) + .binding(LayoutInfo.Binding()) + .descriptorType(LayoutInfo.DescriptorType()) + .descriptorCount(LayoutInfo.DescriptorCount()) + .stageFlags(LayoutInfo.Stage()); + } + var VkLayoutInfo = VkDescriptorSetLayoutCreateInfo.calloc(MemStack) + .sType$Default() + .pBindings(LayoutBindings); + LongBuffer SetLayoutPointer = MemStack.mallocLong(1); + vkCheck(vkCreateDescriptorSetLayout(VkCtx.GetDevice().FetchVulkanDevice(), VkLayoutInfo,null,SetLayoutPointer), + "Failed to create Descriptor Set Layout"); + VkDescriptorLayout = SetLayoutPointer.get(0); + } + } + + public LayoutInformation GetLayoutInfo(){return GetLayoutInfos()[0];} + public LayoutInformation[] GetLayoutInfos(){return LayoutInfos;} + public long GetVkDescriptorLayout(){return VkDescriptorLayout;} + public void CleanUp(VulkanContext VkCtx){ + Logger.debug("Destroying Descriptor Set Layout"); + vkDestroyDescriptorSetLayout(VkCtx.GetDevice().FetchVulkanDevice(),VkDescriptorLayout,null); + } + + public record LayoutInformation(int DescriptorType, int Binding, int DescriptorCount, int Stage){} +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSampler.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSampler.java new file mode 100644 index 0000000..a638536 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSampler.java @@ -0,0 +1,49 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkSamplerCreateInfo; + +import java.nio.LongBuffer; + +import static net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.VK10.*; + +public class TextureSampler { + private static final int MAX_ANISOTROPY = 16; + private final long VkSampler; + + public TextureSampler(VulkanContext VkCtx, TextureSamplerInfo textureSamplerInfo){ + try(var MemStack = MemoryStack.stackPush()){ + var SamplerInfo = VkSamplerCreateInfo.calloc(MemStack) + .sType$Default() + .magFilter(VK_FILTER_NEAREST) + .minFilter(VK_FILTER_NEAREST) + .addressModeU(textureSamplerInfo.AddressMode()) + .addressModeV(textureSamplerInfo.AddressMode()) + .addressModeW(textureSamplerInfo.AddressMode()) + .borderColor(textureSamplerInfo.BorderColour()) + .unnormalizedCoordinates(false) + .compareEnable(false) + .compareOp(VK_COMPARE_OP_NEVER) + .mipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST) + .minLod(0.0f) + .maxLod(textureSamplerInfo.MipLevels()) + .mipLodBias(0.0f); + if(textureSamplerInfo.Anisotrophy() && VkCtx.GetDevice().SamplesAnisotropy()){ + SamplerInfo + .anisotropyEnable(true) + .maxAnisotropy(MAX_ANISOTROPY); + } + LongBuffer LongPointer = MemStack.mallocLong(1); + vkCheck(vkCreateSampler(VkCtx.GetDevice().FetchVulkanDevice(), SamplerInfo,null,LongPointer) + ,"Failed to create sampler"); + VkSampler = LongPointer.get(0); + } + } + + public long GetVkSampler(){return VkSampler;} + public void CleanUp(VulkanContext VkCtx){ + vkDestroySampler(VkCtx.GetDevice().FetchVulkanDevice(), VkSampler,null); + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSamplerInfo.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSamplerInfo.java new file mode 100644 index 0000000..7d382d6 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/Shader/TextureSamplerInfo.java @@ -0,0 +1,4 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader; + +public record TextureSamplerInfo(int AddressMode, int BorderColour, int MipLevels,boolean Anisotropy) { +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/MaterialsCache.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/MaterialsCache.java new file mode 100644 index 0000000..0df9246 --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/MaterialsCache.java @@ -0,0 +1,94 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel; + +import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.TextureCache; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.IndexedLinkedHashMap; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandPool; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.TransferBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue; +import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; +import org.lwjgl.system.MemoryUtil; +import org.tinylog.Logger; +import org.w3c.dom.Text; + +import java.nio.ByteBuffer; +import java.util.List; + +import static org.lwjgl.vulkan.VK10.*; + +public class MaterialsCache { + private static final int MATERIAL_SIZE = VulkanUtils.VEC4_SIZE + VulkanUtils.INT_SIZE * 4; + private final IndexedLinkedHashMap MaterialsMap; + private VulkanBuffer MaterialsBuffer; + + public MaterialsCache(){ + MaterialsMap = new IndexedLinkedHashMap<>(); + } + + //create staging buffer and GPU only accessibly buffer, add any located textures + public void LoadMaterials(VulkanContext VkCtx, List Materials, TextureCache textureCache, CommandPool commandPool, Queue queue){ + int MaterialCount = Materials.size(); + int BufferSize = MATERIAL_SIZE * MaterialCount; + var SrcBuffer = new VulkanBuffer(VkCtx, BufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + MaterialsBuffer = new VulkanBuffer(VkCtx, BufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + var CmdBuffer = new CommandBuffer(VkCtx, commandPool, true, true); + CmdBuffer.BeginRecording(); + + TransferBuffer transferBuffer = new TransferBuffer(SrcBuffer,MaterialsBuffer); + long MappedMemory = SrcBuffer.MapMemory(VkCtx); + ByteBuffer data = MemoryUtil.memByteBuffer(MappedMemory, (int)SrcBuffer.GetRequestedSize()); + + int Offset = 0; + for(int i = 0; i < MaterialCount; i++){ + var Material = Materials.get(i); + String TexturePath = Material.TexturePath(); + boolean ValidTexture = TexturePath != null && !TexturePath.isEmpty(); + if(ValidTexture){ + textureCache.AddTexture(VkCtx, TexturePath, TexturePath, VK_FORMAT_R8G8B8A8_SRGB); + } + VulkanMaterial newMaterial = new VulkanMaterial(Material.ID()); + MaterialsMap.put(newMaterial.ID(), newMaterial); + + Material.DiffuseColour().get(Offset, data); + data.putInt(Offset + VulkanUtils.VEC4_SIZE, ValidTexture ? 1 : 0); + data.putInt(Offset + VulkanUtils.VEC4_SIZE + VulkanUtils.INT_SIZE, textureCache.GetPosition(TexturePath)); + //pad data because the minimum size of data in the shader layout is a multiple of Vec4, + // compensate with 2 more bytes as we only need 6 currently + data.putInt(Offset + VulkanUtils.VEC4_SIZE + VulkanUtils.INT_SIZE * 2,0); + data.putInt(Offset + VulkanUtils.VEC4_SIZE + VulkanUtils.INT_SIZE * 3,0); + Offset += MATERIAL_SIZE; + } + SrcBuffer.UnMapMemory(VkCtx); + transferBuffer.RecordTransferCommand(CmdBuffer); + CmdBuffer.EndRecording(); + CmdBuffer.SubmitAndWait(VkCtx,queue); + CmdBuffer.cleanup(VkCtx,commandPool); + transferBuffer.SrcBuffer().cleanup(VkCtx); + } + + + private void CleanUp(VulkanContext VkCtx){ + if(MaterialsBuffer != null){ + MaterialsBuffer.cleanup(VkCtx); + } + } + + public VulkanMaterial GetMaterial(String ID){return MaterialsMap.get(ID);} + + public VulkanBuffer GetMaterialsBuffer(){return MaterialsBuffer;} + + public int GetPosition(String ID){ + int result = -1; + if(ID != null){ + result = MaterialsMap.GetIndexOf(ID); + } else{ + Logger.warn("Missing Material [{}]",ID); + } + return result; + } +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/ModelsCache.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/ModelsCache.java index 94a4613..81e36f5 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/ModelsCache.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/ModelsCache.java @@ -9,6 +9,10 @@ import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer; import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; import org.lwjgl.system.MemoryUtil; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.IOException; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; @@ -25,41 +29,45 @@ public class ModelsCache { } public void loadModels(VulkanContext VkCtx, List Models, CommandPool commandPool, Queue queue){ - List StagingBufferList = new ArrayList<>(); - var Command = new CommandBuffer(VkCtx, commandPool, true, true); - Command.BeginRecording(); + try { + List StagingBufferList = new ArrayList<>(); + var Command = new CommandBuffer(VkCtx, commandPool, true, true); + Command.BeginRecording(); - for(ModelData modelData : Models){ - VulkanModel VKModel = new VulkanModel(modelData.ID()); - ModelsMap.put(VKModel.GetID(), VKModel); - for(MeshData meshData : modelData.Meshes()){ - TransferBuffer VerticesBuffers = CreateVerticesBuffer(VkCtx,meshData); - TransferBuffer IndicesBuffers = CreateIndicesBuffer(VkCtx,meshData); - StagingBufferList.add(VerticesBuffers.SrcBuffer()); - StagingBufferList.add(IndicesBuffers.SrcBuffer()); - VerticesBuffers.RecordTransferCommand(Command); - IndicesBuffers.RecordTransferCommand(Command); - VulkanMesh VkMesh = new VulkanMesh(meshData.ID(), VerticesBuffers.DstBuffer(),IndicesBuffers.DstBuffer(), - meshData.Indices().length); - VKModel.GetVkMeshList().add(VkMesh); + for (ModelData modelData : Models) { + VulkanModel VKModel = new VulkanModel(modelData.ID()); + ModelsMap.put(VKModel.GetID(), VKModel); + + DataInputStream VertexInput = new DataInputStream(new BufferedInputStream( + new FileInputStream(modelData.vertexPath()))); + DataInputStream IndicesInput = new DataInputStream(new BufferedInputStream( + new FileInputStream(modelData.indexPath()))); + + for (MeshData meshData : modelData.Meshes()) { + TransferBuffer VerticesBuffers = CreateVerticesBuffer(VkCtx, meshData,VertexInput); + TransferBuffer IndicesBuffers = CreateIndicesBuffer(VkCtx, meshData,IndicesInput); + StagingBufferList.add(VerticesBuffers.SrcBuffer()); + StagingBufferList.add(IndicesBuffers.SrcBuffer()); + VerticesBuffers.RecordTransferCommand(Command); + IndicesBuffers.RecordTransferCommand(Command); + VulkanMesh VkMesh = new VulkanMesh(meshData.ID(), VerticesBuffers.DstBuffer(), IndicesBuffers.DstBuffer(), + meshData.IndexSize() / VulkanUtils.INT_SIZE, meshData.MaterialID()); + VKModel.GetVkMeshList().add(VkMesh); + } } - } - Command.EndRecording(); Command.SubmitAndWait(VkCtx,queue); Command.cleanup(VkCtx,commandPool); StagingBufferList.forEach(b -> b.cleanup(VkCtx)); + } catch (Exception exception){ + throw new RuntimeException(exception); + } } - private static TransferBuffer CreateVerticesBuffer(VulkanContext VkCtx, MeshData meshData){ - float[] Positions = meshData.Positions(); - float[] TextureCoords = meshData.textureCoords(); - if(TextureCoords == null || TextureCoords.length == 0){ - TextureCoords = new float[(Positions.length/3) * 2]; - } - int ElementCount = Positions.length + TextureCoords.length; - int BufferSize = ElementCount * VulkanUtils.FLOAT_SIZE; + private static TransferBuffer CreateVerticesBuffer(VulkanContext VkCtx, MeshData meshData, DataInputStream VertexStream) throws IOException { + + int BufferSize = meshData.VertexSize(); var SrcBuffer = new VulkanBuffer(VkCtx,BufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); var DstBuffer = new VulkanBuffer(VkCtx, BufferSize, @@ -67,32 +75,30 @@ public class ModelsCache { long MappedMemory = SrcBuffer.MapMemory(VkCtx); FloatBuffer data = MemoryUtil.memFloatBuffer(MappedMemory, (int) SrcBuffer.GetRequestedSize()); - int Rows = Positions.length / 3; - for(int Row = 0; Row < Rows; Row++){ - int StartPos = Row * 3; - int StartTexCoordPos = Row * 2; - data.put(Positions[StartPos]); - data.put(Positions[StartPos+1]); - data.put(Positions[StartPos+2]); - data.put(TextureCoords[StartTexCoordPos]); - data.put(TextureCoords[StartTexCoordPos + 1]); + + int ValuesToRead = meshData.VertexSize()/VulkanUtils.FLOAT_SIZE; + while(ValuesToRead > 0){ + data.put(VertexStream.readFloat()); + ValuesToRead--; } SrcBuffer.UnMapMemory(VkCtx); return new TransferBuffer(SrcBuffer,DstBuffer); } - private static TransferBuffer CreateIndicesBuffer(VulkanContext VkCtx, MeshData meshData){ - int[] Indices = meshData.Indices(); - int IndexCount = Indices.length; - int BufferSize = IndexCount * VulkanUtils.INT_SIZE; - + private static TransferBuffer CreateIndicesBuffer(VulkanContext VkCtx, MeshData meshData, DataInputStream IndexStream) throws IOException{ + int BufferSize = meshData.IndexSize(); var SrcBuffer = new VulkanBuffer(VkCtx,BufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); var DstBuffer = new VulkanBuffer(VkCtx, BufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); long MappedMemory = SrcBuffer.MapMemory(VkCtx); IntBuffer data = MemoryUtil.memIntBuffer(MappedMemory,(int) SrcBuffer.GetRequestedSize()); - data.put(Indices); + + int ValuesToRead = meshData.IndexSize()/VulkanUtils.INT_SIZE; + while(ValuesToRead > 0){ + data.put(IndexStream.readInt()); + ValuesToRead--; + } SrcBuffer.UnMapMemory(VkCtx); return new TransferBuffer(SrcBuffer,DstBuffer); } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMaterial.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMaterial.java new file mode 100644 index 0000000..03818da --- /dev/null +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMaterial.java @@ -0,0 +1,4 @@ +package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel; + +public record VulkanMaterial(String ID) { +} diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMesh.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMesh.java index 00068e0..1738bdc 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMesh.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Rendering/VkModel/VulkanMesh.java @@ -3,7 +3,7 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel; import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext; import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer; -public record VulkanMesh(String ID, VulkanBuffer VerticesBuffer,VulkanBuffer IndicesBuffer,int IndicesCount) { +public record VulkanMesh(String ID, VulkanBuffer VerticesBuffer,VulkanBuffer IndicesBuffer,int IndicesCount, String MaterialID) { public void Cleanup(VulkanContext VkCtx){ VerticesBuffer.cleanup(VkCtx); IndicesBuffer.cleanup(VkCtx); diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java index 9d7d0f1..b776b4e 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DeviceLayers/Device.java @@ -20,6 +20,7 @@ import static org.lwjgl.vulkan.VK13.*; public class Device { private final VkDevice VulkanDevice; + private final boolean SamplesAnisotropy; public Device(PhysicalDevice PhysDevice){ Logger.debug("Creating Device Interface Layer"); @@ -41,6 +42,13 @@ public class Device { .dynamicRendering(true) .synchronization2(true); var features2 = VkPhysicalDeviceFeatures2.calloc(MemStack).sType$Default(); + var features = features2.features(); + + VkPhysicalDeviceFeatures SupportedFeatures = PhysDevice.GetPhysicalDeviceFeatures(); + SamplesAnisotropy = SupportedFeatures.samplerAnisotropy(); + if(SamplesAnisotropy){ + features.samplerAnisotropy(true); + } features2.pNext(features13.address()); var DeviceCreateInfo = VkDeviceCreateInfo.calloc(MemStack) @@ -109,6 +117,8 @@ public class Device { VulkanContext.VulkanLayersOpen--; } + public boolean SamplesAnisotropy(){return SamplesAnisotropy;} + public VkDevice FetchVulkanDevice() { return VulkanDevice; diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java index b87b206..586f057 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/ImageView.java @@ -16,11 +16,13 @@ public class ImageView { private final int MipLevels; private final long VulkanImage; private final long VulkanImageView; + private final boolean DepthImage; - public ImageView(Device device, long vulkanImage, ImageViewData imageViewData){ + public ImageView(Device device, long vulkanImage, ImageViewData imageViewData, boolean DepthImage){ this.AspectMask = imageViewData.AspectMask; this.MipLevels = imageViewData.MipLevels; this.VulkanImage = vulkanImage; + this.DepthImage = DepthImage; try(var MemStack = MemoryStack.stackPush()){ LongBuffer LongPointer = MemStack.mallocLong(1); var ViewCreateInfo = VkImageViewCreateInfo.calloc(MemStack) @@ -54,6 +56,7 @@ public class ImageView { return VulkanImageView; } public long GetVulkanImage(){return VulkanImage;} + public boolean IsDepthImage(){return DepthImage;} public static class ImageViewData{ private int AspectMask; diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java index 8c9590c..a0e57a9 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SceneRender.java @@ -3,19 +3,22 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen; 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.Main.GameCore; +import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.Attachment; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.TextureCache; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Pipeline; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineBuildInfo; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PushConstantsRange; -import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderCompiler; -import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule; +import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.*; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VertexBufferStructure; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelsCache; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanMesh; import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanModel; import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer; +import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer; import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils; import org.joml.Matrix4f; import org.lwjgl.system.MemoryStack; @@ -33,6 +36,17 @@ import static org.lwjgl.vulkan.KHRSynchronization2.VK_IMAGE_LAYOUT_ATTACHMENT_OP import static org.lwjgl.vulkan.VK13.*; public class SceneRender {//dynamic rendering + + private static final String DESCRIPTOR_ID_MAT = "SCN_DESC_ID_MAT"; + private static final String DESCRIPTOR_ID_PRJ = "SCN_DESC_ID_PRJ"; + private static final String DESCRIPTOR_ID_TEXT = "SCN_DESC_ID_TEXT"; + private static final int PUSH_CONSTANTS_SIZE = VulkanUtils.MATRIX4X4_SIZE + VulkanUtils.INT_SIZE; + private final VulkanBuffer BufferProjectionMatrix; + private final DescriptorSetLayout descriptorLayoutFragStorage; + private final DescriptorSetLayout descriptorLayoutTexture; + private final DescriptorSetLayout descriptorLayoutVertexUniform; + private final TextureSampler textureSampler; + private static final int DEPTH_FORMAT = VK_FORMAT_D16_UNORM; private final VkClearValue ClearValueDepth; private final ByteBuffer PushConstBuffer; @@ -63,9 +77,27 @@ public class SceneRender {//dynamic rendering ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f)); AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour); RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour, AttInfoDepth); - PushConstBuffer = MemoryUtil.memAlloc(VulkanUtils.MATRIX4X4_SIZE * 2); + PushConstBuffer = (MemoryUtil.memAlloc(PUSH_CONSTANTS_SIZE)); + + descriptorLayoutVertexUniform = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0,1,VK_SHADER_STAGE_VERTEX_BIT)); + BufferProjectionMatrix = VulkanUtils.CreateHostVisibleBuffer(vulkanContext, VulkanUtils.MATRIX4X4_SIZE, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, DESCRIPTOR_ID_PRJ, descriptorLayoutVertexUniform); + VulkanUtils.CopyMatrixToBuffer(vulkanContext, BufferProjectionMatrix, PrimaryRuntime.GetEngineInstance().scene().GetProjection().GetProjectionMatrix(),0); + descriptorLayoutFragStorage = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 0,1,VK_SHADER_STAGE_FRAGMENT_BIT)); + var textureSamplerInfo = new TextureSamplerInfo(VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_BORDER_COLOR_INT_OPAQUE_BLACK,1,true); + textureSampler = new TextureSampler(vulkanContext,textureSamplerInfo); + descriptorLayoutTexture = new DescriptorSetLayout(vulkanContext, new DescriptorSetLayout.LayoutInformation( + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, TextureCache.MAX_TEXTURES, VK_SHADER_STAGE_FRAGMENT_BIT + )); + ShaderModule[] shaderModules = SceneRender.CreateShaderModules(vulkanContext); - VkPipeline = CreatePipeline(vulkanContext, shaderModules); + VkPipeline = CreatePipeline(vulkanContext, shaderModules, new DescriptorSetLayout[]{ + descriptorLayoutVertexUniform, descriptorLayoutFragStorage, descriptorLayoutFragStorage + }); + //Continue Here Arrays.asList(shaderModules).forEach(shaderModule -> shaderModule.CleanUp(vulkanContext)); } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java index 63d5cfc..c72fe6b 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Structure/DisplayToScreen/SwapChain.java @@ -94,7 +94,7 @@ public class SwapChain { var ReturnResult = new ImageView[ImageCount]; var ImageViewData = new ImageView.ImageViewData().Format(Format).AspectMask(VK_IMAGE_ASPECT_COLOR_BIT); for(int i = 0; i < ImageCount; i++){ - ReturnResult[i] = new ImageView(device, SwapChainImages.get(i),ImageViewData); + ReturnResult[i] = new ImageView(device, SwapChainImages.get(i),ImageViewData,false); } return ReturnResult; } diff --git a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java index c428c6f..b8c1fcc 100644 --- a/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java +++ b/src/main/java/net/halbear/Terrain4J/EngineCore/Vulkan/Util/VulkanUtils.java @@ -15,6 +15,7 @@ public class VulkanUtils { public static final int FLOAT_SIZE = 4; public static final int INT_SIZE = 4; public static final int MATRIX4X4_SIZE = 16 * FLOAT_SIZE; + public static final int VEC4_SIZE = 4 * FLOAT_SIZE; public static int MemoryTypeFromProperties(VulkanContext vulkanContext, int TypeBits, int ReqMask){ int result = -1; diff --git a/terrain4j.properties b/terrain4j.properties index ee92827..17f22c4 100644 --- a/terrain4j.properties +++ b/terrain4j.properties @@ -28,6 +28,7 @@ vkValidated=true vsync=false #Vsync images RequestedImages=3 +DefaultTexturePath=/EngineResources/Texture/DefaultTexture.png #This is the name of the GPU you want the engine to use PhysicalDeviceName=