/// <summary> /// Create a frame buffer for an attachment image /// </summary> /// <param name="image"></param> /// <returns></returns> public Framebuffer CreateFramebuffer(VKImage image) { // Check arguments if (image is null) { throw new ArgumentNullException(nameof(image)); } // Create framebuffer return(RenderPass.CreateFramebuffer(new FramebufferCreateInfo( new[] { image.ImageView }, image.Extent.Width, image.Extent.Height ))); }
/// <summary> /// Get the command buffer created for a registered image /// </summary> /// <param name="image"></param> /// <returns></returns> public CommandBuffer GetCommandBuffer(VKImage image) { // Check arguments if (image is null) { throw new ArgumentNullException(nameof(image)); } if (CommandBuffers.TryGetValue(image, out var buffer)) { return(buffer); } return(RegisterImage(image)); }
/// <summary> /// Draw the effect /// </summary> public void Draw(Semaphore start, VKImage image) { // Check if disposed if (Disposed) { throw new ObjectDisposedException(Name); } // Make sure this effect is active if (!Active) { throw new InvalidOperationException("Effect is not active"); } // Call OnDraw OnDraw(start, image); }
/// <summary> /// Unregister an image from the effect /// </summary> /// <param name="image"></param> public void UnregisterImage(VKImage image) { // Check arguments if (image is null) { throw new ArgumentNullException(nameof(image)); } // Check if active if (!Active) { throw new InvalidOperationException("Effect is not active"); } // Call OnUnregisterImage and unregister command buffer Graphics.Device.WaitIdle(); OnUnregisterImage(image); CommandBuffers[image].Dispose(); CommandBuffers.Remove(image); }
/// <summary> /// Register an image for rendering to /// </summary> /// <param name="image"></param> public CommandBuffer RegisterImage(VKImage image) { // Check arguments if (image is null) { throw new ArgumentNullException(nameof(image)); } // Check if active if (!Active) { throw new InvalidOperationException("Effect is not active"); } // Create new command buffer, register the image, and reord the command buffer var cmd = Graphics.GraphicsQueueFamily.CreateCommandBuffers(CommandBufferLevel.Primary, 1)[0]; CommandBuffers.Add(image, cmd); OnRegisterImage(image); OnRecordCommandBuffer(image, cmd); return(cmd); }
/// <summary> /// Called when registering an image /// </summary> /// <param name="image"></param> protected virtual void OnUnregisterImage(VKImage image) { }
/// <summary> /// Called to record to a command buffer /// </summary> /// <param name="image"></param> /// <param name="buffer"></param> protected virtual void OnRecordCommandBuffer(VKImage image, CommandBuffer buffer) { }
/// <summary> /// Called when drawing the effect /// </summary> public virtual void OnDraw(Semaphore start, VKImage image = null) { }
public Texture2D( string name, Graphics graphics, TextureData2D data, bool premultiply = true ) { Name = name; Graphics = graphics; // Create image var image = Graphics.Device.CreateImage(new ImageCreateInfo { ImageType = ImageType.Image2D, Format = data.Format, MipLevels = data.MipMaps.Length, ArrayLayers = 1, Samples = SampleCounts.Count1, Tiling = ImageTiling.Optimal, SharingMode = SharingMode.Exclusive, InitialLayout = ImageLayout.Undefined, Extent = new Extent3D(data.MipMaps[0].Extent.Width, data.MipMaps[0].Extent.Height, 1), Usage = ImageUsages.Sampled | ImageUsages.TransferDst }); Image = new VKImage( image, data.Format, data.MipMaps[0].Extent, new ImageSubresourceRange(ImageAspects.Color, 0, data.MipMaps.Length, 0, 1 )); var memReq = Image.Image.GetMemoryRequirements(); DeviceMemory = Graphics.Device.AllocateMemory(new MemoryAllocateInfo( allocationSize: memReq.Size, memoryTypeIndex: Graphics.GetMemoryTypeIndex(memReq.MemoryTypeBits, MemoryProperties.DeviceLocal) )); Image.Image.BindMemory(DeviceMemory); // Copy data to staging buffer var staging = VKBuffer <byte> .StagingBuffer($"{nameof(Texture2D)} staging buffer", Graphics, data.Size); unsafe { var dest = staging.Map(0, data.Size); foreach (var mip in data.MipMaps) { fixed(byte *src = mip.Data) { if (premultiply) { switch (data.Format) { default: throw new NotImplementedException( $"Premultiplying is not implemented for format: {data.Format}" ); case Format.B8G8R8A8UNorm: for (var i = 0; i < mip.Size; i += 4) { var b = src[i]; var g = src[i + 1]; var r = src[i + 2]; var a = src[i + 3]; b = (byte)(b * (a / 255f)); g = (byte)(g * (a / 255f)); r = (byte)(r * (a / 255f)); dest[i] = b; dest[i + 1] = g; dest[i + 2] = r; dest[i + 3] = a; } break; } } else { System.Buffer.MemoryCopy(src, dest, mip.Size, mip.Size); } dest += mip.Size; } } staging.Unmap(); } // Create copy regions var regions = new BufferImageCopy[data.MipMaps.Length]; var offset = 0L; for (var i = 0; i < regions.Length; i++) { regions[i] = new BufferImageCopy { ImageSubresource = new ImageSubresourceLayers(ImageAspects.Color, i, 0, 1), ImageExtent = new Extent3D(data.MipMaps[0].Extent.Width, data.MipMaps[0].Extent.Height, 1), BufferOffset = offset }; offset += data.MipMaps[i].Size; } // Create command buffer var range = new ImageSubresourceRange(ImageAspects.Color, 0, data.MipMaps.Length, 0, 1); var buffer = Graphics.TransferQueueFamily.CreateCommandBuffers(CommandBufferLevel.Primary, 1)[0]; buffer.Begin(new CommandBufferBeginInfo(CommandBufferUsages.OneTimeSubmit)); buffer.CmdPipelineBarrier( PipelineStages.TopOfPipe, PipelineStages.Transfer, imageMemoryBarriers: new ImageMemoryBarrier[] { new ImageMemoryBarrier( Image.Image, range, Accesses.None, Accesses.TransferWrite, ImageLayout.Undefined, ImageLayout.TransferDstOptimal ) } ); buffer.CmdCopyBufferToImage(staging.Buffer, Image.Image, ImageLayout.TransferDstOptimal, regions); buffer.CmdPipelineBarrier( PipelineStages.Transfer, PipelineStages.FragmentShader, imageMemoryBarriers: new ImageMemoryBarrier[] { new ImageMemoryBarrier( Image.Image, range, Accesses.TransferWrite, Accesses.ShaderRead, ImageLayout.TransferDstOptimal, ImageLayout.ShaderReadOnlyOptimal ) } ); buffer.End(); // Submit the buffer var fence = Graphics.Device.CreateFence(new FenceCreateInfo()); Graphics.GraphicsQueueFamily.HighestPriority.Submit( new SubmitInfo( commandBuffers: new CommandBuffer[] { buffer } ), fence ); fence.Wait(); // Clean up fence.Dispose(); staging.Dispose(); }