aurora: Working palette textures

This commit is contained in:
Luke Street 2022-05-14 19:23:47 -04:00
parent ef771e6489
commit ef71c009c6
10 changed files with 221 additions and 98 deletions

View File

@ -748,10 +748,11 @@ enum GXTlutFmt {
GX_MAX_TLUTFMT = 0x3, GX_MAX_TLUTFMT = 0x3,
}; };
namespace aurora::gfx {
struct TextureRef;
} // namespace aurora::gfx
struct GXTlutObj { struct GXTlutObj {
u32 format; std::shared_ptr<aurora::gfx::TextureRef> ref;
u32 addr;
u16 entries;
}; };
enum GXTlut { enum GXTlut {
@ -806,12 +807,9 @@ enum GXCITexFmt {
GX_TF_C14X2 = GX::TF_C14X2, GX_TF_C14X2 = GX::TF_C14X2,
}; };
namespace aurora::gfx {
struct TextureRef;
} // namespace aurora::gfx
struct GXTexObj { struct GXTexObj {
std::shared_ptr<aurora::gfx::TextureRef> ref; std::shared_ptr<aurora::gfx::TextureRef> ref;
void* data; const void* data;
u32 dataSize; u32 dataSize;
u16 width; u16 width;
u16 height; u16 height;
@ -894,12 +892,13 @@ void GXSetTevSwapModeTable(GX::TevSwapSel id, GX::TevColorChan red, GX::TevColor
GX::TevColorChan alpha) noexcept; GX::TevColorChan alpha) noexcept;
void GXSetTevSwapMode(GX::TevStageID stage, GX::TevSwapSel rasSel, GX::TevSwapSel texSel) noexcept; void GXSetTevSwapMode(GX::TevStageID stage, GX::TevSwapSel rasSel, GX::TevSwapSel texSel) noexcept;
void GXSetLineWidth(u8 width, GX::TexOffset texOffset) noexcept; void GXSetLineWidth(u8 width, GX::TexOffset texOffset) noexcept;
void GXInitTlutObj(GXTlutObj* obj, void* data, GXTlutFmt format, u16 entries) noexcept; void GXInitTlutObj(GXTlutObj* obj, const void* data, GXTlutFmt format, u16 entries) noexcept;
void GXLoadTlut(const GXTlutObj* obj, GXTlut idx) noexcept; void GXLoadTlut(const GXTlutObj* obj, GXTlut idx) noexcept;
void GXInitTexObj(GXTexObj* obj, void* data, u16 width, u16 height, GX::TextureFormat format, GXTexWrapMode wrapS, void GXInitTexObj(GXTexObj* obj, const void* data, u16 width, u16 height, GX::TextureFormat format, GXTexWrapMode wrapS,
GXTexWrapMode wrapT, GXBool mipmap) noexcept; GXTexWrapMode wrapT, GXBool mipmap) noexcept;
// Addition for binding render textures // Addition for binding render textures
void GXInitTexObjResolved(GXTexObj* obj, u32 bindIdx, GX::TextureFormat format, GXTexWrapMode wrapS, GXTexWrapMode wrapT); void GXInitTexObjResolved(GXTexObj* obj, u32 bindIdx, GX::TextureFormat format, GXTexWrapMode wrapS,
GXTexWrapMode wrapT);
void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod, float lodBias, void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod, float lodBias,
GXBool biasClamp, GXBool doEdgeLod, GXAnisotropy maxAniso) noexcept; GXBool biasClamp, GXBool doEdgeLod, GXAnisotropy maxAniso) noexcept;
void GXInitTexObjCI(GXTexObj* obj, void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS, void GXInitTexObjCI(GXTexObj* obj, void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS,

View File

@ -155,10 +155,10 @@ void CRasterFont::DrawString(const CDrawStringOptions& opts, int x, int y, int&
if (renderBuf != nullptr) { if (renderBuf != nullptr) {
CGraphicsPalette pal(EPaletteFormat::RGB5A3, 4); CGraphicsPalette pal(EPaletteFormat::RGB5A3, 4);
pal.Lock(); pal.Lock();
*reinterpret_cast<u16*>(pal.GetPaletteData() + 0) = SBIG(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3()); *reinterpret_cast<u16*>(pal.GetPaletteData() + 0) = bswap16(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3());
*reinterpret_cast<u16*>(pal.GetPaletteData() + 2) = SBIG(opts.x4_colors[0].toRGB5A3()); *reinterpret_cast<u16*>(pal.GetPaletteData() + 2) = bswap16(opts.x4_colors[0].toRGB5A3());
*reinterpret_cast<u16*>(pal.GetPaletteData() + 4) = SBIG(opts.x4_colors[1].toRGB5A3()); *reinterpret_cast<u16*>(pal.GetPaletteData() + 4) = bswap16(opts.x4_colors[1].toRGB5A3());
*reinterpret_cast<u16*>(pal.GetPaletteData() + 6) = SBIG(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3()); *reinterpret_cast<u16*>(pal.GetPaletteData() + 6) = bswap16(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3());
pal.UnLock(); pal.UnLock();
renderBuf->AddPaletteChange(pal); renderBuf->AddPaletteChange(pal);
} }

View File

@ -102,11 +102,6 @@ u32 CTextRenderBuffer::GetCurLen() {
} }
void CTextRenderBuffer::Render(const zeus::CColor& color, float time) { void CTextRenderBuffer::Render(const zeus::CColor& color, float time) {
static const GX::VtxDescList skVtxDesc[3]{
{GX::VA_POS, GX::DIRECT},
{GX::VA_TEX0, GX::DIRECT},
{GX::VA_NULL, GX::NONE},
};
x4c_activeFont = -1; x4c_activeFont = -1;
x4d_activePalette = -1; x4d_activePalette = -1;
CMemoryInStream in(x34_bytecode.data(), x44_blobSize); CMemoryInStream in(x34_bytecode.data(), x44_blobSize);
@ -127,8 +122,8 @@ void CTextRenderBuffer::Render(const zeus::CColor& color, float time) {
x4f_queuedPalette = -1; x4f_queuedPalette = -1;
} }
u16 offX = in.ReadShort(); s16 offX = in.ReadShort();
u16 offY = in.ReadShort(); s16 offY = in.ReadShort();
char16_t chr = in.ReadShort(); char16_t chr = in.ReadShort();
zeus::CColor chrColor(static_cast<zeus::Comp32>(in.ReadLong())); zeus::CColor chrColor(static_cast<zeus::Comp32>(in.ReadLong()));
if (x4c_activeFont != -1) { if (x4c_activeFont != -1) {
@ -151,8 +146,8 @@ void CTextRenderBuffer::Render(const zeus::CColor& color, float time) {
} }
} }
} else if (cmd == Command::ImageRender) { } else if (cmd == Command::ImageRender) {
u16 offX = in.ReadShort(); s16 offX = in.ReadShort();
u16 offY = in.ReadShort(); s16 offY = in.ReadShort();
u8 imageIdx = in.ReadChar(); u8 imageIdx = in.ReadChar();
zeus::CColor imageColor(static_cast<zeus::Comp32>(in.ReadLong())); zeus::CColor imageColor(static_cast<zeus::Comp32>(in.ReadLong()));
auto imageDef = x14_images[imageIdx]; auto imageDef = x14_images[imageIdx];
@ -169,7 +164,12 @@ void CTextRenderBuffer::Render(const zeus::CColor& color, float time) {
CGX::SetTevColorIn(GX::TEVSTAGE0, GX::CC_ZERO, GX::CC_TEXC, GX::CC_KONST, GX::CC_ZERO); CGX::SetTevColorIn(GX::TEVSTAGE0, GX::CC_ZERO, GX::CC_TEXC, GX::CC_KONST, GX::CC_ZERO);
CGX::SetTevAlphaIn(GX::TEVSTAGE0, GX::CA_ZERO, GX::CA_TEXA, GX::CA_KONST, GX::CA_ZERO); CGX::SetTevAlphaIn(GX::TEVSTAGE0, GX::CA_ZERO, GX::CA_TEXA, GX::CA_KONST, GX::CA_ZERO);
CGX::SetStandardTevColorAlphaOp(GX::TEVSTAGE0); CGX::SetStandardTevColorAlphaOp(GX::TEVSTAGE0);
CGX::SetVtxDescv(skVtxDesc); constexpr std::array skVtxDesc{
GX::VtxDescList{GX::VA_POS, GX::DIRECT},
GX::VtxDescList{GX::VA_TEX0, GX::DIRECT},
GX::VtxDescList{},
};
CGX::SetVtxDescv(skVtxDesc.data());
CGX::SetNumChans(0); CGX::SetNumChans(0);
CGX::SetNumTexGens(1); CGX::SetNumTexGens(1);
CGX::SetNumTevStages(1); CGX::SetNumTevStages(1);

View File

@ -493,7 +493,7 @@ void GXSetIndTexMtx(GX::IndTexMtxID id, const void* mtx, s8 scaleExp) noexcept {
g_gxState.indTexMtxs[id - 1] = {*static_cast<const aurora::Mat3x2<float>*>(mtx), scaleExp}; g_gxState.indTexMtxs[id - 1] = {*static_cast<const aurora::Mat3x2<float>*>(mtx), scaleExp};
} }
void GXInitTexObj(GXTexObj* obj, void* data, u16 width, u16 height, GX::TextureFormat format, GXTexWrapMode wrapS, void GXInitTexObj(GXTexObj* obj, const void* data, u16 width, u16 height, GX::TextureFormat format, GXTexWrapMode wrapS,
GXTexWrapMode wrapT, GXBool mipmap) noexcept { GXTexWrapMode wrapT, GXBool mipmap) noexcept {
obj->data = data; obj->data = data;
obj->width = width; obj->width = width;
@ -550,7 +550,24 @@ void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter minFilt, GXTexFilter magFilt, fl
} }
void GXInitTexObjCI(GXTexObj* obj, void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS, void GXInitTexObjCI(GXTexObj* obj, void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS,
GXTexWrapMode wrapT, GXBool mipmap, u32 tlut) noexcept { GXTexWrapMode wrapT, GXBool mipmap, u32 tlut) noexcept {
// TODO obj->data = data;
obj->width = width;
obj->height = height;
obj->fmt = static_cast<GX::TextureFormat>(format);
obj->wrapS = wrapS;
obj->wrapT = wrapT;
obj->hasMips = mipmap;
obj->tlut = static_cast<GXTlut>(tlut);
// TODO default values?
obj->minFilter = GX_LINEAR;
obj->magFilter = GX_LINEAR;
obj->minLod = 0.f;
obj->maxLod = 0.f;
obj->lodBias = 0.f;
obj->biasClamp = false;
obj->doEdgeLod = false;
obj->maxAniso = GX_ANISO_4;
obj->dataInvalidated = true;
} }
void GXInitTexObjData(GXTexObj* obj, void* data) noexcept { void GXInitTexObjData(GXTexObj* obj, void* data) noexcept {
obj->data = data; obj->data = data;
@ -573,12 +590,27 @@ void GXLoadTexObj(GXTexObj* obj, GX::TexMapID id) noexcept {
g_gxState.textures[id] = {*obj}; g_gxState.textures[id] = {*obj};
} }
void GXInitTlutObj(GXTlutObj* obj, void* data, GXTlutFmt format, u16 entries) noexcept { void GXInitTlutObj(GXTlutObj* obj, const void* data, GXTlutFmt format, u16 entries) noexcept {
// TODO GX::TextureFormat texFmt;
} switch (format) {
void GXLoadTlut(const GXTlutObj* obj, GXTlut idx) noexcept { case GX_TL_IA8:
// TODO texFmt = GX::TF_IA8;
break;
case GX_TL_RGB565:
texFmt = GX::TF_RGB565;
break;
case GX_TL_RGB5A3:
texFmt = GX::TF_RGB5A3;
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("invalid tlut format {}"), format);
unreachable();
}
obj->ref = aurora::gfx::new_static_texture_2d(
entries, 1, 1, texFmt, aurora::ArrayRef{static_cast<const u8*>(data), static_cast<size_t>(entries) * 2},
"GXInitTlutObj");
} }
void GXLoadTlut(const GXTlutObj* obj, GXTlut idx) noexcept { g_gxState.tluts[idx] = *obj; }
namespace aurora::gfx { namespace aurora::gfx {
static logvisor::Module Log("aurora::gfx::gx"); static logvisor::Module Log("aurora::gfx::gx");
@ -869,7 +901,7 @@ void populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) n
} else { } else {
// Loaded texture object, no conversion necessary // Loaded texture object, no conversion necessary
copyFmt = InvalidTextureFormat; copyFmt = InvalidTextureFormat;
bindFmt = InvalidTextureFormat; bindFmt = bind.texObj.fmt;
} }
config.shaderConfig.textureConfig[i] = {copyFmt, bindFmt, flipUV}; config.shaderConfig.textureConfig[i] = {copyFmt, bindFmt, flipUV};
} }
@ -1005,7 +1037,6 @@ static absl::flat_hash_map<u32, std::pair<wgpu::BindGroupLayout, wgpu::BindGroup
GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& config, GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& config,
const BindGroupRanges& ranges) noexcept { const BindGroupRanges& ranges) noexcept {
const auto layouts = build_bind_group_layouts(info, config); const auto layouts = build_bind_group_layouts(info, config);
u32 textureCount = info.sampledTextures.count();
const std::array uniformEntries{ const std::array uniformEntries{
wgpu::BindGroupEntry{ wgpu::BindGroupEntry{
@ -1039,7 +1070,9 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
}, },
}; };
std::array<wgpu::BindGroupEntry, MaxTextures> samplerEntries; std::array<wgpu::BindGroupEntry, MaxTextures> samplerEntries;
std::array<wgpu::BindGroupEntry, MaxTextures> textureEntries; std::array<wgpu::BindGroupEntry, MaxTextures * 2> textureEntries;
u32 samplerCount = 0;
u32 textureCount = 0;
for (u32 texIdx = 0, i = 0; texIdx < info.sampledTextures.size(); ++texIdx) { for (u32 texIdx = 0, i = 0; texIdx < info.sampledTextures.size(); ++texIdx) {
if (!info.sampledTextures.test(texIdx)) { if (!info.sampledTextures.test(texIdx)) {
continue; continue;
@ -1053,10 +1086,31 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
.binding = i, .binding = i,
.sampler = sampler_ref(tex.get_descriptor()), .sampler = sampler_ref(tex.get_descriptor()),
}; };
++samplerCount;
textureEntries[i] = { textureEntries[i] = {
.binding = i, .binding = i,
.textureView = tex.texObj.ref->view, .textureView = tex.texObj.ref->view,
}; };
// Load palette
const auto& texConfig = config.textureConfig[i];
if (texConfig.loadFmt == GX::TF_C4 || texConfig.loadFmt == GX::TF_C8 || texConfig.loadFmt == GX::TF_C14X2) {
++i;
u32 tlut = tex.texObj.tlut;
if (tlut < GX_TLUT0 || tlut > GX_TLUT7) {
Log.report(logvisor::Fatal, FMT_STRING("tlut out of bounds {}"), tlut);
unreachable();
} else if (!g_gxState.tluts[tlut].ref) {
Log.report(logvisor::Fatal, FMT_STRING("tlut unbound {}"), tlut);
unreachable();
}
textureEntries[i] = {
.binding = i,
.textureView = g_gxState.tluts[tlut].ref->view,
};
textureCount += 2;
} else {
++textureCount;
}
i++; i++;
} }
return { return {
@ -1069,7 +1123,7 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
.samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "GX Sampler Bind Group", .label = "GX Sampler Bind Group",
.layout = layouts.samplerLayout, .layout = layouts.samplerLayout,
.entryCount = textureCount, .entryCount = samplerCount,
.entries = samplerEntries.data(), .entries = samplerEntries.data(),
}), }),
.textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
@ -1146,34 +1200,62 @@ GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const Shader
} }
u32 textureCount = info.sampledTextures.count(); u32 textureCount = info.sampledTextures.count();
const auto textureIt = sTextureBindGroupLayouts.find(textureCount); // const auto textureIt = sTextureBindGroupLayouts.find(textureCount);
if (textureIt != sTextureBindGroupLayouts.end()) { // if (textureIt != sTextureBindGroupLayouts.end()) {
const auto& [sl, tl] = textureIt->second; // const auto& [sl, tl] = textureIt->second;
out.samplerLayout = sl; // out.samplerLayout = sl;
out.textureLayout = tl; // out.textureLayout = tl;
} else { // } else {
u32 numSamplers = 0;
u32 numTextures = 0;
std::array<wgpu::BindGroupLayoutEntry, MaxTextures> samplerEntries; std::array<wgpu::BindGroupLayoutEntry, MaxTextures> samplerEntries;
std::array<wgpu::BindGroupLayoutEntry, MaxTextures> textureEntries; std::array<wgpu::BindGroupLayoutEntry, MaxTextures * 2> textureEntries;
for (u32 i = 0; i < textureCount; ++i) { for (u32 i = 0, t = 0; i < textureCount; ++i, ++t) {
samplerEntries[i] = { samplerEntries[i] = {
.binding = i, .binding = i,
.visibility = wgpu::ShaderStage::Fragment, .visibility = wgpu::ShaderStage::Fragment,
.sampler = {.type = wgpu::SamplerBindingType::Filtering}, .sampler = {.type = wgpu::SamplerBindingType::Filtering},
}; };
textureEntries[i] = { ++numSamplers;
.binding = i, const auto& texConfig = config.textureConfig[i];
.visibility = wgpu::ShaderStage::Fragment, if (texConfig.loadFmt == GX::TF_C4 || texConfig.loadFmt == GX::TF_C8 || texConfig.loadFmt == GX::TF_C14X2) {
.texture = textureEntries[t] = {
{ .binding = t,
.sampleType = wgpu::TextureSampleType::Float, .visibility = wgpu::ShaderStage::Fragment,
.viewDimension = wgpu::TextureViewDimension::e2D, .texture =
}, {
}; .sampleType = wgpu::TextureSampleType::Sint,
.viewDimension = wgpu::TextureViewDimension::e2D,
},
};
++t;
textureEntries[t] = {
.binding = t,
.visibility = wgpu::ShaderStage::Fragment,
.texture =
{
.sampleType = wgpu::TextureSampleType::Float,
.viewDimension = wgpu::TextureViewDimension::e2D,
},
};
numTextures += 2;
} else {
textureEntries[t] = {
.binding = t,
.visibility = wgpu::ShaderStage::Fragment,
.texture =
{
.sampleType = wgpu::TextureSampleType::Float,
.viewDimension = wgpu::TextureViewDimension::e2D,
},
};
++numTextures;
}
} }
{ {
const wgpu::BindGroupLayoutDescriptor descriptor{ const wgpu::BindGroupLayoutDescriptor descriptor{
.label = "GX Sampler Bind Group", .label = "GX Sampler Bind Group",
.entryCount = textureCount, .entryCount = numSamplers,
.entries = samplerEntries.data(), .entries = samplerEntries.data(),
}; };
out.samplerLayout = g_device.CreateBindGroupLayout(&descriptor); out.samplerLayout = g_device.CreateBindGroupLayout(&descriptor);
@ -1181,13 +1263,13 @@ GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const Shader
{ {
const wgpu::BindGroupLayoutDescriptor descriptor{ const wgpu::BindGroupLayoutDescriptor descriptor{
.label = "GX Texture Bind Group", .label = "GX Texture Bind Group",
.entryCount = textureCount, .entryCount = numTextures,
.entries = textureEntries.data(), .entries = textureEntries.data(),
}; };
out.textureLayout = g_device.CreateBindGroupLayout(&descriptor); out.textureLayout = g_device.CreateBindGroupLayout(&descriptor);
} }
sTextureBindGroupLayouts.try_emplace(textureCount, out.samplerLayout, out.textureLayout); // sTextureBindGroupLayouts.try_emplace(textureCount, out.samplerLayout, out.textureLayout);
} // }
return out; return out;
} }

