Creating a Texture2D array

Started by
6 comments, last by Gavin Williams 11 years, 10 months ago
Hi,

How do I create a Texture2D array in SlimDX ? Or alternatively, can I use photoshops dds exporter to create a dds texture array to load into the Texture2d as i do for normal Textured2D's ?

I have looked at using CopySubresource region() which looks like a good candidate for copying a texture into a texture array because i can select the destination subresource. But there seems to be a catch : MapSubresource requires CPUAccessFlags.Write, so that excludes Default and Immutable, leaving Staging which must be BindFlags.None so i then cant use it as a resource and Dynamic which cannot have an array size of anything but [1]. So it appeares to me as though CopySubresource cant be used for texture arrays !! That seems really weird to me.

What I'm after is :


Texture2D.FromFile(SlimDXDirect3D11.Device device, List<String> sourceFiles);


I know I wont get it, because GOD ! that would be just too easy wouldn't it. No one would dare write a texture loading method so straight forward.

Anyway ...

... I now have the following code, (which will compile but i only see black)


// texture2DArray_G1Brushes ===========================================================
ImageLoadInformation loadInfo = new ImageLoadInformation();
loadInfo.CpuAccessFlags = CpuAccessFlags.Read;
loadInfo.BindFlags = BindFlags.None;
loadInfo.Depth = 1;
loadInfo.FilterFlags = FilterFlags.Point;
loadInfo.FirstMipLevel = 1;
loadInfo.Format = Format.R8G8B8A8_UNorm;
loadInfo.Height = 220;
loadInfo.MipFilterFlags = FilterFlags.None;
loadInfo.MipLevels = 1;
loadInfo.OptionFlags = ResourceOptionFlags.None;
loadInfo.Usage = ResourceUsage.Staging;
loadInfo.Width = 200;

DataRectangle[] dataRectangleArray = new DataRectangle[2];

Texture2D tex1 = Texture2D.FromFile(SDXForm.Device11,"Resources/Logo1.dds",loadInfo);
Single[] fArray = new Single[200*220*4];
DataStream dataStream = new DataStream(fArray, true, true);
Texture2D.ToStream(SDXForm.Device11.ImmediateContext, tex1, ImageFileFormat.Dds, dataStream);
dataRectangleArray[0] = new DataRectangle(200*4, dataStream);

Texture2D tex2 = Texture2D.FromFile(SDXForm.Device11, "Resources/Logo2.dds", loadInfo);
Texture2D.ToStream(SDXForm.Device11.ImmediateContext, tex2, ImageFileFormat.Dds, dataStream);
dataRectangleArray[1] = new DataRectangle(200 * 4, dataStream);

Texture2DDescription textureDescription = new Texture2DDescription()
{
ArraySize = 2,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm,
Height = 220,
MipLevels = 1,
OptionFlags = ResourceOptionFlags.None,
SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0),
Usage = ResourceUsage.Immutable,
Width = 200
};

texture2DArray_G1Brushes = new Texture2D(SDXForm.Device11, textureDescription, dataRectangleArray);
AddResource("texture2DArray_G1Brushes", texture2DArray_G1Brushes);

// srv_G1BrushesArray =================================================================
ShaderResourceViewDescription shaderResourceDesc = new ShaderResourceViewDescription()
{
ArraySize = 2,
Format = Format.R8G8B8A8_UNorm,
Dimension = ShaderResourceViewDimension.Texture2DArray,
MostDetailedMip = 0,
MipLevels = 1
};
srv_G1BrushesArray = new ShaderResourceView(SDXForm.Device11, texture2DArray_G1Brushes, shaderResourceDesc);
AddResource("srv_G1BrushesArray", srv_G1BrushesArray);
Advertisement
You don't want to copy in the array slices one by one after you've created the texture. What you want to do is have all of the data for all slices ready in CPU memory, then provide that data as the initialization data when you create your texture array. One way to do this is to load all of the array slices as STAGING resources, map them to get the pointer to their data, and then use those pointers when initializing the IMMUTABLE Texture2D resource for your texture array. Unfortunately I'm not too familar with SlimDX so I'm not sure how exactly to express it with that API, but there should be a way to accomplish this.
I have tried to provide all the textures at once in a DataRectangle[] , which as far as i can tell, is similar to a subresource in c++, though maybe the DataBox is the equivalent. Same thing. And I have seen an approach in c++ that uses a subresources to successfully load initial data. However, Texture2D does provide for a DataRectangle[] as a parameter, so I was looking at that route. I will continue my search now ...
Ive had to go back to basics here (again) to try to get a grasp on how DirectX handles resource construction, just to rule out some issues and get a better understanding. So I'm just filling some textures in manually with colors to prove the array is working... and it actually isn't working at all ..

Here is the texture initialization code, including the creation of the Texture2D array :


DataRectangle[] dataRectangleArray = new DataRectangle[2];
float[] sysResource = new float[loadInfo.Width * loadInfo.Height * 4];
int rowPitch = 200 * 16;
// fill in tex 1
for (int j = 0; j < sysResource.Length; j += 4)
{
sysResource[j + 0] = 0; // r
sysResource[j + 1] = 0; // g
sysResource[j + 2] = 1; // b
sysResource[j + 3] = 1; // a
}
DataStream dataStream = new DataStream(sysResource, true, true);
dataRectangleArray[0] = new DataRectangle(rowPitch, dataStream);

