/// <summary> /// Loads a bitmap from the given texture. Be careful: The texture must have CPU read access and this only matches for staging textures. /// </summary> /// <param name="texture">The texture to be loaded into the bitmap.</param> public static GDI.Bitmap LoadBitmapFromMemoryMappedTexture(MemoryMappedTexture <int> texture) { texture.EnsureNotNullOrDisposed(nameof(texture)); var width = texture.Width; var height = texture.Height; // Create and lock bitmap so it can be accessed for texture loading var resultBitmap = new GDI.Bitmap(width, height); var bitmapData = resultBitmap.LockBits( new GDI.Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); try { SeeingSharpUtil.CopyMemory( texture.Pointer, bitmapData.Scan0, texture.SizeInBytes); } finally { resultBitmap.UnlockBits(bitmapData); } return(resultBitmap); }
/// <summary> /// Loads a bitmap from the given texture. Be careful: The texture must have CPU read access and this only matches for staging textures. /// </summary> /// <param name="device">The device on which the texture is created.</param> /// <param name="stagingTexture">The texture to be loaded into the bitmap.</param> /// <param name="targetBitmap">The target bitmap to write all contents to.</param> /// <param name="pixelWidth">With of the bitmap in pixels.</param> /// <param name="pixelHeight">Height of the bitmap in pixels.</param> /// <param name="lockTimeout">Timeout for locking the target bitmap.</param> internal static void LoadBitmapFromStagingTexture(EngineDevice device, D3D11.ID3D11Texture2D stagingTexture, WriteableBitmap targetBitmap, int pixelWidth, int pixelHeight, TimeSpan lockTimeout) { device.EnsureNotNull(nameof(device)); stagingTexture.EnsureNotNull(nameof(stagingTexture)); targetBitmap.EnsureNotNull(nameof(targetBitmap)); var textureDesc = stagingTexture.Description; pixelWidth.EnsureEqualComparable(textureDesc.Width, $"{nameof(textureDesc)}.{nameof(textureDesc.Width)}"); pixelHeight.EnsureEqualComparable(textureDesc.Height, $"{nameof(textureDesc)}.{nameof(textureDesc.Height)}"); // Prepare target bitmap var dataBox = device.Internals.DeviceImmediateContextD3D11.Map(stagingTexture, 0, D3D11.MapMode.Read, D3D11.MapFlags.None); try { if (!targetBitmap.TryLock(new Duration(lockTimeout))) { return; } try { // Copy data row by row // => Rows from data source may have more pixels because driver changes the size of textures var rowPitch = (uint)(pixelWidth * 4); for (var loopRow = 0; loopRow < pixelHeight; loopRow++) { var rowPitchSource = dataBox.RowPitch; var rowPitchDestination = pixelWidth * 4; SeeingSharpUtil.CopyMemory( dataBox.DataPointer + loopRow * rowPitchSource, targetBitmap.BackBuffer + loopRow * rowPitchDestination, rowPitch); } } finally { targetBitmap.AddDirtyRect(new Int32Rect(0, 0, pixelWidth, pixelHeight)); targetBitmap.Unlock(); } } finally { device.Internals.DeviceImmediateContextD3D11.Unmap(stagingTexture, 0); } }
/// <summary> /// Loads a bitmap from the given texture. Be careful: The texture must have CPU read access and this only matches for staging textures. /// </summary> /// <param name="device">The device on which the texture is created.</param> /// <param name="stagingTexture">The texture to be loaded into the bitmap.</param> /// <param name="width">The width of the texture.</param> /// <param name="height">The height of the texture.</param> internal static GDI.Bitmap LoadBitmapFromStagingTexture(EngineDevice device, D3D11.ID3D11Texture2D stagingTexture, int width, int height) { device.EnsureNotNull(nameof(device)); stagingTexture.EnsureNotNull(nameof(stagingTexture)); width.EnsurePositiveOrZero(nameof(width)); height.EnsurePositiveOrZero(nameof(height)); //Prepare target bitmap var resultBitmap = new GDI.Bitmap(width, height); var dataBox = device.Internals.DeviceImmediateContextD3D11.Map(stagingTexture, 0, D3D11.MapMode.Read, D3D11.MapFlags.None); try { //Lock bitmap so it can be accessed for texture loading var bitmapData = resultBitmap.LockBits( new GDI.Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); try { //Copy data row by row // => Rows form data source may have more pixels because driver changes the size of textures var rowPitch = (uint)(width * 4); for (var loopRow = 0; loopRow < height; loopRow++) { // Copy bitmap data var rowPitchSource = dataBox.RowPitch; var rowPitchDestination = width * 4; SeeingSharpUtil.CopyMemory( dataBox.DataPointer + loopRow * rowPitchSource, bitmapData.Scan0 + loopRow * rowPitchDestination, rowPitch); } } finally { resultBitmap.UnlockBits(bitmapData); } } finally { device.Internals.DeviceImmediateContextD3D11.Unmap(stagingTexture, 0); } return(resultBitmap); }
/// <summary> /// Upload a texture from the graphics hardware. /// </summary> /// <param name="textureToUpload">The texture to be uploaded.</param> /// <param name="targetFloatBuffer">The target buffer to which to copy all data.</param> internal unsafe void UploadToMemoryMappedTexture <T>(D3D11.ID3D11Texture2D textureToUpload, MemoryMappedTexture <T> targetFloatBuffer) where T : unmanaged { if (_isDisposed) { throw new ObjectDisposedException(nameof(TextureUploader)); } this.EnsureNotNullOrDisposed(nameof(targetFloatBuffer)); // Check input texture var textureDesc = textureToUpload.Description; if (textureDesc.Width != _width) { throw new SeeingSharpGraphicsException($"Invalid texture: Width does not match (given: {textureDesc.Width}, expected: {_width})!"); } if (textureDesc.Height != _height) { throw new SeeingSharpGraphicsException($"Invalid texture: Height does not match (given: {textureDesc.Height}, expected: {_height})!"); } if (textureDesc.Format != _format) { throw new SeeingSharpGraphicsException($"Invalid texture: Format does not match (given: {textureDesc.Format}, expected: {_format})!"); } if (GraphicsHelper.IsMultisampled(textureDesc) != _isMultisampled) { throw new SeeingSharpGraphicsException($"Invalid texture: Multisampling does not match (given: {GraphicsHelper.IsMultisampled(textureDesc)}, expected: {_isMultisampled})!"); } // Check source and destination size var targetPixelSize = targetFloatBuffer.PixelSize; if (targetPixelSize.Width != _width) { throw new SeeingSharpGraphicsException("The width of the textures during texture upload does not match!"); } if (targetPixelSize.Height != _height) { throw new SeeingSharpGraphicsException("The height of the textures during texture upload does not match!"); } // Check format compatibility var textureFormatByteSize = FormatHelper.SizeOfInBytes(_format); if (textureFormatByteSize != sizeof(T)) { throw new SeeingSharpGraphicsException( "Format of the texture to upload and the destination buffer does not match " + $"(source: {_format} / {textureFormatByteSize} bytes, target: {typeof(T).Name} / {sizeof(T)} bytes)!"); } // Upload the texture this.CopyTextureToStagingResource(textureToUpload); // Read the data into the .Net data block var dataBox = _device.DeviceImmediateContextD3D11.Map( _copyHelperTextureStaging, 0, D3D11.MapMode.Read, D3D11.MapFlags.None); try { var rowPitchSource = dataBox.RowPitch; var rowPitchDestination = targetFloatBuffer.Width * sizeof(T); if (rowPitchSource > 0 && rowPitchDestination > 0) { for (var loopY = 0; loopY < _height; loopY++) { SeeingSharpUtil.CopyMemory( dataBox.DataPointer + loopY * rowPitchSource, targetFloatBuffer.Pointer + loopY * rowPitchDestination, (uint)rowPitchDestination); } } else { throw new SeeingSharpGraphicsException($"Invalid row pitch (source: {rowPitchSource}, destination: {rowPitchDestination})!"); } } finally { _device.DeviceImmediateContextD3D11.Unmap(_copyHelperTextureStaging, 0); } }