mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Add RG8 and BC1 PC texture formats
This commit is contained in:
@@ -169,7 +169,9 @@ typedef enum {
|
||||
|
||||
#ifdef TARGET_PC
|
||||
GX_TF_R8_PC = 0x1 | _GX_TF_PC,
|
||||
GX_TF_RG8_PC = 0x3 | _GX_TF_PC,
|
||||
GX_TF_RGBA8_PC = 0x6 | _GX_TF_PC,
|
||||
GX_TF_BC1_PC = 0xE | _GX_TF_PC,
|
||||
#endif
|
||||
} GXTexFmt;
|
||||
|
||||
|
||||
@@ -31,6 +31,29 @@ wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) {
|
||||
const uint32_t height = ((size.height + info.blockHeight - 1) / info.blockHeight) * info.blockHeight;
|
||||
return {.width = width, .height = height, .depthOrArrayLayers = size.depthOrArrayLayers};
|
||||
}
|
||||
|
||||
bool setup_swizzle(wgpu::TextureComponentSwizzleDescriptor& swizzle, u32 format) noexcept {
|
||||
if (!webgpu::g_textureComponentSwizzleSupported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case GX_TF_R8_PC:
|
||||
swizzle.swizzle.r = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.g = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.b = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.a = wgpu::ComponentSwizzle::R;
|
||||
return true;
|
||||
case GX_TF_RG8_PC:
|
||||
swizzle.swizzle.r = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.g = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.b = wgpu::ComponentSwizzle::R;
|
||||
swizzle.swizzle.a = wgpu::ComponentSwizzle::G;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TextureFormatInfo format_info(wgpu::TextureFormat format) noexcept {
|
||||
@@ -158,6 +181,10 @@ TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t m
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.mipLevelCount = mips,
|
||||
};
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzle;
|
||||
if (setup_swizzle(swizzle, gxFormat)) {
|
||||
textureViewDescriptor.nextInChain = &swizzle;
|
||||
}
|
||||
auto textureView = texture.CreateView(&textureViewDescriptor);
|
||||
return std::make_shared<TextureRef>(std::move(texture), std::move(textureView), wgpu::TextureView{}, size, wgpuFormat,
|
||||
mips, gxFormat);
|
||||
|
||||
+118
-7
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace aurora::gfx {
|
||||
@@ -192,11 +193,11 @@ static ByteBuffer DecodeTiled(uint32_t width, uint32_t height, uint32_t mips, Ar
|
||||
}
|
||||
|
||||
template <TextureDecoder T>
|
||||
static ByteBuffer DecodeLinear(uint32_t width, ArrayRef<uint8_t> data) {
|
||||
ByteBuffer buf{width * sizeof(typename T::Target)};
|
||||
static ByteBuffer DecodeLinear(uint32_t texelCount, ArrayRef<uint8_t> data) {
|
||||
ByteBuffer buf{texelCount * sizeof(typename T::Target)};
|
||||
auto* target = reinterpret_cast<typename T::Target*>(buf.data());
|
||||
const auto* in = reinterpret_cast<const typename T::Source*>(data.data());
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
for (uint32_t x = 0; x < texelCount; ++x) {
|
||||
T::decode_texel(target, in, x);
|
||||
}
|
||||
return buf;
|
||||
@@ -236,6 +237,25 @@ struct TextureDecoderI8 {
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureDecoderRG8 {
|
||||
struct Source {
|
||||
uint8_t intensity;
|
||||
uint8_t alpha;
|
||||
};
|
||||
using Target = RGBA8;
|
||||
|
||||
static constexpr uint32_t Frac = 1;
|
||||
static constexpr uint32_t BlockWidth = 1;
|
||||
static constexpr uint32_t BlockHeight = 1;
|
||||
|
||||
static void decode_texel(Target* target, const Source* in, const uint32_t x) {
|
||||
target[x].r = in[x].intensity;
|
||||
target[x].g = in[x].intensity;
|
||||
target[x].b = in[x].intensity;
|
||||
target[x].a = in[x].alpha;
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureDecoderIA4 {
|
||||
using Source = uint8_t;
|
||||
using Target = RGBA8;
|
||||
@@ -462,15 +482,106 @@ static ByteBuffer BuildRGBA8FromCMPR(uint32_t width, uint32_t height, uint32_t m
|
||||
return buf;
|
||||
}
|
||||
|
||||
static ByteBuffer BuildRGBA8FromBC1(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||
|
||||
uint32_t h = height;
|
||||
uint32_t w = width;
|
||||
uint8_t* dst = buf.data();
|
||||
const uint8_t* src = data.data();
|
||||
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||
for (uint32_t yy = 0; yy < h; yy += 4) {
|
||||
for (uint32_t xx = 0; xx < w; xx += 4) {
|
||||
const uint16_t color1 = *reinterpret_cast<const uint16_t*>(src);
|
||||
const uint16_t color2 = *reinterpret_cast<const uint16_t*>(src + 2);
|
||||
const uint32_t indices = *reinterpret_cast<const uint32_t*>(src + 4);
|
||||
src += 8;
|
||||
|
||||
std::array<uint8_t, 16> colorTable{};
|
||||
|
||||
colorTable[0] = ExpandTo8<5>(static_cast<uint8_t>((color1 >> 11) & 0x1F));
|
||||
colorTable[1] = ExpandTo8<6>(static_cast<uint8_t>((color1 >> 5) & 0x3F));
|
||||
colorTable[2] = ExpandTo8<5>(static_cast<uint8_t>(color1 & 0x1F));
|
||||
colorTable[3] = 0xFF;
|
||||
|
||||
colorTable[4] = ExpandTo8<5>(static_cast<uint8_t>((color2 >> 11) & 0x1F));
|
||||
colorTable[5] = ExpandTo8<6>(static_cast<uint8_t>((color2 >> 5) & 0x3F));
|
||||
colorTable[6] = ExpandTo8<5>(static_cast<uint8_t>(color2 & 0x1F));
|
||||
colorTable[7] = 0xFF;
|
||||
if (color1 > color2) {
|
||||
colorTable[8] = S3TCBlend(colorTable[4], colorTable[0]);
|
||||
colorTable[9] = S3TCBlend(colorTable[5], colorTable[1]);
|
||||
colorTable[10] = S3TCBlend(colorTable[6], colorTable[2]);
|
||||
colorTable[11] = 0xFF;
|
||||
|
||||
colorTable[12] = S3TCBlend(colorTable[0], colorTable[4]);
|
||||
colorTable[13] = S3TCBlend(colorTable[1], colorTable[5]);
|
||||
colorTable[14] = S3TCBlend(colorTable[2], colorTable[6]);
|
||||
colorTable[15] = 0xFF;
|
||||
} else {
|
||||
colorTable[8] = HalfBlend(colorTable[0], colorTable[4]);
|
||||
colorTable[9] = HalfBlend(colorTable[1], colorTable[5]);
|
||||
colorTable[10] = HalfBlend(colorTable[2], colorTable[6]);
|
||||
colorTable[11] = 0xFF;
|
||||
|
||||
colorTable[12] = 0;
|
||||
colorTable[13] = 0;
|
||||
colorTable[14] = 0;
|
||||
colorTable[15] = 0;
|
||||
}
|
||||
|
||||
for (uint32_t y = 0; y < 4; ++y) {
|
||||
for (uint32_t x = 0; x < 4; ++x) {
|
||||
if (xx + x >= w || yy + y >= h) {
|
||||
continue;
|
||||
}
|
||||
const uint32_t index = (indices >> (2 * (y * 4 + x))) & 3;
|
||||
uint8_t* dstOffs = dst + ((yy + y) * w + (xx + x)) * 4;
|
||||
const uint8_t* colorTableOffs = &colorTable[static_cast<size_t>(index) * 4];
|
||||
memcpy(dstOffs, colorTableOffs, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst += w * h * 4;
|
||||
if (w > 1) {
|
||||
w /= 2;
|
||||
}
|
||||
if (h > 1) {
|
||||
h /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ConvertedTexture convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||
ByteBuffer converted;
|
||||
switch (format) {
|
||||
DEFAULT_FATAL("convert_texture: unknown texture format {}", format);
|
||||
case GX_TF_R8_PC:
|
||||
converted = DecodeLinear<TextureDecoderI8>(width * height, data);
|
||||
break;
|
||||
if (!uses_direct_texture_upload(format)) {
|
||||
converted =
|
||||
DecodeLinear<TextureDecoderI8>(static_cast<uint32_t>(ComputeMippedTexelCount(width, height, mips)), data);
|
||||
break;
|
||||
}
|
||||
return {.format = to_wgpu(format), .width = width, .height = height, .mips = mips};
|
||||
case GX_TF_RG8_PC:
|
||||
if (!uses_direct_texture_upload(format)) {
|
||||
converted =
|
||||
DecodeLinear<TextureDecoderRG8>(static_cast<uint32_t>(ComputeMippedTexelCount(width, height, mips)), data);
|
||||
break;
|
||||
}
|
||||
return {.format = to_wgpu(format), .width = width, .height = height, .mips = mips};
|
||||
case GX_TF_RGBA8_PC:
|
||||
return {}; // No conversion
|
||||
return {.format = to_wgpu(format), .width = width, .height = height, .mips = mips};
|
||||
case GX_TF_BC1_PC:
|
||||
if (uses_direct_texture_upload(format)) {
|
||||
return {.format = to_wgpu(format), .width = width, .height = height, .mips = mips};
|
||||
}
|
||||
converted = BuildRGBA8FromBC1(width, height, mips, data);
|
||||
break;
|
||||
case GX_TF_I4:
|
||||
converted = DecodeTiled<TextureDecoderI4>(width, height, mips, data);
|
||||
break;
|
||||
@@ -506,7 +617,7 @@ ConvertedTexture convert_texture(u32 format, uint32_t width, uint32_t height, ui
|
||||
}
|
||||
const auto wgpuFormat = to_wgpu(format);
|
||||
bool hasArbitraryMips = false;
|
||||
if (wgpuFormat == wgpu::TextureFormat::RGBA8Unorm && mips > 1) {
|
||||
if (!is_pc_texture_format(format) && wgpuFormat == wgpu::TextureFormat::RGBA8Unorm && mips > 1) {
|
||||
hasArbitraryMips = arb_mip_check(width, height, mips, converted);
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -5,12 +5,36 @@
|
||||
#include "../webgpu/gpu.hpp"
|
||||
|
||||
namespace aurora::gfx {
|
||||
static constexpr wgpu::TextureFormat to_wgpu(u32 gxFormat) {
|
||||
inline bool is_pc_texture_format(u32 gxFormat) noexcept {
|
||||
return (gxFormat & _GX_TF_PC) != 0;
|
||||
}
|
||||
|
||||
inline bool uses_direct_texture_upload(u32 gxFormat) noexcept {
|
||||
switch (gxFormat) {
|
||||
case GX_TF_R8_PC:
|
||||
case GX_TF_RG8_PC:
|
||||
return webgpu::g_textureComponentSwizzleSupported;
|
||||
case GX_TF_RGBA8_PC:
|
||||
return true;
|
||||
case GX_TF_BC1_PC:
|
||||
return webgpu::g_bcTexturesSupported;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline wgpu::TextureFormat to_wgpu(u32 gxFormat) noexcept {
|
||||
switch (gxFormat) {
|
||||
case GX_TF_R8_PC:
|
||||
return uses_direct_texture_upload(gxFormat) ? wgpu::TextureFormat::R8Unorm : wgpu::TextureFormat::RGBA8Unorm;
|
||||
case GX_TF_RG8_PC:
|
||||
return uses_direct_texture_upload(gxFormat) ? wgpu::TextureFormat::RG8Unorm : wgpu::TextureFormat::RGBA8Unorm;
|
||||
case GX_TF_C4:
|
||||
case GX_TF_C8:
|
||||
case GX_TF_C14X2:
|
||||
return wgpu::TextureFormat::R16Sint;
|
||||
case GX_TF_BC1_PC:
|
||||
return uses_direct_texture_upload(gxFormat) ? wgpu::TextureFormat::BC1RGBAUnorm : wgpu::TextureFormat::RGBA8Unorm;
|
||||
default:
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
}
|
||||
@@ -25,6 +49,9 @@ struct ConvertedTexture {
|
||||
bool hasArbitraryMips = false;
|
||||
};
|
||||
|
||||
// Returns converted bytes when Aurora must transform the source layout before upload.
|
||||
// Empty data means callers should upload the original bytes directly using to_wgpu(format).
|
||||
// hasArbitraryMips is only meaningful for decoded RGBA8 GC formats; PC formats skip that check.
|
||||
ConvertedTexture convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data);
|
||||
ConvertedTexture convert_texture_palette(u32 textureFormat, uint32_t width, uint32_t height, uint32_t mips,
|
||||
ArrayRef<uint8_t> textureData, GXTlutFmt tlutFormat, uint16_t tlutEntries,
|
||||
|
||||
@@ -197,8 +197,12 @@ uint32_t texture_base_level_size(const GXTexObj_& obj) noexcept {
|
||||
switch (obj.format()) {
|
||||
case GX_TF_R8_PC:
|
||||
return obj.width() * obj.height();
|
||||
case GX_TF_RG8_PC:
|
||||
return obj.width() * obj.height() * 2;
|
||||
case GX_TF_RGBA8_PC:
|
||||
return obj.width() * obj.height() * 4;
|
||||
case GX_TF_BC1_PC:
|
||||
return ((obj.width() + 3) / 4) * ((obj.height() + 3) / 4) * 8;
|
||||
default:
|
||||
return GXGetTexBufferSize(obj.width(), obj.height(), obj.format(), false, 0);
|
||||
}
|
||||
|
||||
@@ -931,8 +931,12 @@ inline std::string format_as(const GXTexFmt& fmt) {
|
||||
return "GX_CTF_Z16L";
|
||||
case GX_TF_R8_PC:
|
||||
return "GX_TF_R8_PC";
|
||||
case GX_TF_RG8_PC:
|
||||
return "GX_TF_RG8_PC";
|
||||
case GX_TF_RGBA8_PC:
|
||||
return "GX_TF_RGBA8_PC";
|
||||
case GX_TF_BC1_PC:
|
||||
return "GX_TF_BC1_PC";
|
||||
default:
|
||||
return fmt::format("GXTexFmt({})", underlying(fmt));
|
||||
}
|
||||
|
||||
+10
-2
@@ -56,6 +56,7 @@ wgpu::Instance g_instance;
|
||||
static wgpu::AdapterInfo g_adapterInfo;
|
||||
static wgpu::SurfaceCapabilities g_surfaceCapabilities;
|
||||
bool g_bcTexturesSupported;
|
||||
bool g_textureComponentSwizzleSupported;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -802,12 +803,19 @@ bool initialize(AuroraBackend auroraBackend, bool allowCpu) {
|
||||
requiredLimits.maxDynamicStorageBuffersPerPipelineLayout, requiredLimits.maxStorageBuffersPerShaderStage,
|
||||
requiredLimits.minUniformBufferOffsetAlignment, requiredLimits.minStorageBufferOffsetAlignment);
|
||||
std::vector<wgpu::FeatureName> requiredFeatures;
|
||||
g_bcTexturesSupported = false;
|
||||
g_textureComponentSwizzleSupported = false;
|
||||
wgpu::SupportedFeatures supportedFeatures;
|
||||
g_adapter.GetFeatures(&supportedFeatures);
|
||||
for (size_t i = 0; i < supportedFeatures.featureCount; ++i) {
|
||||
const auto feature = supportedFeatures.features[i];
|
||||
if (feature == wgpu::FeatureName::TextureCompressionBC) {
|
||||
g_bcTexturesSupported = true;
|
||||
if (feature == wgpu::FeatureName::TextureCompressionBC ||
|
||||
feature == wgpu::FeatureName::TextureComponentSwizzle) {
|
||||
if (feature == wgpu::FeatureName::TextureCompressionBC) {
|
||||
g_bcTexturesSupported = true;
|
||||
} else if (feature == wgpu::FeatureName::TextureComponentSwizzle) {
|
||||
g_textureComponentSwizzleSupported = true;
|
||||
}
|
||||
requiredFeatures.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ extern wgpu::RenderPipeline g_CopyPipeline;
|
||||
extern wgpu::BindGroup g_CopyBindGroup;
|
||||
extern wgpu::Instance g_instance;
|
||||
extern bool g_bcTexturesSupported;
|
||||
extern bool g_textureComponentSwizzleSupported;
|
||||
|
||||
bool initialize(AuroraBackend backend, bool allowCpu);
|
||||
void shutdown();
|
||||
|
||||
@@ -1304,6 +1304,24 @@ TEST_F(GXFifoTest, LoadTexObjPcFormat_PreservesFullFormatMetadata) {
|
||||
EXPECT_EQ(slot.raw_format(), static_cast<u32>(GX_TF_RGBA8));
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, TexImage0BpWrite_ClearsExtendedTextureMetadata) {
|
||||
auto& slot = gxState().loadedTextures[GX_TEXMAP0];
|
||||
slot.mWidth = 1024;
|
||||
slot.mHeight = 1024;
|
||||
slot.mFormat = GX_TF_BC1_PC;
|
||||
|
||||
const u32 image0 = (0x88u << 24) | (7u << 0) | (15u << 10) | (static_cast<u32>(GX_TF_RGBA8) << 20);
|
||||
aurora::gx::fifo::write_u8(0x61);
|
||||
aurora::gx::fifo::write_u32(image0);
|
||||
auto bytes = capture_fifo();
|
||||
|
||||
decode_fifo(bytes);
|
||||
|
||||
EXPECT_EQ(slot.width(), 8u);
|
||||
EXPECT_EQ(slot.height(), 16u);
|
||||
EXPECT_EQ(slot.format(), GX_TF_RGBA8);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, TexObjRawDimensions_WrapAtTenBitBoundary) {
|
||||
auto& slot = gxState().loadedTextures[GX_TEXMAP0];
|
||||
slot.image0 = (0x3FFu << 0) | (0x3FFu << 10);
|
||||
|
||||
Reference in New Issue
Block a user