void loadCubemap(string filename, VkFormat format, bool forceLinearTiling) { KtxFile texCube; using (var fs = File.OpenRead(filename)) { texCube = KtxFile.Load(fs, readKeyValuePairs: false); } cubeMap.width = texCube.Header.PixelWidth; cubeMap.height = texCube.Header.PixelHeight; cubeMap.mipLevels = texCube.Header.NumberOfMipmapLevels; VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = texCube.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); byte[] allTextureData = texCube.GetAllTextureData(); fixed(byte *texCubeDataPtr = &allTextureData[0]) { Unsafe.CopyBlock(data, texCubeDataPtr, (uint)allTextureData.Length); } vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType.Image2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = cubeMap.mipLevels; imageCreateInfo.samples = VkSampleCountFlags.Count1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.usage = VkImageUsageFlags.Sampled; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = cubeMap.width, height = cubeMap.height, depth = 1 }; imageCreateInfo.usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled; // Cube faces count as array layers in Vulkan imageCreateInfo.arrayLayers = 6; // This flag is required for cube map images imageCreateInfo.flags = VkImageCreateFlags.CubeCompatible; Util.CheckResult(vkCreateImage(device, &imageCreateInfo, null, out cubeMap.image)); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, out cubeMap.deviceMemory)); Util.CheckResult(vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0)); VkCommandBuffer copyCmd = createCommandBuffer(VkCommandBufferLevel.Primary, true); // Setup buffer copy regions for each face including all of it's miplevels NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; for (uint face = 0; face < 6; face++) { for (uint level = 0; level < cubeMap.mipLevels; level++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.Color; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texCube.Faces[face].Mipmaps[level].Width; bufferCopyRegion.imageExtent.height = texCube.Faces[face].Mipmaps[level].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += texCube.Faces[face].Mipmaps[level].SizeInBytes; } } // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlags.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = cubeMap.mipLevels; subresourceRange.layerCount = 6; Tools.setImageLayout( copyCmd, cubeMap.image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, cubeMap.image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; Tools.setImageLayout( copyCmd, cubeMap.image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, cubeMap.imageLayout, subresourceRange); flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = Initializers.samplerCreateInfo(); sampler.magFilter = VkFilter.Linear; sampler.minFilter = VkFilter.Linear; sampler.mipmapMode = VkSamplerMipmapMode.Linear; sampler.addressModeU = VkSamplerAddressMode.ClampToEdge; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VkCompareOp.Never; sampler.minLod = 0.0f; sampler.maxLod = cubeMap.mipLevels; sampler.borderColor = VkBorderColor.FloatOpaqueWhite; sampler.maxAnisotropy = 1.0f; if (vulkanDevice.features.samplerAnisotropy == 1) { sampler.maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = True; } Util.CheckResult(vkCreateSampler(device, &sampler, null, out cubeMap.sampler)); // Create image view VkImageViewCreateInfo view = Initializers.imageViewCreateInfo(); // Cube map view type view.viewType = VkImageViewType.ImageCube; view.format = format; view.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; view.subresourceRange = new VkImageSubresourceRange { aspectMask = VkImageAspectFlags.Color, baseMipLevel = 0, layerCount = 1, baseArrayLayer = 0, levelCount = 1 }; // 6 array layers (faces) view.subresourceRange.layerCount = 6; // Set number of mip levels view.subresourceRange.levelCount = cubeMap.mipLevels; view.image = cubeMap.image; Util.CheckResult(vkCreateImageView(device, &view, null, out cubeMap.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, null); vkDestroyBuffer(device, stagingBuffer, null); }
/** * 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.Clear(); parts.Count = (uint)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; } NativeList <float> vertexBuffer = new NativeList <float>(); NativeList <uint> indexBuffer = new NativeList <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 = 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 = (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); }
void loadTexture(string fileName, VkFormat format, bool forceLinearTiling) { KtxFile tex2D; using (var fs = File.OpenRead(fileName)) { tex2D = KtxFile.Load(fs, false); } VkFormatProperties formatProperties; texture.width = tex2D.Header.PixelWidth; texture.height = tex2D.Header.PixelHeight; texture.mipLevels = tex2D.Header.NumberOfMipmapLevels; // Get Device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(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.) uint useStaging = 1; // Only use linear tiling if forced if (forceLinearTiling) { // Don't use linear if format is not supported for (linear) shader sampling useStaging = ((formatProperties.linearTilingFeatures & VkFormatFeatureFlags.SampledImage) != VkFormatFeatureFlags.SampledImage) ? 1u : 0u; } VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs = new VkMemoryRequirements(); if (useStaging == 1) { // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(Device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(Device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = VulkanDevice.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(Device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(Device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(Device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); byte[] allData = tex2D.GetAllTextureData(); fixed(byte *tex2DDataPtr = &allData[0]) { Unsafe.CopyBlock(data, tex2DDataPtr, (uint)allData.Length); } vkUnmapMemory(Device, stagingMemory); // Setup buffer copy regions for each mip level NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < texture.mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.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 = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType._2d; imageCreateInfo.format = format; imageCreateInfo.mipLevels = texture.mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlags._1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; // Set initial layout of the image to undefined imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = texture.width, height = texture.height, depth = 1 }; imageCreateInfo.usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled; Util.CheckResult(vkCreateImage(Device, &imageCreateInfo, null, out texture.image)); vkGetImageMemoryRequirements(Device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = VulkanDevice.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Util.CheckResult(vkAllocateMemory(Device, &memAllocInfo, null, out texture.DeviceMemory)); Util.CheckResult(vkBindImageMemory(Device, texture.image, texture.DeviceMemory, 0)); VkCommandBuffer copyCmd = base.createCommandBuffer(VkCommandBufferLevel.Primary, true); // Image barrier for optimal image // The sub resource range describes the regions of the image we will be transition VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); // Image only contains color data subresourceRange.aspectMask = VkImageAspectFlags.Color; // Start at first mip level subresourceRange.baseMipLevel = 0; // We will transition on all mip levels subresourceRange.levelCount = texture.mipLevels; // The 2D texture only has one layer subresourceRange.layerCount = 1; // Optimal image will be used as destination for the copy, so we must transfer from our // initial undefined image layout to the transfer destination layout setImageLayout( copyCmd, texture.image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd, stagingBuffer, texture.image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; setImageLayout( copyCmd, texture.image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, texture.imageLayout, subresourceRange); flushCommandBuffer(copyCmd, Queue, true); // Clean up staging resources vkFreeMemory(Device, stagingMemory, null); vkDestroyBuffer(Device, 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.) * * VkImage mappableImage; * VkDeviceMemory mappableMemory; * * // Load mip map level 0 to linear tiling image * VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); * imageCreateInfo.imageType = VkImageType._2d; * imageCreateInfo.format = format; * imageCreateInfo.mipLevels = 1; * imageCreateInfo.arrayLayers = 1; * imageCreateInfo.samples = VkSampleCountFlags._1; * imageCreateInfo.tiling = VkImageTiling.Linear; * imageCreateInfo.usage = VkImageUsageFlags.Sampled; * imageCreateInfo.sharingMode = VkSharingMode.Exclusive; * imageCreateInfo.initialLayout = VkImageLayout.Preinitialized; * imageCreateInfo.extent = new VkExtent3D { width = texture.width, height = texture.height, depth = 1 }; * Util.CheckResult(vkCreateImage(Device, &imageCreateInfo, null, &mappableImage)); * * // Get memory requirements for this image * // like size and alignment * vkGetImageMemoryRequirements(Device, 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 = VulkanDevice.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); * * // Allocate host memory * Util.CheckResult(vkAllocateMemory(Device, &memAllocInfo, null, &mappableMemory)); * * // Bind allocated image for use * Util.CheckResult(vkBindImageMemory(Device, mappableImage, mappableMemory, 0)); * * // Get sub resource layout * // Mip map count, array layer, etc. * VkImageSubresource subRes = new VkImageSubresource(); * subRes.aspectMask = VkImageAspectFlags.Color; * * VkSubresourceLayout subResLayout; * void* data; * * // Get sub resources layout * // Includes row pitch, size offsets, etc. * vkGetImageSubresourceLayout(Device, mappableImage, &subRes, &subResLayout); * * // Map image memory * Util.CheckResult(vkMapMemory(Device, mappableMemory, 0, memReqs.size, 0, &data)); * * // Copy image data into memory * memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); * * vkUnmapMemory(Device, mappableMemory); * * // Linear tiled images don't need to be staged * // and can be directly used as textures * texture.image = mappableImage; * texture.DeviceMemory = mappableMemory; * texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; * * VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); * * // Setup image memory barrier transfer image to shader read layout * * // The sub resource range describes the regions of the image we will be transition * VkImageSubresourceRange subresourceRange = { }; * // Image only contains color data * subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; * // Start at first mip level * subresourceRange.baseMipLevel = 0; * // Only one mip level, most implementations won't support more for linear tiled images * subresourceRange.levelCount = 1; * // The 2D texture only has one layer * subresourceRange.layerCount = 1; * * setImageLayout( * copyCmd, * texture.image, * VK_IMAGE_ASPECT_COLOR_BIT, * VK_IMAGE_LAYOUT_PREINITIALIZED, * texture.imageLayout, * subresourceRange); * * VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); */ } // Create sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the // texture data // This means you could have multiple sampler objects // for the same texture with different settings // Similar to the samplers available with OpenGL 3.3 VkSamplerCreateInfo sampler = Initializers.samplerCreateInfo(); sampler.magFilter = VkFilter.Linear; sampler.minFilter = VkFilter.Linear; sampler.mipmapMode = VkSamplerMipmapMode.Linear; sampler.addressModeU = VkSamplerAddressMode.Repeat; sampler.addressModeV = VkSamplerAddressMode.Repeat; sampler.addressModeW = VkSamplerAddressMode.Repeat; sampler.mipLodBias = 0.0f; sampler.compareOp = VkCompareOp.Never; sampler.minLod = 0.0f; // Set max level-of-detail to mip level count of the texture sampler.maxLod = (useStaging == 1) ? (float)texture.mipLevels : 0.0f; // Enable anisotropic filtering // This feature is optional, so we must check if it's supported on the Device if (VulkanDevice.Features.samplerAnisotropy == 1) { // Use max. level of anisotropy for this example sampler.maxAnisotropy = VulkanDevice.Properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = True; } else { // The Device does not support anisotropic filtering sampler.maxAnisotropy = 1.0f; sampler.anisotropyEnable = False; } sampler.borderColor = VkBorderColor.FloatOpaqueWhite; Util.CheckResult(vkCreateSampler(Device, ref sampler, null, out texture.sampler)); // 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 view = Initializers.imageViewCreateInfo(); view.viewType = VkImageViewType._2d; view.format = format; view.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image view.subresourceRange.aspectMask = VkImageAspectFlags.Color; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = (useStaging == 1) ? texture.mipLevels : 1; // The view will be based on the texture's image view.image = texture.image; Util.CheckResult(vkCreateImageView(Device, &view, null, out texture.view)); }
/** * 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, VkImageUsageFlags imageUsageFlags = VkImageUsageFlags.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, out 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 = Initializers.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 = Initializers.bufferCreateInfo(); bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(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, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device.LogicalDevice, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device.LogicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&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 NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.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 = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType.Image2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlags.Count1; 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 & VkImageUsageFlags.TransferDst) == 0) { imageCreateInfo.usage |= VkImageUsageFlags.TransferDst; } Util.CheckResult(vkCreateImage(device.LogicalDevice, &imageCreateInfo, null, out image)); vkGetImageMemoryRequirements(device.LogicalDevice, image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Util.CheckResult(vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, out deviceMemory)); Util.CheckResult(vkBindImageMemory(device.LogicalDevice, image, deviceMemory, 0)); VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlags.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, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd, stagingBuffer, image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all mip levels have been copied this.imageLayout = imageLayout; Tools.setImageLayout( copyCmd, image, VkImageAspectFlags.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 = VkSamplerCreateInfo.New(); 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; Util.CheckResult(vkCreateSampler(device.LogicalDevice, &samplerCreateInfo, null, out sampler)); // 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 = VkImageViewCreateInfo.New(); viewCreateInfo.viewType = VkImageViewType.Image2D; viewCreateInfo.format = format; viewCreateInfo.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; viewCreateInfo.subresourceRange = new VkImageSubresourceRange { aspectMask = VkImageAspectFlags.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; Util.CheckResult(vkCreateImageView(device.LogicalDevice, &viewCreateInfo, null, out view)); // Update descriptor image info member that can be used for setting up descriptor sets updateDescriptor(); }
public VkResult CreateLogicalDevice( VkPhysicalDeviceFeatures enabledFeatures, NativeList <IntPtr> enabledExtensions, bool useSwapChain = true, VkQueueFlags requestedQueueTypes = VkQueueFlags.Graphics | VkQueueFlags.Compute) { // Desired queues need to be requested upon logical device creation // Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application // requests different queue types using (NativeList <VkDeviceQueueCreateInfo> queueCreateInfos = new NativeList <VkDeviceQueueCreateInfo>()) { float defaultQueuePriority = 0.0f; // Graphics queue if ((requestedQueueTypes & VkQueueFlags.Graphics) != 0) { QFIndices.Graphics = GetQueueFamilyIndex(VkQueueFlags.Graphics); VkDeviceQueueCreateInfo queueInfo = new VkDeviceQueueCreateInfo(); queueInfo.sType = VkStructureType.DeviceQueueCreateInfo; queueInfo.queueFamilyIndex = QFIndices.Graphics; queueInfo.queueCount = 1; queueInfo.pQueuePriorities = &defaultQueuePriority; queueCreateInfos.Add(queueInfo); } else { QFIndices.Graphics = (uint)NullHandle; } // Dedicated compute queue if ((requestedQueueTypes & VkQueueFlags.Compute) != 0) { QFIndices.Compute = GetQueueFamilyIndex(VkQueueFlags.Compute); if (QFIndices.Compute != QFIndices.Graphics) { // If compute family index differs, we need an additional queue create info for the compute queue VkDeviceQueueCreateInfo queueInfo = new VkDeviceQueueCreateInfo(); queueInfo.sType = VkStructureType.DeviceQueueCreateInfo; queueInfo.queueFamilyIndex = QFIndices.Compute; queueInfo.queueCount = 1; queueInfo.pQueuePriorities = &defaultQueuePriority; queueCreateInfos.Add(queueInfo); } } else { // Else we use the same queue QFIndices.Compute = QFIndices.Graphics; } // Dedicated transfer queue if ((requestedQueueTypes & VkQueueFlags.Transfer) != 0) { QFIndices.Transfer = GetQueueFamilyIndex(VkQueueFlags.Transfer); if (QFIndices.Transfer != QFIndices.Graphics && QFIndices.Transfer != QFIndices.Compute) { // If compute family index differs, we need an additional queue create info for the transfer queue VkDeviceQueueCreateInfo queueInfo = new VkDeviceQueueCreateInfo(); queueInfo.sType = VkStructureType.DeviceQueueCreateInfo; queueInfo.queueFamilyIndex = QFIndices.Transfer; queueInfo.queueCount = 1; queueInfo.pQueuePriorities = &defaultQueuePriority; queueCreateInfos.Add(queueInfo); } } else { // Else we use the same queue QFIndices.Transfer = QFIndices.Graphics; } // Create the logical device representation using (NativeList <IntPtr> deviceExtensions = new NativeList <IntPtr>(enabledExtensions)) { if (useSwapChain) { // If the device will be used for presenting to a display via a swapchain we need to request the swapchain extension deviceExtensions.Add(Strings.VK_KHR_SWAPCHAIN_EXTENSION_NAME); } VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo.New(); deviceCreateInfo.queueCreateInfoCount = queueCreateInfos.Count; deviceCreateInfo.pQueueCreateInfos = (VkDeviceQueueCreateInfo *)queueCreateInfos.Data.ToPointer(); deviceCreateInfo.pEnabledFeatures = &enabledFeatures; if (deviceExtensions.Count > 0) { deviceCreateInfo.enabledExtensionCount = deviceExtensions.Count; deviceCreateInfo.ppEnabledExtensionNames = (byte **)deviceExtensions.Data.ToPointer(); } VkResult result = vkCreateDevice(PhysicalDevice, &deviceCreateInfo, null, out _logicalDevice); if (result == VkResult.Success) { // Create a default command pool for graphics command buffers CommandPool = CreateCommandPool(QFIndices.Graphics); } return(result); } } }