aurora: Rework indexed attributes support

This commit is contained in:
Luke Street 2022-03-26 20:30:29 -04:00
parent a0d5c5c285
commit 6e7d123389
6 changed files with 235 additions and 195 deletions

View File

@ -619,7 +619,6 @@ static inline Range push(ByteBuffer& target, const uint8_t* data, size_t length,
} }
auto begin = target.size(); auto begin = target.size();
if (length == 0) { if (length == 0) {
// TODO shared zero buf?
length = alignment; length = alignment;
target.append_zeroes(alignment); target.append_zeroes(alignment);
} else { } else {
@ -642,8 +641,8 @@ static inline Range map(ByteBuffer& target, size_t length, size_t alignment) {
target.append_zeroes(length + padding); target.append_zeroes(length + padding);
return {static_cast<uint32_t>(begin), static_cast<uint32_t>(length + padding)}; return {static_cast<uint32_t>(begin), static_cast<uint32_t>(length + padding)};
} }
Range push_verts(const uint8_t* data, size_t length) { return push(g_verts, data, length, 0 /* TODO? */); } Range push_verts(const uint8_t* data, size_t length) { return push(g_verts, data, length, 4); }
Range push_indices(const uint8_t* data, size_t length) { return push(g_indices, data, length, 0 /* TODO? */); } Range push_indices(const uint8_t* data, size_t length) { return push(g_indices, data, length, 4); }
Range push_uniform(const uint8_t* data, size_t length) { Range push_uniform(const uint8_t* data, size_t length) {
wgpu::SupportedLimits limits; wgpu::SupportedLimits limits;
g_device.GetLimits(&limits); g_device.GetLimits(&limits);
@ -662,11 +661,11 @@ Range push_static_storage(const uint8_t* data, size_t length) {
return range; return range;
} }
std::pair<ByteBuffer, Range> map_verts(size_t length) { std::pair<ByteBuffer, Range> map_verts(size_t length) {
const auto range = map(g_verts, length, 0 /* TODO? */); const auto range = map(g_verts, length, 4);
return {ByteBuffer{g_verts.data() + range.offset, range.size}, range}; return {ByteBuffer{g_verts.data() + range.offset, range.size}, range};
} }
std::pair<ByteBuffer, Range> map_indices(size_t length) { std::pair<ByteBuffer, Range> map_indices(size_t length) {
const auto range = map(g_indices, length, 0 /* TODO? */); const auto range = map(g_indices, length, 4);
return {ByteBuffer{g_indices.data() + range.offset, range.size}, range}; return {ByteBuffer{g_indices.data() + range.offset, range.size}, range};
} }
std::pair<ByteBuffer, Range> map_uniform(size_t length) { std::pair<ByteBuffer, Range> map_uniform(size_t length) {

View File

@ -489,10 +489,9 @@ ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primit
config.shaderConfig.tcgs[i] = g_gxState.tcgs[i]; config.shaderConfig.tcgs[i] = g_gxState.tcgs[i];
} }
config.shaderConfig.alphaCompare = g_gxState.alphaCompare; config.shaderConfig.alphaCompare = g_gxState.alphaCompare;
if (std::any_of(config.shaderConfig.vtxAttrs.begin(), config.shaderConfig.vtxAttrs.end(), config.shaderConfig.indexedAttributeCount =
[](const auto type) { return type == GX::INDEX8 || type == GX::INDEX16; })) { std::count_if(config.shaderConfig.vtxAttrs.begin(), config.shaderConfig.vtxAttrs.end(),
config.shaderConfig.hasIndexedAttributes = true; [](const auto type) { return type == GX::INDEX8 || type == GX::INDEX16; });
}
config = { config = {
.shaderConfig = config.shaderConfig, .shaderConfig = config.shaderConfig,
.primitive = primitive, .primitive = primitive,
@ -658,17 +657,17 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
.buffer = g_storageBuffer, .buffer = g_storageBuffer,
.size = ranges.nrmDataRange.size, .size = ranges.nrmDataRange.size,
}, },
// UVs // Packed UVs
wgpu::BindGroupEntry{ wgpu::BindGroupEntry{
.binding = 3, .binding = 3,
.buffer = g_storageBuffer, .buffer = g_storageBuffer,
.size = ranges.tcDataRange.size, .size = ranges.packedTcDataRange.size,
}, },
// Packed UVs // UVs
wgpu::BindGroupEntry{ wgpu::BindGroupEntry{
.binding = 4, .binding = 4,
.buffer = g_storageBuffer, .buffer = g_storageBuffer,
.size = ranges.packedTcDataRange.size, .size = ranges.tcDataRange.size,
}, },
}; };
std::array<wgpu::BindGroupEntry, MaxTextures> samplerEntries; std::array<wgpu::BindGroupEntry, MaxTextures> samplerEntries;
@ -696,7 +695,7 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
.uniformBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .uniformBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "GX Uniform Bind Group", .label = "GX Uniform Bind Group",
.layout = layouts.uniformLayout, .layout = layouts.uniformLayout,
.entryCount = static_cast<uint32_t>(config.hasIndexedAttributes ? uniformEntries.size() : 1), .entryCount = static_cast<uint32_t>(config.indexedAttributeCount > 0 ? uniformEntries.size() : 1),
.entries = uniformEntries.data(), .entries = uniformEntries.data(),
}), }),
.samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
@ -716,7 +715,7 @@ GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& confi
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept { GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept {
GXBindGroupLayouts out; GXBindGroupLayouts out;
u32 uniformSizeKey = info.uniformSize + (config.hasIndexedAttributes ? 1 : 0); u32 uniformSizeKey = info.uniformSize + (config.indexedAttributeCount > 0 ? 1 : 0);
const auto uniformIt = sUniformBindGroupLayouts.find(uniformSizeKey); const auto uniformIt = sUniformBindGroupLayouts.find(uniformSizeKey);
if (uniformIt != sUniformBindGroupLayouts.end()) { if (uniformIt != sUniformBindGroupLayouts.end()) {
out.uniformLayout = uniformIt->second; out.uniformLayout = uniformIt->second;
@ -771,7 +770,7 @@ GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const Shader
}; };
const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{ const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
.label = "GX Uniform Bind Group Layout", .label = "GX Uniform Bind Group Layout",
.entryCount = static_cast<uint32_t>(config.hasIndexedAttributes ? uniformLayoutEntries.size() : 1), .entryCount = static_cast<uint32_t>(config.indexedAttributeCount > 0 ? uniformLayoutEntries.size() : 1),
.entries = uniformLayoutEntries.data(), .entries = uniformLayoutEntries.data(),
}; };
out.uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor); out.uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor);

