internal static DeviceTexture CreateColorTarget(
            Int2 size,
            Format format,
            Device logicalDevice,
            Memory.Pool memoryPool,
            TransientExecutor executor,
            bool allowSampling = true)
        {
            if (logicalDevice == null)
            {
                throw new ArgumentNullException(nameof(logicalDevice));
            }
            if (memoryPool == null)
            {
                throw new ArgumentNullException(nameof(memoryPool));
            }
            if (executor == null)
            {
                throw new ArgumentNullException(nameof(executor));
            }

            var usage = allowSampling
                ? ImageUsages.ColorAttachment | ImageUsages.Sampled
                : ImageUsages.ColorAttachment | ImageUsages.TransientAttachment;
            var aspects = ImageAspects.Color;
            var image   = CreateImage(
                logicalDevice,
                format,
                size,
                mipLevels: 1,
                usage,
                cubeMap: false);
            var memory = memoryPool.AllocateAndBind(image, Chunk.Location.Device);

            //Transition the image to the depth attachment layout
            TransitionImageLayout(image, aspects,
                                  baseMipLevel: 0,
                                  mipLevels: 1,
                                  baseLayer: 0,
                                  layers: 1,
                                  oldLayout: ImageLayout.Undefined,
                                  newLayout: ImageLayout.ColorAttachmentOptimal,
                                  executor: executor);

            var view = CreateView(image, format, mipLevels: 1, aspects, cubeMap: false);

            return(new DeviceTexture(
                       size,
                       format,
                       ImageLayout.ShaderReadOnlyOptimal, //Used for shader reading
                       mipLevels: 1,
                       aspects,
                       image,
                       memory,
                       view));
        }
        private static void BlitImage(
            ImageAspects aspectMask,
            Image fromImage,
            IntRect fromRegion,
            int fromMipLevel,
            int fromLayerCount,
            Image toImage,
            IntRect toRegion,
            int toMipLevel,
            int toLayerCount,
            TransientExecutor executor)
        {
            ImageBlit blit = new ImageBlit
            {
                SrcSubresource = new ImageSubresourceLayers(
                    aspectMask,
                    fromMipLevel,
                    baseArrayLayer: 0,
                    fromLayerCount),
                SrcOffset1     = new Offset3D(fromRegion.Min.X, fromRegion.Min.Y, 0),
                SrcOffset2     = new Offset3D(fromRegion.Max.X, fromRegion.Max.Y, 1),
                DstSubresource = new ImageSubresourceLayers(
                    aspectMask,
                    toMipLevel,
                    baseArrayLayer: 0,
                    toLayerCount),
                DstOffset1 = new Offset3D(toRegion.Min.X, toRegion.Min.Y, 0),
                DstOffset2 = new Offset3D(toRegion.Max.X, toRegion.Max.Y, 1)
            };

            //Execute the blit
            executor.ExecuteBlocking(commandBuffer =>
            {
                commandBuffer.CmdBlitImage(
                    srcImage: fromImage,
                    srcImageLayout: ImageLayout.TransferSrcOptimal,
                    dstImage: toImage,
                    dstImageLayout: ImageLayout.TransferDstOptimal,
                    regions:  new [] { blit },
                    filter: Filter.Linear);
            });
        }
        internal static DeviceTexture UploadTexture(
            IInternalTexture texture,
            Device logicalDevice,
            Memory.Pool memoryPool,
            Memory.HostBuffer stagingBuffer,
            TransientExecutor executor,
            bool generateMipMaps = false)
        {
            if (texture == null)
            {
                throw new ArgumentNullException(nameof(texture));
            }
            if (logicalDevice == null)
            {
                throw new ArgumentNullException(nameof(logicalDevice));
            }
            if (memoryPool == null)
            {
                throw new ArgumentNullException(nameof(memoryPool));
            }
            if (stagingBuffer == null)
            {
                throw new ArgumentNullException(nameof(stagingBuffer));
            }
            if (executor == null)
            {
                throw new ArgumentNullException(nameof(executor));
            }

            int mipLevels = generateMipMaps ? CalculateMipLevels(texture.Size) : 1;
            int layers    = texture.IsCubeMap ? 6 : 1;
            var aspects   = ImageAspects.Color;
            var image     = CreateImage(
                logicalDevice,
                texture.Format,
                texture.Size,
                mipLevels,
                //Also include 'TransferSrc' because we read from the image to generate the mip-maps
                ImageUsages.TransferSrc | ImageUsages.TransferDst | ImageUsages.Sampled,
                cubeMap: texture.IsCubeMap);
            var memory = memoryPool.AllocateAndBind(image, Chunk.Location.Device);

            //Transition the entire image (all mip-levels and layers) to 'TransferDstOptimal'
            TransitionImageLayout(image, aspects,
                                  baseMipLevel: 0, mipLevels, baseLayer: 0, layers,
                                  oldLayout: ImageLayout.Undefined,
                                  newLayout: ImageLayout.TransferDstOptimal,
                                  executor: executor);

            //Upload the data to mipmap 0
            texture.Upload(stagingBuffer, executor, image, aspects);

            //Create the other mipmap levels
            Int2 prevMipSize = texture.Size;

            for (int i = 1; i < mipLevels; i++)
            {
                Int2 curMipSize = new Int2(
                    prevMipSize.X > 1 ? prevMipSize.X / 2 : 1,
                    prevMipSize.Y > 1 ? prevMipSize.Y / 2 : 1);

                //Move the previous mip-level to a transfer source layout
                TransitionImageLayout(image, aspects,
                                      baseMipLevel: i - 1, mipLevels: 1,
                                      baseLayer: 0, layers,
                                      oldLayout: ImageLayout.TransferDstOptimal,
                                      newLayout: ImageLayout.TransferSrcOptimal,
                                      executor: executor);

                //Blit the previous mip-level to the current at half the size
                BlitImage(aspects,
                          fromImage: image,
                          fromRegion: new IntRect(min: Int2.Zero, max: prevMipSize),
                          fromMipLevel: i - 1,
                          fromLayerCount: layers,
                          toImage: image,
                          toRegion: new IntRect(min: Int2.Zero, max: curMipSize),
                          toMipLevel: i,
                          toLayerCount: layers,
                          executor);

                //Transition the previous mip-level to the shader-read layout
                TransitionImageLayout(image, aspects,
                                      baseMipLevel: i - 1, mipLevels: 1,
                                      baseLayer: 0, layers,
                                      oldLayout: ImageLayout.TransferSrcOptimal,
                                      newLayout: ImageLayout.ShaderReadOnlyOptimal,
                                      executor: executor);

                //Update the prev mip-size
                prevMipSize = curMipSize;
            }

            //Transition the last mip-level to the shader-read layout
            TransitionImageLayout(image, aspects,
                                  baseMipLevel: mipLevels - 1, mipLevels: 1,
                                  baseLayer: 0, layers,
                                  oldLayout: ImageLayout.TransferDstOptimal,
                                  newLayout: ImageLayout.ShaderReadOnlyOptimal,
                                  executor: executor);

            var view = CreateView(
                image, texture.Format, mipLevels, aspects, cubeMap: texture.IsCubeMap);

            return(new DeviceTexture(
                       texture.Size,
                       texture.Format,
                       ImageLayout.ShaderReadOnlyOptimal, //Used for shader reading,
                       mipLevels,
                       aspects,
                       image,
                       memory,
                       view));
        }
        private static void TransitionImageLayout(
            Image image,
            ImageAspects aspectMask,
            int baseMipLevel,
            int mipLevels,
            int baseLayer,
            int layers,
            ImageLayout oldLayout,
            ImageLayout newLayout,
            TransientExecutor executor)
        {
            //Get where this transition has to wait and what has to wait for this transition
            Accesses       sourceAccess, destinationAccess;
            PipelineStages sourcePipelineStages, destinationPipelineStages;

            if (oldLayout == ImageLayout.Undefined && newLayout == ImageLayout.TransferDstOptimal)
            {
                sourceAccess              = Accesses.None;
                destinationAccess         = Accesses.TransferWrite;
                sourcePipelineStages      = PipelineStages.TopOfPipe;
                destinationPipelineStages = PipelineStages.Transfer;
            }
            else
            if (oldLayout == ImageLayout.Undefined && newLayout == ImageLayout.DepthStencilAttachmentOptimal)
            {
                sourceAccess              = Accesses.None;
                destinationAccess         = Accesses.DepthStencilAttachmentRead | Accesses.DepthStencilAttachmentWrite;
                sourcePipelineStages      = PipelineStages.TopOfPipe;
                destinationPipelineStages = PipelineStages.EarlyFragmentTests;
            }
            else
            if (oldLayout == ImageLayout.Undefined && newLayout == ImageLayout.ColorAttachmentOptimal)
            {
                sourceAccess              = Accesses.None;
                destinationAccess         = Accesses.ColorAttachmentRead | Accesses.ColorAttachmentWrite;
                sourcePipelineStages      = PipelineStages.TopOfPipe;
                destinationPipelineStages = PipelineStages.ColorAttachmentOutput;
            }
            else
            if (oldLayout == ImageLayout.TransferDstOptimal && newLayout == ImageLayout.TransferSrcOptimal)
            {
                sourceAccess              = Accesses.TransferWrite;
                destinationAccess         = Accesses.TransferRead;
                sourcePipelineStages      = PipelineStages.Transfer;
                destinationPipelineStages = PipelineStages.Transfer;
            }
            else
            if (oldLayout == ImageLayout.TransferDstOptimal && newLayout == ImageLayout.ShaderReadOnlyOptimal)
            {
                sourceAccess              = Accesses.TransferWrite;
                destinationAccess         = Accesses.ShaderRead;
                sourcePipelineStages      = PipelineStages.Transfer;
                destinationPipelineStages = PipelineStages.FragmentShader;
            }
            else
            if (oldLayout == ImageLayout.TransferSrcOptimal && newLayout == ImageLayout.ShaderReadOnlyOptimal)
            {
                sourceAccess              = Accesses.TransferRead;
                destinationAccess         = Accesses.ShaderRead;
                sourcePipelineStages      = PipelineStages.Transfer;
                destinationPipelineStages = PipelineStages.FragmentShader;
            }
            else
            {
                throw new Exception(
                          $"[{nameof(DeviceTexture)}] Unsupported image transition: from: {oldLayout} to: {newLayout}");
            }

            //Create the transition barrier
            var imageMemoryBarrier = new ImageMemoryBarrier(
                image: image,
                subresourceRange: new ImageSubresourceRange(
                    aspectMask: aspectMask,
                    baseMipLevel: baseMipLevel,
                    levelCount: mipLevels,
                    baseArrayLayer: baseLayer,
                    layerCount: layers),
                srcAccessMask: sourceAccess,
                dstAccessMask: destinationAccess,
                oldLayout: oldLayout,
                newLayout: newLayout);

            //Execute the barrier
            executor.ExecuteBlocking(commandBuffer =>
            {
                commandBuffer.CmdPipelineBarrier(
                    srcStageMask: sourcePipelineStages,
                    dstStageMask: destinationPipelineStages,
                    dependencyFlags: Dependencies.None,
                    memoryBarriers: null,
                    bufferMemoryBarriers: null,
                    imageMemoryBarriers: new [] { imageMemoryBarrier });
            });
        }
