More aurora::gx refactoring

This commit is contained in:
Luke Street 2022-03-07 18:53:42 -05:00
parent 35674c27e5
commit 71342ed75e
23 changed files with 843 additions and 383 deletions

View File

@ -4,6 +4,7 @@
#include <cctype> #include <cctype>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <vector>
namespace metaforce { namespace metaforce {
class CInputStream; class CInputStream;

View File

@ -23,6 +23,10 @@ CFont::CFont(float scale) : x0_fontSize(16.f * scale), x4_scale(scale) {
++sNumInstances; ++sNumInstances;
} }
void CFont::Shutdown() {
mpTexture.reset();
}
void CFont::TileCopy8(u8* dest, const u8* src) { void CFont::TileCopy8(u8* dest, const u8* src) {
for (u32 i = 0; i < 4; ++i) { for (u32 i = 0; i < 4; ++i) {
dest[0] = src[0]; dest[0] = src[0];

View File

@ -24,5 +24,7 @@ public:
u32 StringWidth(const char* str) const; u32 StringWidth(const char* str) const;
u32 CharsWidth(const char* str, u32 len) const; u32 CharsWidth(const char* str, u32 len) const;
u32 CharWidth(const char chr) const; u32 CharWidth(const char chr) const;
static void Shutdown();
}; };
} }

View File

@ -565,7 +565,10 @@ void CGraphics::StreamVertex(const zeus::CVector3f& pos) {
aurora::gfx::stream_vertex(sStreamFlags, pos, sQueuedNormal, sQueuedColor, sQueuedTexCoord); aurora::gfx::stream_vertex(sStreamFlags, pos, sQueuedNormal, sQueuedColor, sQueuedTexCoord);
} }
void CGraphics::StreamEnd() { aurora::gfx::stream_end(); } void CGraphics::StreamEnd() {
SetTevStates(sStreamFlags);
aurora::gfx::stream_end();
}
void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* pos, const zeus::CVector3f& normal, void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* pos, const zeus::CVector3f& normal,
const zeus::CColor& col, s32 numVerts) { const zeus::CColor& col, s32 numVerts) {
@ -577,4 +580,16 @@ void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* po
} }
StreamEnd(); StreamEnd();
} }
void CGraphics::SetTevStates(EStreamFlags flags) noexcept {
if (flags & EStreamFlagBits::fHasTexture) {
aurora::gfx::set_tev_order(GX::TEVSTAGE0, GX::TEXCOORD0, GX::TEXMAP0, GX::COLOR0A0);
aurora::gfx::set_tev_order(GX::TEVSTAGE1, GX::TEXCOORD1, GX::TEXMAP1, GX::COLOR0A0);
} else {
aurora::gfx::set_tev_order(GX::TEVSTAGE0, GX::TEXCOORD_NULL, GX::TEXMAP_NULL, GX::COLOR0A0);
aurora::gfx::set_tev_order(GX::TEVSTAGE1, GX::TEXCOORD_NULL, GX::TEXMAP_NULL, GX::COLOR0A0);
}
// TODO load TCGs
aurora::gfx::set_chan_mat_src(GX::COLOR0A0, flags & EStreamFlagBits::fHasColor ? GX::SRC_VTX : GX::SRC_REG);
}
} // namespace metaforce } // namespace metaforce

View File

@ -272,6 +272,7 @@ public:
// static void DrawArray(size_t start, size_t count) { g_BooMainCommandQueue->draw(start, count); } // static void DrawArray(size_t start, size_t count) { g_BooMainCommandQueue->draw(start, count); }
// static void DrawArrayIndexed(size_t start, size_t count) { g_BooMainCommandQueue->drawIndexed(start, count); } // static void DrawArrayIndexed(size_t start, size_t count) { g_BooMainCommandQueue->drawIndexed(start, count); }
static void SetTevStates(EStreamFlags flags) noexcept;
static void SetTevOp(ERglTevStage stage, const CTevCombiners::CTevPass& pass); static void SetTevOp(ERglTevStage stage, const CTevCombiners::CTevPass& pass);
static void StreamBegin(GX::Primitive primitive); static void StreamBegin(GX::Primitive primitive);
static void StreamNormal(const zeus::CVector3f& nrm); static void StreamNormal(const zeus::CVector3f& nrm);

View File

@ -8,6 +8,8 @@ void CTevPass::Execute(ERglTevStage stage) const {
aurora::gfx::disable_tev_stage(stage); aurora::gfx::disable_tev_stage(stage);
} else { } else {
aurora::gfx::update_tev_stage(stage, x4_colorPass, x14_alphaPass, x24_colorOp, x38_alphaOp); aurora::gfx::update_tev_stage(stage, x4_colorPass, x14_alphaPass, x24_colorOp, x38_alphaOp);
aurora::gfx::set_tev_order(static_cast<GX::TevStageID>(stage), GX::TEXCOORD_NULL, static_cast<GX::TexMapID>(stage),
GX::COLOR_NULL);
} }
} }

View File

