Ejemplo n.º 1
0
        protected void CopyImage(VkImage srcImage, uint srcMipLevel, uint srcArrayLayer, VkImage dstImage, uint dstMipLevel, uint dstArrayLayer, uint width, uint height)
        {
            VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers();

            srcSubresource.mipLevel       = srcMipLevel;
            srcSubresource.aspectMask     = VkImageAspectFlags.Color;
            srcSubresource.baseArrayLayer = srcArrayLayer;
            srcSubresource.layerCount     = 1;

            VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers();

            dstSubresource.mipLevel       = dstMipLevel;
            dstSubresource.aspectMask     = VkImageAspectFlags.Color;
            dstSubresource.baseArrayLayer = dstArrayLayer;
            dstSubresource.layerCount     = 1;

            VkImageCopy region = new VkImageCopy();

            region.dstSubresource = dstSubresource;
            region.srcSubresource = srcSubresource;
            region.extent.width   = width;
            region.extent.height  = height;
            region.extent.depth   = 1;

            VkCommandBuffer copyCmd = BeginOneTimeCommands();

            vkCmdCopyImage(copyCmd, srcImage, VkImageLayout.TransferSrcOptimal, dstImage, VkImageLayout.TransferDstOptimal, 1, ref region);
            EndOneTimeCommands(copyCmd);
        }
Ejemplo n.º 2
0
        protected void CopyImage(
            VkImage srcImage,
            uint srcMipLevel,
            VkImage dstImage,
            uint dstMipLevel,
            uint width,
            uint height,
            uint baseArrayLayer = 0)
        {
            VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers();

            srcSubresource.mipLevel       = srcMipLevel;
            srcSubresource.layerCount     = 1;
            srcSubresource.aspectMask     = VkImageAspectFlags.Color;
            srcSubresource.baseArrayLayer = 0;

            VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers();

            dstSubresource.mipLevel       = dstMipLevel;
            dstSubresource.baseArrayLayer = baseArrayLayer;
            dstSubresource.layerCount     = 1;
            dstSubresource.aspectMask     = VkImageAspectFlags.Color;

            VkImageCopy region = new VkImageCopy();

            region.dstSubresource = dstSubresource;
            region.srcSubresource = srcSubresource;
            region.extent.width   = width;
            region.extent.height  = height;
            region.extent.depth   = 1;

            vkCmdCopyImage(
                _cb,
                srcImage,
                VkImageLayout.TransferSrcOptimal,
                dstImage,
                VkImageLayout.TransferDstOptimal,
                1,
                ref region);
        }
