Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 05 Dec 2011
Offline Last Active Feb 29 2012 06:51 AM

Topics I've Started

How to save mipmaps in a DDS file manually ?

29 February 2012 - 01:27 AM

Hi everyone,

I'm trying to save a mip-map chain generated with D3DX11FilterTexture into a DDS file.
I'm using Humus' Framework 3 image class to save as DDS and the ATI Texture Compressor library to compress the texture.

I generate the mipmaps before compressing each one individually and when I save it I end up with the following image and mip-maps:
Attached File  DDS_M0.PNG   31.19KB   49 downloads
Attached File  DDS_M1.PNG   10.8KB   48 downloads
Attached File  DDS_M2.PNG   5.04KB   37 downloads

The first image is correct, the second contains all the mip-maps and the remaining mip-maps are black, so it sounds to me like it's a problem saving the image. I've assumed all along that in a DDS file the mip-maps are stored one after the other and this doc seems to correlate with that.

Here is the code where I generate and store the mip-maps in the in-memory image:

			    D3D11_TEXTURE2D_DESC desc;
			    desc.MiscFlags = 0;
			    // Generate mip-map chain
			    if (mipLevels > 1)
				    if (D3DX11FilterTexture(m_ctx, finalTexture, 0, mipFilter) != S_OK)
					    return -1;
			    FW3Imaging::FORMAT format = FW3Imaging::FORMAT_NONE;
			    for (int i = 0; i < elementsOf(formats); ++i)
				    if (formats[i] == desc.Format)
					    format = (FW3Imaging::FORMAT)i;
			    // TODO: error handling
			    ID3D11Texture2D* staging = NULL;
			    desc.BindFlags = 0;
			    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
			    desc.Usage = D3D11_USAGE_STAGING;
			    if ((hr = m_device->CreateTexture2D(&desc, NULL, &staging)) != S_OK)
				    return -1;
			    m_ctx->CopyResource(staging, finalTexture);
			    D3D11_MAPPED_SUBRESOURCE subresource;
			    if ((hr = m_ctx->Map(staging, D3D11CalcSubresource(0,0,mipLevels), D3D11_MAP_READ, 0, &subresource)) != S_OK)
				    return -1;
			    unsigned char* data = (unsigned char*)subresource.pData;
			    size_t pixelsSize = GetMipMappedSize(desc.Width, desc.Height, 1, format, 0, mipLevels);
			    unsigned char* dest_pixels = new unsigned char[pixelsSize];
			    unsigned char* pixels = dest_pixels;
			    unsigned int width = desc.Width;
			    unsigned int height = desc.Height;
			    for (unsigned int nMip = 0; nMip < mipLevels; ++nMip)
				    unsigned int dest_pitch = getBytesPerPixel(format) * width;
				    // Copy the pixels row by row
				    for (unsigned int i = 0; i < height; ++i)
					    // Copy this row into the pixels
					    memcpy(pixels, data, dest_pitch);
					    pixels += dest_pitch;
					    data += subresource.RowPitch;
				    width = max(1, width >> 1);
				    height = max(1, height >> 1);
			    outImage->Load(dest_pixels, format, desc.Width, desc.Height, 1, mipLevels, false);
			    delete[] dest_pixels;
			    // That only works for power of two textures.
			    //outImage->Load(subresource.pData, format, desc.Width, desc.Height, 1, 1, false);
			    m_ctx->Unmap(staging, 0);