@ -23,6 +23,7 @@
#include "Runtime/Character/CSkinRules.hpp" #include "Runtime/Character/CSkinRules.hpp"
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp" #include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
#include "Runtime/Collision/CCollisionResponseData.hpp" #include "Runtime/Collision/CCollisionResponseData.hpp"
#include "Runtime/Graphics/CFont.hpp"
#include "Runtime/Graphics/CModel.hpp" #include "Runtime/Graphics/CModel.hpp"
#include "Runtime/Graphics/CTexture.hpp" #include "Runtime/Graphics/CTexture.hpp"
#include "Runtime/GuiSys/CGuiFrame.hpp" #include "Runtime/GuiSys/CGuiFrame.hpp"
@ -702,6 +703,7 @@ void CMain::ShutdownSubsystems() {
CMappableObject::Shutdown(); CMappableObject::Shutdown();
// Metaforce additions // Metaforce additions
CMoviePlayer::Shutdown(); CMoviePlayer::Shutdown();
CFont::Shutdown();
} }
void CMain::Shutdown() { void CMain::Shutdown() {

View File

@ -6,7 +6,7 @@
namespace metaforce { namespace metaforce {
template <class T, size_t N> template <class T, size_t N>
void read_reserved_vector(rstl::reserved_vector<T, N>& v, CInputStream& in) { void read_reserved_vector(rstl::reserved_vector<T, N>& v, CInputStream& in) {
u32 count = in.ReadLong(); u32 count = in.ReadUint32();
v.resize(count); v.resize(count);
for (u32 i = 0; i < count; ++i) { for (u32 i = 0; i < count; ++i) {
v[i] = in.Get<T>(); v[i] = in.Get<T>();
@ -15,7 +15,7 @@ void read_reserved_vector(rstl::reserved_vector<T, N>& v, CInputStream& in) {
template <class T> template <class T>
void read_vector(std::vector<T>& v, CInputStream& in) { void read_vector(std::vector<T>& v, CInputStream& in) {
u32 count = in.ReadLong(); u32 count = in.ReadUint32();
v.reserve(count); v.reserve(count);
for (u32 i = 0; i < count; ++i) { for (u32 i = 0; i < count; ++i) {
v.emplace_back(in.Get<T>()); v.emplace_back(in.Get<T>());

View File

@ -6,7 +6,7 @@
namespace metaforce { namespace metaforce {
template <class T, size_t N> template <class T, size_t N>
void write_reserved_vector(const rstl::reserved_vector<T, N>& v, COutputStream& out) { void write_reserved_vector(const rstl::reserved_vector<T, N>& v, COutputStream& out) {
out.Put(v.size()); out.Put<u32>(v.size());
for (const auto& t : v) { for (const auto& t : v) {
out.Put(t); out.Put(t);
} }
@ -14,7 +14,7 @@ void write_reserved_vector(const rstl::reserved_vector<T, N>& v, COutputStream&
template <class T> template <class T>
void write_vector(const std::vector<T>& v, COutputStream& out) { void write_vector(const std::vector<T>& v, COutputStream& out) {
out.Put(v.size()); out.Put<u32>(v.size());
for (const auto& t : v) { for (const auto& t : v) {
out.Put(t); out.Put(t);
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <compare>
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>

View File

@ -284,6 +284,7 @@ void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept;
void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept; void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept;
void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept; void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept;
void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept; void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept;
void set_chan_mat_src(GX::ChannelID id, GX::ColorSrc src) noexcept;
// Model state // Model state
void set_alpha_discard(bool v); void set_alpha_discard(bool v);

View File

@ -301,10 +301,10 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv)
.storeOp = wgpu::StoreOp::Store, .storeOp = wgpu::StoreOp::Store,
.clearColor = .clearColor =
{ {
.r = gfx::g_clearColor.r(), .r = gfx::gx::g_clearColor.r(),
.g = gfx::g_clearColor.g(), .g = gfx::gx::g_clearColor.g(),
.b = gfx::g_clearColor.b(), .b = gfx::gx::g_clearColor.b(),
.a = gfx::g_clearColor.a(), .a = gfx::gx::g_clearColor.a(),
}, },
}, },
}; };

View File

@ -285,7 +285,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type,
const auto vertRange = push_verts(ArrayRef{verts}); const auto vertRange = push_verts(ArrayRef{verts});
const auto uniform = Uniform{ const auto uniform = Uniform{
.xf = get_combined_matrix(), .xf = gx::get_combined_matrix(),
.color = color, .color = color,
}; };
const auto uniformRange = push_uniform(uniform); const auto uniformRange = push_uniform(uniform);

View File

@ -66,7 +66,7 @@ struct Command {
}; };
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>; using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
static std::mutex g_pipelineMutex; std::mutex g_pipelineMutex;
static std::thread g_pipelineThread; static std::thread g_pipelineThread;
static std::atomic_bool g_pipelineThreadEnd; static std::atomic_bool g_pipelineThreadEnd;
static std::condition_variable g_pipelineCv; static std::condition_variable g_pipelineCv;
@ -277,7 +277,10 @@ void shutdown() {
g_pipelineCv.notify_all(); g_pipelineCv.notify_all();
g_pipelineThread.join(); g_pipelineThread.join();
gx::shutdown();
g_cachedBindGroups.clear(); g_cachedBindGroups.clear();
g_cachedSamplers.clear();
g_pipelines.clear(); g_pipelines.clear();
g_vertexBuffer = {}; g_vertexBuffer = {};
g_uniformBuffer = {}; g_uniformBuffer = {};

View File

@ -11,6 +11,8 @@
#endif #endif
namespace aurora { namespace aurora {
template <typename T>
static inline void xxh3_update(XXH3_state_t& state, const T& input);
static inline XXH64_hash_t xxh3_hash(const void* input, size_t len, XXH64_hash_t seed = 0) { static inline XXH64_hash_t xxh3_hash(const void* input, size_t len, XXH64_hash_t seed = 0) {
return XXH3_64bits_withSeed(input, len, seed); return XXH3_64bits_withSeed(input, len, seed);
} }

View File

@ -1,13 +1,142 @@
#include "gx.hpp" #include "gx.hpp"
#include "common.hpp"
#include "../gpu.hpp" #include "../gpu.hpp"
#include "common.hpp"
#include <unordered_map>
#include <magic_enum.hpp> #include <magic_enum.hpp>
namespace aurora::gfx { namespace aurora::gfx {
static logvisor::Module Log("aurora::gfx::gx"); static logvisor::Module Log("aurora::gfx::gx");
// TODO remove this hack for build_shader
extern std::mutex g_pipelineMutex;
// GX state
void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept {
gx::g_textures[static_cast<size_t>(id)] = {tex, clamp, lod};
}
void unbind_texture(GX::TexMapID id) noexcept { gx::g_textures[static_cast<size_t>(id)].reset(); }
void set_cull_mode(metaforce::ERglCullMode mode) noexcept { gx::g_cullMode = mode; }
void set_blend_mode(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor src, metaforce::ERglBlendFactor dst,
metaforce::ERglLogicOp op) noexcept {
gx::g_blendMode = mode;
gx::g_blendFacSrc = src;
gx::g_blendFacDst = dst;
gx::g_blendOp = op;
}
void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept {
gx::g_depthCompare = compare_enable;
gx::g_depthFunc = func;
gx::g_depthUpdate = update_enable;
}
void set_tev_reg_color(GX::TevRegID id, const zeus::CColor& color) noexcept {
if (id < GX::TEVREG0 || id > GX::TEVREG2) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_reg_color: bad reg {}"), id);
unreachable();
}
gx::g_colorRegs[id - 1] = color;
}
void set_tev_k_color(GX::TevKColorID id, const zeus::CColor& color) noexcept {
if (id >= GX::MAX_KCOLOR) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color: bad reg {}"), id);
unreachable();
}
gx::g_kcolors[id] = color;
}
void set_alpha_update(bool enabled) noexcept { gx::g_alphaUpdate = enabled; }
void set_dst_alpha(bool enabled, float value) noexcept {
if (enabled) {
gx::g_dstAlpha = value;
} else {
gx::g_dstAlpha.reset();
}
}
void set_clear_color(const zeus::CColor& color) noexcept { gx::g_clearColor = color; }
void set_alpha_discard(bool v) { gx::g_alphaDiscard = v; }
void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept {
gx::g_mv = mv;
gx::g_mvInv = mv_inv;
}
constexpr zeus::CMatrix4f DepthCorrect{
// clang-format off
1.f, 0.f, 0.f, 0.f,
0.f, 1.f, 0.f, 0.f,
0.f, 0.f, 0.5f, 0.5f,
0.f, 0.f, 0.f, 1.f,
// clang-format on
};
void update_projection(const zeus::CMatrix4f& proj) noexcept { gx::g_proj = DepthCorrect * proj; }
void update_fog_state(const metaforce::CFogState& state) noexcept { gx::g_fogState = state; }
void disable_tev_stage(metaforce::ERglTevStage stage) noexcept { gx::g_tevStages[static_cast<size_t>(stage)].reset(); }
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
const metaforce::CTevCombiners::AlphaPass& alphaPass,
const metaforce::CTevCombiners::CTevOp& colorOp,
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {
gx::g_tevStages[static_cast<size_t>(stage)] = {colPass, alphaPass, colorOp, alphaOp};
}
void set_tev_order(GX::TevStageID id, GX::TexCoordID tcid, GX::TexMapID tmid, GX::ChannelID cid) noexcept {
auto& stage = gx::g_tevStages[id];
if (!stage) {
// Log.report(logvisor::Fatal, FMT_STRING("set_tev_order: disabled stage {}"), id);
// unreachable();
return;
}
stage->texCoordId = tcid;
stage->texMapId = tmid;
stage->channelId = cid;
}
void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept {
auto& stage = gx::g_tevStages[id];
if (!stage) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color_sel: disabled stage {}"), id);
unreachable();
}
stage->kcSel = sel;
}
void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept {
auto& stage = gx::g_tevStages[id];
if (!stage) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_alpha_sel: disabled stage {}"), id);
unreachable();
}
stage->kaSel = sel;
}
void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept {
if (id < GX::COLOR0A0 || id > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("set_chan_amb_color: invalid channel {}"), id);
unreachable();
}
gx::g_colorChannels[id - GX::COLOR0A0].ambColor = color;
}
void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept {
if (id < GX::COLOR0A0 || id > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_color: invalid channel {}"), id);
unreachable();
}
gx::g_colorChannels[id - GX::COLOR0A0].matColor = color;
}
void set_chan_mat_src(GX::ChannelID id, GX::ColorSrc src) noexcept {
if (id < GX::COLOR0A0 || id > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_src: invalid channel {}"), id);
unreachable();
}
gx::g_colorChannels[id - GX::COLOR0A0].matSrc = src;
}
void load_light(GX::LightID id, const Light& light) noexcept { gx::g_lights[id] = light; }
void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept { gx::g_lights[id] = ambient; }
void set_light_state(std::bitset<MaxLights> bits) noexcept { gx::g_lightState = bits; }
namespace gx {
using gpu::g_device;
using gpu::g_graphicsConfig;
zeus::CMatrix4f g_mv; zeus::CMatrix4f g_mv;
zeus::CMatrix4f g_mvInv; zeus::CMatrix4f g_mvInv;
zeus::CMatrix4f g_proj; zeus::CMatrix4f g_proj;
@ -32,123 +161,10 @@ std::array<LightVariant, MaxLights> g_lights;
std::bitset<MaxLights> g_lightState; std::bitset<MaxLights> g_lightState;
std::array<std::optional<STevStage>, maxTevStages> g_tevStages; std::array<std::optional<STevStage>, maxTevStages> g_tevStages;
std::array<STextureBind, maxTextures> g_textures; std::array<TextureBind, maxTextures> g_textures;
// GX state const gx::TextureBind& get_texture(GX::TexMapID id) noexcept { return gx::g_textures[static_cast<size_t>(id)]; }
void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept {
g_textures[static_cast<size_t>(id)] = {tex, clamp, lod};
}
void unbind_texture(GX::TexMapID id) noexcept { g_textures[static_cast<size_t>(id)].reset(); }
const STextureBind& get_texture(GX::TexMapID id) noexcept { return g_textures[static_cast<size_t>(id)]; }
void set_cull_mode(metaforce::ERglCullMode mode) noexcept { g_cullMode = mode; }
void set_blend_mode(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor src, metaforce::ERglBlendFactor dst,
metaforce::ERglLogicOp op) noexcept {
g_blendMode = mode;
g_blendFacSrc = src;
g_blendFacDst = dst;
g_blendOp = op;
}
void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept {
g_depthCompare = compare_enable;
g_depthFunc = func;
g_depthUpdate = update_enable;
}
void set_tev_reg_color(GX::TevRegID id, const zeus::CColor& color) noexcept {
if (id < GX::TEVREG0 || id > GX::TEVREG2) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_reg_color: bad reg {}"), id);
unreachable();
}
g_colorRegs[id - 1] = color;
}
void set_tev_k_color(GX::TevKColorID id, const zeus::CColor& color) noexcept {
if (id >= GX::MAX_KCOLOR) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color: bad reg {}"), id);
unreachable();
}
g_kcolors[id] = color;
}
void set_alpha_update(bool enabled) noexcept { g_alphaUpdate = enabled; }
void set_dst_alpha(bool enabled, float value) noexcept {
if (enabled) {
g_dstAlpha = value;
} else {
g_dstAlpha.reset();
}
}
void set_clear_color(const zeus::CColor& color) noexcept { g_clearColor = color; }
void set_alpha_discard(bool v) { g_alphaDiscard = v; }
void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept {
g_mv = mv;
g_mvInv = mv_inv;
}
constexpr zeus::CMatrix4f DepthCorrect{
// clang-format off
1.f, 0.f, 0.f, 0.f,
0.f, 1.f, 0.f, 0.f,
0.f, 0.f, 0.5f, 0.5f,
0.f, 0.f, 0.f, 1.f,
// clang-format on
};
void update_projection(const zeus::CMatrix4f& proj) noexcept { g_proj = DepthCorrect * proj; }
void update_fog_state(const metaforce::CFogState& state) noexcept { g_fogState = state; }
void disable_tev_stage(metaforce::ERglTevStage stage) noexcept { g_tevStages[static_cast<size_t>(stage)].reset(); }
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
const metaforce::CTevCombiners::AlphaPass& alphaPass,
const metaforce::CTevCombiners::CTevOp& colorOp,
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {
g_tevStages[static_cast<size_t>(stage)] = {colPass, alphaPass, colorOp, alphaOp};
}
void set_tev_order(GX::TevStageID id, GX::TexCoordID tcid, GX::TexMapID tmid, GX::ChannelID cid) noexcept {
auto& stage = g_tevStages[id];
if (!stage) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_order: disabled stage {}"), id);
unreachable();
}
stage->texCoordId = tcid;
stage->texMapId = tmid;
stage->channelId = cid;
}
void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept {
auto& stage = g_tevStages[id];
if (!stage) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color_sel: disabled stage {}"), id);
unreachable();
}
stage->kcSel = sel;
}
void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept {
auto& stage = g_tevStages[id];
if (!stage) {
Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_alpha_sel: disabled stage {}"), id);
unreachable();
}
stage->kaSel = sel;
}
void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept {
if (id < GX::COLOR0A0 || id > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("set_chan_amb_color: invalid channel {}"), id);
unreachable();
}
g_colorChannels[id - GX::COLOR0A0].ambColor = color;
}
void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept {
if (id < GX::COLOR0A0 || id > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_color: invalid channel {}"), id);
unreachable();
}
g_colorChannels[id - GX::COLOR0A0].matColor = color;
}
void load_light(GX::LightID id, const Light& light) noexcept { g_lights[id] = light; }
void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept { g_lights[id] = ambient; }
void set_light_state(std::bitset<MaxLights> bits) noexcept { g_lightState = bits; }
// Pipeline helpers
static inline wgpu::BlendFactor to_blend_factor(metaforce::ERglBlendFactor fac) { static inline wgpu::BlendFactor to_blend_factor(metaforce::ERglBlendFactor fac) {
switch (fac) { switch (fac) {
case metaforce::ERglBlendFactor::Zero: case metaforce::ERglBlendFactor::Zero:
@ -259,9 +275,9 @@ static inline wgpu::PrimitiveState to_primitive_state(GX::Primitive gx_prim, met
}; };
} }
wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers, wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info,
const GXPipelineConfig& config, zstring_view label) noexcept { ArrayRef<wgpu::VertexBufferLayout> vtxBuffers, wgpu::ShaderModule shader,
using gpu::g_graphicsConfig; zstring_view label) noexcept {
const auto depthStencil = wgpu::DepthStencilState{ const auto depthStencil = wgpu::DepthStencilState{
.format = g_graphicsConfig.depthFormat, .format = g_graphicsConfig.depthFormat,
.depthWriteEnabled = config.depthUpdate, .depthWriteEnabled = config.depthUpdate,
@ -273,19 +289,30 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::
.blend = &blendState, .blend = &blendState,
.writeMask = to_write_mask(config.alphaUpdate), .writeMask = to_write_mask(config.alphaUpdate),
}}; }};
const auto shader = build_shader(config.shaderConfig);
const auto fragmentState = wgpu::FragmentState{ const auto fragmentState = wgpu::FragmentState{
.module = shader, .module = shader,
.entryPoint = "fs_main", .entryPoint = "fs_main",
.targetCount = colorTargets.size(), .targetCount = colorTargets.size(),
.targets = colorTargets.data(), .targets = colorTargets.data(),
}; };
const auto layouts = build_bind_group_layouts(info);
const std::array bindGroupLayouts{
layouts.uniformLayout,
layouts.samplerLayout,
layouts.textureLayout,
};
const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{
.label = "GX Pipeline Layout",
.bindGroupLayoutCount = static_cast<uint32_t>(info.sampledTextures.any() ? bindGroupLayouts.size() : 1),
.bindGroupLayouts = bindGroupLayouts.data(),
};
auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor);
const auto descriptor = wgpu::RenderPipelineDescriptor{ const auto descriptor = wgpu::RenderPipelineDescriptor{
.label = label.c_str(), .label = label.c_str(),
.layout = std::move(layout), .layout = std::move(pipelineLayout),
.vertex = .vertex =
{ {
.module = shader, .module = std::move(shader),
.entryPoint = "vs_main", .entryPoint = "vs_main",
.bufferCount = static_cast<uint32_t>(vtxBuffers.size()), .bufferCount = static_cast<uint32_t>(vtxBuffers.size()),
.buffers = vtxBuffers.data(), .buffers = vtxBuffers.data(),
@ -298,11 +325,10 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::
}, },
.fragment = &fragmentState, .fragment = &fragmentState,
}; };
return gpu::g_device.CreateRenderPipeline(&descriptor); return g_device.CreateRenderPipeline(&descriptor);
} }
void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive, ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) noexcept {
std::bitset<maxTextures> enabledTextures) noexcept {
for (size_t idx = 0; const auto& item : g_tevStages) { for (size_t idx = 0; const auto& item : g_tevStages) {
// Copy until disabled TEV stage (indicating end) // Copy until disabled TEV stage (indicating end)
if (!item) { if (!item) {
@ -310,12 +336,9 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti
} }
config.shaderConfig.tevStages[idx++] = item; config.shaderConfig.tevStages[idx++] = item;
} }
config.shaderConfig.channelMatSrcs[0] = g_colorChannels[0].matSrc;
config.shaderConfig.channelMatSrcs[1] = g_colorChannels[1].matSrc;
config.shaderConfig.alphaDiscard = g_alphaDiscard; config.shaderConfig.alphaDiscard = g_alphaDiscard;
for (size_t idx = 0; const auto& item : g_textures) {
if (enabledTextures.test(idx) && item) {
config.shaderConfig.boundTextures.set(idx);
}
}
config = { config = {
.shaderConfig = config.shaderConfig, .shaderConfig = config.shaderConfig,
.primitive = primitive, .primitive = primitive,
@ -330,9 +353,182 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti
.depthUpdate = g_depthUpdate, .depthUpdate = g_depthUpdate,
.alphaUpdate = g_alphaUpdate, .alphaUpdate = g_alphaUpdate,
}; };
// TODO separate shader info from build_shader for async
{
std::lock_guard lk{g_pipelineMutex};
const auto [_, info] = build_shader(config.shaderConfig);
return info;
}
} }
wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept { Range build_uniform(const ShaderInfo& info) noexcept {
ByteBuffer uniBuf;
{
const auto xf = get_combined_matrix();
uniBuf.append(&xf, 64);
}
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
if (!info.sampledColorChannels.test(i)) {
continue;
}
uniBuf.append(&g_colorChannels[i].ambColor, 16);
uniBuf.append(&g_colorChannels[i].matColor, 16);
}
for (int i = 0; i < info.sampledKcolors.size(); ++i) {
if (!info.sampledKcolors.test(i)) {
continue;
}
uniBuf.append(&g_kcolors[i], 16);
}
for (int i = 0; i < info.sampledTextures.size(); ++i) {
if (!info.sampledTextures.test(i)) {
continue;
}
const auto& tex = get_texture(static_cast<GX::TexMapID>(i));
if (!tex) {
Log.report(logvisor::Fatal, FMT_STRING("unbound texture {}"), i);
unreachable();
}
uniBuf.append(&tex.lod, 4);
}
return push_uniform(uniBuf.data(), uniBuf.size());
}
static std::unordered_map<u32, wgpu::BindGroupLayout> sUniformBindGroupLayouts;
static std::unordered_map<u32, std::pair<wgpu::BindGroupLayout, wgpu::BindGroupLayout>> sTextureBindGroupLayouts;
GXBindGroups build_bind_groups(const ShaderInfo& info) noexcept {
const auto layouts = build_bind_group_layouts(info);
u32 textureCount = info.sampledTextures.count();
const std::array uniformEntries{wgpu::BindGroupEntry{
.binding = 0,
.buffer = g_uniformBuffer,
.size = info.uniformSize,
}};
std::array<wgpu::BindGroupEntry, maxTextures> samplerEntries;
std::array<wgpu::BindGroupEntry, maxTextures> textureEntries;
for (u32 texIdx = 0, i = 0; texIdx < info.sampledTextures.size(); ++texIdx) {
if (!info.sampledTextures.test(texIdx)) {
continue;
}
const auto& tex = g_textures[texIdx];
if (!tex) {
Log.report(logvisor::Fatal, FMT_STRING("unbound texture {}"), texIdx);
unreachable();
}
samplerEntries[i] = {
.binding = i,
.sampler = sampler_ref(tex.get_descriptor()),
};
textureEntries[i] = {
.binding = i,
.textureView = tex.handle.ref->view,
};
i++;
}
return {
.uniformBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "GX Uniform Bind Group",
.layout = layouts.uniformLayout,
.entryCount = uniformEntries.size(),
.entries = uniformEntries.data(),
}),
.samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "GX Sampler Bind Group",
.layout = layouts.samplerLayout,
.entryCount = textureCount,
.entries = samplerEntries.data(),
}),
.textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "GX Texture Bind Group",
.layout = layouts.textureLayout,
.entryCount = textureCount,
.entries = textureEntries.data(),
}),
};
}
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info) noexcept {
GXBindGroupLayouts out;
if (sUniformBindGroupLayouts.contains(info.uniformSize)) {
out.uniformLayout = sUniformBindGroupLayouts[info.uniformSize];
} else {
const std::array uniformLayoutEntries{wgpu::BindGroupLayoutEntry{
.binding = 0,
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
.buffer =
wgpu::BufferBindingLayout{
.type = wgpu::BufferBindingType::Uniform,
.hasDynamicOffset = true,
.minBindingSize = info.uniformSize,
},
}};
const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
.label = "GX Uniform Bind Group Layout",
.entryCount = uniformLayoutEntries.size(),
.entries = uniformLayoutEntries.data(),
};
out.uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor);
sUniformBindGroupLayouts.try_emplace(info.uniformSize, out.uniformLayout);
}
u32 textureCount = info.sampledTextures.count();
if (sTextureBindGroupLayouts.contains(textureCount)) {
const auto& [sl, tl] = sTextureBindGroupLayouts[textureCount];
out.samplerLayout = sl;
out.textureLayout = tl;
} else {
std::array<wgpu::BindGroupLayoutEntry, maxTextures> samplerEntries;
std::array<wgpu::BindGroupLayoutEntry, maxTextures> textureEntries;
for (u32 i = 0; i < textureCount; ++i) {
samplerEntries[i] = {
.binding = i,
.visibility = wgpu::ShaderStage::Fragment,
.sampler = {.type = wgpu::SamplerBindingType::Filtering},
};
textureEntries[i] = {
.binding = i,
.visibility = wgpu::ShaderStage::Fragment,
.texture =
{
.sampleType = wgpu::TextureSampleType::Float,
.viewDimension = wgpu::TextureViewDimension::e2D,
},
};
}
{
const wgpu::BindGroupLayoutDescriptor descriptor{
.label = "GX Sampler Bind Group",
.entryCount = textureCount,
.entries = samplerEntries.data(),
};
out.samplerLayout = g_device.CreateBindGroupLayout(&descriptor);
}
{
const wgpu::BindGroupLayoutDescriptor descriptor{
.label = "GX Texture Bind Group",
.entryCount = textureCount,
.entries = textureEntries.data(),
};
out.textureLayout = g_device.CreateBindGroupLayout(&descriptor);
}
sTextureBindGroupLayouts.try_emplace(textureCount, out.samplerLayout, out.textureLayout);
}
return out;
}
// TODO this is awkward
extern std::unordered_map<ShaderRef, std::pair<wgpu::ShaderModule, gx::ShaderInfo>> g_gxCachedShaders;
void shutdown() noexcept {
// TODO we should probably store this all in g_state.gx instead
sUniformBindGroupLayouts.clear();
sTextureBindGroupLayouts.clear();
g_textures.fill({});
g_gxCachedShaders.clear();
}
wgpu::SamplerDescriptor TextureBind::get_descriptor() const noexcept {
wgpu::AddressMode mode; wgpu::AddressMode mode;
switch (clampMode) { switch (clampMode) {
case metaforce::EClampMode::Clamp: case metaforce::EClampMode::Clamp:
@ -354,7 +550,8 @@ wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept {
.magFilter = wgpu::FilterMode::Linear, .magFilter = wgpu::FilterMode::Linear,
.minFilter = wgpu::FilterMode::Linear, .minFilter = wgpu::FilterMode::Linear,
.mipmapFilter = wgpu::FilterMode::Linear, .mipmapFilter = wgpu::FilterMode::Linear,
.maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy, .maxAnisotropy = g_graphicsConfig.textureAnistropy,
}; };
} }
} // namespace gx
} // namespace aurora::gfx } // namespace aurora::gfx

