mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-10-21 18:29:08 +02:00
Latte: Partial support for mips in texture readback
This commit is contained in:
1 parent
8a471c8933
commit
c25dbdf86d
8 files changed
+38
-36
No files matched your search
@@ -333,7 +333,7 @@ float* LatteTexture_getEffectiveTextureScale(LatteConst::ShaderType shaderType,
|
||||
LatteTextureView* LatteTexture_CreateTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth);
|
||||
void LatteTexture_Delete(LatteTexture* texture);
|
||||
|
||||
void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* textureData, uint32 sliceIndex, uint32 mipIndex, uint8* linearPixelData);
|
||||
void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* textureData, uint32 sliceIndex, uint32 mipIndex, uint8* linearPixelData, uint32 sourceRowPitch);
|
||||
|
||||
sint32 LatteTexture_getEffectiveWidth(LatteTexture* texture);
|
||||
bool LatteTexture_doesEffectiveRescaleRatioMatch(LatteTexture* texture1, sint32 mipLevel1, LatteTexture* texture2, sint32 mipLevel2);
|
||||
|
||||
@@ -695,15 +695,12 @@ void LatteTextureLoader_UpdateTextureSliceData(LatteTexture* tex, uint32 sliceIn
|
||||
}
|
||||
|
||||
template<typename copyType>
|
||||
void optimizedLinearReadbackWriteLoop(LatteTextureLoaderCtx* textureLoader, uint8* linearPixelData)
|
||||
void optimizedLinearReadbackWriteLoop(LatteTextureLoaderCtx* textureLoader, uint8* linearPixelData, uint32 sourcePitch)
|
||||
{
|
||||
uint32 pitch = textureLoader->width;
|
||||
// optimized for linear
|
||||
for (sint32 y = 0; y < textureLoader->height; y++)
|
||||
{
|
||||
sint32 yc = y;
|
||||
sint32 pixelOffset = yc * pitch;
|
||||
copyType* rowPixelData = (copyType*)(linearPixelData + pixelOffset * sizeof(copyType));
|
||||
copyType* rowPixelData = (copyType*)(linearPixelData + y * sourcePitch);
|
||||
copyType* blockData = (copyType*)LatteTextureLoader_getInputLinearOptimized_(textureLoader, 0, y, 1, 1, sizeof(copyType) * 8, textureLoader->sliceIndex, 1, 0, textureLoader->pitch, textureLoader->surfaceInfoHeight);
|
||||
if constexpr (sizeof(copyType) == 4)
|
||||
{
|
||||
@@ -721,7 +718,7 @@ void optimizedLinearReadbackWriteLoop(LatteTextureLoaderCtx* textureLoader, uint
|
||||
}
|
||||
}
|
||||
|
||||
void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* textureData, uint32 sliceIndex, uint32 mipIndex, uint8* linearPixelData)
|
||||
void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* textureData, uint32 sliceIndex, uint32 mipIndex, uint8* linearPixelData, uint32 sourceRowPitch)
|
||||
{
|
||||
LatteTextureLoaderCtx textureLoader = { 0 };
|
||||
LatteTextureLoader_begin(&textureLoader, sliceIndex, mipIndex, textureData->physAddress, textureData->physMipAddress, textureData->format, textureData->dim, textureData->width, textureData->height, textureData->depth, textureData->mipLevels, textureData->pitch, textureData->tileMode, textureData->swizzle);
|
||||
@@ -736,22 +733,21 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
|
||||
if (textureData->tileMode == Latte::E_HWTILEMODE::TM_LINEAR_ALIGNED)
|
||||
{
|
||||
uint32 pitch = textureLoader.width;
|
||||
if (textureData->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM ||
|
||||
textureData->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_SRGB)
|
||||
{
|
||||
optimizedLinearReadbackWriteLoop<uint32>(&textureLoader, linearPixelData);
|
||||
optimizedLinearReadbackWriteLoop<uint32>(&textureLoader, linearPixelData, sourceRowPitch);
|
||||
}
|
||||
else if (textureData->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_UNORM)
|
||||
{
|
||||
optimizedLinearReadbackWriteLoop<uint64>(&textureLoader, linearPixelData);
|
||||
optimizedLinearReadbackWriteLoop<uint64>(&textureLoader, linearPixelData, sourceRowPitch);
|
||||
}
|
||||
else if (textureData->format == Latte::E_GX2SURFFMT::R32_G32_B32_A32_FLOAT)
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader.height; y += textureLoader.stepY)
|
||||
{
|
||||
sint32 yc = y;
|
||||
sint32 pixelOffset = (0 + yc * pitch) * 16;
|
||||
sint32 pixelOffset = yc * sourceRowPitch;
|
||||
for (sint32 x = 0; x < textureLoader.width; x += textureLoader.stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_getInputLinearOptimized(&textureLoader, x, y);
|
||||
@@ -771,7 +767,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
for (sint32 x = 0; x < textureLoader.width; x += textureLoader.stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_getInputLinearOptimized(&textureLoader, x, y);
|
||||
sint32 pixelOffset = (x + yc * pitch) * 4;
|
||||
sint32 pixelOffset = yc * sourceRowPitch + x * 4;
|
||||
(*(uint32*)(blockData + 0)) = *(uint32*)(linearPixelData + pixelOffset + 0);
|
||||
}
|
||||
}
|
||||
@@ -784,7 +780,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
for (sint32 x = 0; x < textureLoader.width; x += textureLoader.stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_getInputLinearOptimized(&textureLoader, x, y);
|
||||
sint32 pixelOffset = (x + yc * pitch) * 8;
|
||||
sint32 pixelOffset = yc * sourceRowPitch + x * 8;
|
||||
(*(uint32*)(blockData + 0)) = *(uint32*)(linearPixelData + pixelOffset + 0);
|
||||
(*(uint32*)(blockData + 4)) = *(uint32*)(linearPixelData + pixelOffset + 4);
|
||||
}
|
||||
@@ -792,7 +788,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
}
|
||||
else if (textureData->format == Latte::E_GX2SURFFMT::R8_G8_UNORM)
|
||||
{
|
||||
optimizedLinearReadbackWriteLoop<uint16>(&textureLoader, linearPixelData);
|
||||
optimizedLinearReadbackWriteLoop<uint16>(&textureLoader, linearPixelData, sourceRowPitch);
|
||||
}
|
||||
else if (textureData->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_UNORM)
|
||||
{
|
||||
@@ -800,7 +796,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
}
|
||||
else if (textureData->format == Latte::E_GX2SURFFMT::R16_UNORM)
|
||||
{
|
||||
optimizedLinearReadbackWriteLoop<uint16>(&textureLoader, linearPixelData);
|
||||
optimizedLinearReadbackWriteLoop<uint16>(&textureLoader, linearPixelData, sourceRowPitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -816,7 +812,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
// used in Bayonetta 2
|
||||
for (sint32 y = 0; y < textureLoader.height; y++)
|
||||
{
|
||||
uint8* pixelInput = linearPixelData + (y * textureLoader.width) * 4;
|
||||
uint8* pixelInput = linearPixelData + y * sourceRowPitch;
|
||||
for (sint32 x = 0; x < textureLoader.width; x++)
|
||||
{
|
||||
uint8* outputData = LatteTextureLoader_GetInput(&textureLoader, x, y);
|
||||
@@ -831,7 +827,7 @@ void LatteTextureLoader_writeReadbackTextureToMemory(LatteTextureDefinition* tex
|
||||
// Bayonetta 2 also uses this but it converts the depth buffer to a color texture first
|
||||
for (sint32 y = 0; y < textureLoader.height; y++)
|
||||
{
|
||||
uint8* pixelInput = linearPixelData + (y * textureLoader.width) * 4;
|
||||
uint8* pixelInput = linearPixelData + y * sourceRowPitch;
|
||||
for (sint32 x = 0; x < textureLoader.width; x++)
|
||||
{
|
||||
uint8* outputData = LatteTextureLoader_GetInput(&textureLoader, x, y);
|
||||
|
||||
@@ -145,7 +145,7 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish)
|
||||
}
|
||||
#endif
|
||||
uint8* pixelData = readbackInfo->GetData();
|
||||
LatteTextureLoader_writeReadbackTextureToMemory(&readbackInfo->hostTextureCopy, readbackInfo->m_firstSlice, 0, pixelData);
|
||||
LatteTextureLoader_writeReadbackTextureToMemory(&readbackInfo->hostTextureCopy, readbackInfo->m_firstSlice, readbackInfo->m_firstMip, pixelData, readbackInfo->m_rowPitch);
|
||||
readbackInfo->ReleaseData();
|
||||
// get the original texture if it still exists and invalidate the current data hash
|
||||
LatteTextureView* origTexView = LatteTextureViewLookupCache::lookupSlice(readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.pitch, 0, 0, readbackInfo->hostTextureCopy.format);
|
||||
@@ -172,7 +172,7 @@ bool LatteTextureReadback_ReadbackToLinearBlocking(LatteTextureView* sourceView,
|
||||
|
||||
uint8* data = info->GetData(); // returned pixel format should match Latte format
|
||||
uint32 bpp = Latte::GetFormatBits(sourceView->baseTexture->format) / 8;
|
||||
uint32 srcRowBytes = sourceView->baseTexture->width * bpp;
|
||||
uint32 srcRowBytes = info->m_rowPitch;
|
||||
uint32 dstRowBytes = dstWidth * bpp;
|
||||
for (uint32 y = 0; y < dstHeight; y++)
|
||||
memcpy(dstPtr + y * dstPitch * bpp, data + y * srcRowBytes, dstRowBytes);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
class LatteTextureReadbackInfo
|
||||
{
|
||||
public:
|
||||
LatteTextureReadbackInfo(LatteTextureView* textureView)
|
||||
: hostTextureCopy(textureView->baseTexture), m_firstSlice(textureView->firstSlice), m_textureView(textureView)
|
||||
LatteTextureReadbackInfo(LatteTextureView* textureView, sint32 mipIndex = 0)
|
||||
: hostTextureCopy(textureView->baseTexture), m_firstSlice(textureView->firstSlice), m_firstMip(mipIndex), m_textureView(textureView)
|
||||
{}
|
||||
|
||||
virtual ~LatteTextureReadbackInfo() = default;
|
||||
@@ -25,8 +25,10 @@ public:
|
||||
// texture info
|
||||
LatteTextureDefinition hostTextureCopy{};
|
||||
sint32 m_firstSlice{};
|
||||
sint32 m_firstMip{}; // mip level transferred by the backend
|
||||
uint32 m_rowPitch = 0; // bytes between rows in GetData(). Set by the backend
|
||||
|
||||
protected:
|
||||
LatteTextureView* m_textureView;
|
||||
uint32 m_image_size = 0;
|
||||
};
|
||||
};
|
||||
@@ -19,6 +19,7 @@ void LatteTextureReadbackInfoMtl::StartTransfer()
|
||||
cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D);
|
||||
|
||||
size_t bytesPerRow = GetMtlTextureBytesPerRow(baseTexture->format, baseTexture->isDepth, baseTexture->width);
|
||||
m_rowPitch = static_cast<uint32>(bytesPerRow);
|
||||
size_t bytesPerImage = GetMtlTextureBytesPerImage(baseTexture->format, baseTexture->isDepth, baseTexture->height, bytesPerRow);
|
||||
|
||||
auto blitCommandEncoder = m_mtlr->GetBlitCommandEncoder();
|
||||
|
||||
@@ -542,7 +542,6 @@ void OpenGLRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad
|
||||
texture_bindAndActivate(texView, 0);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &screenshotWidth);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &screenshotHeight);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1); // set alignment to 1
|
||||
|
||||
const sint32 pixelDataSize = screenshotWidth * screenshotHeight * 3;
|
||||
std::vector<uint8> rgb_data(pixelDataSize);
|
||||
|
||||
@@ -2,25 +2,26 @@
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h"
|
||||
|
||||
LatteTextureReadbackInfoGL::LatteTextureReadbackInfoGL(LatteTextureView* textureView)
|
||||
: LatteTextureReadbackInfo(textureView)
|
||||
: LatteTextureReadbackInfo(textureView, textureView->firstMip)
|
||||
{
|
||||
LatteTexture* baseTexture = textureView->baseTexture;
|
||||
uint32 width = baseTexture->GetMipWidth(m_firstMip);
|
||||
// handle format
|
||||
if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_UNORM)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 4;
|
||||
m_rowPitch = width * 4;
|
||||
m_texFormatGL = GL_RGBA;
|
||||
m_texDataTypeGL = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_B8_A8_SRGB)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 4;
|
||||
m_rowPitch = width * 4;
|
||||
m_texFormatGL = GL_RGBA;
|
||||
m_texDataTypeGL = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else if (textureView->format == Latte::E_GX2SURFFMT::R32_G32_B32_A32_FLOAT)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 16;
|
||||
m_rowPitch = width * 16;
|
||||
m_texFormatGL = GL_RGBA;
|
||||
m_texDataTypeGL = GL_FLOAT;
|
||||
}
|
||||
@@ -28,13 +29,13 @@ LatteTextureReadbackInfoGL::LatteTextureReadbackInfoGL(LatteTextureView* texture
|
||||
{
|
||||
if (baseTexture->isDepth)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 4;
|
||||
m_rowPitch = width * 4;
|
||||
m_texFormatGL = GL_DEPTH_COMPONENT;
|
||||
m_texDataTypeGL = GL_FLOAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 4;
|
||||
m_rowPitch = width * 4;
|
||||
m_texFormatGL = GL_RED;
|
||||
m_texDataTypeGL = GL_FLOAT;
|
||||
}
|
||||
@@ -43,33 +44,33 @@ LatteTextureReadbackInfoGL::LatteTextureReadbackInfoGL(LatteTextureView* texture
|
||||
{
|
||||
if (baseTexture->isDepth)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 2;
|
||||
m_rowPitch = width * 2;
|
||||
m_texFormatGL = GL_DEPTH_COMPONENT;
|
||||
m_texDataTypeGL = GL_UNSIGNED_SHORT;
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 2;
|
||||
m_rowPitch = width * 2;
|
||||
m_texFormatGL = GL_RED;
|
||||
m_texDataTypeGL = GL_UNSIGNED_SHORT;
|
||||
}
|
||||
}
|
||||
else if (textureView->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_FLOAT)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 8;
|
||||
m_rowPitch = width * 8;
|
||||
m_texFormatGL = GL_RGBA;
|
||||
m_texDataTypeGL = GL_HALF_FLOAT;
|
||||
}
|
||||
else if (textureView->format == Latte::E_GX2SURFFMT::R8_G8_UNORM)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 2;
|
||||
m_rowPitch = width * 2;
|
||||
m_texFormatGL = GL_RG;
|
||||
m_texDataTypeGL = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else if (textureView->format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_UNORM)
|
||||
{
|
||||
m_image_size = baseTexture->width*baseTexture->height * 8;
|
||||
m_rowPitch = width * 8;
|
||||
m_texFormatGL = GL_RGBA;
|
||||
m_texDataTypeGL = GL_UNSIGNED_SHORT;
|
||||
}
|
||||
@@ -78,6 +79,9 @@ LatteTextureReadbackInfoGL::LatteTextureReadbackInfoGL(LatteTextureView* texture
|
||||
cemuLog_logDebug(LogType::Force, "Unsupported texture readback format {:04x}", (uint32)textureView->format);
|
||||
return;
|
||||
}
|
||||
// OpenGL uses the default 4 byte pack alignment because it gives us slightly better row copy performance
|
||||
m_rowPitch = (m_rowPitch + 3) & ~3u;
|
||||
m_image_size = m_rowPitch * baseTexture->GetMipHeight(m_firstMip);
|
||||
}
|
||||
|
||||
LatteTextureReadbackInfoGL::~LatteTextureReadbackInfoGL()
|
||||
@@ -93,14 +97,13 @@ void LatteTextureReadbackInfoGL::StartTransfer()
|
||||
{
|
||||
cemu_assert(m_textureView);
|
||||
LatteTextureGL* baseTexture = (LatteTextureGL*)m_textureView->baseTexture;
|
||||
cemu_assert_debug(m_textureView->firstMip == 0);
|
||||
cemu_assert_debug(m_textureView->baseTexture->dim != Latte::E_DIM::DIM_3D);
|
||||
// create unsynchronized buffer
|
||||
glGenBuffers(1, &texImageBufferGL);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, texImageBufferGL);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, m_image_size, NULL, GL_DYNAMIC_READ);
|
||||
// request texture read into buffer
|
||||
glGetTextureSubImage(baseTexture->glId_texture, 0, 0, 0, m_firstSlice, baseTexture->width, baseTexture->height, 1, m_texFormatGL, m_texDataTypeGL, m_image_size, NULL);
|
||||
glGetTextureSubImage(baseTexture->glId_texture, m_firstMip, 0, 0, m_firstSlice, baseTexture->GetMipWidth(m_firstMip), baseTexture->GetMipHeight(m_firstMip), 1, m_texFormatGL, m_texDataTypeGL, m_image_size, NULL);
|
||||
glFlush();
|
||||
// create fence sync (so we can check if the image copy operation finished)
|
||||
imageCopyFinSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
@@ -6,6 +6,7 @@ LatteTextureReadbackInfoVk::LatteTextureReadbackInfoVk(VkDevice device, LatteTex
|
||||
: LatteTextureReadbackInfo(textureView), m_device(device)
|
||||
{
|
||||
m_image_size = GetImageSize(textureView);
|
||||
m_rowPitch = m_image_size / textureView->baseTexture->height;
|
||||
}
|
||||
|
||||
LatteTextureReadbackInfoVk::~LatteTextureReadbackInfoVk()
|
||||
|
||||
Reference in new issue
Block a user