HRESULT CreateTextureFromDDS()

in Src/XboxDDSTextureLoader.cpp [416:582]


    HRESULT CreateTextureFromDDS(_In_ ID3D11DeviceX* d3dDevice,
        _In_ const DDS_HEADER* header,
        _In_reads_bytes_(bitSize) const uint8_t* bitData,
        _In_ size_t bitSize,
        _In_ bool forceSRGB,
        _Outptr_opt_ ID3D11Resource** texture,
        _Outptr_opt_ ID3D11ShaderResourceView** textureView,
        _Outptr_ void** grfxMemory) noexcept
    {
        HRESULT hr = S_OK;

        uint32_t width = header->width;
        uint32_t height = header->height;
        uint32_t depth = header->depth;

        uint32_t mipCount = header->mipMapCount;
        if (0 == mipCount)
        {
            mipCount = 1;
        }

        if (!(header->ddspf.flags & DDS_FOURCC)
            || (MAKEFOURCC('X', 'B', 'O', 'X') != header->ddspf.fourCC))
        {
            // Use standard DDSTextureLoader instead
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        auto xboxext = reinterpret_cast<const DDS_HEADER_XBOX*>(reinterpret_cast<const uint8_t*>(header) + sizeof(DDS_HEADER));

#ifndef NDEBUG
        if (xboxext->xdkVer < _XDK_VER)
        {
            OutputDebugStringA("WARNING: DDS XBOX file may be outdated and need regeneration\n");
        }
#endif

        uint32_t arraySize = xboxext->arraySize;
        if (arraySize == 0)
        {
            return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }

        bool isCubeMap = false;

        switch (xboxext->resourceDimension)
        {
        case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
            if ((header->flags & DDS_HEIGHT) && height != 1)
            {
                return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            }
            height = depth = 1;
            break;

        case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
            if (xboxext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE)
            {
                arraySize *= 6;
                isCubeMap = true;
            }
            depth = 1;
            break;

        case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
            if (!(header->flags & DDS_HEADER_FLAGS_VOLUME))
            {
                return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            }

            if (arraySize > 1)
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }
            break;

        default:
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        if (xboxext->tileMode == uint32_t(-1))
        {
            return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }

        // Bound sizes
        if (mipCount > D3D11_REQ_MIP_LEVELS)
        {
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        switch (xboxext->resourceDimension)
        {
        case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
            if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) ||
                (width > D3D11_REQ_TEXTURE1D_U_DIMENSION))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }
            break;

        case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
            if (isCubeMap)
            {
                // This is the right bound because we set arraySize to (NumCubes*6) above
                if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
                    (width > D3D11_REQ_TEXTURECUBE_DIMENSION) ||
                    (height > D3D11_REQ_TEXTURECUBE_DIMENSION))
                {
                    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                }
            }
            else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
                (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) ||
                (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }
            break;

        case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
            if ((arraySize > 1) ||
                (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
                (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
                (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION))
            {
                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
            }
            break;
        }

        if (xboxext->dxgiFormat == DXGI_FORMAT_UNKNOWN)
        {
            return E_FAIL;
        }

        if (!xboxext->dataSize || !xboxext->baseAlignment)
        {
            return E_FAIL;
        }

        if (xboxext->dataSize > bitSize)
        {
            return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        }

        // Allocate graphics memory. Depending on the data size it uses 4MB or 64K pages.
        *grfxMemory = XMemAlloc(xboxext->dataSize, c_XMemAllocAttributes);
        if (!*grfxMemory)
            return E_OUTOFMEMORY;

        // Copy tiled data into graphics memory
        memcpy(*grfxMemory, bitData, xboxext->dataSize);

        // Create the texture
        hr = CreateD3DResources(d3dDevice, xboxext,
            width, height, depth, mipCount, arraySize,
            forceSRGB, isCubeMap, *grfxMemory,
            texture, textureView);
        if (FAILED(hr))
        {
            XMemFree(grfxMemory, c_XMemAllocAttributes);
            *grfxMemory = nullptr;
        }

        return hr;
    }