View File

@ -4,7 +4,7 @@
#include <variant> #include <variant>
namespace aurora::gfx { namespace aurora::gfx::gx {
extern zeus::CMatrix4f g_mv; extern zeus::CMatrix4f g_mv;
extern zeus::CMatrix4f g_mvInv; extern zeus::CMatrix4f g_mvInv;
extern zeus::CMatrix4f g_proj; extern zeus::CMatrix4f g_proj;
@ -27,6 +27,7 @@ extern bool g_alphaDiscard;
struct SChannelState { struct SChannelState {
zeus::CColor matColor; zeus::CColor matColor;
zeus::CColor ambColor; zeus::CColor ambColor;
GX::ColorSrc matSrc;
}; };
extern std::array<SChannelState, 2> g_colorChannels; extern std::array<SChannelState, 2> g_colorChannels;
using LightVariant = std::variant<std::monostate, Light, zeus::CColor>; using LightVariant = std::variant<std::monostate, Light, zeus::CColor>;
@ -50,13 +51,13 @@ struct STevStage {
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept
: colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {} : colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {}
}; };
struct STextureBind { struct TextureBind {
aurora::gfx::TextureHandle handle; aurora::gfx::TextureHandle handle;
metaforce::EClampMode clampMode; metaforce::EClampMode clampMode;
float lod; float lod;
STextureBind() noexcept = default; TextureBind() noexcept = default;
STextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept TextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept
: handle(std::move(handle)), clampMode(clampMode), lod(lod) {} : handle(std::move(handle)), clampMode(clampMode), lod(lod) {}
void reset() noexcept { handle.reset(); }; void reset() noexcept { handle.reset(); };
[[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept; [[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept;
@ -66,22 +67,21 @@ struct STextureBind {
constexpr u32 maxTevStages = GX::MAX_TEVSTAGE; constexpr u32 maxTevStages = GX::MAX_TEVSTAGE;
extern std::array<std::optional<STevStage>, maxTevStages> g_tevStages; extern std::array<std::optional<STevStage>, maxTevStages> g_tevStages;
constexpr u32 maxTextures = 8; constexpr u32 maxTextures = 8;
extern std::array<STextureBind, maxTextures> g_textures; extern std::array<TextureBind, maxTextures> g_textures;
static inline Mat4x4<float> get_combined_matrix() noexcept { return g_proj * g_mv; } static inline Mat4x4<float> get_combined_matrix() noexcept { return g_proj * g_mv; }
const STextureBind& get_texture(GX::TexMapID id) noexcept; void shutdown() noexcept;
const TextureBind& get_texture(GX::TexMapID id) noexcept;
struct GXShaderConfig { struct ShaderConfig {
std::array<std::optional<STevStage>, maxTevStages> tevStages; std::array<std::optional<STevStage>, maxTevStages> tevStages;
std::array<GX::ColorSrc, 2> channelMatSrcs;
bool alphaDiscard; bool alphaDiscard;
bool denormalizedVertexAttributes; bool denormalizedVertexAttributes;
bool denormalizedNorm;
bool denormalizedColor;
std::bitset<maxTextures> boundTextures;
}; };
struct GXPipelineConfig { struct PipelineConfig {
GXShaderConfig shaderConfig; ShaderConfig shaderConfig;
GX::Primitive primitive; GX::Primitive primitive;
metaforce::ERglEnum depthFunc; metaforce::ERglEnum depthFunc;
metaforce::ERglCullMode cullMode; metaforce::ERglCullMode cullMode;
@ -91,9 +91,65 @@ struct GXPipelineConfig {
std::optional<float> dstAlpha; std::optional<float> dstAlpha;
bool depthCompare, depthUpdate, alphaUpdate; bool depthCompare, depthUpdate, alphaUpdate;
}; };
void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive, // Output info from shader generation
std::bitset<maxTextures> enabledTextures) noexcept; struct ShaderInfo {
wgpu::ShaderModule build_shader(const GXShaderConfig& config); std::bitset<maxTextures> sampledTextures;
wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers, std::bitset<4> sampledKcolors;
const GXPipelineConfig& config, zstring_view label) noexcept; std::bitset<2> sampledColorChannels;
} // namespace aurora::gfx u32 uniformSize = 0;
bool usesVtxColor : 1 = false;
bool usesNormal : 1 = false;
};
ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) noexcept;
wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info,
ArrayRef<wgpu::VertexBufferLayout> vtxBuffers, wgpu::ShaderModule shader,
zstring_view label) noexcept;
std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& config) noexcept;
// Range build_vertex_buffer(const GXShaderInfo& info) noexcept;
Range build_uniform(const ShaderInfo& info) noexcept;
struct GXBindGroupLayouts {
wgpu::BindGroupLayout uniformLayout;
wgpu::BindGroupLayout samplerLayout;
wgpu::BindGroupLayout textureLayout;
};
struct GXBindGroups {
BindGroupRef uniformBindGroup;
BindGroupRef samplerBindGroup;
BindGroupRef textureBindGroup;
};
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info) noexcept;
GXBindGroups build_bind_groups(const ShaderInfo& info) noexcept;
} // namespace aurora::gfx::gx
namespace aurora {
template <>
inline void xxh3_update(XXH3_state_t& state, const gfx::gx::STevStage& input) {
XXH3_64bits_update(&state, &input.colorPass, sizeof(metaforce::CTevCombiners::ColorPass));
XXH3_64bits_update(&state, &input.alphaPass, sizeof(metaforce::CTevCombiners::AlphaPass));
XXH3_64bits_update(&state, &input.colorOp, sizeof(metaforce::CTevCombiners::CTevOp));
XXH3_64bits_update(&state, &input.alphaOp, sizeof(metaforce::CTevCombiners::CTevOp));
XXH3_64bits_update(&state, &input.kcSel, sizeof(GX::TevKColorSel));
XXH3_64bits_update(&state, &input.kaSel, sizeof(GX::TevKAlphaSel));
XXH3_64bits_update(&state, &input.texCoordId, sizeof(GX::TexCoordID));
XXH3_64bits_update(&state, &input.texMapId, sizeof(GX::TexMapID));
XXH3_64bits_update(&state, &input.channelId, sizeof(GX::ChannelID));
}
template <>
inline XXH64_hash_t xxh3_hash(const gfx::gx::ShaderConfig& input, XXH64_hash_t seed) {
XXH3_state_t state;
XXH3_INITSTATE(&state);
XXH3_64bits_reset_withSeed(&state, seed);
for (const auto& item : input.tevStages) {
if (!item) {
break;
}
xxh3_update(state, *item);
}
XXH3_64bits_update(&state, input.channelMatSrcs.data(), input.channelMatSrcs.size() * sizeof(GX::ColorSrc));
XXH3_64bits_update(&state, &input.alphaDiscard, sizeof(bool));
XXH3_64bits_update(&state, &input.denormalizedVertexAttributes, sizeof(bool));
return XXH3_64bits_digest(&state);
}
} // namespace aurora

View File

@ -3,14 +3,16 @@
#include "../gpu.hpp" #include "../gpu.hpp"
#include "gx.hpp" #include "gx.hpp"
namespace aurora::gfx { #include <unordered_map>
namespace aurora::gfx::gx {
using namespace fmt::literals; using namespace fmt::literals;
static logvisor::Module Log("aurora::gfx::gx"); static logvisor::Module Log("aurora::gfx::gx");
std::unordered_map<ShaderRef, wgpu::ShaderModule> g_gxCachedShaders; std::unordered_map<ShaderRef, std::pair<wgpu::ShaderModule, gx::ShaderInfo>> g_gxCachedShaders;
static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) { static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx, const STevStage& stage, ShaderInfo& info) {
switch (arg) { switch (arg) {
case GX::CC_CPREV: case GX::CC_CPREV:
return "prev.rgb"; return "prev.rgb";
@ -24,26 +26,145 @@ static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) {
case GX::CC_A2: case GX::CC_A2:
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg); Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg);
unreachable(); unreachable();
case GX::CC_TEXC: case GX::CC_TEXC: {
return fmt::format(FMT_STRING("sampled{}.rgb"), stageIdx); if (stage.texMapId == GX::TEXMAP_NULL) {
case GX::CC_TEXA: Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx);
return fmt::format(FMT_STRING("sampled{}.a"), stageIdx); unreachable();
case GX::CC_RASC: } else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) {
return "rast.rgb"; Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
case GX::CC_RASA: unreachable();
return "rast.a"; }
info.sampledTextures.set(stage.texMapId);
return fmt::format(FMT_STRING("sampled{}.rgb"), stage.texMapId);
}
case GX::CC_TEXA: {
if (stage.texMapId == GX::TEXMAP_NULL) {
Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx);
unreachable();
} else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) {
Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
unreachable();
}
info.sampledTextures.set(stage.texMapId);
return fmt::format(FMT_STRING("sampled{}.a"), stage.texMapId);
}
case GX::CC_RASC: {
if (stage.channelId == GX::COLOR_NULL) {
Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx);
unreachable();
} else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
}
u32 idx = stage.channelId - GX::COLOR0A0;
info.sampledColorChannels.set(idx);
return fmt::format(FMT_STRING("rast{}.rgb"), idx);
}
case GX::CC_RASA: {
if (stage.channelId == GX::COLOR_NULL) {
Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx);
unreachable();
} else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
}
u32 idx = stage.channelId - GX::COLOR0A0;
info.sampledColorChannels.set(idx);
return fmt::format(FMT_STRING("rast{}.a"), idx);
}
case GX::CC_ONE: case GX::CC_ONE:
return "1.0"; return "1.0";
case GX::CC_HALF: case GX::CC_HALF:
return "0.5"; return "0.5";
case GX::CC_KONST: case GX::CC_KONST: {
return fmt::format(FMT_STRING("ubuf.kcolor{}.rgb"), stageIdx); switch (stage.kcSel) {
case GX::TEV_KCSEL_8_8:
return "1.0";
case GX::TEV_KCSEL_7_8:
return "(7.0/8.0)";
case GX::TEV_KCSEL_6_8:
return "(6.0/8.0)";
case GX::TEV_KCSEL_5_8:
return "(5.0/8.0)";
case GX::TEV_KCSEL_4_8:
return "(4.0/8.0)";
case GX::TEV_KCSEL_3_8:
return "(3.0/8.0)";
case GX::TEV_KCSEL_2_8:
return "(2.0/8.0)";
case GX::TEV_KCSEL_1_8:
return "(1.0/8.0)";
case GX::TEV_KCSEL_K0:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.rgb";
case GX::TEV_KCSEL_K1:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.rgb";
case GX::TEV_KCSEL_K2:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.rgb";
case GX::TEV_KCSEL_K3:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.rgb";
case GX::TEV_KCSEL_K0_R:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.r";
case GX::TEV_KCSEL_K1_R:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.r";
case GX::TEV_KCSEL_K2_R:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.r";
case GX::TEV_KCSEL_K3_R:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.r";
case GX::TEV_KCSEL_K0_G:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.g";
case GX::TEV_KCSEL_K1_G:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.g";
case GX::TEV_KCSEL_K2_G:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.g";
case GX::TEV_KCSEL_K3_G:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.g";
case GX::TEV_KCSEL_K0_B:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.b";
case GX::TEV_KCSEL_K1_B:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.b";
case GX::TEV_KCSEL_K2_B:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.b";
case GX::TEV_KCSEL_K3_B:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.b";
case GX::TEV_KCSEL_K0_A:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.a";
case GX::TEV_KCSEL_K1_A:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.a";
case GX::TEV_KCSEL_K2_A:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.a";
case GX::TEV_KCSEL_K3_A:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.a";
default:
Log.report(logvisor::Fatal, FMT_STRING("invalid kcSel {}"), stage.kcSel);
unreachable();
}
}
case GX::CC_ZERO: case GX::CC_ZERO:
return "0.0"; return "0.0";
} }
} }
static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) { static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx, const STevStage& stage, ShaderInfo& info) {
switch (arg) { switch (arg) {
case GX::CA_APREV: case GX::CA_APREV:
return "prev.a"; return "prev.a";
@ -52,12 +173,100 @@ static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) {
case GX::CA_A2: case GX::CA_A2:
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg); Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg);
unreachable(); unreachable();
case GX::CA_TEXA: case GX::CA_TEXA: {
return fmt::format(FMT_STRING("sampled{}.a"), stageIdx); if (stage.texMapId == GX::TEXMAP_NULL) {
case GX::CA_RASA: Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx);
return "rast.a"; unreachable();
case GX::CA_KONST: } else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) {
return fmt::format(FMT_STRING("ubuf.kcolor{}.a"), stageIdx); Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx);
unreachable();
}
info.sampledTextures.set(stage.texMapId);
return fmt::format(FMT_STRING("sampled{}.a"), stage.texMapId);
}
case GX::CA_RASA: {
if (stage.channelId == GX::COLOR_NULL) {
Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx);
unreachable();
} else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) {
Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx);
unreachable();
}
u32 idx = stage.channelId - GX::COLOR0A0;
info.sampledColorChannels.set(idx);
return fmt::format(FMT_STRING("rast{}.a"), idx);
}
case GX::CA_KONST: {
switch (stage.kaSel) {
case GX::TEV_KASEL_8_8:
return "1.0";
case GX::TEV_KASEL_7_8:
return "(7.0/8.0)";
case GX::TEV_KASEL_6_8:
return "(6.0/8.0)";
case GX::TEV_KASEL_5_8:
return "(5.0/8.0)";
case GX::TEV_KASEL_4_8:
return "(4.0/8.0)";
case GX::TEV_KASEL_3_8:
return "(3.0/8.0)";
case GX::TEV_KASEL_2_8:
return "(2.0/8.0)";
case GX::TEV_KASEL_1_8:
return "(1.0/8.0)";
case GX::TEV_KASEL_K0_R:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.r";
case GX::TEV_KASEL_K1_R:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.r";
case GX::TEV_KASEL_K2_R:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.r";
case GX::TEV_KASEL_K3_R:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.r";
case GX::TEV_KASEL_K0_G:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.g";
case GX::TEV_KASEL_K1_G:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.g";
case GX::TEV_KASEL_K2_G:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.g";
case GX::TEV_KASEL_K3_G:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.g";
case GX::TEV_KASEL_K0_B:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.b";
case GX::TEV_KASEL_K1_B:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.b";
case GX::TEV_KASEL_K2_B:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.b";
case GX::TEV_KASEL_K3_B:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.b";
case GX::TEV_KASEL_K0_A:
info.sampledKcolors.set(0);
return "ubuf.kcolor0.a";
case GX::TEV_KASEL_K1_A:
info.sampledKcolors.set(1);
return "ubuf.kcolor1.a";
case GX::TEV_KASEL_K2_A:
info.sampledKcolors.set(2);
return "ubuf.kcolor2.a";
case GX::TEV_KASEL_K3_A:
info.sampledKcolors.set(3);
return "ubuf.kcolor3.a";
default:
Log.report(logvisor::Fatal, FMT_STRING("invalid kaSel {}"), stage.kaSel);
unreachable();
}
}
case GX::CA_ZERO: case GX::CA_ZERO:
return "0.0"; return "0.0";
} }
@ -99,12 +308,51 @@ static std::string_view tev_scale(GX::TevScale scale) {
} }
} }
wgpu::ShaderModule build_shader(const GXShaderConfig& config) { std::pair<wgpu::ShaderModule, ShaderInfo> build_shader(const ShaderConfig& config) noexcept {
const auto hash = xxh3_hash(config); const auto hash = xxh3_hash(config);
if (g_gxCachedShaders.contains(hash)) { if (g_gxCachedShaders.contains(hash)) {
return g_gxCachedShaders[hash]; return g_gxCachedShaders[hash];
} }
Log.report(logvisor::Info, FMT_STRING("Shader config (hash {:x}):"), hash);
{
for (int i = 0; i < config.tevStages.size(); ++i) {
const auto& stage = config.tevStages[i];
if (!stage) {
break;
}
Log.report(logvisor::Info, FMT_STRING(" tevStages[{}]:"), i);
Log.report(logvisor::Info, FMT_STRING(" color_a: {}"), stage->colorPass.x0_a);
Log.report(logvisor::Info, FMT_STRING(" color_b: {}"), stage->colorPass.x4_b);
Log.report(logvisor::Info, FMT_STRING(" color_c: {}"), stage->colorPass.x8_c);
Log.report(logvisor::Info, FMT_STRING(" color_d: {}"), stage->colorPass.xc_d);
Log.report(logvisor::Info, FMT_STRING(" alpha_a: {}"), stage->alphaPass.x0_a);
Log.report(logvisor::Info, FMT_STRING(" alpha_b: {}"), stage->alphaPass.x4_b);
Log.report(logvisor::Info, FMT_STRING(" alpha_c: {}"), stage->alphaPass.x8_c);
Log.report(logvisor::Info, FMT_STRING(" alpha_d: {}"), stage->alphaPass.xc_d);
Log.report(logvisor::Info, FMT_STRING(" color_op_clamp: {}"), stage->colorOp.x0_clamp);
Log.report(logvisor::Info, FMT_STRING(" color_op_op: {}"), stage->colorOp.x4_op);
Log.report(logvisor::Info, FMT_STRING(" color_op_bias: {}"), stage->colorOp.x8_bias);
Log.report(logvisor::Info, FMT_STRING(" color_op_scale: {}"), stage->colorOp.xc_scale);
Log.report(logvisor::Info, FMT_STRING(" color_op_reg_id: {}"), stage->colorOp.x10_regId);
Log.report(logvisor::Info, FMT_STRING(" alpha_op_clamp: {}"), stage->alphaOp.x0_clamp);
Log.report(logvisor::Info, FMT_STRING(" alpha_op_op: {}"), stage->alphaOp.x4_op);
Log.report(logvisor::Info, FMT_STRING(" alpha_op_bias: {}"), stage->alphaOp.x8_bias);
Log.report(logvisor::Info, FMT_STRING(" alpha_op_scale: {}"), stage->alphaOp.xc_scale);
Log.report(logvisor::Info, FMT_STRING(" alpha_op_reg_id: {}"), stage->alphaOp.x10_regId);
Log.report(logvisor::Info, FMT_STRING(" kc_sel: {}"), stage->kcSel);
Log.report(logvisor::Info, FMT_STRING(" ka_sel: {}"), stage->kaSel);
Log.report(logvisor::Info, FMT_STRING(" texCoordId: {}"), stage->texCoordId);
Log.report(logvisor::Info, FMT_STRING(" texMapId: {}"), stage->texMapId);
Log.report(logvisor::Info, FMT_STRING(" channelId: {}"), stage->channelId);
}
for (int i = 0; i < config.channelMatSrcs.size(); ++i) {
Log.report(logvisor::Info, FMT_STRING(" channelMatSrcs[{}]: {}"), i, config.channelMatSrcs[i]);
}
Log.report(logvisor::Info, FMT_STRING(" alphaDiscard: {}"), config.alphaDiscard);
Log.report(logvisor::Info, FMT_STRING(" denormalizedVertexAttributes: {}"), config.denormalizedVertexAttributes);
}
std::string uniBufAttrs; std::string uniBufAttrs;
std::string sampBindings; std::string sampBindings;
std::string texBindings; std::string texBindings;
@ -112,62 +360,23 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
std::string vtxInAttrs; std::string vtxInAttrs;
std::string vtxXfrAttrs; std::string vtxXfrAttrs;
size_t locIdx = 0; size_t locIdx = 0;
if (config.denormalizedNorm) { // TODO
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3<f32>;"), locIdx); // if (config.denormalizedNorm) {
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3<f32>"), ++locIdx); // vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3<f32>;"), locIdx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;")); // vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3<f32>"), ++locIdx);
} // vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;"));
if (config.denormalizedColor) { // }
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4<f32>;"), locIdx);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4<f32>"), ++locIdx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;"));
}
for (int i = 0; i < maxTextures; ++i) {
if (!config.boundTextures.test(i)) {
continue;
}
uniBufAttrs += fmt::format(FMT_STRING("\n tex{}_lod: f32;"), i);
sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({0})\n"
"var tex{0}_samp: sampler;"), i);
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({0})\n"
"var tex{0}: texture_2d<f32>;"), i);
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2<f32>;"), locIdx, i);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2<f32>"), locIdx + 1, i);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = in_tex{0}_uv;"), i);
locIdx++;
}
ShaderInfo info{
.uniformSize = 64, // MVP MTX
};
std::string fragmentFnPre;
std::string fragmentFn; std::string fragmentFn;
bool hasRast = false;
for (size_t idx = 0; const auto& stage : config.tevStages) {
if (!stage) {
break;
}
if (stage->colorPass.x0_a == GX::TevColorArg::CC_TEXC || stage->colorPass.x4_b == GX::TevColorArg::CC_TEXC ||
stage->colorPass.x8_c == GX::TevColorArg::CC_TEXC || stage->colorPass.xc_d == GX::TevColorArg::CC_TEXC ||
stage->alphaPass.x0_a == GX::TevAlphaArg::CA_TEXA || stage->alphaPass.x4_b == GX::TevAlphaArg::CA_TEXA ||
stage->alphaPass.x8_c == GX::TevAlphaArg::CA_TEXA || stage->alphaPass.xc_d == GX::TevAlphaArg::CA_TEXA) {
fragmentFn += fmt::format(
FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"),
idx);
}
if (!hasRast) {
if (stage->colorPass.x0_a == GX::TevColorArg::CC_RASC || stage->colorPass.x4_b == GX::TevColorArg::CC_RASC ||
stage->colorPass.x8_c == GX::TevColorArg::CC_RASC || stage->colorPass.xc_d == GX::TevColorArg::CC_RASC ||
stage->alphaPass.x0_a == GX::TevAlphaArg::CA_RASA || stage->alphaPass.x4_b == GX::TevAlphaArg::CA_RASA ||
stage->alphaPass.x8_c == GX::TevAlphaArg::CA_RASA || stage->alphaPass.xc_d == GX::TevAlphaArg::CA_RASA) {
fragmentFn += fmt::format(FMT_STRING("\n var rast = in.clr; // TODO lighting")); // TODO lighting
hasRast = true;
}
}
idx++;
}
for (size_t idx = 0; const auto& stage : config.tevStages) { for (size_t idx = 0; const auto& stage : config.tevStages) {
if (!stage) { if (!stage) {
break; break;
} }
{ {
std::string op;
std::string outReg; std::string outReg;
switch (stage->colorOp.x10_regId) { switch (stage->colorOp.x10_regId) {
case GX::TevRegID::TEVPREV: case GX::TevRegID::TEVPREV:
@ -176,15 +385,16 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
default: default:
Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"), stage->colorOp.x10_regId); Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"), stage->colorOp.x10_regId);
} }
op = fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), std::string op =
color_arg_reg(stage->colorPass.x0_a, idx), color_arg_reg(stage->colorPass.x4_b, idx), fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"),
color_arg_reg(stage->colorPass.x8_c, idx), color_arg_reg(stage->colorPass.xc_d, idx), color_arg_reg(stage->colorPass.x0_a, idx, *stage, info),
tev_op(stage->colorOp.x4_op), tev_bias(stage->colorOp.x8_bias), color_arg_reg(stage->colorPass.x4_b, idx, *stage, info),
tev_scale(stage->colorOp.xc_scale)); color_arg_reg(stage->colorPass.x8_c, idx, *stage, info),
color_arg_reg(stage->colorPass.xc_d, idx, *stage, info), tev_op(stage->colorOp.x4_op),
tev_bias(stage->colorOp.x8_bias), tev_scale(stage->colorOp.xc_scale));
fragmentFn += fmt::format(FMT_STRING("\n {0} = vec4<f32>({1}, {0}.a);"), outReg, op); fragmentFn += fmt::format(FMT_STRING("\n {0} = vec4<f32>({1}, {0}.a);"), outReg, op);
} }
{ {
std::string op;
std::string outReg; std::string outReg;
switch (stage->alphaOp.x10_regId) { switch (stage->alphaOp.x10_regId) {
case GX::TevRegID::TEVPREV: case GX::TevRegID::TEVPREV:
@ -193,15 +403,74 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
default: default:
Log.report(logvisor::Fatal, FMT_STRING("TODO: alphaOp outReg {}"), stage->alphaOp.x10_regId); Log.report(logvisor::Fatal, FMT_STRING("TODO: alphaOp outReg {}"), stage->alphaOp.x10_regId);
} }
op = fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), std::string op =
alpha_arg_reg(stage->alphaPass.x0_a, idx), alpha_arg_reg(stage->alphaPass.x4_b, idx), fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"),
alpha_arg_reg(stage->alphaPass.x8_c, idx), alpha_arg_reg(stage->alphaPass.xc_d, idx), alpha_arg_reg(stage->alphaPass.x0_a, idx, *stage, info),
tev_op(stage->alphaOp.x4_op), tev_bias(stage->alphaOp.x8_bias), alpha_arg_reg(stage->alphaPass.x4_b, idx, *stage, info),
tev_scale(stage->alphaOp.xc_scale)); alpha_arg_reg(stage->alphaPass.x8_c, idx, *stage, info),
alpha_arg_reg(stage->alphaPass.xc_d, idx, *stage, info), tev_op(stage->alphaOp.x4_op),
tev_bias(stage->alphaOp.x8_bias), tev_scale(stage->alphaOp.xc_scale));
fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op); fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op);
} }
idx++; idx++;
} }
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
if (!info.sampledColorChannels.test(i)) {
continue;
}
uniBufAttrs += fmt::format(FMT_STRING("\n cc{0}_amb: vec4<f32>;"), i);
uniBufAttrs += fmt::format(FMT_STRING("\n cc{0}_mat: vec4<f32>;"), i); // TODO not needed for SRC_VTX
info.uniformSize += 32;
if (config.channelMatSrcs[i] == GX::SRC_VTX) {
if (config.denormalizedVertexAttributes) {
if (!info.usesVtxColor) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4<f32>;"), locIdx);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4<f32>"), ++locIdx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;"));
}
fragmentFnPre += fmt::format(FMT_STRING("\n var rast{} = in.clr; // TODO lighting"), i);
} else {
Log.report(logvisor::Fatal, FMT_STRING("Don't know how to do this yet")); // TODO
}
info.usesVtxColor = true;
} else {
fragmentFnPre += fmt::format(FMT_STRING("\n var rast{0} = ubuf.cc{0}_mat; // TODO lighting"), i);
}
}
for (int i = 0; i < info.sampledKcolors.size(); ++i) {
if (!info.sampledKcolors.test(i)) {
continue;
}
uniBufAttrs += fmt::format(FMT_STRING("\n kcolor{}: vec4<f32>;"), i);
info.uniformSize += 16;
}
for (int i = 0; i < info.sampledTextures.size(); ++i) {
if (!info.sampledTextures.test(i)) {
continue;
}
uniBufAttrs += fmt::format(FMT_STRING("\n tex{}_lod: f32;"), i);
info.uniformSize += 4;
sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({0})\n"
"var tex{0}_samp: sampler;"),
i);
texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({0})\n"
"var tex{0}: texture_2d<f32>;"),
i);
if (config.denormalizedVertexAttributes) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2<f32>;"), locIdx, i);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2<f32>"), locIdx + 1, i);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = in_tex{0}_uv;"), i);
} else {
Log.report(logvisor::Fatal, FMT_STRING("Don't know how to do this yet")); // TODO
}
fragmentFnPre += fmt::format(
FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"), i);
locIdx++;
}
const auto shaderSource = const auto shaderSource =
fmt::format(FMT_STRING(R"""( fmt::format(FMT_STRING(R"""(
@ -226,13 +495,13 @@ fn vs_main(
@stage(fragment) @stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
var prev: vec4<f32>;{fragmentFn} var prev: vec4<f32>;{fragmentFnPre}{fragmentFn}
return prev; return prev;
}} }}
)"""), )"""),
"uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings, "uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings,
"vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs, "vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs,
"fragmentFn"_a = fragmentFn); "fragmentFn"_a = fragmentFn, "fragmentFnPre"_a = fragmentFnPre);
Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource); Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource);
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{}; wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
@ -242,8 +511,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
.label = "GX Shader", .label = "GX Shader",
}; };
auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor); auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor);
g_gxCachedShaders.emplace(hash, shader);
return shader; info.uniformSize = align_uniform(info.uniformSize);
const auto pair = std::make_pair(std::move(shader), info);
g_gxCachedShaders.emplace(hash, pair);
return pair;
} }
} // namespace aurora::gfx } // namespace aurora::gfx::gx

