void loadTexture(string fileName, VkFormat format, bool forceLinearTiling) { KtxFile tex2D; using (var fs = File.OpenRead(fileName)) { tex2D = KtxFile.Load(fs, false); } texture.width = tex2D.Header.PixelWidth; texture.height = tex2D.Header.PixelHeight; texture.mipLevels = tex2D.Header.NumberOfMipmapLevels; // Get Device properites for the requested texture format VkFormatProperties formatProperties; 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 & VkFormatFeatureFlagBits.SampledImage) != VkFormatFeatureFlagBits.SampledImage) ? 1u : 0u; } if (useStaging == 1) { // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; { var info = VkBufferCreateInfo.Alloc(); info[0].size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy info[0].usage = VkBufferUsageFlagBits.TransferSrc; info[0].sharingMode = VkSharingMode.Exclusive; vkCreateBuffer(device, info, null, &stagingBuffer); } VkDeviceMemory stagingMemory; { // Get memory requirements for the staging buffer (alignment, memory type bits) var memReqs = new VkMemoryRequirements(); vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); var memAllocInfo = VkMemoryAllocateInfo.Alloc(); memAllocInfo[0].allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo[0].memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.HostCoherent); vkAllocateMemory(device, memAllocInfo, null, &stagingMemory); vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0); // Copy texture data into staging buffer IntPtr data; vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, &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 var copys = VkBufferImageCopy.Alloc((int)texture.mipLevels); for (uint i = 0, offset = 0; i < texture.mipLevels; i++) { copys[i].imageSubresource.aspectMask = VkImageAspectFlagBits.Color; copys[i].imageSubresource.mipLevel = i; copys[i].imageSubresource.baseArrayLayer = 0; copys[i].imageSubresource.layerCount = 1; copys[i].imageExtent.width = tex2D.Faces[0].Mipmaps[i].Width; copys[i].imageExtent.height = tex2D.Faces[0].Mipmaps[i].Height; copys[i].imageExtent.depth = 1; copys[i].bufferOffset = offset; offset += tex2D.Faces[0].Mipmaps[i].SizeInBytes; } { // Create optimal tiled target image var info = VkImageCreateInfo.Alloc(); info[0].imageType = VkImageType._2d;//.Image2D; info[0].format = format; info[0].mipLevels = texture.mipLevels; info[0].arrayLayers = 1; info[0].samples = VkSampleCountFlagBits._1;//.Count1; info[0].tiling = VkImageTiling.Optimal; info[0].sharingMode = VkSharingMode.Exclusive; // Set initial layout of the image to undefined info[0].initialLayout = VkImageLayout.Undefined; info[0].extent = new VkExtent3D { width = texture.width, height = texture.height, depth = 1 }; info[0].usage = VkImageUsageFlagBits.TransferDst | VkImageUsageFlagBits.Sampled; { VkImage image; vkCreateImage(device, info, null, &image); texture.image = image; } var memReqs = new VkMemoryRequirements(); vkGetImageMemoryRequirements(device, texture.image, &memReqs); var memAllocInfo = VkMemoryAllocateInfo.Alloc(); memAllocInfo[0].allocationSize = memReqs.size; memAllocInfo[0].memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); { VkDeviceMemory memory; vkAllocateMemory(device, memAllocInfo, null, &memory); texture.DeviceMemory = memory; } 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 var subresourceRange = new VkImageSubresourceRange(); // Image only contains color data subresourceRange.aspectMask = VkImageAspectFlagBits.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, VkImageAspectFlagBits.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd, stagingBuffer, texture.image, VkImageLayout.TransferDstOptimal, texture.mipLevels, copys); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; setImageLayout( copyCmd, texture.image, VkImageAspectFlagBits.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 = VkSampleCountFlagBits._1; * imageCreateInfo.tiling = VkImageTiling.Linear; * imageCreateInfo.usage = VkImageUsageFlagBits.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, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.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 = VkImageAspectFlagBits.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 var info = VkSamplerCreateInfo.Alloc(); info[0].magFilter = VkFilter.Linear; info[0].minFilter = VkFilter.Linear; info[0].mipmapMode = VkSamplerMipmapMode.Linear; info[0].addressModeU = VkSamplerAddressMode.Repeat; info[0].addressModeV = VkSamplerAddressMode.Repeat; info[0].addressModeW = VkSamplerAddressMode.Repeat; info[0].mipLodBias = 0.0f; info[0].compareOp = VkCompareOp.Never; info[0].minLod = 0.0f; // Set max level-of-detail to mip level count of the texture info[0].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 == true) { // Use max. level of anisotropy for this example info[0].maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy; info[0].anisotropyEnable = true; } else { // The Device does not support anisotropic filtering info[0].maxAnisotropy = 1.0f; info[0].anisotropyEnable = false; } info[0].borderColor = VkBorderColor.FloatOpaqueWhite; { VkSampler sampler; vkCreateSampler(device, info, null, &sampler); texture.sampler = 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 var info = VkImageViewCreateInfo.Alloc(); info[0].viewType = VkImageViewType._2d;//.Image2D; info[0].format = format; info[0].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 info[0].subresourceRange.aspectMask = VkImageAspectFlagBits.Color; info[0].subresourceRange.baseMipLevel = 0; info[0].subresourceRange.baseArrayLayer = 0; info[0].subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used info[0].subresourceRange.levelCount = (useStaging == 1) ? texture.mipLevels : 1; // The view will be based on the texture's image info[0].image = texture.image; { VkImageView view; vkCreateImageView(device, info, null, &view); texture.view = view; } } }
// Setup the offscreen framebuffer for rendering the blurred scene // The color attachment of this framebuffer will then be used to sample frame in the fragment shader of the final pass void prepareOffscreen() { offscreenPass.width = FB_DIM; offscreenPass.height = FB_DIM; // Find a suitable depth format VkFormat fbDepthFormat; VkBool32 validDepthFormat = Tools.getSupportedDepthFormat(physicalDevice, &fbDepthFormat); Debug.Assert(validDepthFormat); // Color attachment var imageInfo = VkImageCreateInfo.Alloc(); imageInfo->imageType = VkImageType._2d;// VK_IMAGE_TYPE_2D; imageInfo->format = FB_COLOR_FORMAT; imageInfo->extent.width = (uint)offscreenPass.width; imageInfo->extent.height = (uint)offscreenPass.height; imageInfo->extent.depth = 1; imageInfo->mipLevels = 1; imageInfo->arrayLayers = 1; imageInfo->samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; imageInfo->tiling = VkImageTiling.Optimal; // VK_IMAGE_TILING_OPTIMAL; // We will sample directly from the color attachment imageInfo->usage = VkImageUsageFlagBits.ColorAttachment | VkImageUsageFlagBits.Sampled; //VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; var memAlloc = VkMemoryAllocateInfo.Alloc(); VkMemoryRequirements memReqs; { VkImage image; vkCreateImage(device, imageInfo, null, &image); offscreenPass.colorAttachment.image = image; } vkGetImageMemoryRequirements(device, offscreenPass.colorAttachment.image, &memReqs); memAlloc->allocationSize = memReqs.size; memAlloc->memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); //VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); { VkDeviceMemory memory; vkAllocateMemory(device, memAlloc, null, &memory); offscreenPass.colorAttachment.mem = memory; } vkBindImageMemory(device, offscreenPass.colorAttachment.image, offscreenPass.colorAttachment.mem, 0); var colorViewInfo = VkImageViewCreateInfo.Alloc(); colorViewInfo->viewType = VkImageViewType._2d;// VK_IMAGE_VIEW_TYPE_2D; colorViewInfo->format = FB_COLOR_FORMAT; colorViewInfo->subresourceRange = new VkImageSubresourceRange(); colorViewInfo->subresourceRange.aspectMask = VkImageAspectFlagBits.Color;// VK_IMAGE_ASPECT_COLOR_BIT; colorViewInfo->subresourceRange.baseMipLevel = 0; colorViewInfo->subresourceRange.levelCount = 1; colorViewInfo->subresourceRange.baseArrayLayer = 0; colorViewInfo->subresourceRange.layerCount = 1; colorViewInfo->image = offscreenPass.colorAttachment.image; { VkImageView view; vkCreateImageView(device, colorViewInfo, null, &view); offscreenPass.colorAttachment.view = view; } // Create sampler to sample from the attachment in the fragment shader var samplerInfo = VkSamplerCreateInfo.Alloc(); samplerInfo->magFilter = VkFilter.Linear; // VK_FILTER_LINEAR; samplerInfo->minFilter = VkFilter.Linear; // VK_FILTER_LINEAR; samplerInfo->mipmapMode = VkSamplerMipmapMode.Linear; // VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo->addressModeU = VkSamplerAddressMode.ClampToEdge; // VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo->addressModeV = samplerInfo->addressModeU; samplerInfo->addressModeW = samplerInfo->addressModeU; samplerInfo->mipLodBias = 0.0f; samplerInfo->maxAnisotropy = 0; samplerInfo->minLod = 0.0f; samplerInfo->maxLod = 1.0f; samplerInfo->borderColor = VkBorderColor.FloatOpaqueWhite;// VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; { VkSampler sampler; vkCreateSampler(device, samplerInfo, null, &sampler); offscreenPass.sampler = sampler; } // Depth stencil attachment imageInfo->format = fbDepthFormat; imageInfo->usage = VkImageUsageFlagBits.DepthStencilAttachment;// VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; { VkImage image; vkCreateImage(device, imageInfo, null, &image); offscreenPass.depthAttachment.image = image; } vkGetImageMemoryRequirements(device, offscreenPass.depthAttachment.image, &memReqs); memAlloc->allocationSize = memReqs.size; memAlloc->memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); //VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); { VkDeviceMemory memory; vkAllocateMemory(device, memAlloc, null, &memory); offscreenPass.depthAttachment.mem = memory; } vkBindImageMemory(device, offscreenPass.depthAttachment.image, offscreenPass.depthAttachment.mem, 0); var depthViewInfo = VkImageViewCreateInfo.Alloc(); depthViewInfo->viewType = VkImageViewType._2d;// VK_IMAGE_VIEW_TYPE_2D; depthViewInfo->format = fbDepthFormat; depthViewInfo->flags = 0; depthViewInfo->subresourceRange = new VkImageSubresourceRange(); depthViewInfo->subresourceRange.aspectMask = VkImageAspectFlagBits.Depth | VkImageAspectFlagBits.Stencil; //VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthViewInfo->subresourceRange.baseMipLevel = 0; depthViewInfo->subresourceRange.levelCount = 1; depthViewInfo->subresourceRange.baseArrayLayer = 0; depthViewInfo->subresourceRange.layerCount = 1; depthViewInfo->image = offscreenPass.depthAttachment.image; { VkImageView view; vkCreateImageView(device, depthViewInfo, null, &view); offscreenPass.depthAttachment.view = view; } // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering var attchmentDescriptions = new VkAttachmentDescription[2]; // Color attachment attchmentDescriptions[0].format = FB_COLOR_FORMAT; attchmentDescriptions[0].samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[0].loadOp = VkAttachmentLoadOp.Clear; // VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[0].storeOp = VkAttachmentStoreOp.Store; // VK_ATTACHMENT_STORE_OP_STORE; attchmentDescriptions[0].stencilLoadOp = VkAttachmentLoadOp.DontCare; // VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[0].stencilStoreOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[0].initialLayout = VkImageLayout.Undefined; // VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[0].finalLayout = VkImageLayout.ShaderReadOnlyOptimal; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Depth attachment attchmentDescriptions[1].format = fbDepthFormat; attchmentDescriptions[1].samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[1].loadOp = VkAttachmentLoadOp.Clear; // VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[1].storeOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].stencilLoadOp = VkAttachmentLoadOp.DontCare; // VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[1].stencilStoreOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].initialLayout = VkImageLayout.Undefined; // VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[1].finalLayout = VkImageLayout.DepthStencilAttachmentOptimal; // VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; var colorReference = new VkAttachmentReference { attachment = 0, layout = VkImageLayout.ColorAttachmentOptimal// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; var depthReference = new VkAttachmentReference { attachment = 1, layout = VkImageLayout.DepthStencilAttachmentOptimal// VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpassDescription = new VkSubpassDescription(); subpassDescription.pipelineBindPoint = VkPipelineBindPoint.Graphics;// VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorResolveAttachments.SetColorAttachments(colorReference); subpassDescription.pDepthStencilAttachment = &depthReference; // Use subpass dependencies for layout transitions var dependencies = new VkSubpassDependency[2]; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VkPipelineStageFlagBits.BottomOfPipe; // VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VkPipelineStageFlagBits.ColorAttachmentOutput; // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VkAccessFlagBits.MemoryRead; // VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VkAccessFlagBits.ColorAttachmentRead | VkAccessFlagBits.ColorAttachmentWrite; //VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VkDependencyFlagBits.ByRegion;// VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VkPipelineStageFlagBits.ColorAttachmentOutput; // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VkPipelineStageFlagBits.BottomOfPipe; // VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VkAccessFlagBits.ColorAttachmentRead | VkAccessFlagBits.ColorAttachmentWrite; //VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VkAccessFlagBits.MemoryRead; // VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VkDependencyFlagBits.ByRegion; // VK_DEPENDENCY_BY_REGION_BIT; // Create the actual renderpass var renderPassInfo = VkRenderPassCreateInfo.Alloc(); renderPassInfo->attachments = attchmentDescriptions; renderPassInfo->subpasses = subpassDescription; renderPassInfo->dependencies = dependencies; { VkRenderPass renderPass; vkCreateRenderPass(device, renderPassInfo, null, &renderPass); offscreenPass.renderPass = renderPass; } var attachments = new VkImageView[] { offscreenPass.colorAttachment.view, offscreenPass.depthAttachment.view }; var framebufferInfo = VkFramebufferCreateInfo.Alloc(); framebufferInfo->renderPass = offscreenPass.renderPass; framebufferInfo->attachments = attachments; framebufferInfo->width = (uint)offscreenPass.width; framebufferInfo->height = (uint)offscreenPass.height; framebufferInfo->layers = 1; { VkFramebuffer framebuffer; vkCreateFramebuffer(device, framebufferInfo, null, &framebuffer); offscreenPass.framebuffer = framebuffer; } // Fill a descriptor for later use in a descriptor set offscreenPass.descriptorImage.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; offscreenPass.descriptorImage.imageView = offscreenPass.colorAttachment.view; offscreenPass.descriptorImage.sampler = offscreenPass.sampler; }