All the outImage->Load function does is copy the data.
The code that compresses the image (and each mip-map) is as follow:

    bool Image::Compress(const FORMAT newCompressedFormat, const CompressionOptions* pOptions)
	    if (!isCompressedFormat(newCompressedFormat))
		    return false;
	    if (getChannelCount(this->format) >= 3)
		    Swap(0,2);  // Need to swap R & B to get BGR
	    const CompressionOptions& options = *pOptions;
	    ATI_TC_CompressOptions ati_options;
	    memset(&ati_options, 0, sizeof(options));
	    ati_options.dwSize = sizeof(ati_options);
	    ati_options.bUseChannelWeighting = options.UseChannelWeighting;
	    ati_options.fWeightingRed = options.WeightingRed;
	    ati_options.fWeightingGreen = options.WeightingGreen;
	    ati_options.fWeightingBlue = options.WeightingBlue;
	    ati_options.bUseAdaptiveWeighting = options.UseAdaptiveWeighting;
	    ati_options.bDXT1UseAlpha = options.DXT1UseAlpha;
	    ati_options.nAlphaThreshold = options.AlphaThreshold;
	    ati_options.bDisableMultiThreading = options.DisableMultiThreading;
	    ati_options.nCompressionSpeed = (ATI_TC_Speed)options.Speed;
	    uint32 mwidth = this->width;
	    uint32 mheight = this->height;
	    unsigned char *data = this->pixels;
	    std::vector<ATI_TC_Texture> mipMaps(nMipMaps);
	    for (int i = 0; i < nMipMaps; ++i)
		    mipMaps[i].pData = NULL;
	    bool error = false;
	    for (int i = 0; i < nMipMaps; ++i)
		    unsigned int pitch = getBytesPerPixel(format) * mwidth;
		    // Init src texture
		    ATI_TC_Texture srcTexture;
		    srcTexture.dwSize = sizeof(srcTexture);
		    srcTexture.dwWidth = mwidth;
		    srcTexture.dwHeight = mheight;
		    srcTexture.dwPitch = pitch;
		    srcTexture.format = GetATI_TC_Format(this->format);
		    srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
		    srcTexture.pData = (ATI_TC_BYTE*)data;
		    // Init dest texture
		    ATI_TC_Texture& destTexture = mipMaps[i];
		    destTexture.dwSize = sizeof(destTexture);
		    destTexture.dwWidth = mwidth;
		    destTexture.dwHeight = mheight;
		    destTexture.dwPitch = getBytesPerPixel(newCompressedFormat) * mwidth;
		    destTexture.format = GetATI_TC_Format(newCompressedFormat);
		    destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
		    destTexture.pData = new ATI_TC_BYTE[destTexture.dwDataSize];
		    ATI_TC_ERROR ati_tc_error = ATI_TC_ConvertTexture(&srcTexture, &destTexture, &ati_options, NULL, NULL, NULL);
		    if (ati_tc_error != ATI_TC_OK)
			    error = true;
		    data += srcTexture.dwDataSize;
		    mwidth = max(1, mwidth >> 1);
		    mheight = max(1, mheight >> 1);
	    if (!error)
		    size_t newSize = 0;
		    // Compute new global size
		    for (int i = 0; i < nMipMaps; ++i)
			    newSize += mipMaps[i].dwDataSize;
		    // Re-allocate memory
		    this->pixels = new unsigned char[newSize];
		    data = this->pixels;
		    // Copy each mip-map into the new pixels
		    for (int i = 0; i < nMipMaps; ++i)
			    memcpy(data, mipMaps[i].pData, mipMaps[i].dwDataSize);
			    data += mipMaps[i].dwDataSize;
		    this->format = newCompressedFormat;
	    for (int i = 0; i < nMipMaps; ++i)
		    if (mipMaps[i].pData != NULL)
			    delete[] mipMaps[i].pData;
	    return error;

The save function simply writes the header out and then saves all the pixels in one go:

   // Generate header

   swrite(&header, sizeof(header), 1, file);
   if (headerDX10.dxgiFormat)
      swrite(&headerDX10, sizeof(headerDX10), 1, file);
   int size = GetMipMappedSize(0, nMipMaps);
   swrite(pixels, size, 1, file);

Any ideas on what I'm doing wrong ?

Direct Compute up/down scaling

05 December 2011 - 03:25 PM

Hello everyone,

I was wondering how would one go about taking a non-power of two texture and using a compute shader, re-scale it either up or down to the nearest power of two.
I know how to get the nearest power of two, and I know how to initialize DirectX 11 and create the required shaders; however I have never used the compute features of DX11.

I'm not sure what happens when I create my texture (using D3DX11CreateTextureFromFile) with a non-power of 2 texture ? I think it ends up padding the texture which undesirable.
Or should I just create a structured buffer, store all the pixels in there and make an unordered access view of that ? But then I need to figure out how to assign the threads as I cannot split a few pixels in half.

The application has to typically process GB worth of textures, hence why I'm going for Direct Compute.

Any ideas ?