1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-09-18 22:59:54 +00:00

Xbox auxiliary optimization for tiling (#459)

This commit is contained in:
Chuck Walbourn 2024-02-28 11:21:43 -08:00 committed by GitHub
parent b41eda671d
commit aae41e425b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 735 deletions

View File

@ -72,127 +72,6 @@ namespace
return S_OK;
}
//----------------------------------------------------------------------------------
inline HRESULT DetileByElement2D(
const XboxImage& xbox,
uint32_t level,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
_In_reads_(nimages) const Image* const * result,
size_t nimages,
size_t bpp,
size_t w,
size_t h,
bool packed)
{
const uint8_t* sptr = xbox.GetPointer();
const uint8_t* endPtr = sptr + layout.SizeBytes;
for (uint32_t item = 0; item < nimages; ++item)
{
const Image* img = result[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == result[0]->width);
assert(img->height == result[0]->height);
assert(img->rowPitch == result[0]->rowPitch);
assert(img->format == result[0]->format);
uint8_t* dptr = img->pixels;
for (uint32_t y = 0; y < h; ++y)
{
uint8_t* tptr = dptr;
for (size_t x = 0; x < w; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
const UINT64 element = (packed) ? (x >> 1) : x;
const size_t offset = computer->GetTexelElementOffsetBytes(0, level, element, y, item, 0, nullptr);
#else
const size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, item, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
const uint8_t* src = sptr + offset;
if ((src + bpp) > endPtr)
return E_FAIL;
memcpy(tptr, src, bpp);
tptr += bpp;
if (packed)
++x;
}
dptr += img->rowPitch;
}
}
return S_OK;
}
//----------------------------------------------------------------------------------
inline HRESULT DetileByElement3D(
const XboxImage& xbox,
uint32_t level,
uint32_t slices,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const Image& result,
size_t bpp,
size_t w,
size_t h,
bool packed)
{
const uint8_t* sptr = xbox.GetPointer();
const uint8_t* endPtr = sptr + layout.SizeBytes;
uint8_t* dptr = result.pixels;
for (uint32_t z = 0; z < slices; ++z)
{
uint8_t* rptr = dptr;
for (uint32_t y = 0; y < h; ++y)
{
uint8_t* tptr = rptr;
for (size_t x = 0; x < w; ++x)
{
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
const UINT64 element = (packed) ? (x >> 1) : x;
const size_t offset = computer->GetTexelElementOffsetBytes(0, level, element, y, z, 0, nullptr);
#else
const size_t offset = computer->GetTexelElementOffsetBytes(0, level, x, y, z, 0);
#endif
if (offset == size_t(-1))
return E_FAIL;
const uint8_t* src = sptr + offset;
if ((src + bpp) > endPtr)
return E_FAIL;
memcpy(tptr, src, bpp);
tptr += bpp;
if (packed)
++x;
}
rptr += result.rowPitch;
}
dptr += result.slicePitch;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// 1D Tiling
//-------------------------------------------------------------------------------------
@ -331,125 +210,33 @@ namespace
assert(layout.Planes == 1);
const DXGI_FORMAT format = result[0]->format;
uint8_t* baseAddr = xbox.GetPointer();
const auto& metadata = xbox.GetMetadata();
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))
for (uint32_t item = 0; item < nimages; ++item)
{
//--- BC formats use per-block copy -------------------------------------------
const size_t nbw = std::max<size_t>(1, (result[0]->width + 3) / 4);
const size_t nbh = std::max<size_t>(1, (result[0]->height + 3) / 4);
const Image* img = result[item];
if (!img || !img->pixels)
return E_POINTER;
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(img->width == result[0]->width);
assert(img->height == result[0]->height);
assert(img->rowPitch == result[0]->rowPitch);
assert(img->format == result[0]->format);
assert(nbw == layout.Plane[0].MipLayout[level].WidthElements);
assert(nbh == layout.Plane[0].MipLayout[level].HeightElements);
assert(bpb == layout.Plane[0].BytesPerElement);
return DetileByElement2D(xbox, level, computer, layout, result, nimages, 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 = result[0]->width;
const size_t h = result[0]->height;
assert(((w + 1) / 2) == layout.Plane[0].MipLayout[level].WidthElements);
assert(h == layout.Plane[0].MipLayout[level].HeightElements);
return DetileByElement2D(xbox, level, computer, layout, result, nimages, 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 = result[0]->width;
const size_t h = result[0]->height;
assert(w == layout.Plane[0].MipLayout[level].WidthElements);
assert(h == layout.Plane[0].MipLayout[level].HeightElements);
return DetileByElement2D(xbox, level, computer, layout, result, nimages, 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(tiledPixels + result[0]->width);
XMVECTOR* target = scanline.get();
XMVECTOR* tiled = target + result[0]->width;
#ifdef _DEBUG
memset(target, 0xCD, sizeof(XMVECTOR) * result[0]->width);
memset(tiled, 0xDD, sizeof(XMVECTOR) * tiledPixels);
#endif
// Load tiled texture
if ((xbox.GetSize() - mip.OffsetBytes) < mip.SizeBytes)
return E_FAIL;
if (!LoadScanline(tiled, tiledPixels, xbox.GetPointer() + mip.OffsetBytes, mip.SizeBytes, xbox.GetMetadata().format))
return E_FAIL;
// Perform detiling
for (uint32_t item = 0; item < nimages; ++item)
HRESULT hr = computer->CopyFromSubresource(
img->pixels,
0u,
metadata.CalculateSubresource(level, item),
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
0u,
#endif
baseAddr,
static_cast<UINT32>(img->rowPitch),
0u);
if (FAILED(hr))
{
const Image* img = result[item];
if (!img || !img->pixels)
return E_POINTER;
assert(img->width == result[0]->width);
assert(img->height == result[0]->height);
assert(img->rowPitch == result[0]->rowPitch);
assert(img->format == result[0]->format);
auto dptr = reinterpret_cast<uint8_t * __restrict>(img->pixels);
for (uint32_t y = 0; y < img->height; ++y)
{
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);
target[x] = tiled[offset];
}
if (!StoreScanline(dptr, img->rowPitch, img->format, target, img->width))
return E_FAIL;
dptr += img->rowPitch;
}
return hr;
}
}
@ -463,7 +250,6 @@ namespace
HRESULT Detile3D(
const XboxImage& xbox,
uint32_t level,
uint32_t slices,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const Image& result)
@ -475,137 +261,19 @@ namespace
assert(layout.Planes == 1);
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
const bool byelement = true;
#else
const bool byelement = IsTypeless(result.format);
#endif
uint8_t* baseAddr = xbox.GetPointer();
const auto& metadata = xbox.GetMetadata();
if (IsCompressed(result.format))
{
//--- BC formats use per-block copy -------------------------------------------
const size_t nbw = std::max<size_t>(1, (result.width + 3) / 4);
const size_t nbh = std::max<size_t>(1, (result.height + 3) / 4);
const size_t bpb = (result.format == DXGI_FORMAT_BC1_TYPELESS
|| result.format == DXGI_FORMAT_BC1_UNORM
|| result.format == DXGI_FORMAT_BC1_UNORM_SRGB
|| result.format == DXGI_FORMAT_BC4_TYPELESS
|| result.format == DXGI_FORMAT_BC4_UNORM
|| result.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 DetileByElement3D(xbox, level, slices, computer, layout, result, bpb, nbw, nbh, false);
}
else if (IsPacked(result.format))
{
const size_t bpp = (BitsPerPixel(result.format) + 7) / 8;
// XG (XboxOne) incorrectly returns 2 instead of 4 here for layout.Plane[0].BytesPerElement
assert(((result.width + 1) / 2) == layout.Plane[0].MipLayout[level].WidthElements);
assert(result.height == layout.Plane[0].MipLayout[level].HeightElements);
return DetileByElement3D(xbox, level, slices, computer, layout, result, bpp, result.width, result.height, true);
}
else if (byelement)
{
//--- Typeless is done with per-element copy ----------------------------------
const size_t bpp = (BitsPerPixel(result.format) + 7) / 8;
assert(bpp == layout.Plane[0].BytesPerElement);
assert(result.width == layout.Plane[0].MipLayout[level].WidthElements);
assert(result.height == layout.Plane[0].MipLayout[level].HeightElements);
return DetileByElement3D(xbox, level, slices, computer, layout, result, bpp, result.width, result.height, false);
}
else
{
//--- Standard format handling ------------------------------------------------
auto& mip = layout.Plane[0].MipLayout[level];
const UINT32 tiledPixels = mip.PaddedWidthElements * mip.PaddedHeightElements * mip.PaddedDepthOrArraySize;
assert(tiledPixels >= (result.width * result.height * slices));
auto scanline = make_AlignedArrayXMVECTOR(tiledPixels + result.width);
XMVECTOR* target = scanline.get();
XMVECTOR* tiled = target + result.width;
#ifdef _DEBUG
memset(target, 0xCD, sizeof(XMVECTOR) * result.width);
memset(tiled, 0xDD, sizeof(XMVECTOR) * tiledPixels);
return computer->CopyFromSubresource(
result.pixels,
0u,
metadata.CalculateSubresource(level, 0),
#if defined(_GAMING_XBOX_SCARLETT) || defined(_USE_SCARLETT)
0u,
#endif
// Load tiled texture
if ((xbox.GetSize() - mip.OffsetBytes) < mip.SizeBytes)
return E_FAIL;
const uint8_t* sptr = xbox.GetPointer() + mip.OffsetBytes;
const uint8_t* endPtr = sptr + mip.SizeBytes;
XMVECTOR* tptr = tiled;
for (uint32_t z = 0; z < mip.PaddedDepthOrArraySize; ++z)
{
const uint8_t* rptr = sptr;
XMVECTOR* uptr = tptr;
for (uint32_t y = 0; y < mip.PaddedHeightElements; ++y)
{
if ((rptr + mip.PitchBytes) > endPtr)
return E_FAIL;
if (!LoadScanline(uptr, mip.PitchPixels, rptr, mip.PitchBytes, xbox.GetMetadata().format))
return E_FAIL;
rptr += mip.PitchBytes;
uptr += mip.PaddedWidthElements;
}
sptr += mip.Slice2DSizeBytes;
tptr += size_t(mip.PaddedHeightElements) * size_t(mip.PaddedWidthElements);
}
// Perform detiling
uint8_t* dptr = reinterpret_cast<uint8_t*>(result.pixels);
for (uint32_t z = 0; z < slices; ++z)
{
uint8_t* rptr = dptr;
for (uint32_t y = 0; y < result.height; ++y)
{
for (size_t x = 0; x < result.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);
target[x] = tiled[offset];
}
if (!StoreScanline(rptr, result.rowPitch, result.format, target, result.width))
return E_FAIL;
rptr += result.rowPitch;
}
dptr += result.slicePitch;
}
}
return S_OK;
baseAddr,
static_cast<UINT32>(result.rowPitch),
static_cast<UINT32>(result.slicePitch));
}
}
@ -854,7 +522,7 @@ HRESULT Xbox::Detile(
}
// Relies on the fact that slices are contiguous
hr = Detile3D(xbox, level, d, computer.Get(), layout, image.GetImages()[index]);
hr = Detile3D(xbox, level, computer.Get(), layout, image.GetImages()[index]);
if (FAILED(hr))
{
image.Release();

View File

@ -75,126 +75,6 @@ namespace
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)
@ -414,126 +294,32 @@ namespace
assert(layout.Planes == 1);
const DXGI_FORMAT format = images[0]->format;
uint8_t* baseAddr = xbox.GetPointer();
const auto& metadata = xbox.GetMetadata();
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))
for (uint32_t item = 0; item < nimages; ++item)
{
//--- 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 Image* img = images[item];
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;
if (!img || !img->pixels)
return E_POINTER;
assert(nbw == layout.Plane[0].MipLayout[level].WidthElements);
assert(nbh == layout.Plane[0].MipLayout[level].HeightElements);
assert(bpb == layout.Plane[0].BytesPerElement);
assert(img->width == images[0]->width);
assert(img->height == images[0]->height);
assert(img->rowPitch == images[0]->rowPitch);
assert(img->format == images[0]->format);
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)
HRESULT hr = computer->CopyIntoSubresource(
baseAddr,
0u,
metadata.CalculateSubresource(level, item),
img->pixels,
static_cast<UINT32>(img->rowPitch),
0u);
if (FAILED(hr))
{
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];
}
}
return hr;
}
// 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;
@ -546,7 +332,6 @@ namespace
HRESULT Tile3D(
const Image& image,
uint32_t level,
uint32_t slices,
_In_ XGTextureAddressComputer* computer,
const XG_RESOURCE_LAYOUT& layout,
const XboxImage& xbox)
@ -554,141 +339,18 @@ namespace
if (!image.pixels || !computer || !xbox.GetPointer())
return E_POINTER;
assert(slices > 0);
assert(layout.Planes == 1);
assert(image.format == xbox.GetMetadata().format);
uint8_t* baseAddr = xbox.GetPointer();
const auto& metadata = xbox.GetMetadata();
#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;
return computer->CopyIntoSubresource(
baseAddr,
0u,
metadata.CalculateSubresource(level, 0),
image.pixels,
static_cast<UINT32>(image.rowPitch),
static_cast<UINT32>(image.slicePitch));
}
}
@ -1055,7 +717,7 @@ HRESULT Xbox::Tile(
}
// Relies on the fact that slices are contiguous
hr = Tile3D(srcImages[index], level, d, computer.Get(), layout, xbox);
hr = Tile3D(srcImages[index], level, computer.Get(), layout, xbox);
if (FAILED(hr))
{
xbox.Release();

View File

@ -175,7 +175,7 @@ namespace DirectX
DXGI_FORMAT format;
TEX_DIMENSION dimension;
size_t __cdecl ComputeIndex(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const noexcept;
size_t __cdecl ComputeIndex(size_t mip, size_t item, size_t slice) const noexcept;
// Returns size_t(-1) to indicate an out-of-range error
bool __cdecl IsCubemap() const noexcept { return (miscFlags & TEX_MISC_TEXTURECUBE) != 0; }
@ -188,6 +188,10 @@ namespace DirectX
bool __cdecl IsVolumemap() const noexcept { return (dimension == TEX_DIMENSION_TEXTURE3D); }
// Helper for dimension
uint32_t __cdecl CalculateSubresource(size_t mip, size_t item) const noexcept;
uint32_t __cdecl CalculateSubresource(size_t mip, size_t item, size_t plane) const noexcept;
// Returns size_t(-1) to indicate an out-of-range error
};
struct DDSMetaData

View File

@ -1492,7 +1492,6 @@ DXGI_FORMAT DirectX::MakeTypelessFLOAT(DXGI_FORMAT fmt) noexcept
// TexMetadata
//=====================================================================================
_Use_decl_annotations_
size_t TexMetadata::ComputeIndex(size_t mip, size_t item, size_t slice) const noexcept
{
if (mip >= mipLevels)
@ -1541,6 +1540,72 @@ size_t TexMetadata::ComputeIndex(size_t mip, size_t item, size_t slice) const no
}
}
// Equivalent to D3D11CacluateSubresource
uint32_t TexMetadata::CalculateSubresource(size_t mip, size_t item) const noexcept
{
uint32_t result = uint32_t(-1);
if (mip < mipLevels)
{
switch (dimension)
{
case TEX_DIMENSION_TEXTURE1D:
case TEX_DIMENSION_TEXTURE2D:
if (item < arraySize)
{
return static_cast<uint32_t>(mip + item*mipLevels);
}
break;
case TEX_DIMENSION_TEXTURE3D:
// No support for arrays of volumes
if (item == 0)
{
result = static_cast<uint32_t>(mip);
}
break;
default:
break;
}
}
return result;
}
// Equivalent to D3D12CacluateSubresource
uint32_t TexMetadata::CalculateSubresource(size_t mip, size_t item, size_t plane) const noexcept
{
uint32_t result = uint32_t(-1);
if (mip < mipLevels)
{
switch (dimension)
{
case TEX_DIMENSION_TEXTURE1D:
case TEX_DIMENSION_TEXTURE2D:
if (item < arraySize)
{
return static_cast<uint32_t>(mip + item*mipLevels + plane*mipLevels*arraySize);
}
break;
case TEX_DIMENSION_TEXTURE3D:
// No support for arrays of volumes
if (item == 0)
{
result = static_cast<uint32_t>(mip + plane*mipLevels);
}
break;
default:
break;
}
}
return result;
}
//=====================================================================================
// Blob - Bitmap image container