Sign in to follow this  
Gavin Williams

Creating a Texture2D array

Recommended Posts

Gavin Williams    985
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 :

[CODE]
Texture2D.FromFile(SlimDXDirect3D11.Device device, List<String> sourceFiles);
[/CODE]

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)

[code]
// 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);
[/code] Edited by Gavin Williams

Share this post


Link to post
Share on other sites
MJP    19754
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.

Share this post


Link to post
Share on other sites
Gavin Williams    985
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 ...

Share this post


Link to post
Share on other sites
Gavin Williams    985
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 :

[code]
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);
[/code]

And here is the shader code :

[code]
Texture2DArray<float4> gTexture : register (t0);

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

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 Edited by Gavin Williams

Share this post


Link to post
Share on other sites
Gavin Williams    985
At [url="http://www.rastertek.com/dx11tut17.html"]http://www.rastertek.../dx11tut17.html[/url], the author Craig uses a different approach to a texture array,
[code]
// Set shader texture array resource in the pixel shader.
deviceContext->PSSetShaderResources(0, 2, textureArray);
[/code]
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 : [url="http://code.google.com/p/slimdx/issues/detail?id=848"]http://code.google.com/p/slimdx/issues/detail?id=848[/url] seems to confirm this Edited by Gavin Williams

Share this post


Link to post
Share on other sites
Mike.Popoloski    3258
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.

Share this post


Link to post
Share on other sites
Gavin Williams    985
You are absolutely right. Thank you. Putting it all in a for-loop fixed it. Code follows :

[code]
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[i] = new DataRectangle(rowPitch, dataStream);
}
[/code]

I'll also try out the DataStream method to avoid the array and then look at getting actual textures in.

Share this post


Link to post
Share on other sites
Gavin Williams    985
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 :

[code]
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[i] = new DataRectangle(rowPitch, dataStream);
SDXForm.Device11.ImmediateContext.UnmapSubresource(singleTexture, 0)
[/code]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this