Ejemplo n.º 3
0
        internal static void CopyTextureCore_VkCommandBuffer(
            VkCommandBuffer cb,
            Texture source,
            uint srcX, uint srcY, uint srcZ,
            uint srcMipLevel,
            uint srcBaseArrayLayer,
            Texture destination,
            uint dstX, uint dstY, uint dstZ,
            uint dstMipLevel,
            uint dstBaseArrayLayer,
            uint width, uint height, uint depth,
            uint layerCount)
        {
            VkTexture srcVkTexture = Util.AssertSubtype <Texture, VkTexture>(source);
            VkTexture dstVkTexture = Util.AssertSubtype <Texture, VkTexture>(destination);

            bool sourceIsStaging = (source.Usage & TextureUsage.Staging) == TextureUsage.Staging;
            bool destIsStaging   = (destination.Usage & TextureUsage.Staging) == TextureUsage.Staging;

            if (!sourceIsStaging && !destIsStaging)
            {
                VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers
                {
                    aspectMask     = VkImageAspectFlags.Color,
                    layerCount     = layerCount,
                    mipLevel       = srcMipLevel,
                    baseArrayLayer = srcBaseArrayLayer
                };

                VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers
                {
                    aspectMask     = VkImageAspectFlags.Color,
                    layerCount     = layerCount,
                    mipLevel       = dstMipLevel,
                    baseArrayLayer = dstBaseArrayLayer
                };

                VkImageCopy region = new VkImageCopy
                {
                    srcOffset = new VkOffset3D {
                        x = (int)srcX, y = (int)srcY, z = (int)srcZ
                    },
                    dstOffset = new VkOffset3D {
                        x = (int)dstX, y = (int)dstY, z = (int)dstZ
                    },
                    srcSubresource = srcSubresource,
                    dstSubresource = dstSubresource,
                    extent         = new VkExtent3D {
                        width = width, height = height, depth = depth
                    }
                };

                srcVkTexture.TransitionImageLayout(
                    cb,
                    srcMipLevel,
                    1,
                    srcBaseArrayLayer,
                    layerCount,
                    VkImageLayout.TransferSrcOptimal);

                dstVkTexture.TransitionImageLayout(
                    cb,
                    dstMipLevel,
                    1,
                    dstBaseArrayLayer,
                    layerCount,
                    VkImageLayout.TransferDstOptimal);

                vkCmdCopyImage(
                    cb,
                    srcVkTexture.OptimalDeviceImage,
                    VkImageLayout.TransferSrcOptimal,
                    dstVkTexture.OptimalDeviceImage,
                    VkImageLayout.TransferDstOptimal,
                    1,
                    ref region);
            }
            else if (sourceIsStaging && !destIsStaging)
            {
                Vulkan.VkBuffer     srcBuffer = srcVkTexture.StagingBuffer;
                VkSubresourceLayout srcLayout = srcVkTexture.GetSubresourceLayout(
                    srcVkTexture.CalculateSubresource(srcMipLevel, srcBaseArrayLayer));
                VkImage dstImage = dstVkTexture.OptimalDeviceImage;
                dstVkTexture.TransitionImageLayout(
                    cb,
                    dstMipLevel,
                    1,
                    dstBaseArrayLayer,
                    layerCount,
                    VkImageLayout.TransferDstOptimal);

                VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers
                {
                    aspectMask     = VkImageAspectFlags.Color,
                    layerCount     = layerCount,
                    mipLevel       = dstMipLevel,
                    baseArrayLayer = dstBaseArrayLayer
                };

                Util.GetMipDimensions(srcVkTexture, srcMipLevel, out uint mipWidth, out uint mipHeight, out uint mipDepth);
                uint blockSize         = FormatHelpers.IsCompressedFormat(srcVkTexture.Format) ? 4u : 1u;
                uint bufferRowLength   = Math.Max(mipWidth, blockSize);
                uint bufferImageHeight = Math.Max(mipHeight, blockSize);
                uint compressedX       = srcX / blockSize;
                uint compressedY       = srcY / blockSize;
                uint blockSizeInBytes  = blockSize == 1
                    ? FormatHelpers.GetSizeInBytes(srcVkTexture.Format)
                    : FormatHelpers.GetBlockSizeInBytes(srcVkTexture.Format);
                uint rowPitch   = FormatHelpers.GetRowPitch(bufferRowLength, srcVkTexture.Format);
                uint depthPitch = FormatHelpers.GetDepthPitch(rowPitch, bufferImageHeight, srcVkTexture.Format);

                VkBufferImageCopy regions = new VkBufferImageCopy
                {
                    bufferOffset = srcLayout.offset
                                   + (srcZ * depthPitch)
                                   + (compressedY * rowPitch)
                                   + (compressedX * blockSizeInBytes),
                    bufferRowLength   = bufferRowLength,
                    bufferImageHeight = bufferImageHeight,
                    imageExtent       = new VkExtent3D {
                        width = width, height = height, depth = depth
                    },
                    imageOffset = new VkOffset3D {
                        x = (int)dstX, y = (int)dstY, z = (int)dstZ
                    },
                    imageSubresource = dstSubresource
                };

                vkCmdCopyBufferToImage(cb, srcBuffer, dstImage, VkImageLayout.TransferDstOptimal, 1, ref regions);
            }
            else if (!sourceIsStaging && destIsStaging)
            {
                VkImage srcImage = srcVkTexture.OptimalDeviceImage;
                srcVkTexture.TransitionImageLayout(
                    cb,
                    srcMipLevel,
                    1,
                    srcBaseArrayLayer,
                    layerCount,
                    VkImageLayout.TransferSrcOptimal);

                Vulkan.VkBuffer     dstBuffer = dstVkTexture.StagingBuffer;
                VkSubresourceLayout dstLayout = dstVkTexture.GetSubresourceLayout(
                    dstVkTexture.CalculateSubresource(dstMipLevel, dstBaseArrayLayer));
                VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers
                {
                    aspectMask     = VkImageAspectFlags.Color,
                    layerCount     = layerCount,
                    mipLevel       = srcMipLevel,
                    baseArrayLayer = srcBaseArrayLayer
                };

                Util.GetMipDimensions(dstVkTexture, dstMipLevel, out uint mipWidth, out uint mipHeight, out uint mipDepth);
                VkBufferImageCopy region = new VkBufferImageCopy
                {
                    bufferRowLength   = mipWidth,
                    bufferImageHeight = mipHeight,
                    bufferOffset      = dstLayout.offset + (dstX * FormatHelpers.GetSizeInBytes(dstVkTexture.Format)),
                    imageExtent       = new VkExtent3D {
                        width = width, height = height, depth = depth
                    },
                    imageOffset = new VkOffset3D {
                        x = (int)dstX, y = (int)dstY, z = (int)dstZ
                    },
                    imageSubresource = srcSubresource
                };

                vkCmdCopyImageToBuffer(cb, srcImage, VkImageLayout.TransferSrcOptimal, dstBuffer, 1, ref region);
            }
            else
            {
                Debug.Assert(sourceIsStaging && destIsStaging);
                Vulkan.VkBuffer     srcBuffer = srcVkTexture.StagingBuffer;
                VkSubresourceLayout srcLayout = srcVkTexture.GetSubresourceLayout(
                    srcVkTexture.CalculateSubresource(srcMipLevel, srcBaseArrayLayer));
                Vulkan.VkBuffer     dstBuffer = dstVkTexture.StagingBuffer;
                VkSubresourceLayout dstLayout = dstVkTexture.GetSubresourceLayout(
                    dstVkTexture.CalculateSubresource(dstMipLevel, dstBaseArrayLayer));

                uint zLimit = Math.Max(depth, layerCount);
                if (!FormatHelpers.IsCompressedFormat(source.Format))
                {
                    uint pixelSize = FormatHelpers.GetSizeInBytes(srcVkTexture.Format);
                    for (uint zz = 0; zz < zLimit; zz++)
                    {
                        for (uint yy = 0; yy < height; yy++)
                        {
                            VkBufferCopy region = new VkBufferCopy
                            {
                                srcOffset = srcLayout.offset
                                            + srcLayout.depthPitch * (zz + srcZ)
                                            + srcLayout.rowPitch * (yy + srcY)
                                            + pixelSize * srcX,
                                dstOffset = dstLayout.offset
                                            + dstLayout.depthPitch * (zz + dstZ)
                                            + dstLayout.rowPitch * (yy + dstY)
                                            + pixelSize * dstX,
                                size = width * pixelSize,
                            };

                            vkCmdCopyBuffer(cb, srcBuffer, dstBuffer, 1, ref region);
                        }
                    }
                }
                else // IsCompressedFormat
                {
                    uint denseRowSize     = FormatHelpers.GetRowPitch(width, source.Format);
                    uint numRows          = FormatHelpers.GetNumRows(height, source.Format);
                    uint compressedSrcX   = srcX / 4;
                    uint compressedSrcY   = srcY / 4;
                    uint compressedDstX   = dstX / 4;
                    uint compressedDstY   = dstY / 4;
                    uint blockSizeInBytes = FormatHelpers.GetBlockSizeInBytes(source.Format);

                    for (uint zz = 0; zz < zLimit; zz++)
                    {
                        for (uint row = 0; row < numRows; row++)
                        {
                            VkBufferCopy region = new VkBufferCopy
                            {
                                srcOffset = srcLayout.offset
                                            + srcLayout.depthPitch * (zz + srcZ)
                                            + srcLayout.rowPitch * (row + compressedSrcY)
                                            + blockSizeInBytes * compressedSrcX,
                                dstOffset = dstLayout.offset
                                            + dstLayout.depthPitch * (zz + dstZ)
                                            + dstLayout.rowPitch * (row + compressedDstY)
                                            + blockSizeInBytes * compressedDstX,
                                size = denseRowSize,
                            };

                            vkCmdCopyBuffer(cb, srcBuffer, dstBuffer, 1, ref region);
                        }
                    }
                }
            }
        }
