Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Texture.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 //
4 // Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
23 
24 using System;
25 using System.IO;
26 using SiliconStudio.Core;
27 using SiliconStudio.Core.ReferenceCounting;
28 using SiliconStudio.Core.Serialization.Contents;
30 
31 namespace SiliconStudio.Paradox.Graphics
32 {
33  /// <summary>
34  /// Base class for texture resources.
35  /// </summary>
36  /// <typeparam name="T">Type of the <see cref="N:SharpDX.Direct3D11"/> texture resource.</typeparam>
37  [ContentSerializer]
38  public abstract partial class Texture : GraphicsResource
39  {
40  /// <summary>
41  /// Common description for the original texture.
42  /// </summary>
44 
45  /// <summary>
46  /// The width of this texture view.
47  /// </summary>
48  public int Width;
49 
50  /// <summary>
51  /// The height of this texture view.
52  /// </summary>
53  public int Height;
54 
55  /// <summary>
56  /// The depth of this texture view.
57  /// </summary>
58  public readonly int Depth;
59 
60  /// <summary>
61  /// The format of this texture view.
62  /// </summary>
63  public readonly PixelFormat ViewFormat;
64 
65  /// <summary>
66  /// The format of this texture view.
67  /// </summary>
68  public readonly ViewType ViewType;
69 
70  /// <summary>
71  /// The miplevel index of this texture view.
72  /// </summary>
73  public readonly int MipLevel;
74 
75  /// <summary>
76  /// The array index of this texture view.
77  /// </summary>
78  public readonly int ArraySlice;
79 
80  /// <summary>
81  /// Gets a boolean indicating whether this <see cref="Texture"/> is a using a block compress format (BC1, BC2, BC3, BC4, BC5, BC6H, BC7).
82  /// </summary>
83  public readonly bool IsBlockCompressed;
84 
85  /// <summary>
86  /// The width stride in bytes (number of bytes per row).
87  /// </summary>
88  internal readonly int RowStride;
89 
90  /// <summary>
91  /// The depth stride in bytes (number of bytes per depth slice).
92  /// </summary>
93  internal readonly int DepthStride;
94 
95  /// <summary>
96  /// The underlying parent texture (if this is a view).
97  /// </summary>
98  internal readonly Texture ParentTexture;
99 
100  private MipMapDescription[] mipmapDescriptions;
101 
102  protected Texture()
103  {
104  }
105 
106  protected Texture(GraphicsDevice device, Texture parentTexture, ViewType viewType, int viewArraySlice, int viewMipLevel, PixelFormat viewFormat = PixelFormat.None)
107  : this(device, parentTexture.Description, viewType, viewArraySlice, viewMipLevel, viewFormat)
108  {
109  ParentTexture = parentTexture;
110  ParentTexture.AddReferenceInternal();
111  }
112 
113  protected Texture(GraphicsDevice device, TextureDescription description, ViewType viewType, int viewArraySlice, int viewMipLevel, PixelFormat viewFormat = PixelFormat.None) : base(device)
114  {
115  Description = description;
116  IsBlockCompressed = description.Format.IsCompressed();
117  RowStride = this.Description.Width * description.Format.SizeInBytes();
118  DepthStride = RowStride * this.Description.Height;
119  mipmapDescriptions = Image.CalculateMipMapDescription(description);
120 
121  Width = Math.Max(1, Description.Width >> viewMipLevel);
122  Height = Math.Max(1, Description.Height >> viewMipLevel);
123  Depth = Math.Max(1, Description.Depth >> viewMipLevel);
124  MipLevel = viewMipLevel;
125  ViewFormat = viewFormat == PixelFormat.None ? description.Format : viewFormat;
126  ArraySlice = viewArraySlice;
127  ViewType = viewType;
128  }
129 
131  {
132  var description = this.Description;
133  if (description.Usage == GraphicsResourceUsage.Immutable)
134  description.Usage = GraphicsResourceUsage.Default;
135  return description;
136  }
137 
138  protected override void Destroy()
139  {
140  base.Destroy();
141  if (ParentTexture != null)
142  {
143  ParentTexture.ReleaseInternal();
144  }
145  }
146 
147  /// <summary>
148  /// Gets a view on this texture for a particular <see cref="ViewType"/>, array index (or zIndex for Texture3D), and mipmap index.
149  /// </summary>
150  /// <param name="viewType">The type of the view to create.</param>
151  /// <param name="arrayOrDepthSlice"></param>
152  /// <param name="mipMapSlice"></param>
153  /// <returns>A new texture object that is bouded to the requested view.</returns>
154  public T ToTexture<T>(ViewType viewType, int arrayOrDepthSlice, int mipMapSlice) where T : Texture
155  {
156  return (T)ToTexture(viewType, arrayOrDepthSlice, mipMapSlice);
157  }
158 
159  /// <summary>
160  /// Gets a view on this texture for a particular <see cref="ViewType"/>, array index (or zIndex for Texture3D), and mipmap index.
161  /// </summary>
162  /// <param name="viewType">The type of the view to create.</param>
163  /// <param name="arraySlice"></param>
164  /// <param name="mipMapSlice"></param>
165  /// <returns>A new texture object that is bouded to the requested view.</returns>
166  public abstract Texture ToTexture(ViewType viewType, int arraySlice, int mipMapSlice);
167 
169  {
170  return ToRenderTarget(ViewType.Single, 0, 0);
171  }
172 
173  /// <summary>
174  /// Gets the mipmap description of this instance for the specified mipmap level.
175  /// </summary>
176  /// <param name="mipmap">The mipmap.</param>
177  /// <returns>A description of a particular mipmap for this texture.</returns>
179  {
180  return mipmapDescriptions[mipmap];
181  }
182 
183  public static int CalculateMipSize(int width, int mipLevel)
184  {
185  mipLevel = Math.Min(mipLevel, Image.CountMips(width));
186  width = width >> mipLevel;
187  return width > 0 ? width : 1;
188  }
189 
190  /// <summary>
191  /// Gets the absolute sub-resource index from the array and mip slice.
192  /// </summary>
193  /// <param name="arraySlice">The array slice index.</param>
194  /// <param name="mipSlice">The mip slice index.</param>
195  /// <returns>A value equals to arraySlice * Description.MipLevels + mipSlice.</returns>
196  public int GetSubResourceIndex(int arraySlice, int mipSlice)
197  {
198  return arraySlice * Description.MipLevels + mipSlice;
199  }
200 
201  /// <summary>
202  /// Calculates the expected width of a texture using a specified type.
203  /// </summary>
204  /// <typeparam name="TData">The type of the T pixel data.</typeparam>
205  /// <returns>The expected width</returns>
206  /// <exception cref="System.ArgumentException">If the size is invalid</exception>
207  public int CalculateWidth<TData>(int mipLevel = 0) where TData : struct
208  {
209  var widthOnMip = CalculateMipSize((int)Description.Width, mipLevel);
210  var rowStride = widthOnMip * Description.Format.SizeInBytes();
211 
212  var dataStrideInBytes = Utilities.SizeOf<TData>() * widthOnMip;
213  var width = ((double)rowStride / dataStrideInBytes) * widthOnMip;
214  if (Math.Abs(width - (int)width) > Double.Epsilon)
215  throw new ArgumentException("sizeof(TData) / sizeof(Format) * Width is not an integer");
216 
217  return (int)width;
218  }
219 
220  /// <summary>
221  /// Calculates the number of pixel data this texture is requiring for a particular mip level.
222  /// </summary>
223  /// <typeparam name="TData">The type of the T pixel data.</typeparam>
224  /// <param name="mipLevel">The mip level.</param>
225  /// <returns>The number of pixel data.</returns>
226  /// <remarks>This method is used to allocated a texture data buffer to hold pixel datas: var textureData = new T[ texture.CalculatePixelCount&lt;T&gt;() ] ;.</remarks>
227  public int CalculatePixelDataCount<TData>(int mipLevel = 0) where TData : struct
228  {
229  return CalculateWidth<TData>(mipLevel) * CalculateMipSize(Description.Height, mipLevel) * CalculateMipSize(Description.Depth, mipLevel);
230  }
231 
232  /// <summary>
233  /// Makes a copy of this texture.
234  /// </summary>
235  /// <remarks>
236  /// This method doesn't copy the content of the texture.
237  /// </remarks>
238  /// <returns>
239  /// A copy of this texture.
240  /// </returns>
241  public abstract Texture Clone();
242 
243  /// <summary>
244  /// Makes a copy of this texture with type casting.
245  /// </summary>
246  /// <remarks>
247  /// This method doesn't copy the content of the texture.
248  /// </remarks>
249  /// <returns>
250  /// A copy of this texture.
251  /// </returns>
252  public T Clone<T>() where T : Texture
253  {
254  return (T)this.Clone();
255  }
256 
257  /// <summary>
258  /// Gets the content of this texture to an array of data.
259  /// </summary>
260  /// <typeparam name="TData">The type of the T data.</typeparam>
261  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
262  /// <param name="mipSlice">The mip slice index.</param>
263  /// <returns>The texture data.</returns>
264  /// <remarks>
265  /// This method is only working when called from the main thread that is accessing the main <see cref="GraphicsDevice"/>.
266  /// This method creates internally a stagging resource, copies to it and map it to memory. Use method with explicit staging resource
267  /// for optimal performances.</remarks>
268  public TData[] GetData<TData>(int arraySlice = 0, int mipSlice = 0) where TData : struct
269  {
270  var toData = new TData[this.CalculatePixelDataCount<TData>(mipSlice)];
271  GetData(toData, arraySlice, mipSlice);
272  return toData;
273  }
274 
275  /// <summary>
276  /// Copies the content of this texture to an array of data.
277  /// </summary>
278  /// <typeparam name="TData">The type of the T data.</typeparam>
279  /// <param name="toData">The destination buffer to receive a copy of the texture datas.</param>
280  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
281  /// <param name="mipSlice">The mip slice index.</param>
282  /// <param name="doNotWait">if set to <c>true</c> this method will return immediately if the resource is still being used by the GPU for writing. Default is false</param>
283  /// <returns><c>true</c> if data was correctly retrieved, <c>false</c> if <see cref="doNotWait"/> flag was true and the resource is still being used by the GPU for writing.</returns>
284  /// <remarks>
285  /// This method is only working when called from the main thread that is accessing the main <see cref="GraphicsDevice"/>.
286  /// This method creates internally a stagging resource if this texture is not already a stagging resouce, copies to it and map it to memory. Use method with explicit staging resource
287  /// for optimal performances.</remarks>
288  public bool GetData<TData>(TData[] toData, int arraySlice = 0, int mipSlice = 0, bool doNotWait = false) where TData : struct
289  {
290  // Get data from this resource
291  if (Description.Usage == GraphicsResourceUsage.Staging)
292  {
293  // Directly if this is a staging resource
294  return GetData(this, toData, arraySlice, mipSlice, doNotWait);
295  }
296  else
297  {
298  // Unefficient way to use the Copy method using dynamic staging texture
299  using (var throughStaging = this.ToStaging())
300  return GetData(throughStaging, toData, arraySlice, mipSlice, doNotWait);
301  }
302  }
303 
304  /// <summary>
305  /// Copies the content of this texture from GPU memory to an array of data on CPU memory using a specific staging resource.
306  /// </summary>
307  /// <typeparam name="TData">The type of the T data.</typeparam>
308  /// <param name="stagingTexture">The staging texture used to transfer the texture to.</param>
309  /// <param name="toData">To data.</param>
310  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
311  /// <param name="mipSlice">The mip slice index.</param>
312  /// <param name="doNotWait">if set to <c>true</c> this method will return immediately if the resource is still being used by the GPU for writing. Default is false</param>
313  /// <returns><c>true</c> if data was correctly retrieved, <c>false</c> if <see cref="doNotWait"/> flag was true and the resource is still being used by the GPU for writing.</returns>
314  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
315  /// <remarks>
316  /// This method is only working when called from the main thread that is accessing the main <see cref="GraphicsDevice"/>.
317  /// </remarks>
318  public unsafe bool GetData<TData>(Texture stagingTexture, TData[] toData, int arraySlice = 0, int mipSlice = 0, bool doNotWait = false) where TData : struct
319  {
320  return GetData(stagingTexture, new DataPointer((IntPtr)Interop.Fixed(toData), toData.Length * Utilities.SizeOf<TData>()), arraySlice, mipSlice, doNotWait);
321  }
322 
323  /// <summary>
324  /// Copies the content an array of data on CPU memory to this texture into GPU memory.
325  /// </summary>
326  /// <typeparam name="TData">The type of the T data.</typeparam>
327  /// <param name="fromData">The data to copy from.</param>
328  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
329  /// <param name="mipSlice">The mip slice index.</param>
330  /// <param name="region">Destination region</param>
331  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
332  /// <remarks>
333  /// This method is only working on the main graphics device. Use method with explicit graphics device to set data on a deferred context.
334  /// See also unmanaged documentation about Map/UnMap for usage and restrictions.
335  /// </remarks>
336  public void SetData<TData>(TData[] fromData, int arraySlice = 0, int mipSlice = 0, ResourceRegion? region = null) where TData : struct
337  {
338  SetData(GraphicsDevice, fromData, arraySlice, mipSlice, region);
339  }
340 
341  /// <summary>
342  /// Copies the content an data on CPU memory to this texture into GPU memory using the specified <see cref="GraphicsDevice"/> (The graphics device could be deffered).
343  /// </summary>
344  /// <param name="fromData">The data to copy from.</param>
345  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
346  /// <param name="mipSlice">The mip slice index.</param>
347  /// <param name="region">Destination region</param>
348  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
349  /// <remarks>
350  /// This method is only working on the main graphics device. Use method with explicit graphics device to set data on a deferred context.
351  /// See also unmanaged documentation about Map/UnMap for usage and restrictions.
352  /// </remarks>
353  public void SetData(DataPointer fromData, int arraySlice = 0, int mipSlice = 0, ResourceRegion? region = null)
354  {
355  SetData(GraphicsDevice, fromData, arraySlice, mipSlice, region);
356  }
357 
358  /// <summary>
359  /// Copies the content an array of data on CPU memory to this texture into GPU memory using the specified <see cref="GraphicsDevice"/> (The graphics device could be deffered).
360  /// </summary>
361  /// <typeparam name="TData">The type of the T data.</typeparam>
362  /// <param name="device">The <see cref="GraphicsDevice"/>.</param>
363  /// <param name="fromData">The data to copy from.</param>
364  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
365  /// <param name="mipSlice">The mip slice index.</param>
366  /// <param name="region">Destination region</param>
367  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
368  /// <remarks>
369  /// See unmanaged documentation for usage and restrictions.
370  /// </remarks>
371  public unsafe void SetData<TData>(GraphicsDevice device, TData[] fromData, int arraySlice = 0, int mipSlice = 0, ResourceRegion? region = null) where TData : struct
372  {
373  SetData(device, new DataPointer((IntPtr)Interop.Fixed(fromData), fromData.Length * Utilities.SizeOf<TData>()), arraySlice, mipSlice, region);
374  }
375 
376  /// <summary>
377  /// Copies the content of this texture from GPU memory to a pointer on CPU memory using a specific staging resource.
378  /// </summary>
379  /// <param name="stagingTexture">The staging texture used to transfer the texture to.</param>
380  /// <param name="toData">The pointer to data in CPU memory.</param>
381  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
382  /// <param name="mipSlice">The mip slice index.</param>
383  /// <param name="doNotWait">if set to <c>true</c> this method will return immediately if the resource is still being used by the GPU for writing. Default is false</param>
384  /// <returns><c>true</c> if data was correctly retrieved, <c>false</c> if <see cref="doNotWait"/> flag was true and the resource is still being used by the GPU for writing.</returns>
385  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
386  /// <remarks>
387  /// This method is only working when called from the main thread that is accessing the main <see cref="GraphicsDevice"/>.
388  /// </remarks>
389  public unsafe bool GetData(Texture stagingTexture, DataPointer toData, int arraySlice = 0, int mipSlice = 0, bool doNotWait = false)
390  {
391  if (stagingTexture == null) throw new ArgumentNullException("stagingTexture");
392  var device = GraphicsDevice;
393  //var deviceContext = device.NativeDeviceContext;
394 
395  // Get mipmap description for the specified mipSlice
396  var mipmap = this.GetMipMapDescription(mipSlice);
397 
398  // Copy height, depth
399  int height = mipmap.HeightPacked;
400  int depth = mipmap.Depth;
401 
402  // Calculate depth stride based on mipmap level
403  int rowStride = mipmap.RowStride;
404 
405  // Depth Stride
406  int textureDepthStride = mipmap.DepthStride;
407 
408  // MipMap Stride
409  int mipMapSize = mipmap.MipmapSize;
410 
411  // Check size validity of data to copy to
412  if (toData.Size > mipMapSize)
413  throw new ArgumentException(string.Format("Size of toData ({0} bytes) is not compatible expected size ({1} bytes) : Width * Height * Depth * sizeof(PixelFormat) size in bytes", toData.Size, mipMapSize));
414 
415  // Copy the actual content of the texture to the staging resource
416  if (!ReferenceEquals(this, stagingTexture))
417  device.Copy(this, stagingTexture);
418 
419  // Calculate the subResourceIndex for a Texture2D
420  int subResourceIndex = this.GetSubResourceIndex(arraySlice, mipSlice);
421 
422  // Map the staging resource to a CPU accessible memory
423  var mappedResource = device.MapSubresource(stagingTexture, subResourceIndex, MapMode.Read, doNotWait);
424 
425  // Box can be empty if DoNotWait is set to true, return false if empty
426  var box = mappedResource.DataBox;
427  if (box.IsEmpty)
428  {
429  return false;
430  }
431 
432  // If depth == 1 (Texture1D, Texture2D or TextureCube), then depthStride is not used
433  var boxDepthStride = this.Description.Depth == 1 ? box.SlicePitch : textureDepthStride;
434 
435  var isFlippedTexture = IsFlippedTexture();
436 
437  // The fast way: If same stride, we can directly copy the whole texture in one shot
438  if (box.RowPitch == rowStride && boxDepthStride == textureDepthStride && !isFlippedTexture)
439  {
440  Utilities.CopyMemory(toData.Pointer, box.DataPointer, mipMapSize);
441  }
442  else
443  {
444  // Otherwise, the long way by copying each scanline
445  var sourcePerDepthPtr = (byte*)box.DataPointer;
446  var destPtr = (byte*)toData.Pointer;
447 
448  // Iterate on all depths
449  for (int j = 0; j < depth; j++)
450  {
451  var sourcePtr = sourcePerDepthPtr;
452  // Iterate on each line
453 
454  if (isFlippedTexture)
455  {
456  sourcePtr = sourcePtr + box.RowPitch * (height - 1);
457  for (int i = height - 1; i >= 0; i--)
458  {
459  // Copy a single row
460  Utilities.CopyMemory(new IntPtr(destPtr), new IntPtr(sourcePtr), rowStride);
461  sourcePtr -= box.RowPitch;
462  destPtr += rowStride;
463  }
464  }
465  else
466  {
467  for (int i = 0; i < height; i++)
468  {
469  // Copy a single row
470  Utilities.CopyMemory(new IntPtr(destPtr), new IntPtr(sourcePtr), rowStride);
471  sourcePtr += box.RowPitch;
472  destPtr += rowStride;
473  }
474  }
475  sourcePerDepthPtr += box.SlicePitch;
476  }
477  }
478 
479  // Make sure that we unmap the resource in case of an exception
480  device.UnmapSubresource(mappedResource);
481 
482  return true;
483  }
484 
485  /// <summary>
486  /// Copies the content an data on CPU memory to this texture into GPU memory.
487  /// </summary>
488  /// <param name="device">The <see cref="GraphicsDevice"/>.</param>
489  /// <param name="fromData">The data to copy from.</param>
490  /// <param name="arraySlice">The array slice index. This value must be set to 0 for Texture 3D.</param>
491  /// <param name="mipSlice">The mip slice index.</param>
492  /// <param name="region">Destination region</param>
493  /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
494  /// <remarks>
495  /// See unmanaged documentation for usage and restrictions.
496  /// </remarks>
497  public unsafe void SetData(GraphicsDevice device, DataPointer fromData, int arraySlice = 0, int mipSlice = 0, ResourceRegion? region = null)
498  {
499  if (device == null) throw new ArgumentNullException("device");
500  if (region.HasValue && this.Description.Usage != GraphicsResourceUsage.Default)
501  throw new ArgumentException("Region is only supported for textures with ResourceUsage.Default");
502 
503  // Get mipmap description for the specified mipSlice
504  var mipMapDesc = this.GetMipMapDescription(mipSlice);
505 
506  int width = mipMapDesc.Width;
507  int height = mipMapDesc.Height;
508  int depth = mipMapDesc.Depth;
509 
510  // If we are using a region, then check that parameters are fine
511  if (region.HasValue)
512  {
513  int newWidth = region.Value.Right - region.Value.Left;
514  int newHeight = region.Value.Bottom - region.Value.Top;
515  int newDepth = region.Value.Back - region.Value.Front;
516  if (newWidth > width)
517  throw new ArgumentException(string.Format("Region width [{0}] cannot be greater than mipmap width [{1}]", newWidth, width), "region");
518  if (newHeight > height)
519  throw new ArgumentException(string.Format("Region height [{0}] cannot be greater than mipmap height [{1}]", newHeight, height), "region");
520  if (newDepth > depth)
521  throw new ArgumentException(string.Format("Region depth [{0}] cannot be greater than mipmap depth [{1}]", newDepth, depth), "region");
522 
523  width = newWidth;
524  height = newHeight;
525  depth = newDepth;
526  }
527 
528  // Size per pixel
529  var sizePerElement = Description.Format.SizeInBytes();
530 
531  // Calculate depth stride based on mipmap level
532  int rowStride;
533 
534  // Depth Stride
535  int textureDepthStride;
536 
537  // Compute Actual pitch
538  Image.ComputePitch(this.Description.Format, width, height, out rowStride, out textureDepthStride, out width, out height);
539 
540  // Size Of actual texture data
541  int sizeOfTextureData = textureDepthStride * depth;
542 
543  // Check size validity of data to copy to
544  if (fromData.Size != sizeOfTextureData)
545  throw new ArgumentException(string.Format("Size of toData ({0} bytes) is not compatible expected size ({1} bytes) : Width * Height * Depth * sizeof(PixelFormat) size in bytes", fromData.Size, sizeOfTextureData));
546 
547  // Calculate the subResourceIndex for a Texture
548  int subResourceIndex = this.GetSubResourceIndex(arraySlice, mipSlice);
549 
550  // If this texture is declared as default usage, we use UpdateSubresource that supports sub resource region.
551  if (this.Description.Usage == GraphicsResourceUsage.Default)
552  {
553  // If using a specific region, we need to handle this case
554  if (region.HasValue)
555  {
556  var regionValue = region.Value;
557  var sourceDataPtr = fromData.Pointer;
558 
559  // Workaround when using region with a deferred context and a device that does not support CommandList natively
560  // see http://blogs.msdn.com/b/chuckw/archive/2010/07/28/known-issue-direct3d-11-updatesubresource-and-deferred-contexts.aspx
561  if (device.NeedWorkAroundForUpdateSubResource)
562  {
563  if (IsBlockCompressed)
564  {
565  regionValue.Left /= 4;
566  regionValue.Right /= 4;
567  regionValue.Top /= 4;
568  regionValue.Bottom /= 4;
569  }
570  sourceDataPtr = new IntPtr((byte*)sourceDataPtr - (regionValue.Front * textureDepthStride) - (regionValue.Top * rowStride) - (regionValue.Left * sizePerElement));
571  }
572  device.UpdateSubresource(this, subResourceIndex, new DataBox(sourceDataPtr, rowStride, textureDepthStride), regionValue);
573  }
574  else
575  {
576  device.UpdateSubresource(this, subResourceIndex, new DataBox(fromData.Pointer, rowStride, textureDepthStride));
577  }
578  }
579  else
580  {
581  var mappedResource = device.MapSubresource(this, subResourceIndex, this.Description.Usage == GraphicsResourceUsage.Dynamic ? MapMode.WriteDiscard : MapMode.Write);
582  var box = mappedResource.DataBox;
583 
584  // If depth == 1 (Texture1D, Texture2D or TextureCube), then depthStride is not used
585  var boxDepthStride = this.Description.Depth == 1 ? box.SlicePitch : textureDepthStride;
586 
587  // The fast way: If same stride, we can directly copy the whole texture in one shot
588  if (box.RowPitch == rowStride && boxDepthStride == textureDepthStride)
589  {
590  Utilities.CopyMemory(box.DataPointer, fromData.Pointer, sizeOfTextureData);
591  }
592  else
593  {
594  // Otherwise, the long way by copying each scanline
595  var destPerDepthPtr = (byte*)box.DataPointer;
596  var sourcePtr = (byte*)fromData.Pointer;
597 
598  // Iterate on all depths
599  for (int j = 0; j < depth; j++)
600  {
601  var destPtr = destPerDepthPtr;
602  // Iterate on each line
603  for (int i = 0; i < height; i++)
604  {
605  Utilities.CopyMemory((IntPtr)destPtr, (IntPtr)sourcePtr, rowStride);
606  destPtr += box.RowPitch;
607  sourcePtr += rowStride;
608  }
609  destPerDepthPtr += box.SlicePitch;
610  }
611 
612  }
613  device.UnmapSubresource(mappedResource);
614  }
615  }
616 
617  /// <summary>
618  /// Return an equivalent staging texture CPU read-writable from this instance.
619  /// </summary>
620  /// <returns></returns>
621  public abstract Texture ToStaging();
622 
623  /// <summary>
624  /// Loads a texture from a stream.
625  /// </summary>
626  /// <param name="device">The <see cref="GraphicsDevice"/>.</param>
627  /// <param name="stream">The stream to load the texture from.</param>
628  /// <param name="textureFlags">True to load the texture with unordered access enabled. Default is false.</param>
629  /// <param name="usage">Usage of the resource. Default is <see cref="GraphicsResourceUsage.Immutable"/> </param>
630  /// <returns>A texture</returns>
631  public static Texture Load(GraphicsDevice device, Stream stream, TextureFlags textureFlags = TextureFlags.ShaderResource, GraphicsResourceUsage usage = GraphicsResourceUsage.Immutable)
632  {
633  using (var image = Image.Load(stream))
634  return New(device, image, textureFlags, usage);
635  }
636 
637  /// <summary>
638  /// Loads a texture from a stream.
639  /// </summary>
640  /// <param name="device">The <see cref="GraphicsDevice" />.</param>
641  /// <param name="image">The image.</param>
642  /// <param name="textureFlags">True to load the texture with unordered access enabled. Default is false.</param>
643  /// <param name="usage">Usage of the resource. Default is <see cref="GraphicsResourceUsage.Immutable" /></param>
644  /// <returns>A texture</returns>
645  /// <exception cref="System.InvalidOperationException">Dimension not supported</exception>
646  public static Texture New(GraphicsDevice device, Image image, TextureFlags textureFlags = TextureFlags.ShaderResource, GraphicsResourceUsage usage = GraphicsResourceUsage.Immutable)
647  {
648  if (device == null) throw new ArgumentNullException("device");
649  if (image == null) throw new ArgumentNullException("image");
650  switch (image.Description.Dimension)
651  {
652  case TextureDimension.Texture1D:
653  return Texture1D.New(device, image, textureFlags, usage);
654  case TextureDimension.Texture2D:
655  return Texture2D.New(device, image, textureFlags, usage);
656  case TextureDimension.Texture3D:
657  return Texture3D.New(device, image, textureFlags, usage);
658  case TextureDimension.TextureCube:
659  return TextureCube.New(device, image, textureFlags, usage);
660  }
661 
662  throw new InvalidOperationException("Dimension not supported");
663  }
664 
665  /// <summary>
666  /// Creates a new texture with the specified generic texture description.
667  /// </summary>
668  /// <param name="graphicsDevice">The graphics device.</param>
669  /// <param name="description">The description.</param>
670  /// <returns>A Texture instance, either a RenderTarget or DepthStencilBuffer or Texture, depending on Binding flags.</returns>
671  public static Texture New(GraphicsDevice graphicsDevice, TextureDescription description)
672  {
673  if (graphicsDevice == null)
674  {
675  throw new ArgumentNullException("graphicsDevice");
676  }
677  switch (description.Dimension)
678  {
679  case TextureDimension.Texture1D:
680  return new Texture1D(graphicsDevice, description);
681  case TextureDimension.Texture2D:
682  return new Texture2D(graphicsDevice, description);
683  case TextureDimension.Texture3D:
684  return new Texture3D(graphicsDevice, description);
685  case TextureDimension.TextureCube:
686  return new TextureCube(graphicsDevice, description);
687  }
688  return null;
689  }
690 
691  /// <summary>
692  /// Saves this texture to a stream with a specified format.
693  /// </summary>
694  /// <param name="stream">The stream.</param>
695  /// <param name="fileType">Type of the image file.</param>
696  public void Save(Stream stream, ImageFileType fileType)
697  {
698  if (stream == null) throw new ArgumentNullException("stream");
699  using (var staging = ToStaging())
700  Save(stream, staging, fileType);
701  }
702 
703  /// <summary>
704  /// Gets the GPU content of this texture as an <see cref="Image"/> on the CPU.
705  /// </summary>
707  {
708  using (var stagingTexture = ToStaging())
709  return GetDataAsImage(stagingTexture);
710  }
711 
712  /// <summary>
713  /// Gets the GPU content of this texture to an <see cref="Image"/> on the CPU.
714  /// </summary>
715  /// <param name="stagingTexture">The staging texture used to temporary transfer the image from the GPU to CPU.</param>
716  /// <exception cref="ArgumentException">If stagingTexture is not a staging texture.</exception>
717  public Image GetDataAsImage(Texture stagingTexture)
718  {
719  if (stagingTexture == null) throw new ArgumentNullException("stagingTexture");
720  if (stagingTexture.Description.Usage != GraphicsResourceUsage.Staging)
721  throw new ArgumentException("Invalid texture used as staging. Must have Usage = GraphicsResourceUsage.Staging", "stagingTexture");
722 
723  var image = Image.New(stagingTexture.Description);
724  try {
725  for (int arrayIndex = 0; arrayIndex < image.Description.ArraySize; arrayIndex++)
726  {
727  for (int mipLevel = 0; mipLevel < image.Description.MipLevels; mipLevel++)
728  {
729  var pixelBuffer = image.PixelBuffer[arrayIndex, mipLevel];
730  GetData(stagingTexture, new DataPointer(pixelBuffer.DataPointer, pixelBuffer.BufferStride), arrayIndex, mipLevel);
731  }
732  }
733 
734  } catch (Exception)
735  {
736  // If there was an exception, free the allocated image to avoid any memory leak.
737  image.Dispose();
738  throw;
739  }
740  return image;
741  }
742 
743  /// <summary>
744  /// Saves this texture to a stream with a specified format.
745  /// </summary>
746  /// <param name="stream">The stream.</param>
747  /// <param name="stagingTexture">The staging texture used to temporary transfer the image from the GPU to CPU.</param>
748  /// <param name="fileType">Type of the image file.</param>
749  /// <exception cref="ArgumentException">If stagingTexture is not a staging texture.</exception>
750  public void Save(Stream stream, Texture stagingTexture, ImageFileType fileType)
751  {
752  using (var image = GetDataAsImage(stagingTexture))
753  image.Save(stream, fileType);
754  }
755 
756  /// <summary>
757  /// Calculates the mip map count from a requested level.
758  /// </summary>
759  /// <param name="requestedLevel">The requested level.</param>
760  /// <param name="width">The width.</param>
761  /// <param name="height">The height.</param>
762  /// <param name="depth">The depth.</param>
763  /// <returns>The resulting mipmap count (clamp to [1, maxMipMapCount] for this texture)</returns>
764  internal static int CalculateMipMapCount(MipMapCount requestedLevel, int width, int height = 0, int depth = 0)
765  {
766  int size = Math.Max(Math.Max(width, height), depth);
767  int maxMipMap = 1 + (int)Math.Ceiling(Math.Log(size) / Math.Log(2.0));
768 
769  return requestedLevel == 0 ? maxMipMap : Math.Min(requestedLevel, maxMipMap);
770  }
771 
772  protected static DataBox GetDataBox<T>(PixelFormat format, int width, int height, int depth, T[] textureData, IntPtr fixedPointer) where T : struct
773  {
774  // Check that the textureData size is correct
775  if (textureData == null) throw new ArgumentNullException("textureData");
776  int rowPitch;
777  int slicePitch;
778  int widthCount;
779  int heightCount;
780  Image.ComputePitch(format, width, height, out rowPitch, out slicePitch, out widthCount, out heightCount);
781  if (Utilities.SizeOf(textureData) != (slicePitch * depth)) throw new ArgumentException("Invalid size for Image");
782 
783  return new DataBox(fixedPointer, rowPitch, slicePitch);
784  }
785 
786  internal static TextureDescription CreateTextureDescriptionFromImage(Image image, TextureFlags textureFlags, GraphicsResourceUsage usage)
787  {
788  var desc = (TextureDescription)image.Description;
789  desc.Flags = textureFlags;
790  desc.Usage = usage;
791  if ((textureFlags & TextureFlags.UnorderedAccess) != 0)
792  desc.Usage = GraphicsResourceUsage.Default;
793  return desc;
794  }
795 
796  internal void GetViewSliceBounds(ViewType viewType, ref int arrayOrDepthIndex, ref int mipIndex, out int arrayOrDepthCount, out int mipCount)
797  {
798  int arrayOrDepthSize = this.Description.Depth > 1 ? this.Description.Depth : this.Description.ArraySize;
799 
800  switch (viewType)
801  {
802  case ViewType.Full:
803  arrayOrDepthIndex = 0;
804  mipIndex = 0;
805  arrayOrDepthCount = arrayOrDepthSize;
806  mipCount = this.Description.MipLevels;
807  break;
808  case ViewType.Single:
809  arrayOrDepthCount = 1;
810  mipCount = 1;
811  break;
812  case ViewType.MipBand:
813  arrayOrDepthCount = arrayOrDepthSize - arrayOrDepthIndex;
814  mipCount = 1;
815  break;
816  case ViewType.ArrayBand:
817  arrayOrDepthCount = 1;
818  mipCount = Description.MipLevels - mipIndex;
819  break;
820  default:
821  arrayOrDepthCount = 0;
822  mipCount = 0;
823  break;
824  }
825  }
826 
827  internal int GetViewCount()
828  {
829  int arrayOrDepthSize = this.Description.Depth > 1 ? this.Description.Depth : this.Description.ArraySize;
830  return GetViewIndex((ViewType)4, arrayOrDepthSize, this.Description.MipLevels);
831  }
832 
833  internal int GetViewIndex(ViewType viewType, int arrayOrDepthIndex, int mipIndex)
834  {
835  int arrayOrDepthSize = this.Description.Depth > 1 ? this.Description.Depth : this.Description.ArraySize;
836  return (((int)viewType) * arrayOrDepthSize + arrayOrDepthIndex) * this.Description.MipLevels + mipIndex;
837  }
838 
839  internal static GraphicsResourceUsage GetUsageWithFlags(GraphicsResourceUsage usage, TextureFlags flags)
840  {
841  // If we have a texture supporting render target or unordered access, force to UsageDefault
842  if ((flags & TextureFlags.RenderTarget) != 0 || (flags & TextureFlags.UnorderedAccess) != 0)
843  return GraphicsResourceUsage.Default;
844  return usage;
845  }
846  }
847 }
Image GetDataAsImage(Texture stagingTexture)
Gets the GPU content of this texture to an Image on the CPU.
Definition: Texture.cs:717
Texture(GraphicsDevice device, Texture parentTexture, ViewType viewType, int viewArraySlice, int viewMipLevel, PixelFormat viewFormat=PixelFormat.None)
Definition: Texture.cs:106
Texture(GraphicsDevice device, TextureDescription description, ViewType viewType, int viewArraySlice, int viewMipLevel, PixelFormat viewFormat=PixelFormat.None)
Definition: Texture.cs:113
readonly PixelFormat ViewFormat
The format of this texture view.
Definition: Texture.cs:63
static Texture New(GraphicsDevice graphicsDevice, TextureDescription description)
Creates a new texture with the specified generic texture description.
Definition: Texture.cs:671
ImageFileType
Image file format used by Image.Save(string,SiliconStudio.Paradox.Graphics.ImageFileType) ...
A simple wrapper to specify number of mipmaps. Set to true to specify all mipmaps or sets an integer ...
Definition: MipMapCount.cs:43
readonly int MipLevel
The miplevel index of this texture view.
Definition: Texture.cs:73
static int CalculateMipSize(int width, int mipLevel)
Definition: Texture.cs:183
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
Provides method to instantiate an image 1D/2D/3D supporting TextureArray and mipmaps on the CPU or to...
Definition: Image.cs:88
A TextureCube frontend to SharpDX.Direct3D11.Texture2D.
Definition: TextureCube.cs:37
Image GetDataAsImage()
Gets the GPU content of this texture as an Image on the CPU.
Definition: Texture.cs:706
GraphicsResourceUsage
Identifies expected resource use during rendering. The usage directly reflects whether a resource is ...
ImageDescription Description
Description of this image.
Definition: Image.cs:137
static Texture New(GraphicsDevice device, Image image, TextureFlags textureFlags=TextureFlags.ShaderResource, GraphicsResourceUsage usage=GraphicsResourceUsage.Immutable)
Loads a texture from a stream.
Definition: Texture.cs:646
readonly int Depth
The depth of this texture view.
Definition: Texture.cs:58
readonly int ArraySlice
The array index of this texture view.
Definition: Texture.cs:78
virtual TextureDescription GetCloneableDescription()
Definition: Texture.cs:130
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
static Texture Load(GraphicsDevice device, Stream stream, TextureFlags textureFlags=TextureFlags.ShaderResource, GraphicsResourceUsage usage=GraphicsResourceUsage.Immutable)
Loads a texture from a stream.
Definition: Texture.cs:631
A Common description for all textures.
TextureDimension Dimension
The dimension of a texture.
int Height
The height of this texture view.
Definition: Texture.cs:53
SiliconStudio.Core.Utilities Utilities
Definition: Texture.cs:29
unsafe bool GetData(Texture stagingTexture, DataPointer toData, int arraySlice=0, int mipSlice=0, bool doNotWait=false)
Copies the content of this texture from GPU memory to a pointer on CPU memory using a specific stagin...
Definition: Texture.cs:389
ViewType
Defines how a view is selected from a resource.
Definition: ViewType.cs:31
A Texture 3D frontend to SharpDX.Direct3D11.Texture3D.
Definition: Texture3D.cs:37
int Width
The width of this texture view.
Definition: Texture.cs:48
A Texture 2D frontend to SharpDX.Direct3D11.Texture2D.
Definition: Texture2D.cs:37
A Texture 1D frontend to SharpDX.Direct3D11.Texture1D.
Definition: Texture1D.cs:37
void Save(Stream stream, ImageFileType fileType)
Saves this texture to a stream with a specified format.
Definition: Texture.cs:696
override void Destroy()
Disposes of object resources.
Definition: Texture.cs:138
Provides access to data organized in 3D.
Definition: DataBox.cs:12
MipMapDescription GetMipMapDescription(int mipmap)
Gets the mipmap description of this instance for the specified mipmap level.
Definition: Texture.cs:178
readonly TextureDescription Description
Common description for the original texture.
Definition: Texture.cs:43
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Definition: DirectXTexP.h:175
unsafe void SetData(GraphicsDevice device, DataPointer fromData, int arraySlice=0, int mipSlice=0, ResourceRegion?region=null)
Copies the content an data on CPU memory to this texture into GPU memory.
Definition: Texture.cs:497
void Save(Stream stream, Texture stagingTexture, ImageFileType fileType)
Saves this texture to a stream with a specified format.
Definition: Texture.cs:750
_In_ size_t _In_ size_t size
Definition: DirectXTexP.h:175
PixelFormat
Defines various types of pixel formats.
Definition: PixelFormat.cs:32
int GetSubResourceIndex(int arraySlice, int mipSlice)
Gets the absolute sub-resource index from the array and mip slice.
Definition: Texture.cs:196
readonly bool IsBlockCompressed
Gets a boolean indicating whether this Texture is a using a block compress format (BC1...
Definition: Texture.cs:83
TextureDimension Dimension
The dimension of a texture.
readonly ViewType ViewType
The format of this texture view.
Definition: Texture.cs:68
Base class for texture resources.
Definition: Texture.cs:38
void SetData(DataPointer fromData, int arraySlice=0, int mipSlice=0, ResourceRegion?region=null)
Copies the content an data on CPU memory to this texture into GPU memory using the specified Graphics...
Definition: Texture.cs:353