Example #5
0
        public RenderScene(Window window,
                           TextureInfo reflectionTexture,
                           ShaderProgram postVertProg,
                           ShaderProgram bloomFragProg,
                           ShaderProgram aoFragProg,
                           ShaderProgram gaussBlurFragProg,
                           ShaderProgram boxBlurFragProg,
                           ShaderProgram compositionFragProg,
                           Logger logger = null)
        {
            if (window == null)
            {
                throw new ArgumentNullException(nameof(window));
            }
            if (reflectionTexture.Texture == null)
            {
                throw new ArgumentNullException(nameof(reflectionTexture));
            }
            if (postVertProg == null)
            {
                throw new ArgumentNullException(nameof(postVertProg));
            }
            if (bloomFragProg == null)
            {
                throw new ArgumentNullException(nameof(bloomFragProg));
            }
            if (aoFragProg == null)
            {
                throw new ArgumentNullException(nameof(aoFragProg));
            }
            if (gaussBlurFragProg == null)
            {
                throw new ArgumentNullException(nameof(gaussBlurFragProg));
            }
            if (boxBlurFragProg == null)
            {
                throw new ArgumentNullException(nameof(boxBlurFragProg));
            }
            if (compositionFragProg == null)
            {
                throw new ArgumentNullException(nameof(compositionFragProg));
            }

            this.window = window;
            this.logger = logger;
            camera      = new Camera();

            //Create resources
            executor      = new TransientExecutor(window.LogicalDevice, window.GraphicsFamilyIndex);
            memoryPool    = new Memory.Pool(window.LogicalDevice, window.HostDevice, logger);
            stagingBuffer = new Memory.HostBuffer(
                window.LogicalDevice,
                memoryPool,
                BufferUsages.TransferSrc,
                size: ByteUtils.MegabyteToByte(16));
            sceneDataBuffer = new Memory.HostBuffer(
                window.LogicalDevice, memoryPool, BufferUsages.UniformBuffer,
                size: SceneData.SIZE * window.SwapchainCount);
            inputManager = new ShaderInputManager(window.LogicalDevice, logger);

            //Create techniques
            gbufferTechnique = new GBufferTechnique(this, logger);
            shadowTechnique  = new ShadowTechnique(this, logger);
            bloomTechnique   = new BloomTechnique(
                gbufferTechnique, postVertProg, bloomFragProg, gaussBlurFragProg, this, logger);
            aoTechnique = new AmbientOcclusionTechnique(
                gbufferTechnique, postVertProg, aoFragProg, boxBlurFragProg, this, logger);
            deferredTechnique = new DeferredTechnique(
                gbufferTechnique, shadowTechnique, bloomTechnique, aoTechnique,
                reflectionTexture, postVertProg, compositionFragProg,
                this, logger);
        }