Ejemplo n.º 4
0
        protected override void CopyTextureCore(
            Texture source,
            uint srcX, uint srcY, uint srcZ,
            uint srcMipLevel,
            uint srcBaseArrayLayer,
            Texture destination,
            uint dstX, uint dstY, uint dstZ,
            uint dstMipLevel,
            uint dstBaseArrayLayer,
            uint width, uint height, uint depth,
            uint layerCount)
        {
            EnsureNoRenderPass();

            bool sourceIsStaging = (source.Usage & TextureUsage.Staging) == TextureUsage.Staging;
            bool destIsStaging   = (destination.Usage & TextureUsage.Staging) == TextureUsage.Staging;

            if ((destIsStaging || sourceIsStaging) && layerCount > 1)
            {
                // Need to issue one copy per array layer.
                throw new NotImplementedException();
            }

            VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers
            {
                aspectMask     = VkImageAspectFlags.Color,
                layerCount     = layerCount,
                mipLevel       = sourceIsStaging ? 0 : srcMipLevel,
                baseArrayLayer = sourceIsStaging ? 0 : srcBaseArrayLayer
            };

            VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers
            {
                aspectMask     = VkImageAspectFlags.Color,
                layerCount     = layerCount,
                mipLevel       = destIsStaging ? 0 : dstMipLevel,
                baseArrayLayer = destIsStaging ? 0 : dstBaseArrayLayer
            };

            VkImageCopy region = new VkImageCopy
            {
                srcOffset = new VkOffset3D {
                    x = (int)srcX, y = (int)srcY, z = (int)srcZ
                },
                dstOffset = new VkOffset3D {
                    x = (int)dstX, y = (int)dstY, z = (int)dstZ
                },
                srcSubresource = srcSubresource,
                dstSubresource = dstSubresource,
                extent         = new VkExtent3D {
                    width = width, height = height, depth = depth
                }
            };

            VkTexture srcVkTexture = Util.AssertSubtype <Texture, VkTexture>(source);
            VkTexture dstVkTexture = Util.AssertSubtype <Texture, VkTexture>(destination);

            srcVkTexture.TransitionImageLayout(
                _cb,
                srcMipLevel,
                1,
                srcBaseArrayLayer,
                layerCount,
                VkImageLayout.TransferSrcOptimal);

            dstVkTexture.TransitionImageLayout(
                _cb,
                dstMipLevel,
                1,
                dstBaseArrayLayer,
                layerCount,
                VkImageLayout.TransferDstOptimal);

            VkImage srcImage = sourceIsStaging
                ? srcVkTexture.GetStagingImage(source.CalculateSubresource(srcMipLevel, srcBaseArrayLayer))
                : srcVkTexture.OptimalDeviceImage;
            VkImage dstImage = destIsStaging
                ? dstVkTexture.GetStagingImage(destination.CalculateSubresource(dstMipLevel, dstBaseArrayLayer))
                : dstVkTexture.OptimalDeviceImage;

            vkCmdCopyImage(
                _cb,
                srcImage,
                VkImageLayout.TransferSrcOptimal,
                dstImage,
                VkImageLayout.TransferDstOptimal,
                1,
                ref region);

            _referencedResources.Add(srcVkTexture);
            _referencedResources.Add(dstVkTexture);
        }
