• Advertisement
Sign in to follow this  

[DX10]How to create a DX10 texture object from system raw data?

This topic is 2127 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys, I try to use D3DX10CreateTextureFromMemory() to create a DX10 texture from my system mem raw data(which is stored in my RGBA 4bytes unsgined char buffer). But the API always return failed.Seems like it only accepts an image file format in system memory(bmp,jpg,png...). So which API could I use to create a DX10 texture and upload my custom image data?

Share this post


Link to post
Share on other sites
Advertisement
Use the CreateTexture* method of the device interface, Map() it, write data to the mapped pointer, Unmap() it. Alternatively, CreateTexture* takes a pointer (pInitialData parameter) with which you can specify the initial resource data.

Share this post


Link to post
Share on other sites
[source lang='c++']
uint texels[width*height]; // 32-bit texels used as initial data; fill this array before calling CreateTexture
//...
D3D10_TEXTURE2D_DESC tDesc; // fill with apt values; be sure to use same width and height as the initial data size
//...
D3D10_SUBRESOURCE_DATA initialData;
initialData.pSysMem = (void*)&texels[0]; // you want to use the texel array as the data
initialData.SysMemPitch = sizeof(uint) * width; // distance between two adjacent lines of texels in memory
initialData.SysMemSlicePitch = 0; // slice pitch is meaningless for 2d textures, but could be width*height*sizeof(texel) if this was a volume
ID3D10Texture2D *pTexture; // though smart pointer is recommended here
HRESULT hr = pDevice->CreateTexture2D(&tDesc, &initialData, &pTexture);
[/source]

Share this post


Link to post
Share on other sites
When you use Map, you get the Pitch from the system instead of defining it yourself. Pitch is greater than or equal to width*sizeof(texel), so you should not assume anything about it. Instead, copy width*sizeof(texel) texels at a time and find each individual scanline pointer by using pitch*y.

Share this post


Link to post
Share on other sites
Also note that if you want to use a manually-filled mip chain as an initial data, you need to define an initial data structure for each of the mip levels separately. The initial data parameter of CreateTexture* expects an array of D3D10_SUBRESOURCE_DATA structs if the texture descriptor asks for more than one mip level to be created.

Share this post


Link to post
Share on other sites
Hi Nik02,
Thanks for your help.
I tried your approach but ended up with function call [size=2]CreateTexture2D() fails. The debug output is:

