Пример #1
0
        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;
                }
            }
        }
Пример #2
0
        // 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;
        }