diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h index 7b2c109b..21ef9dc9 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h @@ -46,6 +46,7 @@ void decodeBC3Block_UNORM(uint8* inputData, float* imageRGBA); void decodeBC4Block_UNORM(uint8* blockStorage, float* rOutput); void decodeBC5Block_UNORM(uint8* blockStorage, float* rgOutput); void decodeBC5Block_SNORM(uint8* blockStorage, float* rgOutput); +using decodingFn = void (uint8 *, float *); inline void BC1_GetPixel(uint8* inputData, sint32 x, sint32 y, uint8 rgba[4]) { @@ -1684,6 +1685,99 @@ public: } }; +class TextureDecoder_BC1_To_R8G8B8A8 : public TextureDecoder, public SingletonClass +{ +public: + + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 blockSizeX = (std::min)(4, textureLoader->width - x); + sint32 blockSizeY = (std::min)(4, textureLoader->height - y); + // decode 4x4 pixels at once + float rgbaBlock[4 * 4 * 4]; + decodeBC1Block(blockData, rgbaBlock); + for (sint32 py = 0; py < blockSizeY; py++) + { + sint32 yc = y + py; + for (sint32 px = 0; px < blockSizeX; px++) + { + sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer + float red = rgbaBlock[(px + py * 4) * 4 + 0]; + float green = rgbaBlock[(px + py * 4) * 4 + 1]; + float blue = rgbaBlock[(px + py * 4) * 4 + 2]; + float alpha = rgbaBlock[(px + py * 4) * 4 + 3]; + *(outputData + pixelOffset + 0) = red * 255; + *(outputData + pixelOffset + 1) = green * 255; + *(outputData + pixelOffset + 2) = blue * 255; + *(outputData + pixelOffset + 3) = alpha * 255; + } + } + } + } + } + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + return; + } +}; + +class TextureDecoder_BC2_To_R8G8B8A8 : public TextureDecoder, public SingletonClass +{ +public: + + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 blockSizeX = (std::min)(4, textureLoader->width - x); + sint32 blockSizeY = (std::min)(4, textureLoader->height - y); + // decode 4x4 pixels at once + float rgbaBlock[4 * 4 * 4]; + decodeBC2Block_UNORM(blockData, rgbaBlock); + for (sint32 py = 0; py < blockSizeY; py++) + { + sint32 yc = y + py; + for (sint32 px = 0; px < blockSizeX; px++) + { + sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer + float red = rgbaBlock[(px + py * 4) * 4 + 0]; + float green = rgbaBlock[(px + py * 4) * 4 + 1]; + float blue = rgbaBlock[(px + py * 4) * 4 + 2]; + float alpha = rgbaBlock[(px + py * 4) * 4 + 3]; + *(outputData + pixelOffset + 0) = red * 255; + *(outputData + pixelOffset + 1) = green * 255; + *(outputData + pixelOffset + 2) = blue * 255; + *(outputData + pixelOffset + 3) = alpha * 255; + } + } + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + return; + } +}; + class TextureDecoder_BC2 : public TextureDecoder, public SingletonClass { public: @@ -1893,6 +1987,53 @@ public: } }; +class TextureDecoder_BC3_To_R8G8B8A8 : public TextureDecoder, public SingletonClass +{ +public: + + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 blockSizeX = (std::min)(4, textureLoader->width - x); + sint32 blockSizeY = (std::min)(4, textureLoader->height - y); + // decode 4x4 pixels at once + float rgbaBlock[4 * 4 * 4]; + decodeBC3Block_UNORM(blockData, rgbaBlock); + for (sint32 py = 0; py < blockSizeY; py++) + { + sint32 yc = y + py; + for (sint32 px = 0; px < blockSizeX; px++) + { + sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer + float red = rgbaBlock[(px + py * 4) * 4 + 0]; + float green = rgbaBlock[(px + py * 4) * 4 + 1]; + float blue = rgbaBlock[(px + py * 4) * 4 + 2]; + float alpha = rgbaBlock[(px + py * 4) * 4 + 3]; + *(outputData + pixelOffset + 0) = (uint8)(red * 255); + *(outputData + pixelOffset + 1) = (uint8)(green * 255); + *(outputData + pixelOffset + 2) = (uint8)(blue * 255); + *(outputData + pixelOffset + 3) = (uint8)(alpha * 255); + } + } + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + return; + } +}; + class TextureDecoder_BC3_UNORM_uncompress : public TextureDecoder_BC3_uncompress_generic, public SingletonClass { // reuse TextureDecoder_BC3_uncompress_generic @@ -1991,6 +2132,55 @@ public: } }; + +class TextureDecoder_BC4_To_R8 : public TextureDecoder, public SingletonClass +{ +public: + + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 1; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 blockSizeX = (std::min)(4, textureLoader->width - x); + sint32 blockSizeY = (std::min)(4, textureLoader->height - y); + // decode 4x4 pixels at once + float rBlock[4 * 4 * 1]; + decodeBC4Block_UNORM(blockData, rBlock); + + for (sint32 py = 0; py < blockSizeY; py++) + { + sint32 yc = y + py; + for (sint32 px = 0; px < blockSizeX; px++) + { + sint32 pixelOffset = (x + px + yc * textureLoader->width); // write to target buffer + float red = rBlock[(px + py * 4) * 1 + 0]; + *(outputData + pixelOffset + 0) = (uint8)(red * 255); + } + } + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + float rBlock[4 * 4 * 1]; + decodeBC4Block_UNORM(blockData, rBlock); + float red = rBlock[(blockOffsetX + blockOffsetY * 4) * 1 + 0]; + *(outputPixel + 0) = (uint8)(red * 255.0f); + *(outputPixel + 1) = 0; + *(outputPixel + 2) = 0; + *(outputPixel + 3) = 255; + } +}; + class TextureDecoder_BC4 : public TextureDecoder, public SingletonClass { public: @@ -2026,6 +2216,57 @@ public: *(outputPixel + 3) = 255; } }; +template +class TextureDecoder_BC5_To_R8G8 : public TextureDecoder, public SingletonClass> +{ +public: + + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 2; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 blockSizeX = (std::min)(4, textureLoader->width - x); + sint32 blockSizeY = (std::min)(4, textureLoader->height - y); + // decode 4x4 pixels at once + float rgBlock[4 * 4 * 2]; + fn(blockData, rgBlock); + + for (sint32 py = 0; py < blockSizeY; py++) + { + sint32 yc = y + py; + for (sint32 px = 0; px < blockSizeX; px++) + { + sint32 pixelOffset = (x + px + yc * textureLoader->width) * 2; // write to target buffer + float red = rgBlock[(px + py * 4) * 2 + 0]; + float green = rgBlock[(px + py * 4) * 2 + 1]; + *(outputData + pixelOffset + 0) = (uint8)(red * 255); + *(outputData + pixelOffset + 1) = (uint8)(green * 255); + } + } + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + float rgBlock[4 * 4 * 2]; + decodeBC5Block_UNORM(blockData, rgBlock); + float red = rgBlock[(blockOffsetX + blockOffsetY * 4) * 2 + 0]; + float green = rgBlock[(blockOffsetX + blockOffsetY * 4) * 2 + 1]; + *(outputPixel + 0) = (uint8)(red * 255.0f); + *(outputPixel + 1) = (uint8)(green * 255.0f); + *(outputPixel + 2) = 0; + *(outputPixel + 3) = 255; + } +}; class TextureDecoder_BC5_UNORM_uncompress : public TextureDecoder, public SingletonClass { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 99ba79f1..5e8ad85e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1960,42 +1960,31 @@ void VulkanRenderer::QueryMemoryInfo() void VulkanRenderer::QueryAvailableFormats() { - VkFormatProperties fmtProp{}; - vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_D24_UNORM_S8_UINT, &fmtProp); + // todo - more restrictive check + auto isFormatOptimal = [this](VkFormat format) -> bool { + VkFormatProperties fmtProp{}; + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &fmtProp); + return fmtProp.optimalTilingFeatures != 0; + }; + // BCn + m_supportedFormatInfo.fmt_bc1 = isFormatOptimal(VK_FORMAT_BC1_RGBA_SRGB_BLOCK) && isFormatOptimal(VK_FORMAT_BC1_RGBA_UNORM_BLOCK); + m_supportedFormatInfo.fmt_bc2 = isFormatOptimal(VK_FORMAT_BC2_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC2_SRGB_BLOCK); + m_supportedFormatInfo.fmt_bc3 = isFormatOptimal(VK_FORMAT_BC3_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC3_SRGB_BLOCK); + m_supportedFormatInfo.fmt_bc4 = isFormatOptimal(VK_FORMAT_BC4_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC4_SNORM_BLOCK); + m_supportedFormatInfo.fmt_bc5 = isFormatOptimal(VK_FORMAT_BC5_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC5_SNORM_BLOCK); // D24S8 - if (fmtProp.optimalTilingFeatures != 0) // todo - more restrictive check - { - m_supportedFormatInfo.fmt_d24_unorm_s8_uint = true; - } + m_supportedFormatInfo.fmt_d24_unorm_s8_uint = isFormatOptimal(VK_FORMAT_D24_UNORM_S8_UINT); // R4G4 - fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R4G4_UNORM_PACK8, &fmtProp); - if (fmtProp.optimalTilingFeatures != 0) - { - m_supportedFormatInfo.fmt_r4g4_unorm_pack = true; - } + m_supportedFormatInfo.fmt_r4g4_unorm_pack = isFormatOptimal(VK_FORMAT_R4G4_UNORM_PACK8);; // R5G6B5 - fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R5G6B5_UNORM_PACK16, &fmtProp); - if (fmtProp.optimalTilingFeatures != 0) - { - m_supportedFormatInfo.fmt_r5g6b5_unorm_pack = true; - } + m_supportedFormatInfo.fmt_r5g6b5_unorm_pack = isFormatOptimal(VK_FORMAT_R5G6B5_UNORM_PACK16); // R4G4B4A4 - fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_R4G4B4A4_UNORM_PACK16, &fmtProp); - if (fmtProp.optimalTilingFeatures != 0) - { - m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack = true; - } + m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack = isFormatOptimal(VK_FORMAT_R4G4B4A4_UNORM_PACK16); // A1R5G5B5 - fmtProp = {}; - vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_A1R5G5B5_UNORM_PACK16, &fmtProp); - if (fmtProp.optimalTilingFeatures != 0) - { - m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack = true; - } + m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack = isFormatOptimal(VK_FORMAT_A1R5G5B5_UNORM_PACK16); + // print info about unsupported formats to log + VkFormatProperties fmtProp; for (auto& it : requestedFormatList) { fmtProp = {}; @@ -2698,44 +2687,124 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD break; // compressed formats case Latte::E_GX2SURFFMT::BC1_SRGB: - formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // todo - verify - formatInfoOut->decoder = TextureDecoder_BC1::getInstance(); + if (m_supportedFormatInfo.fmt_bc1) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // todo - verify + formatInfoOut->decoder = TextureDecoder_BC1::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB; + formatInfoOut->decoder = TextureDecoder_BC1_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC1_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // todo - verify - formatInfoOut->decoder = TextureDecoder_BC1::getInstance(); + if (m_supportedFormatInfo.fmt_bc1) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // todo - verify + formatInfoOut->decoder = TextureDecoder_BC1::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = TextureDecoder_BC1_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC2_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC2_UNORM_BLOCK; // todo - verify - formatInfoOut->decoder = TextureDecoder_BC2::getInstance(); + if (m_supportedFormatInfo.fmt_bc2) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC2_UNORM_BLOCK; // todo - verify + formatInfoOut->decoder = TextureDecoder_BC2::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = TextureDecoder_BC2_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC2_SRGB: - formatInfoOut->vkImageFormat = VK_FORMAT_BC2_SRGB_BLOCK; // todo - verify - formatInfoOut->decoder = TextureDecoder_BC2::getInstance(); + if (m_supportedFormatInfo.fmt_bc2) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC2_SRGB_BLOCK; // todo - verify + formatInfoOut->decoder = TextureDecoder_BC2::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB; + formatInfoOut->decoder = TextureDecoder_BC2_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC3_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC3_UNORM_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC3::getInstance(); + if (m_supportedFormatInfo.fmt_bc3) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC3_UNORM_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC3::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = TextureDecoder_BC3_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC3_SRGB: - formatInfoOut->vkImageFormat = VK_FORMAT_BC3_SRGB_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC3::getInstance(); + if (m_supportedFormatInfo.fmt_bc3) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC3_SRGB_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC3::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB; + formatInfoOut->decoder = TextureDecoder_BC3_To_R8G8B8A8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC4_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC4_UNORM_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC4::getInstance(); + if (m_supportedFormatInfo.fmt_bc4) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC4_UNORM_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC4::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8_UNORM; + formatInfoOut->decoder = TextureDecoder_BC4_To_R8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC4_SNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC4_SNORM_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC4::getInstance(); + if (m_supportedFormatInfo.fmt_bc4) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC4_SNORM_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC4::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8_SNORM; + formatInfoOut->decoder = TextureDecoder_BC4_To_R8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC5_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC5_UNORM_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC5::getInstance(); + if (m_supportedFormatInfo.fmt_bc5) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC5_UNORM_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC5::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8_UNORM; + formatInfoOut->decoder = TextureDecoder_BC5_To_R8G8::getInstance(); + } break; case Latte::E_GX2SURFFMT::BC5_SNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_BC5_SNORM_BLOCK; - formatInfoOut->decoder = TextureDecoder_BC5::getInstance(); + if (m_supportedFormatInfo.fmt_bc5) + { + formatInfoOut->vkImageFormat = VK_FORMAT_BC5_SNORM_BLOCK; + formatInfoOut->decoder = TextureDecoder_BC5::getInstance(); + } + else + { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8_SNORM; + formatInfoOut->decoder = TextureDecoder_BC5_To_R8G8::getInstance(); + } break; case Latte::E_GX2SURFFMT::R24_X8_UNORM: formatInfoOut->vkImageFormat = VK_FORMAT_R32_SFLOAT; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 2909440f..464bff32 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -20,6 +20,11 @@ struct VkSupportedFormatInfo_t bool fmt_r5g6b5_unorm_pack{}; bool fmt_r4g4b4a4_unorm_pack{}; bool fmt_a1r5g5b5_unorm_pack{}; + bool fmt_bc1{}; + bool fmt_bc2{}; + bool fmt_bc3{}; + bool fmt_bc4{}; + bool fmt_bc5{}; }; struct VkDescriptorSetInfo