View File

@ -3,6 +3,8 @@
#include "../common.hpp" #include "../common.hpp"
#include "../gx.hpp" #include "../gx.hpp"
#include <unordered_map>
namespace aurora::gfx::model { namespace aurora::gfx::model {
struct DrawData { struct DrawData {
PipelineRef pipeline; PipelineRef pipeline;
@ -14,7 +16,7 @@ struct DrawData {
BindGroupRef textureBindGroup; BindGroupRef textureBindGroup;
}; };
struct PipelineConfig : GXPipelineConfig { struct PipelineConfig : gx::PipelineConfig {
ShaderRef shader; ShaderRef shader;
uint32_t uniformSize; uint32_t uniformSize;
}; };

View File

@ -4,6 +4,8 @@
#include "common.hpp" #include "common.hpp"
#include "gx.hpp" #include "gx.hpp"
#include <unordered_map>
namespace aurora::gfx { namespace aurora::gfx {
static logvisor::Module Log("aurora::gfx::stream"); static logvisor::Module Log("aurora::gfx::stream");
@ -53,74 +55,19 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co
} }
void stream_end() noexcept { void stream_end() noexcept {
const auto& tex = get_texture(GX::TEXMAP0);
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture && !tex) {
Log.report(logvisor::Fatal, FMT_STRING("Stream has texture but no texture bound!"));
unreachable();
}
const auto vertRange = push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size()); const auto vertRange = push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size());
ByteBuffer uniBuf; stream::PipelineConfig config{};
std::bitset<g_colorRegs.size()> usedColors;
{
const auto xf = get_combined_matrix();
uniBuf.append(&xf, 64);
}
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) {
uniBuf.append(&tex.lod, 4);
}
const auto uniRange = push_uniform(uniBuf.data(), uniBuf.size());
const auto uniform_size = align_uniform(uniBuf.size());
stream::PipelineConfig config{
.uniformSize = uniform_size,
.flags = sStreamState->flags,
};
config.shaderConfig.denormalizedVertexAttributes = true; config.shaderConfig.denormalizedVertexAttributes = true;
config.shaderConfig.denormalizedNorm = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasNormal); const auto info = populate_pipeline_config(config, sStreamState->primitive);
config.shaderConfig.denormalizedColor = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasColor);
populate_gx_pipeline_config(config, sStreamState->primitive, {1});
const auto pipeline = pipeline_ref(config); const auto pipeline = pipeline_ref(config);
BindGroupRef samplerBindGroup{};
BindGroupRef textureBindGroup{};
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) {
const auto& state = get_state<stream::State>();
{
const std::array samplerEntries{wgpu::BindGroupEntry{
.binding = 0,
.sampler = sampler_ref(tex.get_descriptor()),
}};
samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "Stream Sampler Bind Group",
.layout = state.samplerLayout,
.entryCount = samplerEntries.size(),
.entries = samplerEntries.data(),
});
}
{
const std::array textureEntries{wgpu::BindGroupEntry{
.binding = 0,
.textureView = tex.handle.ref->view,
}};
textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "Stream Texture Bind Group",
.layout = state.textureLayout,
.entryCount = textureEntries.size(),
.entries = textureEntries.data(),
});
}
}
push_draw_command(stream::DrawData{ push_draw_command(stream::DrawData{
.pipeline = pipeline, .pipeline = pipeline,
.vertRange = vertRange, .vertRange = vertRange,
.uniformRange = uniRange, .uniformRange = build_uniform(info),
.vertexCount = sStreamState->vertexCount, .vertexCount = sStreamState->vertexCount,
.uniformSize = uniform_size, .bindGroups = build_bind_groups(info),
.samplerBindGroup = samplerBindGroup,
.textureBindGroup = textureBindGroup,
}); });
sStreamState.reset(); sStreamState.reset();

View File

@ -14,10 +14,10 @@ namespace aurora::gfx::stream {
static logvisor::Module Log("aurora::gfx::stream"); static logvisor::Module Log("aurora::gfx::stream");
using gpu::g_device; using gpu::g_device;
using gpu::g_graphicsConfig;
using gpu::utils::make_vertex_state;
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);
std::array<wgpu::VertexAttribute, 4> attributes{}; std::array<wgpu::VertexAttribute, 4> attributes{};
attributes[0] = wgpu::VertexAttribute{ attributes[0] = wgpu::VertexAttribute{
.format = wgpu::VertexFormat::Float32x3, .format = wgpu::VertexFormat::Float32x3,
@ -26,7 +26,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
}; };
uint64_t offset = 12; uint64_t offset = 12;
uint32_t shaderLocation = 1; uint32_t shaderLocation = 1;
if (config.flags & metaforce::EStreamFlagBits::fHasNormal) { if (info.usesNormal) {
attributes[shaderLocation] = wgpu::VertexAttribute{ attributes[shaderLocation] = wgpu::VertexAttribute{
.format = wgpu::VertexFormat::Float32x3, .format = wgpu::VertexFormat::Float32x3,
.offset = offset, .offset = offset,
@ -35,7 +35,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
offset += 12; offset += 12;
shaderLocation++; shaderLocation++;
} }
if (config.flags & metaforce::EStreamFlagBits::fHasColor) { if (info.usesVtxColor) {
attributes[shaderLocation] = wgpu::VertexAttribute{ attributes[shaderLocation] = wgpu::VertexAttribute{
.format = wgpu::VertexFormat::Float32x4, .format = wgpu::VertexFormat::Float32x4,
.offset = offset, .offset = offset,
@ -44,7 +44,11 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
offset += 16; offset += 16;
shaderLocation++; shaderLocation++;
} }
if (config.flags & metaforce::EStreamFlagBits::fHasTexture) { // TODO only sample 1?
for (int i = 0; i < info.sampledTextures.size(); ++i) {
if (!info.sampledTextures.test(i)) {
continue;
}
attributes[shaderLocation] = wgpu::VertexAttribute{ attributes[shaderLocation] = wgpu::VertexAttribute{
.format = wgpu::VertexFormat::Float32x2, .format = wgpu::VertexFormat::Float32x2,
.offset = offset, .offset = offset,
@ -59,57 +63,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
.attributes = attributes.data(), .attributes = attributes.data(),
}}; }};
wgpu::BindGroupLayout uniformLayout; return build_pipeline(config, info, vertexBuffers, shader, "Stream Pipeline");
if (state.uniform.contains(config.uniformSize)) {
uniformLayout = state.uniform.at(config.uniformSize).layout;
} else {
const std::array uniformLayoutEntries{wgpu::BindGroupLayoutEntry{
.binding = 0,
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
.buffer =
wgpu::BufferBindingLayout{
.type = wgpu::BufferBindingType::Uniform,
.hasDynamicOffset = true,
.minBindingSize = config.uniformSize,
},
}};
const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
.label = "Stream Uniform Bind Group Layout",
.entryCount = uniformLayoutEntries.size(),
.entries = uniformLayoutEntries.data(),
};
uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor);
const std::array uniformBindGroupEntries{wgpu::BindGroupEntry{
.binding = 0,
.buffer = g_uniformBuffer,
.size = config.uniformSize,
}};
const auto uniformBindGroupDescriptor = wgpu::BindGroupDescriptor{
.label = "Stream Quad Uniform Bind Group",
.layout = uniformLayout,
.entryCount = uniformBindGroupEntries.size(),
.entries = uniformBindGroupEntries.data(),
};
auto uniformBindGroup = g_device.CreateBindGroup(&uniformBindGroupDescriptor);
state.uniform.try_emplace(config.uniformSize, uniformLayout, std::move(uniformBindGroup));
}
const std::array bindGroupLayouts{
uniformLayout,
state.samplerLayout,
state.textureLayout,
};
const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{
.label = "Stream Pipeline Layout",
.bindGroupLayoutCount =
static_cast<uint32_t>(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1),
.bindGroupLayouts = bindGroupLayouts.data(),
};
auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor);
return build_pipeline(pipelineLayout, vertexBuffers, config, "Stream Pipeline");
} }
State construct_state() { State construct_state() {
@ -160,10 +114,10 @@ void render(const State& state, const DrawData& data, const wgpu::RenderPassEnco
} }
const std::array offsets{data.uniformRange.first}; const std::array offsets{data.uniformRange.first};
pass.SetBindGroup(0, state.uniform.at(data.uniformSize).bindGroup, offsets.size(), offsets.data()); pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(), offsets.data());
if (data.samplerBindGroup && data.textureBindGroup) { if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) {
pass.SetBindGroup(1, find_bind_group(data.samplerBindGroup)); pass.SetBindGroup(1, find_bind_group(data.bindGroups.samplerBindGroup));
pass.SetBindGroup(2, find_bind_group(data.textureBindGroup)); pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup));
} }
pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second); pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second);
pass.Draw(data.vertexCount); pass.Draw(data.vertexCount);