// fill in tex 2
for (int j = 0; j < sysResource.Length; j += 4)
{
sysResource[j + 0] = 1; // r
sysResource[j + 1] = 0; // g
sysResource[j + 2] = 0; // b
sysResource[j + 3] = 1; // a
}
dataStream = new DataStream(sysResource, true, true);
dataRectangleArray[1] = new DataRectangle(rowPitch, dataStream);


// create texture array - with initial data from dataRectangleArray
Texture2DDescription textureDescription = new Texture2DDescription()
{
ArraySize = 2,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
Format = SlimDX.DXGI.Format.R32G32B32A32_Float,
Height = 220,
MipLevels = 1,
OptionFlags = ResourceOptionFlags.None,
SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0),
Usage = ResourceUsage.Immutable,
Width = 200
};
texture2DArray_G1Brushes = new Texture2D(SDXForm.Device11, textureDescription, dataRectangleArray);
AddResource("texture2DArray_G1Brushes", texture2DArray_G1Brushes);

// srv_G1BrushesArray =================================================================
ShaderResourceViewDescription shaderResourceDesc = new ShaderResourceViewDescription()
{
Dimension = ShaderResourceViewDimension.Texture2DArray,
MipLevels = 1,
MostDetailedMip = 0,
FirstArraySlice = 0,
ArraySize = 2,
};
srv_G1BrushesArray = new ShaderResourceView(SDXForm.Device11, texture2DArray_G1Brushes, shaderResourceDesc);
AddResource("srv_G1BrushesArray", srv_G1BrushesArray);


And here is the shader code :


Texture2DArray<float4> gTexture : register (t0);

float4 PShader(PSIN input) : SV_Target
{
float4 color = gTexture.Sample(PointSampler, float3(input.texcoord, 0));
return color;
}


And unexpectedly I'm getting a red texture ! But clearly it should be blue from this code ! So could anyone point out what I'm doing wrong in that regard ? By the way, it acutally doesn't matter what number i put into the sample shader in the z postion 0 - 500, it doesn't change anything, I would expect it to at least have the decency to crash on me.

Oh, and can anyone confirm that rowPitch = texture.width * format.width ? So for a texture 200 pixels across and Format.R32G32B32A32_Float, rowPitch = 200*16
At http://www.rastertek.../dx11tut17.html, the author Craig uses a different approach to a texture array,

// Set shader texture array resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 2, textureArray);

but i suspect that all he's doing is loading the textures into consecutive registers, and not utilizing texture array capabilities at all, can someone confirm this ? Because his way looks a lot easier, just by providing an array of ShaderResourcesViews.

Edit : http://code.google.com/p/slimdx/issues/detail?id=848 seems to confirm this
I believe your problem is that you're expecting DataStream to create a copy of the data you give it; it does not. It essentially creates a pointer to the data, so after creating the first array slice you go on and overwrite all the data and fill it with red instead, which is why your texture is ending up red no matter what you do.

You can either create separate managed arrays for the data, or you can use the DataStream overload that takes an integer instead of an array. This will allocate the given number of bytes in native memory (avoiding the GC overhead for the large data arrays typically used by textures) and let you write into it with the provided Write() functions. If you do it this way, make sure you rewind the stream to the start by setting Position = 0 before passing it to D3D.
Mike Popoloski | Journal | SlimDX
You are absolutely right. Thank you. Putting it all in a for-loop fixed it. Code follows :


DataRectangle[] dataRectangleArray = new DataRectangle[textureFiles.Count];
for(int i = 0; i < textureFiles.Count; i++)
{
float[] sysResource = new float[loadInfo.Width * loadInfo.Height * 4];
int rowPitch = 200 * 16;

FillArrayWithColorData(ref sysResource);

DataStream dataStream = new DataStream(sysResource, true, true);
dataRectangleArray = new DataRectangle(rowPitch, dataStream);
}


I'll also try out the DataStream method to avoid the array and then look at getting actual textures in.
Well I finally got it working, a few kinks to work out, but textures are 'mostly loading'. here's the core code of the copy operation :


DataStream dataStream = new DataStream(width * height * formatWidth, true, true);
DataBox databox = SDXForm.Device11.ImmediateContext.MapSubresource(singleTexture, 0,0, MapMode.Read, SlimDX.Direct3D11.MapFlags.None);
// copy databox into dataStream
dataStream.Position = 0;
databox.Data.Position = 0;
byte[] buffer = new byte[formatWidth];
while (dataStream.Position < dataStream.Length)
{
databox.Data.Read(buffer,0,formatWidth);
dataStream.Write(buffer, 0, formatWidth);
}
dataStream.Position = 0;
databox.Data.Position = 0;

dataRectangleArray = new DataRectangle(rowPitch, dataStream);
SDXForm.Device11.ImmediateContext.UnmapSubresource(singleTexture, 0)

This topic is closed to new replies.

Advertisement