Ejemplo n.º 5
0
 public void CopyImage(Image srcImage, VkImageLayout srcImageLayout, Image dstImage, VkImageLayout dstImageLayout, VkImageCopy regions)
 {
     unsafe
     {
         Device.Commands.cmdCopyImage(commandBuffer,
                                      srcImage.Native, srcImageLayout,
                                      dstImage.Native, dstImageLayout,
                                      1, (IntPtr)(&regions));
     }
 }
Ejemplo n.º 6
0
        void Preprocess()
        {
            Shader shader = Resources.Load <Shader>("shaders/brdf.shader");

            {
                Pass pass = shader.GetPass("SpMap");

                uint numMipTailLevels = (uint)kEnvMapLevels - 1;

                // Compute pre-filtered specular environment map.
                var specializationInfo = new SpecializationInfo(new VkSpecializationMapEntry(0, 0, sizeof(uint)));
                specializationInfo.Write(0, numMipTailLevels);
                pass.ComputeShader.SpecializationInfo = specializationInfo;
                DescriptorSetLayout resLayout = pass.GetResourceLayout(0);

                spSet = new DescriptorSet(resLayout);

                CommandBuffer commandBuffer = Graphics.BeginPrimaryCmd();

                // Copy base mipmap level into destination environment map.
                {
                    Span <VkImageMemoryBarrier> preCopyBarriers = stackalloc[]
                    {
                        new VkImageMemoryBarrier(cubeMap.image, 0, VkAccessFlags.TransferRead, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferSrcOptimal, VkImageAspectFlags.Color, 0, 1),
                        new VkImageMemoryBarrier(envMap.image, 0, VkAccessFlags.TransferWrite, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal),
                    };

                    Span <VkImageMemoryBarrier> postCopyBarriers = stackalloc[]
                    {
                        new VkImageMemoryBarrier(cubeMap.image, VkAccessFlags.TransferRead, VkAccessFlags.ShaderRead, VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal, VkImageAspectFlags.Color, 0, 1),
                        new VkImageMemoryBarrier(envMap.image, VkAccessFlags.TransferWrite, VkAccessFlags.ShaderWrite, VkImageLayout.TransferDstOptimal, VkImageLayout.General),
                    };

                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.TopOfPipe, VkPipelineStageFlags.Transfer, preCopyBarriers);

                    VkImageCopy copyRegion = new VkImageCopy
                    {
                        extent = new VkExtent3D(envMap.width, envMap.height, 1)
                    };

                    copyRegion.srcSubresource.aspectMask = VkImageAspectFlags.Color;
                    copyRegion.srcSubresource.layerCount = envMap.layers;
                    copyRegion.dstSubresource            = copyRegion.srcSubresource;

                    commandBuffer.CopyImage(cubeMap.image, VkImageLayout.TransferSrcOptimal, envMap.image, VkImageLayout.TransferDstOptimal, ref copyRegion);
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ComputeShader, postCopyBarriers);

                    // Pre-filter rest of the mip-chain.
                    List <ImageView> envTextureMipTailViews = new List <ImageView>();

                    var inputTexture = new VkDescriptorImageInfo(computeSampler, cubeMap.imageView, VkImageLayout.ShaderReadOnlyOptimal);
                    spSet.Bind(0, ref inputTexture);

                    Span <VkDescriptorImageInfo> envTextureMipTailDescriptors = stackalloc VkDescriptorImageInfo[(int)numMipTailLevels];
                    for (uint level = 0; level < numMipTailLevels; ++level)
                    {
                        var view = ImageView.Create(envMap.image, VkImageViewType.ImageCube, VkFormat.R16G16B16A16SFloat, VkImageAspectFlags.Color, level + 1, 1, 0, envMap.image.arrayLayers);
                        envTextureMipTailViews.Add(view);
                        envTextureMipTailDescriptors[(int)level] = new VkDescriptorImageInfo(VkSampler.Null, view, VkImageLayout.General);
                    }

                    spSet.Bind(1, envTextureMipTailDescriptors);
                    spSet.UpdateSets();

                    commandBuffer.BindComputePipeline(pass);
                    commandBuffer.BindComputeResourceSet(pass.PipelineLayout, 0, spSet);

                    float deltaRoughness = 1.0f / Math.Max((float)numMipTailLevels, 1.0f);
                    for (uint level = 1, size = kEnvMapSize / 2; level < kEnvMapLevels; ++level, size /= 2)
                    {
                        uint numGroups = Math.Max(1, size / 32);

                        var pushConstants = new SpecularFilterPushConstants {
                            level = level - 1, roughness = level * deltaRoughness
                        };
                        commandBuffer.PushConstants(pass.PipelineLayout, VkShaderStageFlags.Compute, 0, ref pushConstants);
                        commandBuffer.Dispatch(numGroups, numGroups, 6);
                    }

                    var barrier = new VkImageMemoryBarrier(envMap.image, VkAccessFlags.ShaderWrite, 0, VkImageLayout.General, VkImageLayout.ShaderReadOnlyOptimal);
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.BottomOfPipe, ref barrier);
                }

                Graphics.EndPrimaryCmd(commandBuffer);
            }

            // Compute diffuse irradiance cubemap
            {
                Pass pass = shader.GetPass("IrMap");
                DescriptorSetLayout resLayout = pass.GetResourceLayout(0);
                irSet = new DescriptorSet(resLayout, cubeMap, irMap);

                CommandBuffer commandBuffer = Graphics.BeginPrimaryCmd();
                {
                    Span <VkImageMemoryBarrier> barriers = stackalloc [] { new VkImageMemoryBarrier(irMap.image, 0, VkAccessFlags.ShaderWrite, VkImageLayout.Undefined, VkImageLayout.General) };
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.TopOfPipe, VkPipelineStageFlags.ComputeShader, barriers);

                    commandBuffer.BindComputePipeline(pass);
                    commandBuffer.BindComputeResourceSet(pass.PipelineLayout, 0, irSet);
                    commandBuffer.Dispatch(kIrradianceMapSize / 32, kIrradianceMapSize / 32, 6);

                    Span <VkImageMemoryBarrier> postDispatchBarrier = stackalloc [] { new VkImageMemoryBarrier(irMap.image, VkAccessFlags.ShaderWrite, 0, VkImageLayout.General, VkImageLayout.ShaderReadOnlyOptimal) };
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.BottomOfPipe, postDispatchBarrier);
                }

                Graphics.EndPrimaryCmd(commandBuffer);
            }

            // Compute Cook-Torrance BRDF 2D LUT for split-sum approximation.
            {
                var pass = shader.GetPass("BrdfLUT");
                DescriptorSetLayout resLayout = pass.GetResourceLayout(0);
                brdfLUTSet = new DescriptorSet(resLayout, brdfLUT);

                CommandBuffer commandBuffer = Graphics.BeginPrimaryCmd();
                {
                    Span <VkImageMemoryBarrier> barriers = stackalloc [] { new VkImageMemoryBarrier(brdfLUT.image, 0, VkAccessFlags.ShaderWrite, VkImageLayout.Undefined, VkImageLayout.General) };
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.TopOfPipe, VkPipelineStageFlags.ComputeShader, barriers);

                    commandBuffer.BindComputePipeline(pass);
                    commandBuffer.BindComputeResourceSet(pass.PipelineLayout, 0, brdfLUTSet);
                    commandBuffer.Dispatch(kBRDF_LUT_Size / 32, kBRDF_LUT_Size / 32, 6);

                    Span <VkImageMemoryBarrier> postDispatchBarrier = stackalloc [] { new VkImageMemoryBarrier(brdfLUT.image, VkAccessFlags.ShaderWrite, 0, VkImageLayout.General, VkImageLayout.ShaderReadOnlyOptimal) };
                    commandBuffer.PipelineBarrier(VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.BottomOfPipe, postDispatchBarrier);
                }

                Graphics.EndPrimaryCmd(commandBuffer);
            }
        }

        int selected = 0;