D3D10: ERROR: ID3D10Device::CreateTexture2D: pInitialData[5].pSysMem cannot be NULL. [ STATE_CREATION ERROR #100: CREATETEXTURE2D_INVALIDINITIALDATA ]

D3D10: ERROR: ID3D10Device::CreateTexture2D: pInitialData[6].pSysMem cannot be NULL. [ STATE_CREATION ERROR #100: CREATETEXTURE2D_INVALIDINITIALDATA ]
[size=2]

D3D10: ERROR: ID3D10Device::CreateTexture2D: pInitialData[6].SysMemPitch cannot be 0 [ STATE_CREATION ERROR #100: CREATETEXTURE2D_INVALIDINITIALDATA ]
[size=2]

D3D10: ERROR: ID3D10Device::CreateTexture2D: pInitialData[9].pSysMem cannot be NULL. [ STATE_CREATION ERROR #100: CREATETEXTURE2D_INVALIDINITIALDATA ]
[size=2]

First-chance exception at 0x7676b9bc in mexplorer.exe: Microsoft C++ exception: _com_error at memory location 0x012df3b4..
[size=2]

D3D10: ERROR: ID3D10Device::CreateTexture2D: Returning E_INVALIDARG, meaning invalid parameters were passed. [ STATE_CREATION ERROR #104: CREATETEXTURE2D_INVALIDARG_RETURN ]
[size=2]

..\..\micaps\render\directx\mgral_directx.cpp(339): iD3dDevice_->CreateTexture2D(&texDesc, &initialData, &pTexture) hr=E_INVALIDARG (0x80070057)


The input texels is an array of the type unsigned char (MGRALubyte), of length 4*width*height. That is the RGBA component of a texel is represented by 4 unsigned chars. So in my code (attatched at the end of this post), I allocated another array and convert each texel of the input array to the new array. Then I followed your code.


MGRALuint mgral_directx::generateTexture(MGRALvoid *pixels, MGRALsizei *w, MGRALsizei *h)
{
UINT width = *w, height = *h;
UINT *texels, *pt, r, g, b, a;
MGRALubyte *pp;
texels = (UINT *)malloc(width*height*sizeof(UINT));
if ( texels == NULL )
return 0;
// 32-bit texels used as initial data; fill this array
// before calling CreateTexture2D
pt = texels; pp = (MGRALubyte *)pixels;
for ( int i = 0; i < height; i++ )
for ( int j = 0; j < width; j++ ) {
r = UINT(*pp++);
g = UINT(*pp++);
b = UINT(*pp++);
a = UINT(*pp++);
*pt++ = r<<24 | g<<16 | b<<8 | a;
}
D3D10_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(D3D10_TEXTURE2D_DESC));
texDesc.Width = width;
texDesc.Height = height;
// To generate mipmap levels automatically, set the number of mipmap levels to 0.
texDesc.MipLevels = 0;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UINT;
DXGI_SAMPLE_DESC sampleDesc;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D10_USAGE_IMMUTABLE;
texDesc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;

D3D10_SUBRESOURCE_DATA initialData;
initialData.pSysMem = (void *)texels;
// distance between two adjacent lines of texels in memory
initialData.SysMemPitch = width*sizeof(UINT);
// slice pitch is meaningless for 2d textures, but could be
// width*height*sizeof(texel) if this was a 3d volume
initialData.SysMemSlicePitch = 0;
ID3D10Texture2D *pTexture=NULL;
HR(iD3dDevice_->CreateTexture2D(&texDesc, &initialData, &pTexture));
free(texels);
return (MGRALuint)pTexture;
}

Share this post


Link to post
Share on other sites
You are asking the driver to allocate the mip chain to the texture (by setting the texDesc.MipLevels to 0). Thus, you should supply the initial data of the texture as an array of D3D10_SUBRESOURCE_DATA structures.

To determine the number of needed mip levels for a given width and height, create a loop in which you divide width and height by 2 at each iteration, and keep both at least at 1 (because width or height cannot be 0 on any mip level). When both width and height are 1, the number of iterations that the loop ran until that point represents the number of mip levels given initial width and height.

That said:

If you want to make this easier, first create the texture (and mip levels) without specifying initial data. Then fill the first mip level with UpdateSubresource or Map/Unmap. Finally, you can call D3DX10FilterTexture to fill the rest of the mip levels automatically. This means that the texture cannot be allocated as immutable, though.

Share this post


Link to post
Share on other sites

You are asking the driver to allocate the mip chain to the texture (by setting the texDesc.MipLevels to 0). Thus, you should supply the initial data of the texture as an array of D3D10_SUBRESOURCE_DATA structures.

To determine the number of needed mip levels for a given width and height, create a loop in which you divide width and height by 2 at each iteration, and keep both at least at 1 (because width or height cannot be 0 on any mip level). When both width and height are 1, the number of iterations that the loop ran until that point represents the number of mip levels given initial width and height.

That said:

If you want to make this easier, first create the texture (and mip levels) without specifying initial data. Then fill the first mip level with UpdateSubresource or Map/Unmap. Finally, you can call D3DX10FilterTexture to fill the rest of the mip levels automatically. This means that the texture cannot be allocated as immutable, though.


Thanks a lot, Nik02.
I changed the mipmap level field "[size="2"]MipLevels" from 0 to 1 and it works. Actually my texture is 3200 pixels wide plus 1600 pixels high. I wonder if this large size results in performance issue if only one mipmap level is given. In addition, does Direc3D require the texture size to be the power of 2 like OpenGL?

Share this post


Link to post
Share on other sites
D3D10 hardware does not require powers of two, but performance can be better if you use them. In addition, the driver has an easier job of allocating physical memory for power of two dimensions.

If the usage pattern for this texture is such that you mostly use it in its full dimensions, performance is not impacted by the lack of mipmaps. But if the texture is mostly viewed far away (such as a terrain albedo map), mipmaps would definitely give more performance (and quality).

Share this post


Link to post
Share on other sites

D3D10 hardware does not require powers of two, but performance can be better if you use them. In addition, the driver has an easier job of allocating physical memory for power of two dimensions.

If the usage pattern for this texture is such that you mostly use it in its full dimensions, performance is not impacted by the lack of mipmaps. But if the texture is mostly viewed far away (such as a terrain albedo map), mipmaps would definitely give more performance (and quality).


I am still not very clear about the mipmap levels field "MipLevels". The MSDN says:

"Number of subtextures (also called mipmap levels). Use 1 for a multisampled texture; or 0 to generate a full set of subtextures"

I don't know who is responsible for generate the textures for all mipmap level, app developer or D3D? If it's the app developer's job, I guess an array of D3D10_SUBRESOURCE_DATA should be passed as the second argument for CreateTexture2D method.

Share this post


Link to post
Share on other sites
The documentation is being unclear here. The driver does allocate the mip chain, but filling it is the responsibility of the developer (hence the method expects to find an array of initial data structs). You have to take into account that mipmaps can be used for other things than classic trilinear texture filtering - the API makes no assumptions about the actual usage.

You can give an array of D3D10_SUBRESOURCE_DATA structures to the CreateTexture function; the pointer is to the first element, and the number of needed elements (+ mip dimensions for each level) can be determined by using the formula I posted earlier in this thread.

In case you only have one level, the array happens to contain only one element (pointer to array contents is the same as pointer to its first element).

Also, I repeat my previous advice: use UpdateSubresource and D3DX10FilterTexture if you don't specifically need an immutable texture; these will make "standard" mip filling very easy.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement