diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp new file mode 100644 index 0000000000..6d6cf383f8 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -0,0 +1,544 @@ +#include "stdafx.h" +#include "Emu/Memory/vm.h" +#include "TextureUtils.h" +#include "../RSXThread.h" +#include "Utilities/Log.h" + + +#define MAX2(a, b) ((a) > (b)) ? (a) : (b) + +unsigned LinearToSwizzleAddress(unsigned x, unsigned y, unsigned z, unsigned log2_width, unsigned log2_height, unsigned log2_depth) +{ + unsigned offset = 0; + unsigned shift_count = 0; + while (log2_width | log2_height | log2_depth) { + if (log2_width) + { + offset |= (x & 0x01) << shift_count; + x >>= 1; + ++shift_count; + --log2_width; + } + if (log2_height) + { + offset |= (y & 0x01) << shift_count; + y >>= 1; + ++shift_count; + --log2_height; + } + if (log2_depth) + { + offset |= (z & 0x01) << shift_count; + z >>= 1; + ++shift_count; + --log2_depth; + } + } + return offset; +} + + +/** +* Write data, assume src pixels are packed but not mipmaplevel +*/ +inline std::vector +writeTexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight; + currentMipmapLevelInfo.width = currentWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + for (unsigned row = 0; row < currentHeight; row++) + memcpy((char*)dst + offsetInDst + row * rowPitch, (char*)src + offsetInSrc + row * widthInBlock * blockSize, currentWidth * blockSize); + + offsetInDst += currentHeight * rowPitch; + offsetInDst = align(offsetInDst, 512); + offsetInSrc += currentHeight * widthInBlock * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + +/** +* Write data, assume src pixels are swizzled and but not mipmaplevel +*/ +inline std::vector +writeTexelsSwizzled(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight; + currentMipmapLevelInfo.width = currentWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + u32 *castedSrc, *castedDst; + u32 log2width, log2height; + + castedSrc = (u32*)src + offsetInSrc; + castedDst = (u32*)dst + offsetInDst; + + log2width = (u32)(logf((float)currentWidth) / logf(2.f)); + log2height = (u32)(logf((float)currentHeight) / logf(2.f)); + + for (int row = 0; row < currentHeight; row++) + for (int j = 0; j < currentWidth; j++) + castedDst[(row * rowPitch / 4) + j] = castedSrc[LinearToSwizzleAddress(j, row, 0, log2width, log2height, 0)]; + + offsetInDst += currentHeight * rowPitch; + offsetInSrc += currentHeight * widthInBlock * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + + +/** +* Write data, assume compressed (DXTCn) format +*/ +inline std::vector +writeCompressedTexel(const char *src, char *dst, size_t widthInBlock, size_t blockWidth, size_t heightInBlock, size_t blockHeight, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight * blockHeight; + currentMipmapLevelInfo.width = currentWidth * blockWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + for (unsigned row = 0; row < currentHeight; row++) + memcpy((char*)dst + offsetInDst + row * rowPitch, (char*)src + offsetInSrc + row * currentWidth * blockSize, currentWidth * blockSize); + + offsetInDst += currentHeight * rowPitch; + offsetInDst = align(offsetInDst, 512); + offsetInSrc += currentHeight * currentWidth * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + + +/** +* Write 16 bytes pixel textures, assume src pixels are swizzled and but not mipmaplevel +*/ +inline std::vector +write16bTexelsSwizzled(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight; + currentMipmapLevelInfo.width = currentWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + u16 *castedSrc, *castedDst; + u16 log2width, log2height; + + castedSrc = (u16*)src + offsetInSrc; + castedDst = (u16*)dst + offsetInDst; + + log2width = (u32)(logf((float)currentWidth) / logf(2.f)); + log2height = (u32)(logf((float)currentHeight) / logf(2.f)); + + for (int row = 0; row < currentHeight; row++) + for (int j = 0; j < currentWidth; j++) + castedDst[(row * rowPitch / 2) + j] = castedSrc[LinearToSwizzleAddress(j, row, 0, log2width, log2height, 0)]; + + offsetInDst += currentHeight * rowPitch; + offsetInSrc += currentHeight * widthInBlock * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + +/** +* Write 16 bytes pixel textures, assume src pixels are packed but not mipmaplevel +*/ +inline std::vector +write16bTexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + size_t srcPitch = widthInBlock * blockSize; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight; + currentMipmapLevelInfo.width = currentWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + unsigned short *castedDst = (unsigned short *)dst, *castedSrc = (unsigned short *)src; + + for (unsigned row = 0; row < heightInBlock; row++) + for (int j = 0; j < currentWidth; j++) + { + u16 tmp = castedSrc[offsetInSrc / 2 + row * srcPitch / 2 + j]; + castedDst[offsetInDst / 2 + row * rowPitch / 2 + j] = (tmp >> 8) | (tmp << 8); + } + + offsetInDst += currentHeight * rowPitch; + offsetInSrc += currentHeight * widthInBlock * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + +/** +* Write 16 bytes pixel textures, assume src pixels are packed but not mipmaplevel +*/ +inline std::vector +write16bX4TexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) +{ + std::vector Result; + size_t offsetInDst = 0, offsetInSrc = 0; + size_t currentHeight = heightInBlock, currentWidth = widthInBlock; + size_t srcPitch = widthInBlock * blockSize; + for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) + { + size_t rowPitch = align(currentWidth * blockSize, 256); + + MipmapLevelInfo currentMipmapLevelInfo = {}; + currentMipmapLevelInfo.offset = offsetInDst; + currentMipmapLevelInfo.height = currentHeight; + currentMipmapLevelInfo.width = currentWidth; + currentMipmapLevelInfo.rowPitch = rowPitch; + Result.push_back(currentMipmapLevelInfo); + + unsigned short *castedDst = (unsigned short *)dst, *castedSrc = (unsigned short *)src; + + for (unsigned row = 0; row < heightInBlock; row++) + for (int j = 0; j < currentWidth * 4; j++) + { + u16 tmp = castedSrc[offsetInSrc / 2 + row * srcPitch / 2 + j]; + castedDst[offsetInDst / 2 + row * rowPitch / 2 + j] = (tmp >> 8) | (tmp << 8); + } + + offsetInDst += currentHeight * rowPitch; + offsetInSrc += currentHeight * widthInBlock * blockSize; + currentHeight = MAX2(currentHeight / 2, 1); + currentWidth = MAX2(currentWidth / 2, 1); + } + return Result; +} + + +size_t getPlacedTextureStorageSpace(const RSXTexture &texture, size_t rowPitchAlignement) +{ + size_t w = texture.GetWidth(), h = texture.GetHeight(); + + size_t blockSizeInByte, blockWidthInPixel, blockHeightInPixel; + int format = texture.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); + + switch (format) + { + case CELL_GCM_TEXTURE_COMPRESSED_HILO8: + case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: + case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + default: + LOG_ERROR(RSX, "Unimplemented Texture format : %x", format); + break; + case CELL_GCM_TEXTURE_B8: + blockSizeInByte = 1; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_A1R5G5B5: + blockSizeInByte = 2; + blockHeightInPixel = 1, blockWidthInPixel = 1; + break; + case CELL_GCM_TEXTURE_A4R4G4B4: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R5G6B5: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_A8R8G8B8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: + blockSizeInByte = 8; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + blockSizeInByte = 16; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: + blockSizeInByte = 16; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_G8B8: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R6G5B5: + // Not native + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH24_D8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH16: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_X16: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_Y16_X16: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R5G5B5A1: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: + blockSizeInByte = 8; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: + blockSizeInByte = 16; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_X32_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_D1R5G5B5: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_Y16_X16_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_D8R8G8B8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + blockSizeInByte = 4; + blockWidthInPixel = 2, blockHeightInPixel = 2; + break; + case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + blockSizeInByte = 4; + blockWidthInPixel = 2, blockHeightInPixel = 2; + break; + } + + size_t heightInBlocks = (h + blockHeightInPixel - 1) / blockHeightInPixel; + size_t widthInBlocks = (w + blockWidthInPixel - 1) / blockWidthInPixel; + + size_t rowPitch = align(blockSizeInByte * widthInBlocks, rowPitchAlignement); + + return rowPitch * heightInBlocks * 2; // * 2 for mipmap levels +} + +std::vector uploadPlacedTexture(const RSXTexture &texture, size_t rowPitchAlignement, void* textureData) +{ + size_t w = texture.GetWidth(), h = texture.GetHeight(); + + int format = texture.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); + + size_t blockSizeInByte, blockWidthInPixel, blockHeightInPixel; + switch (format) + { + case CELL_GCM_TEXTURE_COMPRESSED_HILO8: + case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: + case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + default: + LOG_ERROR(RSX, "Unimplemented Texture format : %x", format); + break; + case CELL_GCM_TEXTURE_B8: + blockSizeInByte = 1; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_A1R5G5B5: + blockSizeInByte = 2; + blockHeightInPixel = 1, blockWidthInPixel = 1; + break; + case CELL_GCM_TEXTURE_A4R4G4B4: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R5G6B5: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_A8R8G8B8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: + blockSizeInByte = 8; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + blockSizeInByte = 16; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: + blockSizeInByte = 16; + blockWidthInPixel = 4, blockHeightInPixel = 4; + break; + case CELL_GCM_TEXTURE_G8B8: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R6G5B5: + // Not native + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH24_D8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH16: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_X16: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_Y16_X16: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_R5G5B5A1: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: + blockSizeInByte = 8; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: + blockSizeInByte = 16; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_X32_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_D1R5G5B5: + blockSizeInByte = 2; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_Y16_X16_FLOAT: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_D8R8G8B8: + blockSizeInByte = 4; + blockWidthInPixel = 1, blockHeightInPixel = 1; + break; + case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + blockSizeInByte = 4; + blockWidthInPixel = 2, blockHeightInPixel = 2; + break; + case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + blockSizeInByte = 4; + blockWidthInPixel = 2, blockHeightInPixel = 2; + break; + } + + size_t heightInBlocks = (h + blockHeightInPixel - 1) / blockHeightInPixel; + size_t widthInBlocks = (w + blockWidthInPixel - 1) / blockWidthInPixel; + + std::vector mipInfos; + + const u32 texaddr = GetAddress(texture.GetOffset(), texture.GetLocation()); + auto pixels = vm::get_ptr(texaddr); + bool is_swizzled = !(texture.GetFormat() & CELL_GCM_TEXTURE_LN); + switch (format) + { + case CELL_GCM_TEXTURE_A8R8G8B8: + if (is_swizzled) + return writeTexelsSwizzled((char*)pixels, (char*)textureData, w, h, 4, texture.GetMipmap()); + else + return writeTexelsGeneric((char*)pixels, (char*)textureData, w, h, 4, texture.GetMipmap()); + case CELL_GCM_TEXTURE_A1R5G5B5: + case CELL_GCM_TEXTURE_A4R4G4B4: + case CELL_GCM_TEXTURE_R5G6B5: + if (is_swizzled) + return write16bTexelsSwizzled((char*)pixels, (char*)textureData, w, h, 2, texture.GetMipmap()); + else + return write16bTexelsGeneric((char*)pixels, (char*)textureData, w, h, 2, texture.GetMipmap()); + case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: + return write16bX4TexelsGeneric((char*)pixels, (char*)textureData, w, h, 8, texture.GetMipmap()); + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: + return writeCompressedTexel((char*)pixels, (char*)textureData, widthInBlocks, blockWidthInPixel, heightInBlocks, blockHeightInPixel, blockSizeInByte, texture.GetMipmap()); + default: + return writeTexelsGeneric((char*)pixels, (char*)textureData, w, h, blockSizeInByte, texture.GetMipmap()); + } + +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h new file mode 100644 index 0000000000..6a5e5ae295 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -0,0 +1,26 @@ +#pragma once +#include "../RSXTexture.h" +#include + +struct MipmapLevelInfo +{ + size_t offset; + size_t width; + size_t height; + size_t rowPitch; +}; + +unsigned LinearToSwizzleAddress(unsigned x, unsigned y, unsigned z, unsigned log2_width, unsigned log2_height, unsigned log2_depth); + +/** +* Get size to store texture in a linear fashion. +* Storage is assumed to use a rowPitchAlignement boundary for every row of texture. +*/ +size_t getPlacedTextureStorageSpace(const RSXTexture &texture, size_t rowPitchAlignement); + +/** +* Write texture data to textureData. +* Data are not packed, they are stored per rows using rowPitchAlignement. +* Similarly, offset for every mipmaplevel is aligned to rowPitchAlignement boundary. +*/ +std::vector uploadPlacedTexture(const RSXTexture &texture, size_t rowPitchAlignement, void* textureData); \ No newline at end of file diff --git a/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp b/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp index f17b53f240..5c4f6e14c6 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12Texture.cpp @@ -2,40 +2,9 @@ #if defined(DX12_SUPPORT) #include "D3D12GSRender.h" #include "d3dx12.h" +#include "../Common/TextureUtils.h" // For clarity this code deals with texture but belongs to D3D12GSRender class - -static -u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth) -{ - u32 offset = 0; - u32 shift_count = 0; - while (log2_width | log2_height | log2_depth) { - if (log2_width) - { - offset |= (x & 0x01) << shift_count; - x >>= 1; - ++shift_count; - --log2_width; - } - if (log2_height) - { - offset |= (y & 0x01) << shift_count; - y >>= 1; - ++shift_count; - --log2_height; - } - if (log2_depth) - { - offset |= (z & 0x01) << shift_count; - z >>= 1; - ++shift_count; - --log2_depth; - } - } - return offset; -} - static D3D12_COMPARISON_FUNC getSamplerCompFunc[] = { @@ -151,239 +120,6 @@ D3D12_SAMPLER_DESC getSamplerDesc(const RSXTexture &texture) return samplerDesc; } -struct MipmapLevelInfo -{ - size_t offset; - size_t width; - size_t height; - size_t rowPitch; -}; - -#define MAX2(a, b) ((a) > (b)) ? (a) : (b) - -/** - * Write data, assume src pixels are packed but not mipmaplevel - */ -static std::vector -writeTexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight; - currentMipmapLevelInfo.width = currentWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - for (unsigned row = 0; row < currentHeight; row++) - memcpy((char*)dst + offsetInDst + row * rowPitch, (char*)src + offsetInSrc + row * widthInBlock * blockSize, currentWidth * blockSize); - - offsetInDst += currentHeight * rowPitch; - offsetInDst = align(offsetInDst, 512); - offsetInSrc += currentHeight * widthInBlock * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} - -/** -* Write data, assume src pixels are swizzled and but not mipmaplevel -*/ -static std::vector -writeTexelsSwizzled(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight; - currentMipmapLevelInfo.width = currentWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - u32 *castedSrc, *castedDst; - u32 log2width, log2height; - - castedSrc = (u32*)src + offsetInSrc; - castedDst = (u32*)dst + offsetInDst; - - log2width = (u32)(logf((float)currentWidth) / logf(2.f)); - log2height = (u32)(logf((float)currentHeight) / logf(2.f)); - - for (int row = 0; row < currentHeight; row++) - for (int j = 0; j < currentWidth; j++) - castedDst[(row * rowPitch / 4) + j] = castedSrc[LinearToSwizzleAddress(j, row, 0, log2width, log2height, 0)]; - - offsetInDst += currentHeight * rowPitch; - offsetInSrc += currentHeight * widthInBlock * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} - - -/** -* Write data, assume compressed (DXTCn) format -*/ -static std::vector -writeCompressedTexel(const char *src, char *dst, size_t widthInBlock, size_t blockWidth, size_t heightInBlock, size_t blockHeight, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight * blockHeight; - currentMipmapLevelInfo.width = currentWidth * blockWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - for (unsigned row = 0; row < currentHeight; row++) - memcpy((char*)dst + offsetInDst + row * rowPitch, (char*)src + offsetInSrc + row * currentWidth * blockSize, currentWidth * blockSize); - - offsetInDst += currentHeight * rowPitch; - offsetInDst = align(offsetInDst, 512); - offsetInSrc += currentHeight * currentWidth * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} - - -/** -* Write 16 bytes pixel textures, assume src pixels are swizzled and but not mipmaplevel -*/ -static std::vector -write16bTexelsSwizzled(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight; - currentMipmapLevelInfo.width = currentWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - u16 *castedSrc, *castedDst; - u16 log2width, log2height; - - castedSrc = (u16*)src + offsetInSrc; - castedDst = (u16*)dst + offsetInDst; - - log2width = (u32)(logf((float)currentWidth) / logf(2.f)); - log2height = (u32)(logf((float)currentHeight) / logf(2.f)); - - for (int row = 0; row < currentHeight; row++) - for (int j = 0; j < currentWidth; j++) - castedDst[(row * rowPitch / 2) + j] = castedSrc[LinearToSwizzleAddress(j, row, 0, log2width, log2height, 0)]; - - offsetInDst += currentHeight * rowPitch; - offsetInSrc += currentHeight * widthInBlock * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} - -/** -* Write 16 bytes pixel textures, assume src pixels are packed but not mipmaplevel -*/ -static std::vector -write16bTexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - size_t srcPitch = widthInBlock * blockSize; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight; - currentMipmapLevelInfo.width = currentWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - unsigned short *castedDst = (unsigned short *)dst, *castedSrc = (unsigned short *)src; - - for (unsigned row = 0; row < heightInBlock; row++) - for (int j = 0; j < currentWidth; j++) - { - u16 tmp = castedSrc[offsetInSrc / 2 + row * srcPitch / 2 + j]; - castedDst[offsetInDst / 2 + row * rowPitch / 2 + j] = (tmp >> 8) | (tmp << 8); - } - - offsetInDst += currentHeight * rowPitch; - offsetInSrc += currentHeight * widthInBlock * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} - -/** -* Write 16 bytes pixel textures, assume src pixels are packed but not mipmaplevel -*/ -static std::vector -write16bX4TexelsGeneric(const char *src, char *dst, size_t widthInBlock, size_t heightInBlock, size_t blockSize, size_t mipmapCount) -{ - std::vector Result; - size_t offsetInDst = 0, offsetInSrc = 0; - size_t currentHeight = heightInBlock, currentWidth = widthInBlock; - size_t srcPitch = widthInBlock * blockSize; - for (unsigned mipLevel = 0; mipLevel < mipmapCount; mipLevel++) - { - size_t rowPitch = align(currentWidth * blockSize, 256); - - MipmapLevelInfo currentMipmapLevelInfo = {}; - currentMipmapLevelInfo.offset = offsetInDst; - currentMipmapLevelInfo.height = currentHeight; - currentMipmapLevelInfo.width = currentWidth; - currentMipmapLevelInfo.rowPitch = rowPitch; - Result.push_back(currentMipmapLevelInfo); - - unsigned short *castedDst = (unsigned short *)dst, *castedSrc = (unsigned short *)src; - - for (unsigned row = 0; row < heightInBlock; row++) - for (int j = 0; j < currentWidth * 4; j++) - { - u16 tmp = castedSrc[offsetInSrc / 2 + row * srcPitch / 2 + j]; - castedDst[offsetInDst / 2 + row * rowPitch / 2 + j] = (tmp >> 8) | (tmp << 8); - } - - offsetInDst += currentHeight * rowPitch; - offsetInSrc += currentHeight * widthInBlock * blockSize; - currentHeight = MAX2(currentHeight / 2, 1); - currentWidth = MAX2(currentWidth / 2, 1); - } - return Result; -} /** * Create a texture residing in default heap and generate uploads commands in commandList, @@ -399,204 +135,17 @@ ID3D12Resource *uploadSingleTexture( ID3D12Resource *vramTexture; size_t w = texture.GetWidth(), h = texture.GetHeight(); - size_t blockSizeInByte, blockWidthInPixel, blockHeightInPixel; int format = texture.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); DXGI_FORMAT dxgiFormat = getTextureDXGIFormat(format); - const u32 texaddr = GetAddress(texture.GetOffset(), texture.GetLocation()); - - bool is_swizzled = !(texture.GetFormat() & CELL_GCM_TEXTURE_LN); - size_t srcPitch; - switch (format) - { - case CELL_GCM_TEXTURE_COMPRESSED_HILO8: - case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: - case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: - case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: - default: - LOG_ERROR(RSX, "Unimplemented Texture format : %x", format); - break; - case CELL_GCM_TEXTURE_B8: - blockSizeInByte = 1; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w; - break; - case CELL_GCM_TEXTURE_A1R5G5B5: - blockSizeInByte = 2; - blockHeightInPixel = 1, blockWidthInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_A4R4G4B4: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_R5G6B5: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_A8R8G8B8: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: - blockSizeInByte = 8; - blockWidthInPixel = 4, blockHeightInPixel = 4; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: - blockSizeInByte = 16; - blockWidthInPixel = 4, blockHeightInPixel = 4; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: - blockSizeInByte = 16; - blockWidthInPixel = 4, blockHeightInPixel = 4; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_G8B8: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_R6G5B5: - // Not native - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_DEPTH24_D8: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_DEPTH16: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_DEPTH16_FLOAT: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_X16: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_Y16_X16: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_R5G5B5A1: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: - blockSizeInByte = 8; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 8; - break; - case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: - blockSizeInByte = 16; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 16; - break; - case CELL_GCM_TEXTURE_X32_FLOAT: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_D1R5G5B5: - blockSizeInByte = 2; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 2; - break; - case CELL_GCM_TEXTURE_Y16_X16_FLOAT: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_D8R8G8B8: - blockSizeInByte = 4; - blockWidthInPixel = 1, blockHeightInPixel = 1; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: - blockSizeInByte = 4; - blockWidthInPixel = 2, blockHeightInPixel = 2; - srcPitch = w * 4; - break; - case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: - blockSizeInByte = 4; - blockWidthInPixel = 2, blockHeightInPixel = 2; - srcPitch = w * 4; - break; - } - - size_t heightInBlocks = (h + blockHeightInPixel - 1) / blockHeightInPixel; - size_t widthInBlocks = (w + blockWidthInPixel - 1) / blockWidthInPixel; - // Multiple of 256 - size_t rowPitch = align(blockSizeInByte * widthInBlocks, 256); - - size_t textureSize = rowPitch * heightInBlocks * 2; // * 4 for mipmap levels + size_t textureSize = getPlacedTextureStorageSpace(texture, 256); assert(textureBuffersHeap.canAlloc(textureSize)); size_t heapOffset = textureBuffersHeap.alloc(textureSize); - auto pixels = vm::get_ptr(texaddr); void *buffer; ThrowIfFailed(textureBuffersHeap.m_heap->Map(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize), &buffer)); void *textureData = (char*)buffer + heapOffset; - std::vector mipInfos; - - switch (format) - { - case CELL_GCM_TEXTURE_A8R8G8B8: - { - if (is_swizzled) - mipInfos = writeTexelsSwizzled((char*)pixels, (char*)textureData, w, h, 4, texture.GetMipmap()); - else - mipInfos = writeTexelsGeneric((char*)pixels, (char*)textureData, w, h, 4, texture.GetMipmap()); - break; - } - case CELL_GCM_TEXTURE_A1R5G5B5: - case CELL_GCM_TEXTURE_A4R4G4B4: - case CELL_GCM_TEXTURE_R5G6B5: - { - if (is_swizzled) - mipInfos = write16bTexelsSwizzled((char*)pixels, (char*)textureData, w, h, 2, texture.GetMipmap()); - else - mipInfos = write16bTexelsGeneric((char*)pixels, (char*)textureData, w, h, 2, texture.GetMipmap()); - break; - } - case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: - { - mipInfos = write16bX4TexelsGeneric((char*)pixels, (char*)textureData, w, h, 8, texture.GetMipmap()); - break; - } - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: - { - mipInfos = writeCompressedTexel((char*)pixels, (char*)textureData, widthInBlocks, blockWidthInPixel, heightInBlocks, blockHeightInPixel, blockSizeInByte, texture.GetMipmap()); - break; - } - default: - { - mipInfos = writeTexelsGeneric((char*)pixels, (char*)textureData, w, h, blockSizeInByte, texture.GetMipmap()); - break; - } - } + std::vector mipInfos = uploadPlacedTexture(texture, 256, textureData); textureBuffersHeap.m_heap->Unmap(0, &CD3DX12_RANGE(heapOffset, heapOffset + textureSize)); D3D12_RESOURCE_DESC texturedesc = CD3DX12_RESOURCE_DESC::Tex2D(dxgiFormat, (UINT)w, (UINT)h, 1, texture.GetMipmap()); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index b6be267c86..03b0946261 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -6,6 +6,7 @@ #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "GLGSRender.h" +#include "../Common/TextureUtils.h" #define CMD_DEBUG 0 #define DUMP_VERTEX_DATA 0 @@ -2152,34 +2153,4 @@ void GLGSRender::semaphorePGRAPHBackendRelease(u32 offset, u32 value) void GLGSRender::semaphorePFIFOAcquire(u32 offset, u32 value) { -} - -u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth) -{ - u32 offset = 0; - u32 shift_count = 0; - while (log2_width | log2_height | log2_depth){ - if (log2_width) - { - offset |= (x & 0x01) << shift_count; - x >>= 1; - ++shift_count; - --log2_width; - } - if (log2_height) - { - offset |= (y & 0x01) << shift_count; - y >>= 1; - ++shift_count; - --log2_height; - } - if (log2_depth) - { - offset |= (z & 0x01) << shift_count; - z >>= 1; - ++shift_count; - --log2_depth; - } - } - return offset; -} +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 6cc29c3d67..31e6ac6a93 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -18,7 +18,6 @@ extern GLenum g_last_gl_error; void printGlError(GLenum err, const char* situation); void printGlError(GLenum err, const std::string& situation); -u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth); class GLTexture diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 7b0d7d7616..eb021b8964 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -62,6 +62,7 @@ + @@ -532,6 +533,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index bc79cb2297..c46abfddf6 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -989,6 +989,9 @@ Utilities + + Emu\GPU\RSX\Common + @@ -1882,5 +1885,8 @@ Emu\GPU\RSX\D3D12 + + Emu\GPU\RSX\Common + \ No newline at end of file