View File

@ -162,7 +162,7 @@ struct ShaderConfig {
std::array<ColorChannelConfig, MaxColorChannels> colorChannels; std::array<ColorChannelConfig, MaxColorChannels> colorChannels;
std::array<TcgConfig, MaxTexCoord> tcgs; std::array<TcgConfig, MaxTexCoord> tcgs;
AlphaCompare alphaCompare; AlphaCompare alphaCompare;
bool hasIndexedAttributes = false; u32 indexedAttributeCount = 0;
bool operator==(const ShaderConfig&) const = default; bool operator==(const ShaderConfig&) const = default;
}; };
struct PipelineConfig { struct PipelineConfig {
@ -216,16 +216,6 @@ Range build_uniform(const ShaderInfo& info) noexcept;
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept; GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept;
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;
struct DlVert {
s16 pos;
s16 norm;
// colors ignored
std::array<s16, 7> uvs;
// pn_mtx_idx ignored
// tex_mtx_idxs ignored
s16 _pad;
};
} // namespace aurora::gfx::gx } // namespace aurora::gfx::gx
namespace aurora { namespace aurora {
@ -301,6 +291,6 @@ inline void xxh3_update(XXH3_state_t& state, const gfx::gx::ShaderConfig& input)
if (input.alphaCompare) { if (input.alphaCompare) {
xxh3_update(state, input.alphaCompare); xxh3_update(state, input.alphaCompare);
} }
XXH3_64bits_update(&state, &input.hasIndexedAttributes, sizeof(gfx::gx::ShaderConfig::hasIndexedAttributes)); XXH3_64bits_update(&state, &input.indexedAttributeCount, sizeof(gfx::gx::ShaderConfig::indexedAttributeCount));
} }
} // namespace aurora } // namespace aurora

View File