View File

@ -3,21 +3,18 @@
#include "../common.hpp" #include "../common.hpp"
#include "../gx.hpp" #include "../gx.hpp"
#include <unordered_map>
namespace aurora::gfx::stream { namespace aurora::gfx::stream {
struct DrawData { struct DrawData {
PipelineRef pipeline; PipelineRef pipeline;
Range vertRange; Range vertRange;
Range uniformRange; Range uniformRange;
uint32_t vertexCount; uint32_t vertexCount;
uint32_t uniformSize; gx::GXBindGroups bindGroups;
BindGroupRef samplerBindGroup;
BindGroupRef textureBindGroup;
}; };
struct PipelineConfig : public GXPipelineConfig { struct PipelineConfig : public gx::PipelineConfig {};
uint32_t uniformSize;
metaforce::EStreamFlags flags;
};
struct CachedBindGroup { struct CachedBindGroup {
wgpu::BindGroupLayout layout; wgpu::BindGroupLayout layout;
@ -30,6 +27,7 @@ struct State {
wgpu::BindGroupLayout textureLayout; wgpu::BindGroupLayout textureLayout;
mutable std::unordered_map<uint32_t, CachedBindGroup> uniform; mutable std::unordered_map<uint32_t, CachedBindGroup> uniform;
mutable std::unordered_map<uint64_t, wgpu::Sampler> sampler; mutable std::unordered_map<uint64_t, wgpu::Sampler> sampler;
mutable std::unordered_map<PipelineRef, gx::ShaderInfo> shaderInfo;
}; };
State construct_state(); State construct_state();

View File

@ -348,7 +348,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type,
const auto vertRange = push_verts(ArrayRef{verts}); const auto vertRange = push_verts(ArrayRef{verts});
const auto uniform = Uniform{ const auto uniform = Uniform{
.xf = get_combined_matrix(), .xf = gx::get_combined_matrix(),
.color = color, .color = color,
.lod = lod, .lod = lod,
}; };