Ejemplo n.º 7
0
        public Image generateCubeMap(Queue staggingQ, CommandPool cmdPool, CBTarget target)
        {
            const float deltaPhi   = (2.0f * (float)Math.PI) / 180.0f;
            const float deltaTheta = (0.5f * (float)Math.PI) / 64.0f;

            VkFormat format = VkFormat.R32g32b32a32Sfloat;
            uint     dim    = 64;

            if (target == CBTarget.PREFILTEREDENV)
            {
                format = VkFormat.R16g16b16a16Sfloat;
                dim    = 512;
            }

            uint numMips = (uint)Math.Floor(Math.Log(dim, 2)) + 1;

            Image imgFbOffscreen = new Image(Dev, format, VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment,
                                             VkMemoryPropertyFlags.DeviceLocal, dim, dim);

            imgFbOffscreen.SetName("offscreenfb");
            imgFbOffscreen.CreateView();

            Image cmap = new Image(Dev, format, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled,
                                   VkMemoryPropertyFlags.DeviceLocal, dim, dim, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal,
                                   numMips, 6, 1, VkImageCreateFlags.CubeCompatible);

            if (target == CBTarget.PREFILTEREDENV)
            {
                cmap.SetName("prefilterenvmap");
            }
            else
            {
                cmap.SetName("irradianceCube");
            }
            cmap.CreateView(VkImageViewType.Cube, VkImageAspectFlags.Color, 6, 0, numMips);
            cmap.CreateSampler(VkSamplerAddressMode.ClampToEdge);

            DescriptorPool dsPool = new DescriptorPool(Dev, 2, new VkDescriptorPoolSize(VkDescriptorType.CombinedImageSampler));

            DescriptorSetLayout dsLayout = new DescriptorSetLayout(Dev,
                                                                   new VkDescriptorSetLayoutBinding(0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));


            GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault(VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false);

            cfg.Layout = new PipelineLayout(Dev, dsLayout);
            cfg.Layout.AddPushConstants(
                new VkPushConstantRange(VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf <Matrix4x4> () + 8));

            cfg.RenderPass = new RenderPass(Dev);
            cfg.RenderPass.AddAttachment(format, VkImageLayout.ColorAttachmentOptimal);
            cfg.RenderPass.ClearValues.Add(new VkClearValue {
                color = new VkClearColorValue(0, 0, 0)
            });
            cfg.RenderPass.AddSubpass(new SubPass(VkImageLayout.ColorAttachmentOptimal));

            cfg.AddVertexBinding(0, 3 * sizeof(float));
            cfg.AddVertexAttributes(0, VkFormat.R32g32b32Sfloat);

            cfg.AddShader(VkShaderStageFlags.Vertex, "shaders/filtercube.vert.spv");
            if (target == CBTarget.PREFILTEREDENV)
            {
                cfg.AddShader(VkShaderStageFlags.Fragment, "shaders/prefilterenvmap.frag.spv");
            }
            else
            {
                cfg.AddShader(VkShaderStageFlags.Fragment, "shaders/irradiancecube.frag.spv");
            }

            Matrix4x4[] matrices =
            {
                // POSITIVE_X
                Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(90)),
                // NEGATIVE_X
                Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(-90)),
                // POSITIVE_Y
                Matrix4x4.CreateRotationX(Utils.DegreesToRadians(-90)),
                // NEGATIVE_Y
                Matrix4x4.CreateRotationX(Utils.DegreesToRadians(90)),
                // POSITIVE_Z
                Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)),
                // NEGATIVE_Z
                Matrix4x4.CreateRotationZ(Utils.DegreesToRadians(180))
            };

            VkImageSubresourceRange subRes = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, numMips, 0, 6);

            using (GraphicPipeline pl = new GraphicPipeline(cfg)) {
                DescriptorSet       dset     = dsPool.Allocate(dsLayout);
                DescriptorSetWrites dsUpdate = new DescriptorSetWrites(dsLayout);
                dsUpdate.Write(Dev, dset, cubemap.Descriptor);
                Dev.WaitIdle();

                using (Framebuffer fb = new Framebuffer(pl.RenderPass, dim, dim, imgFbOffscreen)) {
                    CommandBuffer cmd = cmdPool.AllocateCommandBuffer();
                    cmd.Start(VkCommandBufferUsageFlags.OneTimeSubmit);

                    cmap.SetLayout(cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subRes);

                    float roughness = 0;

                    cmd.SetScissor(dim, dim);
                    cmd.SetViewport((float)(dim), (float)dim);

                    for (int m = 0; m < numMips; m++)
                    {
                        roughness = (float)m / ((float)numMips - 1f);

                        for (int f = 0; f < 6; f++)
                        {
                            pl.RenderPass.Begin(cmd, fb);

                            pl.Bind(cmd);

                            float viewPortSize = (float)Math.Pow(0.5, m) * dim;
                            cmd.SetViewport(viewPortSize, viewPortSize);
                            cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment,
                                             matrices[f] * Matrix4x4.CreatePerspectiveFieldOfView(Utils.DegreesToRadians(90), 1f, 0.1f, 512f));
                            if (target == CBTarget.IRRADIANCE)
                            {
                                cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaPhi, (uint)Marshal.SizeOf <Matrix4x4> ());
                                cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf <Matrix4x4> () + 4);
                            }
                            else
                            {
                                cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf <Matrix4x4> ());
                                cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 64u, (uint)Marshal.SizeOf <Matrix4x4> () + 4);
                            }

                            cmd.BindDescriptorSet(pl.Layout, dset);
                            cmd.BindVertexBuffer(vboSkybox);
                            cmd.Draw(36);

                            pl.RenderPass.End(cmd);

                            imgFbOffscreen.SetLayout(cmd, VkImageAspectFlags.Color,
                                                     VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal);

                            VkImageCopy region = new VkImageCopy();
                            region.srcSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, 1);
                            region.dstSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, 1, (uint)m, (uint)f);
                            region.extent         = new VkExtent3D {
                                width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1
                            };

                            Vk.vkCmdCopyImage(cmd.Handle,
                                              imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal,
                                              cmap.Handle, VkImageLayout.TransferDstOptimal,
                                              1, region.Pin());
                            region.Unpin();

                            imgFbOffscreen.SetLayout(cmd, VkImageAspectFlags.Color,
                                                     VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal);
                        }
                    }

                    cmap.SetLayout(cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes);

                    cmd.End();

                    staggingQ.Submit(cmd);
                    staggingQ.WaitIdle();

                    cmd.Free();
                }
            }
            cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;

            dsLayout.Dispose();
            imgFbOffscreen.Dispose();
            dsPool.Dispose();

            return(cmap);
        }