View File

@ -173,6 +173,7 @@ struct GXState {
std::array<Light, GX::MaxLights> lights; std::array<Light, GX::MaxLights> lights;
std::array<TevStage, MaxTevStages> tevStages; std::array<TevStage, MaxTevStages> tevStages;
std::array<TextureBind, MaxTextures> textures; std::array<TextureBind, MaxTextures> textures;
std::array<GXTlutObj, MaxTextures> tluts;
std::array<TexMtxVariant, MaxTexMtx> texMtxs; std::array<TexMtxVariant, MaxTexMtx> texMtxs;
std::array<Mat4x4<float>, MaxPTTexMtx> ptTexMtxs; std::array<Mat4x4<float>, MaxPTTexMtx> ptTexMtxs;
std::array<TcgConfig, MaxTexCoord> tcgs; std::array<TcgConfig, MaxTexCoord> tcgs;

View File

@ -562,7 +562,7 @@ static inline std::string vtx_attr(const ShaderConfig& config, GX::Attr attr) {
unreachable(); unreachable();
} }
static inline std::string texture_conversion(const TextureConfig& tex, u32 stageIdx) { static inline std::string texture_conversion(const TextureConfig& tex, u32 stageIdx, u32 texMapId) {
std::string out; std::string out;
switch (tex.copyFmt) { switch (tex.copyFmt) {
default: default:
@ -572,15 +572,25 @@ static inline std::string texture_conversion(const TextureConfig& tex, u32 stage
out += fmt::format(FMT_STRING("\n sampled{0}.a = 1.0;"), stageIdx); out += fmt::format(FMT_STRING("\n sampled{0}.a = 1.0;"), stageIdx);
break; break;
case GX::TF_I4: case GX::TF_I4:
case GX::TF_I8:
// Perform intensity conversion // Perform intensity conversion
out += fmt::format( out += fmt::format(
FMT_STRING("\n {{" FMT_STRING("\n {{"
"\n var intensity = dot(sampled{0}.rgb, vec3(0.257, 0.504, 0.098)) + 16.0 / 255.0;" "\n var intensity = dot(sampled{0}.rgb, vec3(0.257, 0.504, 0.098)) + 16.0 / 255.0;"
"\n sampled{0} = vec4<f32>(intensity);" "\n sampled{0} = vec4<f32>(intensity, 0.f, 0.f, 1.f);"
"\n }}"), "\n }}"),
stageIdx); stageIdx);
break; break;
} }
switch (tex.loadFmt) {
default:
break;
case GX::TF_I4:
case GX::TF_I8:
// Splat R to RGBA
out += fmt::format(FMT_STRING("\n sampled{0} = vec4<f32>(sampled{0}.r);"), stageIdx);
break;
}
return out; return out;
} }
@ -1058,10 +1068,16 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
// } else { // } else {
uvIn = fmt::format(FMT_STRING("in.tex{0}_uv"), stage.texCoordId); uvIn = fmt::format(FMT_STRING("in.tex{0}_uv"), stage.texCoordId);
// } // }
fragmentFnPre += if (texConfig.loadFmt == GX::TF_C4 || texConfig.loadFmt == GX::TF_C8 || texConfig.loadFmt == GX::TF_C14X2) {
fmt::format(FMT_STRING("\n var sampled{0} = textureSampleBias(tex{1}, tex{1}_samp, {2}, ubuf.tex{1}_lod);"), fragmentFnPre +=
i, stage.texMapId, uvIn); fmt::format(FMT_STRING("\n var sampled{0} = textureSamplePalette(tex{1}, tex{1}_samp, {2}, tlut{1});"),
fragmentFnPre += texture_conversion(texConfig, i); i, stage.texMapId, uvIn);
} else {
fragmentFnPre += fmt::format(
FMT_STRING("\n var sampled{0} = textureSampleBias(tex{1}, tex{1}_samp, {2}, ubuf.tex{1}_lod);"), i,
stage.texMapId, uvIn);
}
fragmentFnPre += texture_conversion(texConfig, i, stage.texMapId);
} }
for (int i = 0; i < info.usesTexMtx.size(); ++i) { for (int i = 0; i < info.usesTexMtx.size(); ++i) {
if (info.usesTexMtx.test(i)) { if (info.usesTexMtx.test(i)) {
@ -1135,9 +1151,21 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in
sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({})\n" sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({})\n"
"var tex{}_samp: sampler;"), "var tex{}_samp: sampler;"),
texBindIdx, i); texBindIdx, i);
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({})\n"
"var tex{}: texture_2d<f32>;"), const auto& texConfig = config.textureConfig[i];
texBindIdx, i); if (texConfig.loadFmt == GX::TF_C4 || texConfig.loadFmt == GX::TF_C8 || texConfig.loadFmt == GX::TF_C14X2) {
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({})\n"
"var tex{}: texture_2d<i32>;"),
texBindIdx, i);
++texBindIdx;
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({})\n"
"var tlut{}: texture_2d<f32>;"),
texBindIdx, i);
} else {
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({})\n"
"var tex{}: texture_2d<f32>;"),
texBindIdx, i);
}
++texBindIdx; ++texBindIdx;
} }
@ -1184,6 +1212,19 @@ struct VertexOutput {{
@builtin(position) pos: vec4<f32>,{vtxOutAttrs} @builtin(position) pos: vec4<f32>,{vtxOutAttrs}
}}; }};
fn textureSamplePalette(tex: texture_2d<i32>, samp: sampler, uv: vec2<f32>, tlut: texture_2d<f32>) -> vec4<f32> {{
var f = fract(uv * vec2<f32>(textureDimensions(tex)) + 0.5);
var i = textureGather(0, tex, samp, uv);
var sX = textureLoad(tlut, vec2<i32>(i.x, 0), 0);
var sY = textureLoad(tlut, vec2<i32>(i.y, 0), 0);
var sZ = textureLoad(tlut, vec2<i32>(i.z, 0), 0);
var sW = textureLoad(tlut, vec2<i32>(i.w, 0), 0);
// Bilinear filtering
var tA = mix(sW, sZ, f.x);
var tB = mix(sX, sY, f.x);
return mix(tA, tB, f.y);
}}
@stage(vertex) @stage(vertex)
fn vs_main({vtxInAttrs} fn vs_main({vtxInAttrs}
) -> VertexOutput {{ ) -> VertexOutput {{

View File

@ -22,6 +22,8 @@ static TextureFormatInfo format_info(wgpu::TextureFormat format) {
switch (format) { switch (format) {
case wgpu::TextureFormat::R8Unorm: case wgpu::TextureFormat::R8Unorm:
return {1, 1, 1, false}; return {1, 1, 1, false};
case wgpu::TextureFormat::R16Sint:
return {1, 1, 2, false};
case wgpu::TextureFormat::RGBA8Unorm: case wgpu::TextureFormat::RGBA8Unorm:
case wgpu::TextureFormat::R32Float: case wgpu::TextureFormat::R32Float:
return {1, 1, 4, false}; return {1, 1, 4, false};

View File

@ -87,11 +87,11 @@ constexpr T bswap16(T val) noexcept {
static ByteBuffer BuildI4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) { static ByteBuffer BuildI4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
const size_t texelCount = ComputeMippedTexelCount(width, height, mips); const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
ByteBuffer buf{sizeof(RGBA8) * texelCount}; ByteBuffer buf{texelCount};
uint32_t w = width; uint32_t w = width;
uint32_t h = height; uint32_t h = height;
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data()); u8* targetMip = buf.data();
const uint8_t* in = data.data(); const uint8_t* in = data.data();
for (uint32_t mip = 0; mip < mips; ++mip) { for (uint32_t mip = 0; mip < mips; ++mip) {
const uint32_t bwidth = (w + 7) / 8; const uint32_t bwidth = (w + 7) / 8;
@ -101,12 +101,9 @@ static ByteBuffer BuildI4FromGCN(uint32_t width, uint32_t height, uint32_t mips,
for (uint32_t bx = 0; bx < bwidth; ++bx) { for (uint32_t bx = 0; bx < bwidth; ++bx) {
const uint32_t baseX = bx * 8; const uint32_t baseX = bx * 8;
for (uint32_t y = 0; y < std::min(h, 8u); ++y) { for (uint32_t y = 0; y < std::min(h, 8u); ++y) {
RGBA8* target = targetMip + (baseY + y) * w + baseX; u8* target = targetMip + (baseY + y) * w + baseX;
for (uint32_t x = 0; x < std::min(w, 8u); ++x) { for (uint32_t x = 0; x < std::min(w, 8u); ++x) {
target[x].r = Convert4To8(in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf); target[x] = Convert4To8(in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf);
target[x].g = target[x].r;
target[x].b = target[x].r;
target[x].a = target[x].r;
} }
in += std::min<size_t>(w / 4, 4); in += std::min<size_t>(w / 4, 4);
} }
@ -247,13 +244,13 @@ ByteBuffer BuildIA8FromGCN(uint32_t width, uint32_t height, uint32_t mips, Array
return buf; return buf;
} }
ByteBuffer BuildC4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data, RGBA8* palette) { ByteBuffer BuildC4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
const size_t texelCount = ComputeMippedTexelCount(width, height, mips); const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
ByteBuffer buf{sizeof(RGBA8) * texelCount}; ByteBuffer buf{texelCount * 2};
uint32_t w = width; uint32_t w = width;
uint32_t h = height; uint32_t h = height;
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data()); u16* targetMip = reinterpret_cast<u16*>(buf.data());
const uint8_t* in = data.data(); const uint8_t* in = data.data();
for (uint32_t mip = 0; mip < mips; ++mip) { for (uint32_t mip = 0; mip < mips; ++mip) {
const uint32_t bwidth = (w + 7) / 8; const uint32_t bwidth = (w + 7) / 8;
@ -262,13 +259,13 @@ ByteBuffer BuildC4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayR
const uint32_t baseY = by * 8; const uint32_t baseY = by * 8;
for (uint32_t bx = 0; bx < bwidth; ++bx) { for (uint32_t bx = 0; bx < bwidth; ++bx) {
const uint32_t baseX = bx * 8; const uint32_t baseX = bx * 8;
for (uint32_t y = 0; y < 8; ++y) { for (uint32_t y = 0; y < std::min(8u, h); ++y) {
RGBA8* target = targetMip + (baseY + y) * w + baseX; u16* target = targetMip + (baseY + y) * w + baseX;
const auto n = std::min(w, 8u); const auto n = std::min(w, 8u);
for (size_t x = 0; x < n; ++x) { for (size_t x = 0; x < n; ++x) {
target[x] = palette[in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf]; target[x] = in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf;
} }
in += n; in += n / 2;
} }
} }
} }
@ -284,13 +281,13 @@ ByteBuffer BuildC4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayR
return buf; return buf;
} }
ByteBuffer BuildC8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data, RGBA8* palette) { ByteBuffer BuildC8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
const size_t texelCount = ComputeMippedTexelCount(width, height, mips); const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
ByteBuffer buf{sizeof(RGBA8) * texelCount}; ByteBuffer buf{texelCount * 2};
uint32_t w = width; uint32_t w = width;
uint32_t h = height; uint32_t h = height;
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data()); u16* targetMip = reinterpret_cast<u16*>(buf.data());
const uint8_t* in = data.data(); const uint8_t* in = data.data();
for (uint32_t mip = 0; mip < mips; ++mip) { for (uint32_t mip = 0; mip < mips; ++mip) {
const uint32_t bwidth = (w + 7) / 8; const uint32_t bwidth = (w + 7) / 8;
@ -300,10 +297,10 @@ ByteBuffer BuildC8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayR
for (uint32_t bx = 0; bx < bwidth; ++bx) { for (uint32_t bx = 0; bx < bwidth; ++bx) {
const uint32_t baseX = bx * 8; const uint32_t baseX = bx * 8;
for (uint32_t y = 0; y < 4; ++y) { for (uint32_t y = 0; y < 4; ++y) {
RGBA8* target = targetMip + (baseY + y) * w + baseX; u16* target = targetMip + (baseY + y) * w + baseX;
const auto n = std::min(w, 8u); const auto n = std::min(w, 8u);
for (size_t x = 0; x < n; ++x) { for (size_t x = 0; x < n; ++x) {
target[x] = palette[in[x]]; target[x] = in[x];
} }
in += n; in += n;
} }
@ -336,9 +333,9 @@ ByteBuffer BuildRGB565FromGCN(uint32_t width, uint32_t height, uint32_t mips, Ar
const uint32_t baseY = by * 4; const uint32_t baseY = by * 4;
for (uint32_t bx = 0; bx < bwidth; ++bx) { for (uint32_t bx = 0; bx < bwidth; ++bx) {
const uint32_t baseX = bx * 4; const uint32_t baseX = bx * 4;
for (uint32_t y = 0; y < 4; ++y) { for (uint32_t y = 0; y < std::min(4u, h); ++y) {
RGBA8* target = targetMip + (baseY + y) * w + baseX; RGBA8* target = targetMip + (baseY + y) * w + baseX;
for (size_t x = 0; x < 4; ++x) { for (size_t x = 0; x < std::min(4u, w); ++x) {
const auto texel = bswap16(in[x]); const auto texel = bswap16(in[x]);
target[x].r = Convert5To8(texel >> 11 & 0x1f); target[x].r = Convert5To8(texel >> 11 & 0x1f);
target[x].g = Convert6To8(texel >> 5 & 0x3f); target[x].g = Convert6To8(texel >> 5 & 0x3f);
@ -376,9 +373,9 @@ ByteBuffer BuildRGB5A3FromGCN(uint32_t width, uint32_t height, uint32_t mips, Ar
const uint32_t baseY = by * 4; const uint32_t baseY = by * 4;
for (uint32_t bx = 0; bx < bwidth; ++bx) { for (uint32_t bx = 0; bx < bwidth; ++bx) {
const uint32_t baseX = bx * 4; const uint32_t baseX = bx * 4;
for (uint32_t y = 0; y < 4; ++y) { for (uint32_t y = 0; y < std::min(4u, h); ++y) {
RGBA8* target = targetMip + (baseY + y) * w + baseX; RGBA8* target = targetMip + (baseY + y) * w + baseX;
for (size_t x = 0; x < 4; ++x) { for (size_t x = 0; x < std::min(4u, w); ++x) {
const auto texel = bswap16(in[x]); const auto texel = bswap16(in[x]);
if ((texel & 0x8000) != 0) { if ((texel & 0x8000) != 0) {
target[x].r = Convert5To8(texel >> 10 & 0x1f); target[x].r = Convert5To8(texel >> 10 & 0x1f);
@ -599,13 +596,9 @@ ByteBuffer convert_texture(GX::TextureFormat format, uint32_t width, uint32_t he
case GX::TF_IA8: case GX::TF_IA8:
return BuildIA8FromGCN(width, height, mips, data); return BuildIA8FromGCN(width, height, mips, data);
case GX::TF_C4: case GX::TF_C4:
Log.report(logvisor::Fatal, FMT_STRING("convert_texture: C4 unimplemented")); return BuildC4FromGCN(width, height, mips, data);
unreachable();
// return BuildC4FromGCN(width, height, mips, data);
case GX::TF_C8: case GX::TF_C8:
Log.report(logvisor::Fatal, FMT_STRING("convert_texture: C8 unimplemented")); return BuildC8FromGCN(width, height, mips, data);
unreachable();
// return BuildC8FromGCN(width, height, mips, data);
case GX::TF_C14X2: case GX::TF_C14X2:
Log.report(logvisor::Fatal, FMT_STRING("convert_texture: C14X2 unimplemented")); Log.report(logvisor::Fatal, FMT_STRING("convert_texture: C14X2 unimplemented"));
unreachable(); unreachable();

View File

@ -7,9 +7,13 @@
namespace aurora::gfx { namespace aurora::gfx {
static wgpu::TextureFormat to_wgpu(GX::TextureFormat format) { static wgpu::TextureFormat to_wgpu(GX::TextureFormat format) {
switch (format) { switch (format) {
case GX::TF_C8: case GX::TF_I4:
case GX::TF_I8: case GX::TF_I8:
return wgpu::TextureFormat::R8Unorm; return wgpu::TextureFormat::R8Unorm;
case GX::TF_C4:
case GX::TF_C8:
case GX::TF_C14X2:
return wgpu::TextureFormat::R16Sint;
case GX::TF_CMPR: case GX::TF_CMPR:
if (gpu::g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) { if (gpu::g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
return wgpu::TextureFormat::BC1RGBAUnorm; return wgpu::TextureFormat::BC1RGBAUnorm;

View File

@ -285,10 +285,11 @@ void initialize(SDL_Window* window) {
"use_dxc", "use_dxc",
#endif #endif
#ifdef NDEBUG #ifdef NDEBUG
"skip_validation", "disable_robustness", "skip_validation",
#else "disable_robustness",
"use_user_defined_labels_in_backend",
#endif #endif
"use_user_defined_labels_in_backend",
"disable_symbol_renaming",
/* clang-format on */ /* clang-format on */
}; };
wgpu::DawnTogglesDeviceDescriptor togglesDescriptor{}; wgpu::DawnTogglesDeviceDescriptor togglesDescriptor{};