@ -396,42 +396,30 @@ static inline std::string vtx_attr(const ShaderConfig& config, GX::Attr attr) {
unreachable(); unreachable();
} }
if (attr == GX::VA_POS) { if (attr == GX::VA_POS) {
if (type == GX::DIRECT) { return "in_pos";
return "in_pos";
}
return "v_verts.data[in_pos_nrm_idx[0]].xyz";
} }
if (attr == GX::VA_NRM) { if (attr == GX::VA_NRM) {
if (type == GX::DIRECT) { return "in_nrm";
return "in_nrm";
}
return "v_norms.data[in_pos_nrm_idx[1]].xyz";
} }
if (attr == GX::VA_CLR0 || attr == GX::VA_CLR1) { if (attr == GX::VA_CLR0 || attr == GX::VA_CLR1) {
const auto idx = attr - GX::VA_CLR0; const auto idx = attr - GX::VA_CLR0;
if (type == GX::DIRECT) { return fmt::format(FMT_STRING("in_clr{}"), idx);
return fmt::format(FMT_STRING("in_clr{}"), idx);
}
Log.report(logvisor::Fatal, FMT_STRING("indexed color unsupported"));
unreachable();
} }
if (attr >= GX::VA_TEX0 && attr <= GX::VA_TEX7) { if (attr >= GX::VA_TEX0 && attr <= GX::VA_TEX7) {
const auto idx = attr - GX::VA_TEX0; const auto idx = attr - GX::VA_TEX0;
if (type == GX::DIRECT) { return fmt::format(FMT_STRING("in_tex{}_uv"), idx);
return fmt::format(FMT_STRING("in_tex{}_uv"), idx);
}
if (idx == 0) {
return "v_packed_uvs.data[in_uv_0_4_idx[0]]";
}
if (idx < 4) {
return fmt::format(FMT_STRING("v_uvs.data[in_uv_0_4_idx[{}]]"), idx);
}
return fmt::format(FMT_STRING("v_uvs.data[in_uv_5_7_idx[{}]]"), idx - 4);
} }
Log.report(logvisor::Fatal, FMT_STRING("unhandled attr {}"), attr); Log.report(logvisor::Fatal, FMT_STRING("unhandled attr {}"), attr);
unreachable(); unreachable();
} }
constexpr std::array<std::string_view, MaxVtxAttr> VtxAttributeNames{
"pn_mtx", "tex0_mtx", "tex1_mtx", "tex2_mtx", "tex3_mtx", "tex4_mtx", "tex5_mtx",
"tex6_mtx", "tex7_mtx", "pos", "nrm", "clr0", "clr1", "tex0_uv",
"tex1_uv", "tex2_uv", "tex3_uv", "tex4_uv", "tex5_uv", "tex6_uv", "tex7_uv",
"pos_mtx_array", "nrm_mtx_array", "tex_mtx_array", "light_array", "nbt",
};
std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& config) noexcept { std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& config) noexcept {
const auto hash = xxh3_hash(config); const auto hash = xxh3_hash(config);
const auto it = g_gxCachedShaders.find(hash); const auto it = g_gxCachedShaders.find(hash);
@ -496,7 +484,7 @@ std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& confi
Log.report(logvisor::Info, FMT_STRING(" alphaCompare: comp0 {} ref0 {} op {} comp1 {} ref1 {}"), Log.report(logvisor::Info, FMT_STRING(" alphaCompare: comp0 {} ref0 {} op {} comp1 {} ref1 {}"),
config.alphaCompare.comp0, config.alphaCompare.ref0, config.alphaCompare.op, config.alphaCompare.comp1, config.alphaCompare.comp0, config.alphaCompare.ref0, config.alphaCompare.op, config.alphaCompare.comp1,
config.alphaCompare.ref1); config.alphaCompare.ref1);
Log.report(logvisor::Info, FMT_STRING(" hasIndexedAttributes: {}"), config.hasIndexedAttributes); Log.report(logvisor::Info, FMT_STRING(" indexedAttributeCount: {}"), config.indexedAttributeCount);
Log.report(logvisor::Info, FMT_STRING(" fogType: {}"), config.fogType); Log.report(logvisor::Info, FMT_STRING(" fogType: {}"), config.fogType);
} }
@ -511,29 +499,66 @@ std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& confi
std::string vtxXfrAttrs; std::string vtxXfrAttrs;
size_t locIdx = 0; size_t locIdx = 0;
size_t vtxOutIdx = 0; size_t vtxOutIdx = 0;
if (config.hasIndexedAttributes) { size_t uniBindingIdx = 1;
if (config.indexedAttributeCount > 0) {
// Display list attributes // Display list attributes
vtxInAttrs += int currAttrIdx = 0;
"\n @location(0) in_pos_nrm_idx: vec2<i32>" bool addedTex1Uv = false;
"\n , @location(1) in_uv_0_4_idx: vec4<i32>" for (GX::Attr attr{}; attr < MaxVtxAttr; attr = GX::Attr(attr + 1)) {
"\n , @location(2) in_uv_5_7_idx: vec4<i32>"; // Indexed attributes
locIdx += 3; if (config.vtxAttrs[attr] != GX::INDEX8 && config.vtxAttrs[attr] != GX::INDEX16) {
uniformBindings += R"""( continue;
struct Vec3Block { }
data: array<vec4<f32>>; const auto [div, rem] = std::div(currAttrIdx, 4);
}; std::string_view attrName;
struct Vec2Block { bool addUniformBinding = true;
data: array<vec2<f32>>; // TODO: this is a hack to only have to bind tex0_uv and tex1_uv for MP
}; // should figure out a more generic approach
@group(0) @binding(1) if (attr >= GX::VA_TEX1 && attr <= GX::VA_TEX7) {
var<storage, read> v_verts: Vec3Block; attrName = VtxAttributeNames[GX::VA_TEX1];
@group(0) @binding(2) addUniformBinding = !addedTex1Uv;
var<storage, read> v_norms: Vec3Block; addedTex1Uv = true;
@group(0) @binding(3) } else {
var<storage, read> v_uvs: Vec2Block; attrName = VtxAttributeNames[attr];
@group(0) @binding(4) }
var<storage, read> v_packed_uvs: Vec2Block; vtxXfrAttrsPre += fmt::format(FMT_STRING("\n var {} = v_arr_{}[in_dl{}[{}]];"), vtx_attr(config, attr), attrName, div, rem);
)"""; if (addUniformBinding) {
std::string_view arrType;
if (attr == GX::VA_POS || attr == GX::VA_NRM) {
arrType = "vec3<f32>";
} else if (attr >= GX::VA_TEX0 && attr <= GX::VA_TEX7) {
arrType = "vec2<f32>";
}
uniformBindings += fmt::format(
"\n@group(0) @binding({})"
"\nvar<storage, read> v_arr_{}: array<{}>;",
uniBindingIdx++, attrName, arrType);
}
++currAttrIdx;
}
auto [num4xAttrArrays, rem] = std::div(currAttrIdx, 4);
u32 num2xAttrArrays = 0;
if (rem > 2) {
++num4xAttrArrays;
} else if (rem > 0) {
num2xAttrArrays = 1;
}
for (u32 i = 0; i < num4xAttrArrays; ++i) {
if (locIdx > 0) {
vtxInAttrs += "\n , ";
} else {
vtxInAttrs += "\n ";
}
vtxInAttrs += fmt::format(FMT_STRING("@location({}) in_dl{}: vec4<i32>"), locIdx++, i);
}
for (u32 i = 0; i < num2xAttrArrays; ++i) {
if (locIdx > 0) {
vtxInAttrs += "\n , ";
} else {
vtxInAttrs += "\n ";
}
vtxInAttrs += fmt::format(FMT_STRING("@location({}) in_dl{}: vec2<i32>"), locIdx++, num4xAttrArrays + i);
}
} }
for (GX::Attr attr{}; attr < MaxVtxAttr; attr = GX::Attr(attr + 1)) { for (GX::Attr attr{}; attr < MaxVtxAttr; attr = GX::Attr(attr + 1)) {
// Direct attributes // Direct attributes

View File

@ -13,82 +13,70 @@ static const std::vector<zeus::CVector3f>* vtxData;
static const std::vector<zeus::CVector3f>* nrmData; static const std::vector<zeus::CVector3f>* nrmData;
static const std::vector<Vec2<float>>* tex0TcData; static const std::vector<Vec2<float>>* tex0TcData;
static const std::vector<Vec2<float>>* tcData; static const std::vector<Vec2<float>>* tcData;
static std::optional<Range> staticVtxRange; static std::optional<Range> cachedVtxRange;
static std::optional<Range> staticNrmRange; static std::optional<Range> cachedNrmRange;
static std::optional<Range> staticPackedTcRange; static std::optional<Range> cachedPackedTcRange;
static std::optional<Range> staticTcRange; static std::optional<Range> cachedTcRange;
static inline std::pair<gx::DlVert, size_t> readVert(const u8* data) noexcept { static inline void read_vert(ByteBuffer& out, const u8* data) noexcept {
gx::DlVert out{};
size_t offset = 0; size_t offset = 0;
const auto vtxTypes = gx::g_gxState.vtxDesc; for (const auto& type : gx::g_gxState.vtxDesc) {
const auto read8 = [/*data, &offset*/](GX::AttrType type) -> s8 { if (type == GX::INDEX8) {
// if (type == GX::INDEX8) { const auto v = static_cast<s16>(data[offset]); // expand to s16
// s8 v = static_cast<s8>(data[offset]); out.append(&v, 2);
// ++offset; ++offset;
// return v; } else if (type == GX::INDEX16) {
// } const s16 v = metaforce::SBig(*reinterpret_cast<const s16*>(data + offset));
#ifndef NDEBUG out.append(&v, 2);
if (type != GX::NONE) {
Log.report(logvisor::Fatal, FMT_STRING("unsupported vtx attr"));
unreachable();
}
#endif
return 0;
};
const auto read16 = [data, &offset](GX::AttrType type) -> s16 {
if (type == GX::INDEX16) {
s16 v = metaforce::SBig(*reinterpret_cast<const u16*>(data + offset));
offset += 2; offset += 2;
return v;
} }
return 0; }
}; constexpr size_t align = 4; // Sint16x2
read8(vtxTypes[GX::VA_PNMTXIDX]); if (offset % align != 0) {
read8(vtxTypes[GX::VA_TEX0MTXIDX]); out.append_zeroes(align - (offset % align));
read8(vtxTypes[GX::VA_TEX1MTXIDX]); }
read8(vtxTypes[GX::VA_TEX2MTXIDX]);
read8(vtxTypes[GX::VA_TEX3MTXIDX]);
read8(vtxTypes[GX::VA_TEX4MTXIDX]);
read8(vtxTypes[GX::VA_TEX5MTXIDX]);
read8(vtxTypes[GX::VA_TEX6MTXIDX]);
out.pos = read16(vtxTypes[GX::VA_POS]);
out.norm = read16(vtxTypes[GX::VA_NRM]);
read16(vtxTypes[GX::VA_CLR0]);
read16(vtxTypes[GX::VA_CLR1]);
out.uvs[0] = read16(vtxTypes[GX::VA_TEX0]);
out.uvs[1] = read16(vtxTypes[GX::VA_TEX1]);
out.uvs[2] = read16(vtxTypes[GX::VA_TEX2]);
out.uvs[3] = read16(vtxTypes[GX::VA_TEX3]);
out.uvs[4] = read16(vtxTypes[GX::VA_TEX4]);
out.uvs[5] = read16(vtxTypes[GX::VA_TEX5]);
out.uvs[6] = read16(vtxTypes[GX::VA_TEX6]);
return {out, offset};
} }
static absl::flat_hash_map<XXH64_hash_t, std::pair<std::vector<gx::DlVert>, std::vector<u32>>> sCachedDisplayLists; static absl::flat_hash_map<XXH64_hash_t, std::pair<ByteBuffer, ByteBuffer>> sCachedDisplayLists;
void queue_surface(const u8* dlStart, u32 dlSize) noexcept { void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
const auto hash = xxh3_hash(dlStart, dlSize, 0); const auto hash = xxh3_hash(dlStart, dlSize, 0);
Range vertRange, idxRange; Range vertRange, idxRange;
uint32_t numIndices; u32 numIndices = 0;
auto it = sCachedDisplayLists.find(hash); auto it = sCachedDisplayLists.find(hash);
if (it != sCachedDisplayLists.end()) { if (it != sCachedDisplayLists.end()) {
const auto& [verts, indices] = it->second; const auto& [verts, indices] = it->second;
numIndices = indices.size(); numIndices = indices.size() / 2;
vertRange = push_verts(ArrayRef{verts}); vertRange = push_verts(verts.data(), verts.size());
idxRange = push_indices(ArrayRef{indices}); idxRange = push_indices(indices.data(), indices.size());
} else { } else {
std::vector<gx::DlVert> verts; ByteBuffer vtxBuf;
std::vector<u32> indices; ByteBuffer idxBuf;
u8 inVtxSize = 0;
u8 outVtxSize = 0;
for (const auto& type : gx::g_gxState.vtxDesc) {
if (type == GX::NONE || type == GX::DIRECT) {
continue;
}
if (type == GX::INDEX8) {
++inVtxSize;
outVtxSize += 2;
} else if (type == GX::INDEX16) {
inVtxSize += 2;
outVtxSize += 2;
} else {
Log.report(logvisor::Fatal, FMT_STRING("unexpected vtx type {}"), type);
unreachable();
}
}
outVtxSize = ALIGN(outVtxSize, 4);
u16 vtxStart = 0;
size_t offset = 0; size_t offset = 0;
while (offset < dlSize - 6) { while (offset < dlSize - 6) {
const auto header = dlStart[offset]; const auto header = dlStart[offset];
const auto primitive = static_cast<GX::Primitive>(header & 0xF8); const auto primitive = static_cast<GX::Primitive>(header & 0xF8);
const auto vtxCount = metaforce::SBig(*reinterpret_cast<const u16*>(dlStart + offset + 1)); const auto dlVtxCount = metaforce::SBig(*reinterpret_cast<const u16*>(dlStart + offset + 1));
offset += 3; offset += 3;
if (primitive == 0) { if (primitive == 0) {
@ -99,66 +87,81 @@ void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
unreachable(); unreachable();
} }
const u32 idxStart = indices.size(); vtxBuf.reserve_extra(dlVtxCount * outVtxSize);
const u16 vertsStart = verts.size(); if (dlVtxCount > 3 && (primitive == GX::TRIANGLEFAN || primitive == GX::TRIANGLESTRIP)) {
verts.reserve(vertsStart + vtxCount); idxBuf.reserve_extra(((u32(dlVtxCount) - 3) * 3 + 3) * 2);
if (vtxCount > 3 && (primitive == GX::TRIANGLEFAN || primitive == GX::TRIANGLESTRIP)) {
indices.reserve(idxStart + (u32(vtxCount) - 3) * 3 + 3);
} else { } else {
indices.reserve(idxStart + vtxCount); idxBuf.reserve_extra(dlVtxCount * 2);
} }
auto curVert = vertsStart; u16 curVert = vtxStart;
for (int v = 0; v < vtxCount; ++v) { for (u16 v = 0; v < dlVtxCount; ++v) {
const auto [vert, read] = readVert(dlStart + offset); read_vert(vtxBuf, dlStart + offset);
verts.push_back(vert); offset += inVtxSize;
offset += read;
if (primitive == GX::TRIANGLES || v < 3) { if (primitive == GX::TRIANGLES || v < 3) {
// pass idxBuf.append(&curVert, 2);
++numIndices;
} else if (primitive == GX::TRIANGLEFAN) { } else if (primitive == GX::TRIANGLEFAN) {
indices.push_back(vertsStart); const std::array<u16, 3> idxs{
indices.push_back(curVert - 1); vtxStart,
u16(curVert - 1),
curVert,
};
idxBuf.append(idxs.data(), 6);
numIndices += 3;
} else if (primitive == GX::TRIANGLESTRIP) { } else if (primitive == GX::TRIANGLESTRIP) {
if ((v & 1) == 0) { if ((v & 1) == 0) {
indices.push_back(curVert - 2); const std::array<u16, 3> idxs{
indices.push_back(curVert - 1); u16(curVert - 2),
u16(curVert - 1),
curVert,
};
idxBuf.append(idxs.data(), 6);
} else { } else {
indices.push_back(curVert - 1); const std::array<u16, 3> idxs{
indices.push_back(curVert - 2); u16(curVert - 1),
u16(curVert - 2),
curVert,
};
idxBuf.append(idxs.data(), 6);
} }
numIndices += 3;
} }
indices.push_back(curVert);
++curVert; ++curVert;
} }
vtxStart += dlVtxCount;
} }
numIndices = indices.size(); vertRange = push_verts(vtxBuf.data(), vtxBuf.size());
vertRange = push_verts(ArrayRef{verts}); idxRange = push_indices(idxBuf.data(), idxBuf.size());
idxRange = push_indices(ArrayRef{indices}); sCachedDisplayLists.try_emplace(hash, std::move(vtxBuf), std::move(idxBuf));
sCachedDisplayLists.try_emplace(hash, std::move(verts), std::move(indices));
} }
Range sVtxRange, sNrmRange, sTcRange, sPackedTcRange; Range sVtxRange, sNrmRange, sTcRange, sPackedTcRange;
if (staticVtxRange) { if (cachedVtxRange) {
sVtxRange = *staticVtxRange; sVtxRange = *cachedVtxRange;
} else { } else {
sVtxRange = push_storage(reinterpret_cast<const uint8_t*>(vtxData->data()), vtxData->size() * 16); sVtxRange = push_storage(reinterpret_cast<const uint8_t*>(vtxData->data()), vtxData->size() * 16);
cachedVtxRange = sVtxRange;
} }
if (staticNrmRange) { if (cachedNrmRange) {
sNrmRange = *staticNrmRange; sNrmRange = *cachedNrmRange;
} else { } else {
sNrmRange = push_storage(reinterpret_cast<const uint8_t*>(nrmData->data()), nrmData->size() * 16); sNrmRange = push_storage(reinterpret_cast<const uint8_t*>(nrmData->data()), nrmData->size() * 16);
cachedNrmRange = sNrmRange;
} }
if (staticTcRange) { if (cachedTcRange) {
sTcRange = *staticTcRange; sTcRange = *cachedTcRange;
} else { } else {
sTcRange = push_storage(reinterpret_cast<const uint8_t*>(tcData->data()), tcData->size() * 8); sTcRange = push_storage(reinterpret_cast<const uint8_t*>(tcData->data()), tcData->size() * 8);
cachedTcRange = sTcRange;
} }
if (staticPackedTcRange) { if (cachedPackedTcRange) {
sPackedTcRange = *staticPackedTcRange; sPackedTcRange = *cachedPackedTcRange;
} else if (tcData == tex0TcData) { } else if (tcData == tex0TcData) {
sPackedTcRange = sTcRange; sPackedTcRange = sTcRange;
} else { } else {
sPackedTcRange = push_storage(reinterpret_cast<const uint8_t*>(tex0TcData->data()), tex0TcData->size() * 8); sPackedTcRange = push_storage(reinterpret_cast<const uint8_t*>(tex0TcData->data()), tex0TcData->size() * 8);
cachedPackedTcRange = sPackedTcRange;
} }
model::PipelineConfig config{}; model::PipelineConfig config{};
@ -188,11 +191,40 @@ State construct_state() { return {}; }
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) { wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
const auto [shader, info] = build_shader(config.shaderConfig); const auto [shader, info] = build_shader(config.shaderConfig);
const auto attributes = gpu::utils::make_vertex_attributes( std::array<wgpu::VertexAttribute, gx::MaxVtxAttr> vtxAttrs;
std::array{wgpu::VertexFormat::Sint16x2, wgpu::VertexFormat::Sint16x4, wgpu::VertexFormat::Sint16x4}); auto [num4xAttr, rem] = std::div(config.shaderConfig.indexedAttributeCount, 4);
const std::array vertexBuffers{gpu::utils::make_vertex_buffer_layout(sizeof(gx::DlVert), attributes)}; u32 num2xAttr = 0;
if (rem > 2) {
++num4xAttr;
} else if (rem > 0) {
++num2xAttr;
}
u32 offset = 0;
for (u32 i = 0; i < num4xAttr; ++i) {
vtxAttrs[i] = {
.format = wgpu::VertexFormat::Sint16x4,
.offset = offset,
.shaderLocation = i,
};
offset += 8;
}
for (u32 i = 0; i < num2xAttr; ++i) {
const u32 idx = num4xAttr + i;
vtxAttrs[idx] = {
.format = wgpu::VertexFormat::Sint16x2,
.offset = offset,
.shaderLocation = idx,
};
offset += 4;
}
const std::array vtxBuffers{wgpu::VertexBufferLayout{
.arrayStride = offset,
.stepMode = wgpu::VertexStepMode::Vertex,
.attributeCount = num4xAttr + num2xAttr,
.attributes = vtxAttrs.data(),
}};
return build_pipeline(config, info, vertexBuffers, shader, "Model Pipeline"); return build_pipeline(config, info, vtxBuffers, shader, "Model Pipeline");
} }
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) { void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) {
@ -204,8 +236,8 @@ void render(const State& state, const DrawData& data, const wgpu::RenderPassEnco
data.uniformRange.offset, data.uniformRange.offset,
storage_offset(data.dataRanges.vtxDataRange), storage_offset(data.dataRanges.vtxDataRange),
storage_offset(data.dataRanges.nrmDataRange), storage_offset(data.dataRanges.nrmDataRange),
storage_offset(data.dataRanges.tcDataRange),
storage_offset(data.dataRanges.packedTcDataRange), storage_offset(data.dataRanges.packedTcDataRange),
storage_offset(data.dataRanges.tcDataRange),
}; };
pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(), offsets.data()); pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(), offsets.data());
if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) { if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) {
@ -213,7 +245,7 @@ void render(const State& state, const DrawData& data, const wgpu::RenderPassEnco
pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup)); pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup));
} }
pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.offset, data.vertRange.size); pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.offset, data.vertRange.size);
pass.SetIndexBuffer(g_indexBuffer, wgpu::IndexFormat::Uint32, data.idxRange.offset, data.idxRange.size); pass.SetIndexBuffer(g_indexBuffer, wgpu::IndexFormat::Uint16, data.idxRange.offset, data.idxRange.size);
if (data.dstAlpha) { if (data.dstAlpha) {
const wgpu::Color color{0.f, 0.f, 0.f, *data.dstAlpha}; const wgpu::Color color{0.f, 0.f, 0.f, *data.dstAlpha};
pass.SetBlendConstant(&color); pass.SetBlendConstant(&color);
@ -227,35 +259,23 @@ template <typename Vec>
static inline void cache_array(const void* data, Vec*& outPtr, std::optional<aurora::gfx::Range>& outRange, u8 stride) { static inline void cache_array(const void* data, Vec*& outPtr, std::optional<aurora::gfx::Range>& outRange, u8 stride) {
Vec* vecPtr = static_cast<Vec*>(data); Vec* vecPtr = static_cast<Vec*>(data);
outPtr = vecPtr; outPtr = vecPtr;
if (stride == 1) { outRange.reset();
// const auto hash = aurora::xxh3_hash(vecPtr->data(), vecPtr->size() * sizeof(typename Vec::value_type), 0);
// const auto it = sCachedRanges.find(hash);
// if (it != sCachedRanges.end()) {
// outRange = it->second;
// } else {
// const auto range = aurora::gfx::push_static_storage(aurora::ArrayRef{*vecPtr});
// sCachedRanges.try_emplace(hash, range);
// outRange = range;
// }
} else {
outRange.reset();
}
} }
void GXSetArray(GX::Attr attr, const void* data, u8 stride) noexcept { void GXSetArray(GX::Attr attr, const void* data, u8 stride) noexcept {
using namespace aurora::gfx::model; using namespace aurora::gfx::model;
switch (attr) { switch (attr) {
case GX::VA_POS: case GX::VA_POS:
cache_array(data, vtxData, staticVtxRange, stride); cache_array(data, vtxData, cachedVtxRange, stride);
break; break;
case GX::VA_NRM: case GX::VA_NRM:
cache_array(data, nrmData, staticNrmRange, stride); cache_array(data, nrmData, cachedNrmRange, stride);
break; break;
case GX::VA_TEX0: case GX::VA_TEX0:
cache_array(data, tex0TcData, staticPackedTcRange, stride); cache_array(data, tex0TcData, cachedPackedTcRange, stride);
break; break;
case GX::VA_TEX1: case GX::VA_TEX1:
cache_array(data, tcData, staticTcRange, stride); cache_array(data, tcData, cachedTcRange, stride);
break; break;
default: default:
Log.report(logvisor::Fatal, FMT_STRING("GXSetArray: invalid attr {}"), attr); Log.report(logvisor::Fatal, FMT_STRING("GXSetArray: invalid attr {}"), attr);

View File

@ -8,6 +8,19 @@ static logvisor::Module Log("aurora::gfx::stream");
using aurora::gfx::gx::g_gxState; using aurora::gfx::gx::g_gxState;
#ifndef NDEBUG
static inline GX::Attr next_attr(size_t begin) {
auto iter = std::find_if(g_gxState.vtxDesc.begin() + begin, g_gxState.vtxDesc.end(),
[](const auto type) { return type != GX::NONE; });
if (begin > 0 && iter == g_gxState.vtxDesc.end()) {
// wrap around
iter = std::find_if(g_gxState.vtxDesc.begin(), g_gxState.vtxDesc.end(),
[](const auto type) { return type != GX::NONE; });
}
return GX::Attr(iter - g_gxState.vtxDesc.begin());
}
#endif
struct SStreamState { struct SStreamState {
GX::Primitive primitive; GX::Primitive primitive;
u16 vertexCount = 0; u16 vertexCount = 0;
@ -27,8 +40,7 @@ struct SStreamState {
indices.reserve(numVerts); indices.reserve(numVerts);
} }
#ifndef NDEBUG #ifndef NDEBUG
nextAttr = nextAttr = next_attr(0);
GX::Attr(std::find(g_gxState.vtxDesc.begin(), g_gxState.vtxDesc.end(), GX::DIRECT) - g_gxState.vtxDesc.begin());
#endif #endif
} }
}; };
@ -54,9 +66,8 @@ void GXBegin(GX::Primitive primitive, GX::VtxFmt vtxFmt, u16 nVerts) noexcept {
Log.report(logvisor::Fatal, FMT_STRING("don't know how to handle attr {}"), attr); Log.report(logvisor::Fatal, FMT_STRING("don't know how to handle attr {}"), attr);
unreachable(); unreachable();
} }
} else if (type != GX::NONE) { } else if (type == GX::INDEX8 || type == GX::INDEX16) {
Log.report(logvisor::Fatal, FMT_STRING("invalid vtx type {} for attr {}"), type, attr); vertexSize += 2;
unreachable();
} }
attr = GX::Attr(attr + 1); attr = GX::Attr(attr + 1);
} }
@ -76,11 +87,7 @@ static inline void check_attr_order(GX::Attr attr) noexcept {
Log.report(logvisor::Fatal, FMT_STRING("bad attribute order: {}, expected {}"), attr, sStreamState->nextAttr); Log.report(logvisor::Fatal, FMT_STRING("bad attribute order: {}, expected {}"), attr, sStreamState->nextAttr);
unreachable(); unreachable();
} }
auto nextAttr = std::find(g_gxState.vtxDesc.begin() + attr + 1, g_gxState.vtxDesc.end(), GX::DIRECT); sStreamState->nextAttr = next_attr(attr + 1);
if (nextAttr == g_gxState.vtxDesc.end()) {
nextAttr = std::find(g_gxState.vtxDesc.begin(), g_gxState.vtxDesc.end(), GX::DIRECT);
}
sStreamState->nextAttr = GX::Attr(nextAttr - g_gxState.vtxDesc.begin());
#endif #endif
} }
void GXPosition3f32(const zeus::CVector3f& pos) noexcept { void GXPosition3f32(const zeus::CVector3f& pos) noexcept {