1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-09-19 15:19:56 +00:00
DirectXTex/Auxiliary/DirectXTexXboxTile.cpp
2024-01-07 19:26:09 -08:00

1079 lines
38 KiB
C++

//--------------------------------------------------------------------------------------
// File: DirectXTexXboxTile.cpp
//
// DirectXTex Auxilary functions for converting from linear to Xbox tiling
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//--------------------------------------------------------------------------------------
#include "DirectXTexP.h"
#include "DirectXTexXbox.h"
//#define VERBOSE
using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::Internal;
using namespace Xbox;
namespace
{
//----------------------------------------------------------------------------------
inline HRESULT TileByElement1D(
_In_reads_(nimages) const Image* const * images,
size_t nimages,
uint32_t level,
_In_ XGTextureAddressComputer* computer,
_In_ const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox,
size_t bpp,
size_t w,
bool packed)
{
uint8_t* dptr = xbox.GetPointer();
const uint8_t* endPtr = dptr + layout.SizeBytes;
for (uint32_t item = 0; item < nimages; ++item)
{
const Image* img = images[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == images[0]->width);
assert(img->height == images[0]->height);
assert(img->rowPitch == images[0]->rowPitch);
assert(img->format == images[0]->format);
const uint8_t* sptr = img->pixels;
for (size_t x = 0; x < w; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
UINT64 element = (packed) ? (x >> 1) : x;
size_t offset = computer->GetTexelElementOffsetBytes(0, level, element, 0, item, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, 0, item, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
uint8_t* dest = dptr + offset;
if ((dest + bpp) > endPtr)
return E_FAIL;
memcpy(dest, sptr, bpp);
sptr += bpp;
if (packed)
++x;
}
}
return S_OK;
}
//----------------------------------------------------------------------------------
inline HRESULT TileByElement2D(
_In_reads_(nimages) const Image* const * images,
size_t nimages,
uint32_t level,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox,
size_t bpp,
size_t w,
size_t h,
bool packed)
{
uint8_t* dptr = xbox.GetPointer();
const uint8_t* endPtr = dptr + layout.SizeBytes;
for (uint32_t item = 0; item < nimages; ++item)
{
const Image* img = images[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == images[0]->width);
assert(img->height == images[0]->height);
assert(img->rowPitch == images[0]->rowPitch);
assert(img->format == images[0]->format);
const uint8_t* sptr = img->pixels;
for (uint32_t y = 0; y < h; ++y)
{
const uint8_t* tptr = sptr;
for (size_t x = 0; x < w; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
UINT64 element = (packed) ? (x >> 1) : x;
size_t offset = computer->GetTexelElementOffsetBytes(0, level, element, y, item, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, item, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
uint8_t* dest = dptr + offset;
if ((dest + bpp) > endPtr)
return E_FAIL;
memcpy(dest, tptr, bpp);
tptr += bpp;
if (packed)
++x;
}
sptr += img->rowPitch;
}
}
return S_OK;
}
//----------------------------------------------------------------------------------
inline HRESULT TileByElement3D(
const Image& image,
uint32_t level,
uint32_t slices,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox,
size_t bpp,
size_t w,
size_t h,
bool packed)
{
uint8_t* dptr = xbox.GetPointer();
const uint8_t* endPtr = dptr + layout.SizeBytes;
const uint8_t* sptr = image.pixels;
for (uint32_t z = 0; z < slices; ++z)
{
const uint8_t* rptr = sptr;
for (uint32_t y = 0; y < h; ++y)
{
const uint8_t* tptr = rptr;
for (size_t x = 0; x < w; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
UINT64 element = (packed) ? (x >> 1) : x;
size_t offset = computer->GetTexelElementOffsetBytes(0, level, element, y, z, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, z, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
uint8_t* dest = dptr + offset;
if ((dest + bpp) > endPtr)
return E_FAIL;
memcpy(dest, tptr, bpp);
tptr += bpp;
if (packed)
++x;
}
rptr += image.rowPitch;
}
sptr += image.slicePitch;
}
return S_OK;
}
//----------------------------------------------------------------------------------
#ifdef VERBOSE
void DebugPrintDesc(const XG_TEXTURE1D_DESC& desc)
{
wchar_t buff[2048] = {};
swprintf_s(buff, L"XG_TEXTURE1D_DESC = { %u, %u, %u, %u, %u, %u, %u, %u, %u, %u }\n",
desc.Width, desc.MipLevels, desc.ArraySize, desc.Format, desc.Usage, desc.BindFlags, desc.CPUAccessFlags, desc.MiscFlags,
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode,
#else
desc.TileMode,
#endif
desc.Pitch);
OutputDebugStringW(buff);
}
void DebugPrintDesc(const XG_TEXTURE2D_DESC& desc)
{
wchar_t buff[2048] = {};
swprintf_s(buff, L"XG_TEXTURE2D_DESC = { %u, %u, %u, %u, %u, { %u, %u }, %u, %u, %u, %u, %u, %u }\n",
desc.Width, desc.Height, desc.MipLevels, desc.ArraySize, desc.Format, desc.SampleDesc.Count, desc.SampleDesc.Quality, desc.Usage, desc.BindFlags, desc.CPUAccessFlags, desc.MiscFlags,
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode,
#else
desc.TileMode,
#endif
desc.Pitch);
OutputDebugStringW(buff);
}
void DebugPrintDesc(const XG_TEXTURE3D_DESC& desc)
{
wchar_t buff[2048] = {};
swprintf_s(buff, L"XG_TEXTURE3D_DESC = { %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u }\n",
desc.Width, desc.Height, desc.Depth, desc.MipLevels, desc.Format, desc.Usage, desc.BindFlags, desc.CPUAccessFlags, desc.MiscFlags,
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode,
#else
desc.TileMode,
#endif
desc.Pitch);
OutputDebugStringW(buff);
}
void DebugPrintLayout(const XG_RESOURCE_LAYOUT& layout)
{
wchar_t buff[2048] = {};
swprintf_s(buff, L"Layout %u planes, %uD, %u mips, %llu size, %llu alignment\n", layout.Planes, layout.Dimension - 1, layout.MipLevels, layout.SizeBytes, layout.BaseAlignmentBytes);
OutputDebugStringW(buff);
for (size_t p = 0; p < layout.Planes; ++p)
{
auto& plane = layout.Plane[p];
swprintf_s(buff, L"Plane %zu: %u bpe, %llu size, %llu offset, %llu alignment\n", p, plane.BytesPerElement, plane.SizeBytes, plane.BaseOffsetBytes, plane.BaseAlignmentBytes);
OutputDebugStringW(buff);
for (size_t level = 0; level < layout.MipLevels; ++level)
{
auto& mip = plane.MipLayout[level];
swprintf_s(buff, L"\tLevel %zu: %llu size, %llu slice2D, %llu offset, %u alignment\n", level, mip.SizeBytes, mip.Slice2DSizeBytes, mip.OffsetBytes, mip.AlignmentBytes);
OutputDebugStringW(buff);
swprintf_s(buff, L"\t\t%u x %u x %u (padded %u x %u x %u)\n", mip.WidthElements, mip.HeightElements, mip.DepthOrArraySize,
mip.PaddedWidthElements, mip.PaddedHeightElements, mip.PaddedDepthOrArraySize);
OutputDebugStringW(buff);
swprintf_s(buff, L"\t\tpitch %u pixels (%u bytes)\n", mip.PitchPixels, mip.PitchBytes);
OutputDebugStringW(buff);
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
swprintf_s(buff, L"\t\t\t%u samples, %u swizzlemode\n", mip.SampleCount, mip.SwizzleMode);
#else
swprintf_s(buff, L"\t\t\t%u samples, %u tilemode\n", mip.SampleCount, mip.TileMode);
#endif
OutputDebugStringW(buff);
}
}
}
#endif
//-------------------------------------------------------------------------------------
// 1D Tiling
//-------------------------------------------------------------------------------------
HRESULT Tile1D(
_In_reads_(nimages) const Image** images,
size_t nimages,
uint32_t level,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox)
{
if (!nimages)
return E_INVALIDARG;
if (!images || !images[0] || !computer || !xbox.GetPointer())
return E_POINTER;
assert(layout.Planes == 1);
const DXGI_FORMAT format = images[0]->format;
assert(format == xbox.GetMetadata().format);
assert(!IsCompressed(format));
bool byelement = IsTypeless(format);
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
if (nimages > 1)
byelement = true;
#endif
if (IsPacked(format))
{
const size_t bpp = (BitsPerPixel(format) + 7) / 8;
// XG (XboxOne) incorrectly returns 2 instead of 4 here for layout.Plane[0].BytesPerElement
const size_t w = images[0]->width;
assert(((w + 1) / 2) == layout.Plane[0].MipLayout[level].WidthElements);
return TileByElement1D(images, nimages, level, computer, layout, xbox, bpp, w, true);
}
else if (byelement)
{
//--- Typeless is done with per-element copy ----------------------------------
const size_t bpp = (BitsPerPixel(format) + 7) / 8;
assert(bpp == layout.Plane[0].BytesPerElement);
const size_t w = images[0]->width;
assert(w == layout.Plane[0].MipLayout[level].WidthElements);
return TileByElement1D(images, nimages, level, computer, layout, xbox, bpp, w, false);
}
else
{
//--- Standard format handling ------------------------------------------------
auto& mip = layout.Plane[0].MipLayout[level];
const UINT32 tiledPixels = mip.PitchPixels * mip.PaddedDepthOrArraySize;
auto scanline = make_AlignedArrayXMVECTOR(images[0]->width + tiledPixels);
XMVECTOR* row = scanline.get();
XMVECTOR* tiled = row + images[0]->width;
#ifdef _DEBUG
memset(row, 0xCD, sizeof(XMVECTOR) * images[0]->width);
#endif
memset(tiled, 0, sizeof(XMVECTOR) * tiledPixels);
// Perform tiling
for (uint32_t item = 0; item < nimages; ++item)
{
const Image* img = images[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == images[0]->width);
assert(img->height == images[0]->height);
assert(img->rowPitch == images[0]->rowPitch);
assert(img->format == images[0]->format);
if (!LoadScanline(row, img->width, img->pixels, img->rowPitch, img->format))
return E_FAIL;
for (size_t x = 0; x < img->width; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, 0, item, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, 0, item, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
assert(offset >= mip.OffsetBytes);
assert(offset < mip.OffsetBytes + mip.SizeBytes);
offset = (offset - mip.OffsetBytes) / layout.Plane[0].BytesPerElement;
assert(offset < tiledPixels);
tiled[offset] = row[x];
}
}
// Store tiled texture
assert(mip.OffsetBytes + mip.SizeBytes <= layout.SizeBytes);
if (!StoreScanline(xbox.GetPointer() + mip.OffsetBytes, mip.SizeBytes, xbox.GetMetadata().format, tiled, tiledPixels))
return E_FAIL;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// 2D Tiling
//-------------------------------------------------------------------------------------
HRESULT Tile2D(
_In_reads_(nimages) const Image** images,
size_t nimages,
uint32_t level,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox)
{
if (!nimages)
return E_INVALIDARG;
if (!images || !images[0] || !computer || !xbox.GetPointer())
return E_POINTER;
assert(layout.Planes == 1);
const DXGI_FORMAT format = images[0]->format;
assert(format == xbox.GetMetadata().format);
bool byelement = IsTypeless(format);
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
if (nimages > 1)
byelement = true;
#endif
if (IsCompressed(format))
{
//--- BC formats use per-block copy -------------------------------------------
const size_t nbw = std::max<size_t>(1, (images[0]->width + 3) / 4);
const size_t nbh = std::max<size_t>(1, (images[0]->height + 3) / 4);
const size_t bpb = (format == DXGI_FORMAT_BC1_TYPELESS
|| format == DXGI_FORMAT_BC1_UNORM
|| format == DXGI_FORMAT_BC1_UNORM_SRGB
|| format == DXGI_FORMAT_BC4_TYPELESS
|| format == DXGI_FORMAT_BC4_UNORM
|| format == DXGI_FORMAT_BC4_SNORM) ? 8 : 16;
assert(nbw == layout.Plane[0].MipLayout[level].WidthElements);
assert(nbh == layout.Plane[0].MipLayout[level].HeightElements);
assert(bpb == layout.Plane[0].BytesPerElement);
return TileByElement2D(images, nimages, level, computer, layout, xbox, bpb, nbw, nbh, false);
}
else if (IsPacked(format))
{
const size_t bpp = (BitsPerPixel(format) + 7) / 8;
// XG (XboxOne) incorrectly returns 2 instead of 4 here for layout.Plane[0].BytesPerElement
const size_t w = images[0]->width;
const size_t h = images[0]->height;
assert(((w + 1) / 2) == layout.Plane[0].MipLayout[level].WidthElements);
assert(h == layout.Plane[0].MipLayout[level].HeightElements);
return TileByElement2D(images, nimages, level, computer, layout, xbox, bpp, w, h, true);
}
else if (byelement)
{
//--- Typeless is done with per-element copy ----------------------------------
const size_t bpp = (BitsPerPixel(format) + 7) / 8;
assert(bpp == layout.Plane[0].BytesPerElement);
const size_t w = images[0]->width;
const size_t h = images[0]->height;
assert(w == layout.Plane[0].MipLayout[level].WidthElements);
assert(h == layout.Plane[0].MipLayout[level].HeightElements);
return TileByElement2D(images, nimages, level, computer, layout, xbox, bpp, w, h, false);
}
else
{
//--- Standard format handling ------------------------------------------------
auto& mip = layout.Plane[0].MipLayout[level];
const UINT32 tiledPixels = mip.PaddedWidthElements * mip.PaddedHeightElements * mip.PaddedDepthOrArraySize;
auto scanline = make_AlignedArrayXMVECTOR(images[0]->width + tiledPixels);
XMVECTOR* row = scanline.get();
XMVECTOR* tiled = row + images[0]->width;
#ifdef _DEBUG
memset(row, 0xCD, sizeof(XMVECTOR) * images[0]->width);
#endif
memset(tiled, 0, sizeof(XMVECTOR) * tiledPixels);
// Perform tiling
for (uint32_t item = 0; item < nimages; ++item)
{
const Image* img = images[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == images[0]->width);
assert(img->height == images[0]->height);
assert(img->rowPitch == images[0]->rowPitch);
assert(img->format == images[0]->format);
auto sptr = reinterpret_cast<const uint8_t * __restrict>(img->pixels);
for (uint32_t y = 0; y < img->height; ++y)
{
if (!LoadScanline(row, img->width, sptr, img->rowPitch, img->format))
return E_FAIL;
sptr += img->rowPitch;
for (size_t x = 0; x < img->width; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, item, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, item, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
assert(offset >= mip.OffsetBytes);
assert(offset < mip.OffsetBytes + mip.SizeBytes);
offset = (offset - mip.OffsetBytes) / layout.Plane[0].BytesPerElement;
assert(offset < tiledPixels);
tiled[offset] = row[x];
}
}
}
// Store tiled texture
assert(mip.OffsetBytes + mip.SizeBytes <= layout.SizeBytes);
if (!StoreScanline(xbox.GetPointer() + mip.OffsetBytes, mip.SizeBytes, xbox.GetMetadata().format, tiled, tiledPixels))
return E_FAIL;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// 3D Tiling
//-------------------------------------------------------------------------------------
HRESULT Tile3D(
const Image& image,
uint32_t level,
uint32_t slices,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox)
{
if (!image.pixels || !computer || !xbox.GetPointer())
return E_POINTER;
assert(slices > 0);
assert(layout.Planes == 1);
assert(image.format == xbox.GetMetadata().format);
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
const bool byelement = true;
#else
const bool byelement = IsTypeless(image.format);
#endif
if (IsCompressed(image.format))
{
//--- BC formats use per-block copy -------------------------------------------
const size_t nbw = std::max<size_t>(1, (image.width + 3) / 4);
const size_t nbh = std::max<size_t>(1, (image.height + 3) / 4);
const size_t bpb = (image.format == DXGI_FORMAT_BC1_TYPELESS
|| image.format == DXGI_FORMAT_BC1_UNORM
|| image.format == DXGI_FORMAT_BC1_UNORM_SRGB
|| image.format == DXGI_FORMAT_BC4_TYPELESS
|| image.format == DXGI_FORMAT_BC4_UNORM
|| image.format == DXGI_FORMAT_BC4_SNORM) ? 8 : 16;
assert(nbw == layout.Plane[0].MipLayout[level].WidthElements);
assert(nbh == layout.Plane[0].MipLayout[level].HeightElements);
assert(bpb == layout.Plane[0].BytesPerElement);
return TileByElement3D(image, level, slices, computer, layout, xbox, bpb, nbw, nbh, false);
}
else if (IsPacked(image.format))
{
const size_t bpp = (BitsPerPixel(image.format) + 7) / 8;
// XG (XboxOne) incorrectly returns 2 instead of 4 here for layout.Plane[0].BytesPerElement
assert(((image.width + 1) / 2) == layout.Plane[0].MipLayout[level].WidthElements);
assert(image.height == layout.Plane[0].MipLayout[level].HeightElements);
return TileByElement3D(image, level, slices, computer, layout, xbox, bpp, image.width, image.height, true);
}
else if (byelement)
{
//--- Typeless is done with per-element copy ----------------------------------
const size_t bpp = (BitsPerPixel(image.format) + 7) / 8;
assert(bpp == layout.Plane[0].BytesPerElement);
assert(image.width == layout.Plane[0].MipLayout[level].WidthElements);
assert(image.height == layout.Plane[0].MipLayout[level].HeightElements);
return TileByElement3D(image, level, slices, computer, layout, xbox, bpp, image.width, image.height, false);
}
else
{
//--- Standard format handling ------------------------------------------------
auto& mip = layout.Plane[0].MipLayout[level];
const UINT32 tiledPixels = mip.PaddedWidthElements * mip.PaddedHeightElements * mip.PaddedDepthOrArraySize;
assert(tiledPixels >= (image.width * image.height * slices));
auto scanline = make_AlignedArrayXMVECTOR(image.width + tiledPixels);
XMVECTOR* row = scanline.get();
XMVECTOR* tiled = row + image.width;
#ifdef _DEBUG
memset(row, 0xCD, sizeof(XMVECTOR) * image.width);
#endif
memset(tiled, 0, sizeof(XMVECTOR) * tiledPixels);
// Perform tiling
const uint8_t* sptr = reinterpret_cast<const uint8_t*>(image.pixels);
for (uint32_t z = 0; z < slices; ++z)
{
const uint8_t* rptr = sptr;
for (uint32_t y = 0; y < image.height; ++y)
{
if (!LoadScanline(row, image.width, rptr, image.rowPitch, image.format))
return E_FAIL;
rptr += image.rowPitch;
for (size_t x = 0; x < image.width; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, z, 0, nullptr);
#else
size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, z, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
assert(offset >= mip.OffsetBytes);
assert(offset < mip.OffsetBytes + mip.SizeBytes);
offset = (offset - mip.OffsetBytes) / layout.Plane[0].BytesPerElement;
assert(offset < tiledPixels);
tiled[offset] = row[x];
}
}
sptr += image.slicePitch;
}
// Store tiled texture
uint8_t* dptr = xbox.GetPointer() + mip.OffsetBytes;
const uint8_t* endPtr = dptr + mip.SizeBytes;
const XMVECTOR* tptr = tiled;
for (uint32_t z = 0; z < mip.PaddedDepthOrArraySize; ++z)
{
uint8_t* rptr = dptr;
const XMVECTOR* uptr = tptr;
for (uint32_t y = 0; y < mip.PaddedHeightElements; ++y)
{
if ((rptr + mip.PitchBytes) > endPtr)
return E_FAIL;
if (!StoreScanline(rptr, mip.PitchBytes, xbox.GetMetadata().format, uptr, mip.PitchPixels))
return E_FAIL;
rptr += mip.PitchBytes;
uptr += mip.PaddedWidthElements;
}
dptr += mip.Slice2DSizeBytes;
tptr += size_t(mip.PaddedHeightElements) * size_t(mip.PaddedWidthElements);
}
}
return S_OK;
}
}
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Tile image
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT Xbox::Tile(
const DirectX::Image& srcImage,
XboxImage& xbox,
XboxTileMode mode)
{
if (!srcImage.pixels
|| srcImage.width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
|| srcImage.height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)
return E_INVALIDARG;
xbox.Release();
if (srcImage.format == DXGI_FORMAT_R1_UNORM
|| IsVideo(srcImage.format))
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
if (mode == c_XboxTileModeInvalid)
{
// If no specific tile mode is given, assume the optimal default
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
mode = XGComputeOptimalSwizzleMode(XG_RESOURCE_DIMENSION_TEXTURE2D, static_cast<XG_FORMAT>(srcImage.format),
static_cast<UINT>(srcImage.width), static_cast<UINT>(srcImage.height),
1, 1, XG_BIND_SHADER_RESOURCE);
#else
mode = XGComputeOptimalTileMode(XG_RESOURCE_DIMENSION_TEXTURE2D, static_cast<XG_FORMAT>(srcImage.format),
static_cast<UINT>(srcImage.width), static_cast<UINT>(srcImage.height),
1, 1, XG_BIND_SHADER_RESOURCE);
#endif
}
XG_TEXTURE2D_DESC desc = {};
desc.Width = static_cast<UINT>(srcImage.width);
desc.Height = static_cast<UINT>(srcImage.height);
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = static_cast<XG_FORMAT>(srcImage.format);
desc.SampleDesc.Count = 1;
desc.Usage = XG_USAGE_DEFAULT;
desc.BindFlags = XG_BIND_SHADER_RESOURCE;
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode = mode;
#else
desc.TileMode = mode;
#endif
ComPtr<XGTextureAddressComputer> computer;
HRESULT hr = XGCreateTexture2DComputer(&desc, computer.GetAddressOf());
if (FAILED(hr))
return hr;
XG_RESOURCE_LAYOUT layout;
hr = computer->GetResourceLayout(&layout);
if (FAILED(hr))
return hr;
if (layout.Planes != 1)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
hr = xbox.Initialize(desc, layout);
if (FAILED(hr))
return hr;
const Image* images = &srcImage;
hr = Tile2D(&images, 1, 0, computer.Get(), layout, xbox);
if (FAILED(hr))
{
xbox.Release();
return hr;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// Tile image (complex)
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT Xbox::Tile(
const DirectX::Image* srcImages,
size_t nimages,
const DirectX::TexMetadata& metadata,
XboxImage& xbox,
XboxTileMode mode)
{
if (!srcImages
|| !nimages
|| metadata.width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
|| metadata.height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
|| metadata.depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION
|| metadata.arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION
|| metadata.mipLevels > D3D11_REQ_MIP_LEVELS)
return E_INVALIDARG;
xbox.Release();
if (metadata.format == DXGI_FORMAT_R1_UNORM
|| IsVideo(metadata.format))
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
switch (metadata.format)
{
case DXGI_FORMAT_R32G32B32_TYPELESS:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R32G32B32_UINT:
case DXGI_FORMAT_R32G32B32_SINT:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
default:
break;
}
if (mode == c_XboxTileModeInvalid)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
mode = XGComputeOptimalSwizzleMode(static_cast<XG_RESOURCE_DIMENSION>(metadata.dimension), static_cast<XG_FORMAT>(metadata.format),
static_cast<UINT>(metadata.width), static_cast<UINT>(metadata.height),
static_cast<UINT>((metadata.dimension == TEX_DIMENSION_TEXTURE3D) ? metadata.depth : metadata.arraySize),
1, XG_BIND_SHADER_RESOURCE);
#else
// If no specific tile mode is given, assume the optimal default
mode = XGComputeOptimalTileMode(static_cast<XG_RESOURCE_DIMENSION>(metadata.dimension), static_cast<XG_FORMAT>(metadata.format),
static_cast<UINT>(metadata.width), static_cast<UINT>(metadata.height),
static_cast<UINT>((metadata.dimension == TEX_DIMENSION_TEXTURE3D) ? metadata.depth : metadata.arraySize),
1, XG_BIND_SHADER_RESOURCE);
#endif
}
XG_RESOURCE_LAYOUT layout = {};
switch (metadata.dimension)
{
case TEX_DIMENSION_TEXTURE1D:
{
XG_TEXTURE1D_DESC desc = {};
desc.Width = static_cast<UINT>(metadata.width);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.ArraySize = static_cast<UINT>(metadata.arraySize);
desc.Format = static_cast<XG_FORMAT>(metadata.format);
desc.Usage = XG_USAGE_DEFAULT;
desc.BindFlags = XG_BIND_SHADER_RESOURCE;
desc.MiscFlags = (metadata.IsCubemap()) ? XG_RESOURCE_MISC_TEXTURECUBE : 0;
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode = mode;
#else
desc.TileMode = mode;
#endif
#ifdef VERBOSE
DebugPrintDesc(desc);
#endif
ComPtr<XGTextureAddressComputer> computer;
HRESULT hr = XGCreateTexture1DComputer(&desc, computer.GetAddressOf());
if (FAILED(hr))
return hr;
hr = computer->GetResourceLayout(&layout);
if (FAILED(hr))
return hr;
#ifdef VERBOSE
DebugPrintLayout(layout);
#endif
if (layout.Planes != 1)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
hr = xbox.Initialize(desc, layout, metadata.miscFlags2);
if (FAILED(hr))
return hr;
for (uint32_t level = 0; level < metadata.mipLevels; ++level)
{
if (metadata.arraySize > 1)
{
std::vector<const Image*> images;
images.reserve(metadata.arraySize);
for (uint32_t item = 0; item < metadata.arraySize; ++item)
{
const size_t index = metadata.ComputeIndex(level, item, 0);
if (index >= nimages)
{
xbox.Release();
return E_FAIL;
}
images.push_back(&srcImages[index]);
}
hr = Tile1D(&images[0], images.size(), level, computer.Get(), layout, xbox);
}
else
{
const size_t index = metadata.ComputeIndex(level, 0, 0);
if (index >= nimages)
{
xbox.Release();
return E_FAIL;
}
const Image* images = &srcImages[index];
hr = Tile1D(&images, 1, level, computer.Get(), layout, xbox);
}
if (FAILED(hr))
{
xbox.Release();
return hr;
}
}
}
break;
case TEX_DIMENSION_TEXTURE2D:
{
XG_TEXTURE2D_DESC desc = {};
desc.Width = static_cast<UINT>(metadata.width);
desc.Height = static_cast<UINT>(metadata.height);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.ArraySize = static_cast<UINT>(metadata.arraySize);
desc.Format = static_cast<XG_FORMAT>(metadata.format);
desc.SampleDesc.Count = 1;
desc.Usage = XG_USAGE_DEFAULT;
desc.BindFlags = XG_BIND_SHADER_RESOURCE;
desc.MiscFlags = (metadata.miscFlags & TEX_MISC_TEXTURECUBE) ? XG_RESOURCE_MISC_TEXTURECUBE : 0;
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode = mode;
#else
desc.TileMode = mode;
#endif
#ifdef VERBOSE
DebugPrintDesc(desc);
#endif
ComPtr<XGTextureAddressComputer> computer;
HRESULT hr = XGCreateTexture2DComputer(&desc, computer.GetAddressOf());
if (FAILED(hr))
return hr;
hr = computer->GetResourceLayout(&layout);
if (FAILED(hr))
return hr;
#ifdef VERBOSE
DebugPrintLayout(layout);
#endif
if (layout.Planes != 1)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
hr = xbox.Initialize(desc, layout, metadata.miscFlags2);
if (FAILED(hr))
return hr;
for (uint32_t level = 0; level < metadata.mipLevels; ++level)
{
if (metadata.arraySize > 1)
{
std::vector<const Image*> images;
images.reserve(metadata.arraySize);
for (uint32_t item = 0; item < metadata.arraySize; ++item)
{
const size_t index = metadata.ComputeIndex(level, item, 0);
if (index >= nimages)
{
xbox.Release();
return E_FAIL;
}
images.push_back(&srcImages[index]);
}
hr = Tile2D(&images[0], images.size(), level, computer.Get(), layout, xbox);
}
else
{
const size_t index = metadata.ComputeIndex(level, 0, 0);
if (index >= nimages)
{
xbox.Release();
return E_FAIL;
}
const Image* images = &srcImages[index];
hr = Tile2D(&images, 1, level, computer.Get(), layout, xbox);
}
if (FAILED(hr))
{
xbox.Release();
return hr;
}
}
}
break;
case TEX_DIMENSION_TEXTURE3D:
{
XG_TEXTURE3D_DESC desc = {};
desc.Width = static_cast<UINT>(metadata.width);
desc.Height = static_cast<UINT>(metadata.height);
desc.Depth = static_cast<UINT>(metadata.depth);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.Format = static_cast<XG_FORMAT>(metadata.format);
desc.Usage = XG_USAGE_DEFAULT;
desc.BindFlags = XG_BIND_SHADER_RESOURCE;
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
desc.SwizzleMode = mode;
#else
desc.TileMode = mode;
#endif
#ifdef VERBOSE
DebugPrintDesc(desc);
#endif
ComPtr<XGTextureAddressComputer> computer;
HRESULT hr = XGCreateTexture3DComputer(&desc, computer.GetAddressOf());
if (FAILED(hr))
return hr;
hr = computer->GetResourceLayout(&layout);
if (FAILED(hr))
return hr;
#ifdef VERBOSE
DebugPrintLayout(layout);
#endif
if (layout.Planes != 1)
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
hr = xbox.Initialize(desc, layout, metadata.miscFlags2);
if (FAILED(hr))
return hr;
uint32_t d = static_cast<uint32_t>(metadata.depth);
size_t index = 0;
for (uint32_t level = 0; level < metadata.mipLevels; ++level)
{
if ((index + d) > nimages)
{
xbox.Release();
return E_FAIL;
}
// Relies on the fact that slices are contiguous
hr = Tile3D(srcImages[index], level, d, computer.Get(), layout, xbox);
if (FAILED(hr))
{
xbox.Release();
return hr;
}
index += d;
if (d > 1)
d >>= 1;
}
}
break;
default:
return E_FAIL;
}
return S_OK;
}