/** * Load a 2D texture including all mip levels * * @param filename File to load (supports .ktx and .dds) * @param format Vulkan format of the image data stored in the file * @param device Vulkan device to create the texture on * @param copyQueue Queue used for the texture staging copy commands (must support transfer) * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false) * */ public void loadFromFile( string filename, VkFormat format, vksVulkanDevice device, VkQueue copyQueue, VkImageUsageFlagBits imageUsageFlags = VkImageUsageFlagBits.Sampled, VkImageLayout imageLayout = VkImageLayout.ShaderReadOnlyOptimal, bool forceLinear = false) { KtxFile tex2D; using (var fs = File.OpenRead(filename)) { tex2D = KtxFile.Load(fs, false); } this.device = device; width = tex2D.Header.PixelWidth; height = tex2D.Header.PixelHeight; if (height == 0) { height = width; } mipLevels = tex2D.Header.NumberOfMipmapLevels; // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.PhysicalDevice, format, &formatProperties); // Only use linear tiling if requested (and supported by the device) // Support for linear tiling is mostly limited, so prefer to use // optimal tiling instead // On most implementations linear tiling will only support a very // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) bool useStaging = !forceLinear; VkMemoryAllocateInfo memAllocInfo = new VkMemoryAllocateInfo(); memAllocInfo.sType = MemoryAllocateInfo; VkMemoryRequirements memReqs; // Use a separate command buffer for texture loading VkCommandBuffer copyCmd = device.createCommandBuffer(VkCommandBufferLevel.Primary, true); if (useStaging) { // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = new VkBufferCreateInfo(); bufferCreateInfo.sType = BufferCreateInfo; bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlagBits.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; vkCreateBuffer(device.LogicalDevice, &bufferCreateInfo, null, &stagingBuffer); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device.LogicalDevice, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.HostCoherent); vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &stagingMemory); vkBindBufferMemory(device.LogicalDevice, stagingBuffer, stagingMemory, 0); // Copy texture data into staging buffer IntPtr data; vkMapMemory(device.LogicalDevice, stagingMemory, 0, memReqs.size, 0, &data); byte[] pixelData = tex2D.GetAllTextureData(); fixed(byte *pixelDataPtr = &pixelData[0]) { Unsafe.CopyBlock(data, pixelDataPtr, (uint)pixelData.Length); } vkUnmapMemory(device.LogicalDevice, stagingMemory); // Setup buffer copy regions for each mip level var bufferCopyRegions = new List <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlagBits.Color; bufferCopyRegion.imageSubresource.mipLevel = i; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2D.Faces[0].Mipmaps[i].Width; bufferCopyRegion.imageExtent.height = tex2D.Faces[0].Mipmaps[i].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); offset += tex2D.Faces[0].Mipmaps[i].SizeInBytes; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = new VkImageCreateInfo(); imageCreateInfo.sType = ImageCreateInfo; imageCreateInfo.imageType = VkImageType._2d; imageCreateInfo.format = format; imageCreateInfo.mipLevels = mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlagBits._1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 }; imageCreateInfo.usage = imageUsageFlags; // Ensure that the TRANSFER_DST bit is set for staging if ((imageCreateInfo.usage & VkImageUsageFlagBits.TransferDst) == 0) { imageCreateInfo.usage |= VkImageUsageFlagBits.TransferDst; } { VkImage vkImage; vkCreateImage(device.LogicalDevice, &imageCreateInfo, null, &vkImage); this.image = vkImage; } vkGetImageMemoryRequirements(device.LogicalDevice, image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); { VkDeviceMemory memory; vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &memory); this.deviceMemory = memory; } vkBindImageMemory(device.LogicalDevice, image, deviceMemory, 0); VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlagBits.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = mipLevels; subresourceRange.layerCount = 1; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy Tools.setImageLayout( copyCmd, image, VkImageAspectFlagBits.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer fixed(VkBufferImageCopy *pointer = bufferCopyRegions.ToArray()) { vkCmdCopyBufferToImage( copyCmd, stagingBuffer, image, VkImageLayout.TransferDstOptimal, (UInt32)bufferCopyRegions.Count, pointer); } // Change texture image layout to shader read after all mip levels have been copied this.imageLayout = imageLayout; Tools.setImageLayout( copyCmd, image, VkImageAspectFlagBits.Color, VkImageLayout.TransferDstOptimal, imageLayout, subresourceRange); device.flushCommandBuffer(copyCmd, copyQueue); // Clean up staging resources vkFreeMemory(device.LogicalDevice, stagingMemory, null); vkDestroyBuffer(device.LogicalDevice, stagingBuffer, null); } else { throw new NotImplementedException(); /* * // Prefer using optimal tiling, as linear tiling * // may support only a small set of features * // depending on implementation (e.g. no mip maps, only one layer, etc.) * * // Check if this support is supported for linear tiling * Debug.Assert((formatProperties.linearTilingFeatures & VkFormatFeatureFlags.SampledImage) != 0); * * VkImage mappableImage; * VkDeviceMemory mappableMemory; * * VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); * imageCreateInfo.imageType = VkImageType._2d; * imageCreateInfo.format = format; * imageCreateInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 }; * imageCreateInfo.mipLevels = 1; * imageCreateInfo.arrayLayers = 1; * imageCreateInfo.samples = VkSampleCountFlags._1; * imageCreateInfo.tiling = VkImageTiling.Linear; * imageCreateInfo.usage = imageUsageFlags; * imageCreateInfo.sharingMode = VkSharingMode.Exclusive; * imageCreateInfo.initialLayout = VkImageLayout.Undefined; * * // Load mip map level 0 to linear tiling image * Util.CheckResult(vkCreateImage(device.LogicalDevice, &imageCreateInfo, null, &mappableImage)); * * // Get memory requirements for this image * // like size and alignment * vkGetImageMemoryRequirements(device.LogicalDevice, mappableImage, &memReqs); * // Set memory allocation size to required memory size * memAllocInfo.allocationSize = memReqs.size; * * // Get memory type that can be mapped to host memory * memAllocInfo.memoryTypeIndex = device.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); * * // Allocate host memory * Util.CheckResult(vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &mappableMemory)); * * // Bind allocated image for use * Util.CheckResult(vkBindImageMemory(device.LogicalDevice, mappableImage, mappableMemory, 0)); * * // Get sub resource layout * // Mip map count, array layer, etc. * VkImageSubresource subRes = new VkImageSubresource(); * subRes.aspectMask = VkImageAspectFlags.Color; * subRes.mipLevel = 0; * * VkSubresourceLayout subResLayout; * void* data; * * // Get sub resources layout * // Includes row pitch, size offsets, etc. * vkGetImageSubresourceLayout(device.LogicalDevice, mappableImage, &subRes, &subResLayout); * * // Map image memory * Util.CheckResult(vkMapMemory(device.LogicalDevice, mappableMemory, 0, memReqs.size, 0, &data)); * * // Copy image data into memory * memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); * * vkUnmapMemory(device.LogicalDevice, mappableMemory); * * // Linear tiled images don't need to be staged * // and can be directly used as textures * image = mappableImage; * deviceMemory = mappableMemory; * imageLayout = imageLayout; * * // Setup image memory barrier * vks::tools::setImageLayout(copyCmd, image, VkImageAspectFlags.Color, VkImageLayout.Undefined, imageLayout); * * device.flushCommandBuffer(copyCmd, copyQueue); */ } // Create a defaultsampler VkSamplerCreateInfo samplerCreateInfo = new VkSamplerCreateInfo(); samplerCreateInfo.sType = SamplerCreateInfo; samplerCreateInfo.magFilter = VkFilter.Linear; samplerCreateInfo.minFilter = VkFilter.Linear; samplerCreateInfo.mipmapMode = VkSamplerMipmapMode.Linear; samplerCreateInfo.addressModeU = VkSamplerAddressMode.Repeat; samplerCreateInfo.addressModeV = VkSamplerAddressMode.Repeat; samplerCreateInfo.addressModeW = VkSamplerAddressMode.Repeat; samplerCreateInfo.mipLodBias = 0.0f; samplerCreateInfo.compareOp = VkCompareOp.Never; samplerCreateInfo.minLod = 0.0f; // Max level-of-detail should match mip level count samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f; // Enable anisotropic filtering samplerCreateInfo.maxAnisotropy = 8; samplerCreateInfo.anisotropyEnable = true; samplerCreateInfo.borderColor = VkBorderColor.FloatOpaqueWhite; { VkSampler vkSampler; vkCreateSampler(device.LogicalDevice, &samplerCreateInfo, null, &vkSampler); this.sampler = vkSampler; } // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo viewCreateInfo = new VkImageViewCreateInfo(); viewCreateInfo.sType = ImageViewCreateInfo; viewCreateInfo.viewType = VkImageViewType._2d; viewCreateInfo.format = format; viewCreateInfo.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; viewCreateInfo.subresourceRange = new VkImageSubresourceRange { aspectMask = VkImageAspectFlagBits.Color, baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1 }; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1; viewCreateInfo.image = image; { VkImageView vkImageView; vkCreateImageView(device.LogicalDevice, &viewCreateInfo, null, &vkImageView); this.view = vkImageView; } // Update descriptor image info member that can be used for setting up descriptor sets updateDescriptor(); }
/** * Loads a 3D model from a file into Vulkan buffers * * @param device Pointer to the Vulkan device used to generated the vertex and index buffers on * @param filename File to load (must be a model format supported by ASSIMP) * @param layout Vertex layout components (position, normals, tangents, etc.) * @param createInfo MeshCreateInfo structure for load time settings like scale, center, etc. * @param copyQueue Queue used for the memory staging copy commands (must support transfer) * @param (Optional) flags ASSIMP model loading flags */ //public bool loadFromFile(string filename, vksVertexLayout layout, vksModelCreateInfo* createInfo, vksVulkanDevice device, VkQueue copyQueue, int flags) //{ // this.device = device.LogicalDevice; // ObjFile objFile; // using (var fs = File.OpenRead(filename)) // { // objFile = new ObjParser().Parse(fs); // } // string mtlFilename = Path.ChangeExtension(filename, "mtl"); // MtlFile mtlFile; // using (var fs = File.OpenRead(mtlFilename)) // { // mtlFile = new MtlParser().Parse(fs); // } // if (objFile != null) // { // parts.Clear(); // parts.Resize((uint)objFile.MeshGroups.Length); // Vector3 scale = new Vector3(1.0f); // Vector2 uvscale = new Vector2(1.0f); // Vector3 center = new Vector3(0.0f); // if (createInfo != null) // { // scale = createInfo->Scale; // uvscale = createInfo->UVScale; // center = createInfo->Center; // } // NativeList<float> vertexBuffer = new NativeList<float>(); // NativeList<uint> indexBuffer = new NativeList<uint>(); // vertexCount = 0; // indexCount = 0; // // Load meshes // parts.Count = (uint)objFile.MeshGroups.Length; // for (uint i = 0; i < objFile.MeshGroups.Length; i++) // { // ConstructedMeshInfo mesh = objFile.GetMesh(objFile.MeshGroups[i]); // parts[i] = new ModelPart(); // parts[i].vertexBase = vertexCount; // parts[i].indexBase = indexCount; // vertexCount += (uint)mesh.Vertices.Length; // var material = mtlFile.Definitions[objFile.MeshGroups[i].Material]; // Vector3 pColor = material.DiffuseReflectivity; // Vector3 Zero3D = Vector3.Zero; // for (uint j = 0; j < mesh.Vertices.Length; j++) // { // VertexPositionNormalTexture vertex = mesh.Vertices[j]; // Vector3* pPos = &vertex.Position; // Vector3* pNormal = &vertex.Normal; // Vector2* pTexCoord = &vertex.TextureCoordinates; // Vector3* pTangent = &Zero3D; // Vector3* pBiTangent = &Zero3D; // /* // const aiVector3D* pPos = &(paiMesh->mVertices[j]); // const aiVector3D* pNormal = &(paiMesh->mNormals[j]); // const aiVector3D* pTexCoord = (paiMesh->HasTextureCoords(0)) ? &(paiMesh->mTextureCoords[0][j]) : &Zero3D; // const aiVector3D* pTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mTangents[j]) : &Zero3D; // const aiVector3D* pBiTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mBitangents[j]) : &Zero3D; // */ // foreach (var component in layout.Components) // { // switch (component) // { // case VertexComponent.VERTEX_COMPONENT_POSITION: // vertexBuffer.Add(pPos.X * scale.X + center.X); // vertexBuffer.Add(-pPos.Y * scale.Y + center.Y); // vertexBuffer.Add(pPos.Z * scale.Z + center.Z); // break; // case VertexComponent.VERTEX_COMPONENT_NORMAL: // vertexBuffer.Add(pNormal.X); // vertexBuffer.Add(-pNormal.Y); // vertexBuffer.Add(pNormal.Z); // break; // case VertexComponent.VERTEX_COMPONENT_UV: // vertexBuffer.Add(pTexCoord.X * uvscale.X); // vertexBuffer.Add(pTexCoord.Y * uvscale.Y); // break; // case VertexComponent.VERTEX_COMPONENT_COLOR: // vertexBuffer.Add(pColor.X); // vertexBuffer.Add(pColor.Y); // vertexBuffer.Add(pColor.Z); // break; // case VertexComponent.VERTEX_COMPONENT_TANGENT: // vertexBuffer.Add(pTangent.X); // vertexBuffer.Add(pTangent.Y); // vertexBuffer.Add(pTangent.Z); // break; // case VertexComponent.VERTEX_COMPONENT_BITANGENT: // vertexBuffer.Add(pBiTangent.X); // vertexBuffer.Add(pBiTangent.Y); // vertexBuffer.Add(pBiTangent.Z); // break; // // Dummy components for padding // case VertexComponent.VERTEX_COMPONENT_DUMMY_FLOAT: // vertexBuffer.Add(0.0f); // break; // case VertexComponent.VERTEX_COMPONENT_DUMMY_VEC4: // vertexBuffer.Add(0.0f); // vertexBuffer.Add(0.0f); // vertexBuffer.Add(0.0f); // vertexBuffer.Add(0.0f); // break; // }; // } // dim.Max.X = Math.Max(pPos.X, dim.Max.X); // dim.Max.Y = Math.Max(pPos.Y, dim.Max.Y); // dim.Max.Z = Math.Max(pPos.Z, dim.Max.Z); // dim.Min.X = Math.Min(pPos.X, dim.Min.X); // dim.Min.Y = Math.Min(pPos.Y, dim.Min.Y); // dim.Min.Z = Math.Min(pPos.Z, dim.Min.Z); // } // dim.Size = dim.Max - dim.Min; // parts[i].vertexCount = (uint)mesh.Vertices.Length; // uint indexBase = indexBuffer.Count; // foreach (var index in mesh.Indices) // { // indexBuffer.Add(index); // } // indexCount += (uint)mesh.Indices.Length; // parts[i].indexCount += (uint)mesh.Indices.Length; // /* // for (uint j = 0; j < paiMesh->mNumFaces; j++) // { // const aiFace&Face = paiMesh->mFaces[j]; // if (Face.mNumIndices != 3) // continue; // indexBuffer.Add(indexBase + Face.mIndices[0]); // indexBuffer.Add(indexBase + Face.mIndices[1]); // indexBuffer.Add(indexBase + Face.mIndices[2]); // parts[i].indexCount += 3; // indexCount += 3; // } // */ // } // uint vBufferSize = vertexBuffer.Count * sizeof(float); // uint iBufferSize = indexBuffer.Count * sizeof(uint); // // Use staging buffer to move vertex and index buffer to device local memory // // Create staging buffers // vksBuffer vertexStaging = new vksBuffer(); // vksBuffer indexStaging = new vksBuffer(); // // Vertex buffer // Util.CheckResult(device.createBuffer( // VkBufferUsageFlags.TransferSrc, // VkMemoryPropertyFlags.HostVisible, // vertexStaging, // vBufferSize, // vertexBuffer.Data.ToPointer())); // // Index buffer // Util.CheckResult(device.createBuffer( // VkBufferUsageFlags.TransferSrc, // VkMemoryPropertyFlags.HostVisible, // indexStaging, // iBufferSize, // indexBuffer.Data.ToPointer())); // // Create device local target buffers // // Vertex buffer // Util.CheckResult(device.createBuffer( // VkBufferUsageFlags.VertexBuffer | VkBufferUsageFlags.TransferDst, // VkMemoryPropertyFlags.DeviceLocal, // vertices, // vBufferSize)); // // Index buffer // Util.CheckResult(device.createBuffer( // VkBufferUsageFlags.IndexBuffer | VkBufferUsageFlags.TransferDst, // VkMemoryPropertyFlags.DeviceLocal, // indices, // iBufferSize)); // // Copy from staging buffers // VkCommandBuffer copyCmd = device.createCommandBuffer(VkCommandBufferLevel.Primary, true); // VkBufferCopy copyRegion = new VkBufferCopy(); // copyRegion.size = vertices.size; // vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertices.buffer, 1, ©Region); // copyRegion.size = indices.size; // vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, ©Region); // device.flushCommandBuffer(copyCmd, copyQueue); // // Destroy staging resources // vkDestroyBuffer(device.LogicalDevice, vertexStaging.buffer, null); // vkFreeMemory(device.LogicalDevice, vertexStaging.memory, null); // vkDestroyBuffer(device.LogicalDevice, indexStaging.buffer, null); // vkFreeMemory(device.LogicalDevice, indexStaging.memory, null); // return true; // } // else // { // Console.WriteLine("Error loading file."); // return false; // } //} /** * Loads a 3D model from a file into Vulkan buffers * * @param device Pointer to the Vulkan device used to generated the vertex and index buffers on * @param filename File to load (must be a model format supported by ASSIMP) * @param layout Vertex layout components (position, normals, tangents, etc.) * @param createInfo MeshCreateInfo structure for load time settings like scale, center, etc. * @param copyQueue Queue used for the memory staging copy commands (must support transfer) * @param (Optional) flags ASSIMP model loading flags */ bool loadFromFile(string filename, vksVertexLayout layout, vksModelCreateInfo *createInfo, vksVulkanDevice device, VkQueue copyQueue, PostProcessSteps flags = DefaultPostProcessSteps) { this.device = device.LogicalDevice; // Load file var assimpContext = new AssimpContext(); var pScene = assimpContext.ImportFile(filename, flags); parts = new ModelPart[pScene.Meshes.Count]; Vector3 scale = new Vector3(1.0f); Vector2 uvscale = new Vector2(1.0f); Vector3 center = new Vector3(0.0f); if (createInfo != null) { scale = createInfo->Scale; uvscale = createInfo->UVScale; center = createInfo->Center; } var vertexBuffer = new List <float>(); var indexBuffer = new List <uint>(); vertexCount = 0; indexCount = 0; // Load meshes for (int i = 0; i < pScene.Meshes.Count; i++) { var paiMesh = pScene.Meshes[i]; parts[i] = new ModelPart(); parts[i].vertexBase = vertexCount; parts[i].indexBase = indexCount; vertexCount += (uint)paiMesh.VertexCount; var pColor = pScene.Materials[paiMesh.MaterialIndex].ColorDiffuse; Vector3D Zero3D = new Vector3D(0.0f, 0.0f, 0.0f); for (int j = 0; j < paiMesh.VertexCount; j++) { Vector3D pPos = paiMesh.Vertices[j]; Vector3D pNormal = paiMesh.Normals[j]; Vector3D pTexCoord = paiMesh.HasTextureCoords(0) ? paiMesh.TextureCoordinateChannels[0][j] : Zero3D; Vector3D pTangent = paiMesh.HasTangentBasis ? paiMesh.Tangents[j] : Zero3D; Vector3D pBiTangent = paiMesh.HasTangentBasis ? paiMesh.BiTangents[j] : Zero3D; foreach (var component in layout.Components) { switch (component) { case VertexComponent.VERTEX_COMPONENT_POSITION: vertexBuffer.Add(pPos.X * scale.X + center.X); vertexBuffer.Add(-pPos.Y * scale.Y + center.Y); vertexBuffer.Add(pPos.Z * scale.Z + center.Z); break; case VertexComponent.VERTEX_COMPONENT_NORMAL: vertexBuffer.Add(pNormal.X); vertexBuffer.Add(-pNormal.Y); vertexBuffer.Add(pNormal.Z); break; case VertexComponent.VERTEX_COMPONENT_UV: vertexBuffer.Add(pTexCoord.X * uvscale.X); vertexBuffer.Add(pTexCoord.Y * uvscale.Y); break; case VertexComponent.VERTEX_COMPONENT_COLOR: vertexBuffer.Add(pColor.R); vertexBuffer.Add(pColor.G); vertexBuffer.Add(pColor.B); break; case VertexComponent.VERTEX_COMPONENT_TANGENT: vertexBuffer.Add(pTangent.X); vertexBuffer.Add(pTangent.Y); vertexBuffer.Add(pTangent.Z); break; case VertexComponent.VERTEX_COMPONENT_BITANGENT: vertexBuffer.Add(pBiTangent.X); vertexBuffer.Add(pBiTangent.Y); vertexBuffer.Add(pBiTangent.Z); break; // Dummy components for padding case VertexComponent.VERTEX_COMPONENT_DUMMY_FLOAT: vertexBuffer.Add(0.0f); break; case VertexComponent.VERTEX_COMPONENT_DUMMY_VEC4: vertexBuffer.Add(0.0f); vertexBuffer.Add(0.0f); vertexBuffer.Add(0.0f); vertexBuffer.Add(0.0f); break; } ; } dim.Max.X = (float)Math.Max(pPos.X, dim.Max.X); dim.Max.Y = (float)Math.Max(pPos.Y, dim.Max.Y); dim.Max.Z = (float)Math.Max(pPos.Z, dim.Max.Z); dim.Min.X = (float)Math.Min(pPos.X, dim.Min.X); dim.Min.Y = (float)Math.Min(pPos.Y, dim.Min.Y); dim.Min.Z = (float)Math.Min(pPos.Z, dim.Min.Z); } dim.Size = dim.Max - dim.Min; parts[i].vertexCount = (uint)paiMesh.VertexCount; uint indexBase = (uint)indexBuffer.Count; for (uint j = 0; j < paiMesh.FaceCount; j++) { var Face = paiMesh.Faces[(int)j]; if (Face.IndexCount != 3) { continue; } indexBuffer.Add(indexBase + (uint)Face.Indices[0]); indexBuffer.Add(indexBase + (uint)Face.Indices[1]); indexBuffer.Add(indexBase + (uint)Face.Indices[2]); parts[i].indexCount += 3; indexCount += 3; } } uint vBufferSize = (uint)(vertexBuffer.Count * sizeof(float)); uint iBufferSize = (uint)(indexBuffer.Count * sizeof(uint)); // Use staging buffer to move vertex and index buffer to device local memory // Create staging buffers vksBuffer vertexStaging = new vksBuffer(); vksBuffer indexStaging = new vksBuffer(); // Vertex buffer fixed(float *data = vertexBuffer.ToArray()) { device.createBuffer( VkBufferUsageFlagBits.TransferSrc, VkMemoryPropertyFlagBits.HostVisible, vertexStaging, vBufferSize, data); } // Index buffer fixed(uint *data = indexBuffer.ToArray()) { device.createBuffer( VkBufferUsageFlagBits.TransferSrc, VkMemoryPropertyFlagBits.HostVisible, indexStaging, iBufferSize, data); } // Create device local target buffers // Vertex buffer device.createBuffer( VkBufferUsageFlagBits.VertexBuffer | VkBufferUsageFlagBits.TransferDst, VkMemoryPropertyFlagBits.DeviceLocal, vertices, vBufferSize); // Index buffer device.createBuffer( VkBufferUsageFlagBits.IndexBuffer | VkBufferUsageFlagBits.TransferDst, VkMemoryPropertyFlagBits.DeviceLocal, indices, iBufferSize); // Copy from staging buffers VkCommandBuffer copyCmd = device.createCommandBuffer(VkCommandBufferLevel.Primary, true); VkBufferCopy copyRegion = new VkBufferCopy(); copyRegion.size = vertices.size; vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertices.buffer, 1, ©Region); copyRegion.size = indices.size; vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, ©Region); device.flushCommandBuffer(copyCmd, copyQueue); // Destroy staging resources vkDestroyBuffer(device.LogicalDevice, vertexStaging.buffer, null); vkFreeMemory(device.LogicalDevice, vertexStaging.memory, null); vkDestroyBuffer(device.LogicalDevice, indexStaging.buffer, null); vkFreeMemory(device.LogicalDevice, indexStaging.memory, null); return(true); }
/** * Loads a 3D model from a file into Vulkan buffers * * @param device Pointer to the Vulkan device used to generated the vertex and index buffers on * @param filename File to load (must be a model format supported by ASSIMP) * @param layout Vertex layout components (position, normals, tangents, etc.) * @param scale Load time scene scale * @param copyQueue Queue used for the memory staging copy commands (must support transfer) * @param (Optional) flags ASSIMP model loading flags */ public bool loadFromFile(string filename, vksVertexLayout layout, float scale, vksVulkanDevice device, VkQueue copyQueue, PostProcessSteps flags = DefaultPostProcessSteps) { vksModelCreateInfo modelCreateInfo = new vksModelCreateInfo(scale, 1.0f, 0.0f); return(loadFromFile(filename, layout, &modelCreateInfo, device, copyQueue, flags)); }
//protected InputSnapshot snapshot; public void InitVulkan() { VkResult err; err = CreateInstance(false); if (err != VkResult.Success) { throw new InvalidOperationException("Could not create Vulkan instance. Error: " + err); } if (Settings.Validation) { } // Physical Device uint gpuCount = 0; vkEnumeratePhysicalDevices(Instance, &gpuCount, null); Debug.Assert(gpuCount > 0); // Enumerate devices IntPtr *physicalDevices = stackalloc IntPtr[(int)gpuCount]; err = vkEnumeratePhysicalDevices(Instance, &gpuCount, (VkPhysicalDevice *)physicalDevices); if (err != VkResult.Success) { throw new InvalidOperationException("Could not enumerate physical devices."); } // GPU selection // Select physical Device to be used for the Vulkan example // Defaults to the first Device unless specified by command line uint selectedDevice = 0; // TODO: Implement arg parsing, etc. physicalDevice = ((VkPhysicalDevice *)physicalDevices)[selectedDevice]; // Store properties (including limits) and features of the phyiscal Device // So examples can check against them and see if a feature is actually supported VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); DeviceProperties = deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); DeviceFeatures = deviceFeatures; // Gather physical Device memory properties VkPhysicalDeviceMemoryProperties deviceMemoryProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); DeviceMemoryProperties = deviceMemoryProperties; // Derived examples can override this to set actual features (based on above readings) to enable for logical device creation getEnabledFeatures(); // Vulkan Device creation // This is handled by a separate class that gets a logical Device representation // and encapsulates functions related to a Device vulkanDevice = new vksVulkanDevice(physicalDevice); VkResult res = vulkanDevice.CreateLogicalDevice(enabledFeatures, EnabledExtensions); if (res != VkResult.Success) { throw new InvalidOperationException("Could not create Vulkan Device."); } device = vulkanDevice.LogicalDevice; // Get a graphics queue from the Device VkQueue queue; vkGetDeviceQueue(device, vulkanDevice.QFIndices.Graphics, 0, &queue); this.queue = queue; // Find a suitable depth format VkFormat depthFormat; bool validDepthFormat = Tools.getSupportedDepthFormat(physicalDevice, &depthFormat); Debug.Assert(validDepthFormat == true); DepthFormat = depthFormat; Swapchain.Connect(Instance, physicalDevice, device); // Create synchronization objects VkSemaphoreCreateInfo semaphoreCreateInfo = new VkSemaphoreCreateInfo(); semaphoreCreateInfo.sType = SemaphoreCreateInfo; // Create a semaphore used to synchronize image presentation // Ensures that the image is displayed before we start submitting new commands to the queu vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->PresentComplete); // Create a semaphore used to synchronize command submission // Ensures that the image is not presented until all commands have been sumbitted and executed vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->RenderComplete); // Create a semaphore used to synchronize command submission // Ensures that the image is not presented until all commands for the text overlay have been sumbitted and executed // Will be inserted after the render complete semaphore if the text overlay is enabled vkCreateSemaphore(device, &semaphoreCreateInfo, null, &GetSemaphoresPtr()->TextOverlayComplete); // Set up submit info structure // Semaphores will stay the same during application lifetime // Command buffer submission info is set by each example submitInfo = VkSubmitInfo.Alloc(); submitInfo->waitSemaphoresDstStageMasks.Set(submitPipelineStages); submitInfo->waitSemaphoresDstStageMasks.Set(GetSemaphoresPtr()->PresentComplete); submitInfo->signalSemaphores = GetSemaphoresPtr()->RenderComplete; }