mirror of https://github.com/AxioDL/metaforce.git
More aurora::gx refactoring
This commit is contained in:
parent
35674c27e5
commit
71342ed75e
|
@ -4,6 +4,7 @@
|
|||
#include <cctype>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace metaforce {
|
||||
class CInputStream;
|
||||
|
|
|
@ -23,6 +23,10 @@ CFont::CFont(float scale) : x0_fontSize(16.f * scale), x4_scale(scale) {
|
|||
++sNumInstances;
|
||||
}
|
||||
|
||||
void CFont::Shutdown() {
|
||||
mpTexture.reset();
|
||||
}
|
||||
|
||||
void CFont::TileCopy8(u8* dest, const u8* src) {
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
dest[0] = src[0];
|
||||
|
|
|
@ -24,5 +24,7 @@ public:
|
|||
u32 StringWidth(const char* str) const;
|
||||
u32 CharsWidth(const char* str, u32 len) const;
|
||||
u32 CharWidth(const char chr) const;
|
||||
|
||||
static void Shutdown();
|
||||
};
|
||||
}
|
|
@ -178,14 +178,14 @@ void CGraphics::Render2D(CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus::
|
|||
SetCullMode(ERglCullMode::None);
|
||||
tex.Load(GX::TEXMAP0, EClampMode::Repeat);
|
||||
|
||||
// float hPad, vPad;
|
||||
// if (CGraphics::GetViewportAspect() >= 1.78f) {
|
||||
// hPad = 1.78f / CGraphics::GetViewportAspect();
|
||||
// vPad = 1.78f / 1.33f;
|
||||
// } else {
|
||||
// hPad = 1.f;
|
||||
// vPad = CGraphics::GetViewportAspect() / 1.33f;
|
||||
// }
|
||||
// float hPad, vPad;
|
||||
// if (CGraphics::GetViewportAspect() >= 1.78f) {
|
||||
// hPad = 1.78f / CGraphics::GetViewportAspect();
|
||||
// vPad = 1.78f / 1.33f;
|
||||
// } else {
|
||||
// hPad = 1.f;
|
||||
// vPad = CGraphics::GetViewportAspect() / 1.33f;
|
||||
// }
|
||||
// TODO make this right
|
||||
float scaledX = static_cast<float>(x) / 640.f * static_cast<float>(g_Viewport.x8_width);
|
||||
float scaledY = static_cast<float>(y) / 448.f * static_cast<float>(g_Viewport.xc_height);
|
||||
|
@ -565,7 +565,10 @@ void CGraphics::StreamVertex(const zeus::CVector3f& pos) {
|
|||
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,
|
||||
const zeus::CColor& col, s32 numVerts) {
|
||||
|
@ -577,4 +580,16 @@ void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* po
|
|||
}
|
||||
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
|
||||
|
|
|
@ -272,6 +272,7 @@ public:
|
|||
// 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 SetTevStates(EStreamFlags flags) noexcept;
|
||||
static void SetTevOp(ERglTevStage stage, const CTevCombiners::CTevPass& pass);
|
||||
static void StreamBegin(GX::Primitive primitive);
|
||||
static void StreamNormal(const zeus::CVector3f& nrm);
|
||||
|
|
|
@ -8,6 +8,8 @@ void CTevPass::Execute(ERglTevStage stage) const {
|
|||
aurora::gfx::disable_tev_stage(stage);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Runtime/Character/CSkinRules.hpp"
|
||||
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
|
||||
#include "Runtime/Collision/CCollisionResponseData.hpp"
|
||||
#include "Runtime/Graphics/CFont.hpp"
|
||||
#include "Runtime/Graphics/CModel.hpp"
|
||||
#include "Runtime/Graphics/CTexture.hpp"
|
||||
#include "Runtime/GuiSys/CGuiFrame.hpp"
|
||||
|
@ -702,6 +703,7 @@ void CMain::ShutdownSubsystems() {
|
|||
CMappableObject::Shutdown();
|
||||
// Metaforce additions
|
||||
CMoviePlayer::Shutdown();
|
||||
CFont::Shutdown();
|
||||
}
|
||||
|
||||
void CMain::Shutdown() {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
namespace metaforce {
|
||||
template <class T, size_t N>
|
||||
void read_reserved_vector(rstl::reserved_vector<T, N>& v, CInputStream& in) {
|
||||
u32 count = in.ReadLong();
|
||||
u32 count = in.ReadUint32();
|
||||
v.resize(count);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
v[i] = in.Get<T>();
|
||||
|
@ -15,7 +15,7 @@ void read_reserved_vector(rstl::reserved_vector<T, N>& v, CInputStream& in) {
|
|||
|
||||
template <class T>
|
||||
void read_vector(std::vector<T>& v, CInputStream& in) {
|
||||
u32 count = in.ReadLong();
|
||||
u32 count = in.ReadUint32();
|
||||
v.reserve(count);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
v.emplace_back(in.Get<T>());
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
namespace metaforce {
|
||||
template <class T, size_t N>
|
||||
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) {
|
||||
out.Put(t);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ void write_reserved_vector(const rstl::reserved_vector<T, N>& v, COutputStream&
|
|||
|
||||
template <class T>
|
||||
void write_vector(const std::vector<T>& v, COutputStream& out) {
|
||||
out.Put(v.size());
|
||||
out.Put<u32>(v.size());
|
||||
for (const auto& t : v) {
|
||||
out.Put(t);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <compare>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
|
|
|
@ -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_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_src(GX::ChannelID id, GX::ColorSrc src) noexcept;
|
||||
|
||||
// Model state
|
||||
void set_alpha_discard(bool v);
|
||||
|
|
|
@ -301,10 +301,10 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv)
|
|||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearColor =
|
||||
{
|
||||
.r = gfx::g_clearColor.r(),
|
||||
.g = gfx::g_clearColor.g(),
|
||||
.b = gfx::g_clearColor.b(),
|
||||
.a = gfx::g_clearColor.a(),
|
||||
.r = gfx::gx::g_clearColor.r(),
|
||||
.g = gfx::gx::g_clearColor.g(),
|
||||
.b = gfx::gx::g_clearColor.b(),
|
||||
.a = gfx::gx::g_clearColor.a(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -285,7 +285,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type,
|
|||
const auto vertRange = push_verts(ArrayRef{verts});
|
||||
|
||||
const auto uniform = Uniform{
|
||||
.xf = get_combined_matrix(),
|
||||
.xf = gx::get_combined_matrix(),
|
||||
.color = color,
|
||||
};
|
||||
const auto uniformRange = push_uniform(uniform);
|
||||
|
|
|
@ -66,7 +66,7 @@ struct Command {
|
|||
};
|
||||
|
||||
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||
static std::mutex g_pipelineMutex;
|
||||
std::mutex g_pipelineMutex;
|
||||
static std::thread g_pipelineThread;
|
||||
static std::atomic_bool g_pipelineThreadEnd;
|
||||
static std::condition_variable g_pipelineCv;
|
||||
|
@ -277,7 +277,10 @@ void shutdown() {
|
|||
g_pipelineCv.notify_all();
|
||||
g_pipelineThread.join();
|
||||
|
||||
gx::shutdown();
|
||||
|
||||
g_cachedBindGroups.clear();
|
||||
g_cachedSamplers.clear();
|
||||
g_pipelines.clear();
|
||||
g_vertexBuffer = {};
|
||||
g_uniformBuffer = {};
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#endif
|
||||
|
||||
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) {
|
||||
return XXH3_64bits_withSeed(input, len, seed);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,142 @@
|
|||
#include "gx.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include "../gpu.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
namespace aurora::gfx {
|
||||
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_mvInv;
|
||||
zeus::CMatrix4f g_proj;
|
||||
|
@ -32,123 +161,10 @@ std::array<LightVariant, MaxLights> g_lights;
|
|||
std::bitset<MaxLights> g_lightState;
|
||||
|
||||
std::array<std::optional<STevStage>, maxTevStages> g_tevStages;
|
||||
std::array<STextureBind, maxTextures> g_textures;
|
||||
std::array<TextureBind, maxTextures> g_textures;
|
||||
|
||||
// GX state
|
||||
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)]; }
|
||||
const gx::TextureBind& get_texture(GX::TexMapID id) noexcept { return gx::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) {
|
||||
switch (fac) {
|
||||
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,
|
||||
const GXPipelineConfig& config, zstring_view label) noexcept {
|
||||
using gpu::g_graphicsConfig;
|
||||
wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info,
|
||||
ArrayRef<wgpu::VertexBufferLayout> vtxBuffers, wgpu::ShaderModule shader,
|
||||
zstring_view label) noexcept {
|
||||
const auto depthStencil = wgpu::DepthStencilState{
|
||||
.format = g_graphicsConfig.depthFormat,
|
||||
.depthWriteEnabled = config.depthUpdate,
|
||||
|
@ -273,19 +289,30 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::
|
|||
.blend = &blendState,
|
||||
.writeMask = to_write_mask(config.alphaUpdate),
|
||||
}};
|
||||
const auto shader = build_shader(config.shaderConfig);
|
||||
const auto fragmentState = wgpu::FragmentState{
|
||||
.module = shader,
|
||||
.entryPoint = "fs_main",
|
||||
.targetCount = colorTargets.size(),
|
||||
.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{
|
||||
.label = label.c_str(),
|
||||
.layout = std::move(layout),
|
||||
.layout = std::move(pipelineLayout),
|
||||
.vertex =
|
||||
{
|
||||
.module = shader,
|
||||
.module = std::move(shader),
|
||||
.entryPoint = "vs_main",
|
||||
.bufferCount = static_cast<uint32_t>(vtxBuffers.size()),
|
||||
.buffers = vtxBuffers.data(),
|
||||
|
@ -298,11 +325,10 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::
|
|||
},
|
||||
.fragment = &fragmentState,
|
||||
};
|
||||
return gpu::g_device.CreateRenderPipeline(&descriptor);
|
||||
return g_device.CreateRenderPipeline(&descriptor);
|
||||
}
|
||||
|
||||
void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive,
|
||||
std::bitset<maxTextures> enabledTextures) noexcept {
|
||||
ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) noexcept {
|
||||
for (size_t idx = 0; const auto& item : g_tevStages) {
|
||||
// Copy until disabled TEV stage (indicating end)
|
||||
if (!item) {
|
||||
|
@ -310,12 +336,9 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti
|
|||
}
|
||||
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;
|
||||
for (size_t idx = 0; const auto& item : g_textures) {
|
||||
if (enabledTextures.test(idx) && item) {
|
||||
config.shaderConfig.boundTextures.set(idx);
|
||||
}
|
||||
}
|
||||
config = {
|
||||
.shaderConfig = config.shaderConfig,
|
||||
.primitive = primitive,
|
||||
|
@ -330,9 +353,182 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti
|
|||
.depthUpdate = g_depthUpdate,
|
||||
.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;
|
||||
switch (clampMode) {
|
||||
case metaforce::EClampMode::Clamp:
|
||||
|
@ -354,7 +550,8 @@ wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept {
|
|||
.magFilter = wgpu::FilterMode::Linear,
|
||||
.minFilter = wgpu::FilterMode::Linear,
|
||||
.mipmapFilter = wgpu::FilterMode::Linear,
|
||||
.maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy,
|
||||
.maxAnisotropy = g_graphicsConfig.textureAnistropy,
|
||||
};
|
||||
}
|
||||
} // namespace gx
|
||||
} // namespace aurora::gfx
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <variant>
|
||||
|
||||
namespace aurora::gfx {
|
||||
namespace aurora::gfx::gx {
|
||||
extern zeus::CMatrix4f g_mv;
|
||||
extern zeus::CMatrix4f g_mvInv;
|
||||
extern zeus::CMatrix4f g_proj;
|
||||
|
@ -27,6 +27,7 @@ extern bool g_alphaDiscard;
|
|||
struct SChannelState {
|
||||
zeus::CColor matColor;
|
||||
zeus::CColor ambColor;
|
||||
GX::ColorSrc matSrc;
|
||||
};
|
||||
extern std::array<SChannelState, 2> g_colorChannels;
|
||||
using LightVariant = std::variant<std::monostate, Light, zeus::CColor>;
|
||||
|
@ -50,13 +51,13 @@ struct STevStage {
|
|||
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept
|
||||
: colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {}
|
||||
};
|
||||
struct STextureBind {
|
||||
struct TextureBind {
|
||||
aurora::gfx::TextureHandle handle;
|
||||
metaforce::EClampMode clampMode;
|
||||
float lod;
|
||||
|
||||
STextureBind() noexcept = default;
|
||||
STextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept
|
||||
TextureBind() noexcept = default;
|
||||
TextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept
|
||||
: handle(std::move(handle)), clampMode(clampMode), lod(lod) {}
|
||||
void reset() noexcept { handle.reset(); };
|
||||
[[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept;
|
||||
|
@ -66,22 +67,21 @@ struct STextureBind {
|
|||
constexpr u32 maxTevStages = GX::MAX_TEVSTAGE;
|
||||
extern std::array<std::optional<STevStage>, maxTevStages> g_tevStages;
|
||||
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; }
|
||||
|
||||
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<GX::ColorSrc, 2> channelMatSrcs;
|
||||
bool alphaDiscard;
|
||||
bool denormalizedVertexAttributes;
|
||||
bool denormalizedNorm;
|
||||
bool denormalizedColor;
|
||||
std::bitset<maxTextures> boundTextures;
|
||||
};
|
||||
struct GXPipelineConfig {
|
||||
GXShaderConfig shaderConfig;
|
||||
struct PipelineConfig {
|
||||
ShaderConfig shaderConfig;
|
||||
GX::Primitive primitive;
|
||||
metaforce::ERglEnum depthFunc;
|
||||
metaforce::ERglCullMode cullMode;
|
||||
|
@ -91,9 +91,65 @@ struct GXPipelineConfig {
|
|||
std::optional<float> dstAlpha;
|
||||
bool depthCompare, depthUpdate, alphaUpdate;
|
||||
};
|
||||
void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive,
|
||||
std::bitset<maxTextures> enabledTextures) noexcept;
|
||||
wgpu::ShaderModule build_shader(const GXShaderConfig& config);
|
||||
wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers,
|
||||
const GXPipelineConfig& config, zstring_view label) noexcept;
|
||||
} // namespace aurora::gfx
|
||||
// Output info from shader generation
|
||||
struct ShaderInfo {
|
||||
std::bitset<maxTextures> sampledTextures;
|
||||
std::bitset<4> sampledKcolors;
|
||||
std::bitset<2> sampledColorChannels;
|
||||
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
|
|
@ -3,14 +3,16 @@
|
|||
#include "../gpu.hpp"
|
||||
#include "gx.hpp"
|
||||
|
||||
namespace aurora::gfx {
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aurora::gfx::gx {
|
||||
using namespace fmt::literals;
|
||||
|
||||
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) {
|
||||
case GX::CC_CPREV:
|
||||
return "prev.rgb";
|
||||
|
@ -24,26 +26,145 @@ static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) {
|
|||
case GX::CC_A2:
|
||||
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg);
|
||||
unreachable();
|
||||
case GX::CC_TEXC:
|
||||
return fmt::format(FMT_STRING("sampled{}.rgb"), stageIdx);
|
||||
case GX::CC_TEXA:
|
||||
return fmt::format(FMT_STRING("sampled{}.a"), stageIdx);
|
||||
case GX::CC_RASC:
|
||||
return "rast.rgb";
|
||||
case GX::CC_RASA:
|
||||
return "rast.a";
|
||||
case GX::CC_TEXC: {
|
||||
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{}.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:
|
||||
return "1.0";
|
||||
case GX::CC_HALF:
|
||||
return "0.5";
|
||||
case GX::CC_KONST:
|
||||
return fmt::format(FMT_STRING("ubuf.kcolor{}.rgb"), stageIdx);
|
||||
case GX::CC_KONST: {
|
||||
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:
|
||||
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) {
|
||||
case GX::CA_APREV:
|
||||
return "prev.a";
|
||||
|
@ -52,12 +173,100 @@ static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) {
|
|||
case GX::CA_A2:
|
||||
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg);
|
||||
unreachable();
|
||||
case GX::CA_TEXA:
|
||||
return fmt::format(FMT_STRING("sampled{}.a"), stageIdx);
|
||||
case GX::CA_RASA:
|
||||
return "rast.a";
|
||||
case GX::CA_KONST:
|
||||
return fmt::format(FMT_STRING("ubuf.kcolor{}.a"), stageIdx);
|
||||
case GX::CA_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::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:
|
||||
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);
|
||||
if (g_gxCachedShaders.contains(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 sampBindings;
|
||||
std::string texBindings;
|
||||
|
@ -112,62 +360,23 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
|
|||
std::string vtxInAttrs;
|
||||
std::string vtxXfrAttrs;
|
||||
size_t locIdx = 0;
|
||||
if (config.denormalizedNorm) {
|
||||
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3<f32>;"), locIdx);
|
||||
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++;
|
||||
}
|
||||
// TODO
|
||||
// if (config.denormalizedNorm) {
|
||||
// vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3<f32>;"), locIdx);
|
||||
// vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3<f32>"), ++locIdx);
|
||||
// vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;"));
|
||||
// }
|
||||
|
||||
ShaderInfo info{
|
||||
.uniformSize = 64, // MVP MTX
|
||||
};
|
||||
std::string fragmentFnPre;
|
||||
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) {
|
||||
if (!stage) {
|
||||
break;
|
||||
}
|
||||
{
|
||||
std::string op;
|
||||
std::string outReg;
|
||||
switch (stage->colorOp.x10_regId) {
|
||||
case GX::TevRegID::TEVPREV:
|
||||
|
@ -176,15 +385,16 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
|
|||
default:
|
||||
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}"),
|
||||
color_arg_reg(stage->colorPass.x0_a, idx), color_arg_reg(stage->colorPass.x4_b, idx),
|
||||
color_arg_reg(stage->colorPass.x8_c, idx), color_arg_reg(stage->colorPass.xc_d, idx),
|
||||
tev_op(stage->colorOp.x4_op), tev_bias(stage->colorOp.x8_bias),
|
||||
tev_scale(stage->colorOp.xc_scale));
|
||||
std::string op =
|
||||
fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"),
|
||||
color_arg_reg(stage->colorPass.x0_a, idx, *stage, info),
|
||||
color_arg_reg(stage->colorPass.x4_b, idx, *stage, info),
|
||||
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);
|
||||
}
|
||||
{
|
||||
std::string op;
|
||||
std::string outReg;
|
||||
switch (stage->alphaOp.x10_regId) {
|
||||
case GX::TevRegID::TEVPREV:
|
||||
|
@ -193,15 +403,74 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
|
|||
default:
|
||||
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}"),
|
||||
alpha_arg_reg(stage->alphaPass.x0_a, idx), alpha_arg_reg(stage->alphaPass.x4_b, idx),
|
||||
alpha_arg_reg(stage->alphaPass.x8_c, idx), alpha_arg_reg(stage->alphaPass.xc_d, idx),
|
||||
tev_op(stage->alphaOp.x4_op), tev_bias(stage->alphaOp.x8_bias),
|
||||
tev_scale(stage->alphaOp.xc_scale));
|
||||
std::string op =
|
||||
fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"),
|
||||
alpha_arg_reg(stage->alphaPass.x0_a, idx, *stage, info),
|
||||
alpha_arg_reg(stage->alphaPass.x4_b, idx, *stage, info),
|
||||
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);
|
||||
}
|
||||
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 =
|
||||
fmt::format(FMT_STRING(R"""(
|
||||
|
@ -226,13 +495,13 @@ fn vs_main(
|
|||
|
||||
@stage(fragment)
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
|
||||
var prev: vec4<f32>;{fragmentFn}
|
||||
var prev: vec4<f32>;{fragmentFnPre}{fragmentFn}
|
||||
return prev;
|
||||
}}
|
||||
)"""),
|
||||
"uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings,
|
||||
"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);
|
||||
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
|
@ -242,8 +511,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
|
|||
.label = "GX Shader",
|
||||
};
|
||||
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
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "../common.hpp"
|
||||
#include "../gx.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aurora::gfx::model {
|
||||
struct DrawData {
|
||||
PipelineRef pipeline;
|
||||
|
@ -14,7 +16,7 @@ struct DrawData {
|
|||
BindGroupRef textureBindGroup;
|
||||
};
|
||||
|
||||
struct PipelineConfig : GXPipelineConfig {
|
||||
struct PipelineConfig : gx::PipelineConfig {
|
||||
ShaderRef shader;
|
||||
uint32_t uniformSize;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "common.hpp"
|
||||
#include "gx.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aurora::gfx {
|
||||
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 {
|
||||
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());
|
||||
|
||||
ByteBuffer uniBuf;
|
||||
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,
|
||||
};
|
||||
stream::PipelineConfig config{};
|
||||
config.shaderConfig.denormalizedVertexAttributes = true;
|
||||
config.shaderConfig.denormalizedNorm = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasNormal);
|
||||
config.shaderConfig.denormalizedColor = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasColor);
|
||||
populate_gx_pipeline_config(config, sStreamState->primitive, {1});
|
||||
const auto info = populate_pipeline_config(config, sStreamState->primitive);
|
||||
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{
|
||||
.pipeline = pipeline,
|
||||
.vertRange = vertRange,
|
||||
.uniformRange = uniRange,
|
||||
.uniformRange = build_uniform(info),
|
||||
.vertexCount = sStreamState->vertexCount,
|
||||
.uniformSize = uniform_size,
|
||||
.samplerBindGroup = samplerBindGroup,
|
||||
.textureBindGroup = textureBindGroup,
|
||||
.bindGroups = build_bind_groups(info),
|
||||
});
|
||||
|
||||
sStreamState.reset();
|
||||
|
|
|
@ -14,10 +14,10 @@ namespace aurora::gfx::stream {
|
|||
static logvisor::Module Log("aurora::gfx::stream");
|
||||
|
||||
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) {
|
||||
const auto [shader, info] = build_shader(config.shaderConfig);
|
||||
|
||||
std::array<wgpu::VertexAttribute, 4> attributes{};
|
||||
attributes[0] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x3,
|
||||
|
@ -26,7 +26,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
|
|||
};
|
||||
uint64_t offset = 12;
|
||||
uint32_t shaderLocation = 1;
|
||||
if (config.flags & metaforce::EStreamFlagBits::fHasNormal) {
|
||||
if (info.usesNormal) {
|
||||
attributes[shaderLocation] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x3,
|
||||
.offset = offset,
|
||||
|
@ -35,7 +35,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
|
|||
offset += 12;
|
||||
shaderLocation++;
|
||||
}
|
||||
if (config.flags & metaforce::EStreamFlagBits::fHasColor) {
|
||||
if (info.usesVtxColor) {
|
||||
attributes[shaderLocation] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x4,
|
||||
.offset = offset,
|
||||
|
@ -44,7 +44,11 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
|
|||
offset += 16;
|
||||
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{
|
||||
.format = wgpu::VertexFormat::Float32x2,
|
||||
.offset = offset,
|
||||
|
@ -59,57 +63,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
|
|||
.attributes = attributes.data(),
|
||||
}};
|
||||
|
||||
wgpu::BindGroupLayout uniformLayout;
|
||||
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");
|
||||
return build_pipeline(config, info, vertexBuffers, shader, "Stream Pipeline");
|
||||
}
|
||||
|
||||
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};
|
||||
pass.SetBindGroup(0, state.uniform.at(data.uniformSize).bindGroup, offsets.size(), offsets.data());
|
||||
if (data.samplerBindGroup && data.textureBindGroup) {
|
||||
pass.SetBindGroup(1, find_bind_group(data.samplerBindGroup));
|
||||
pass.SetBindGroup(2, find_bind_group(data.textureBindGroup));
|
||||
pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(), offsets.data());
|
||||
if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) {
|
||||
pass.SetBindGroup(1, find_bind_group(data.bindGroups.samplerBindGroup));
|
||||
pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup));
|
||||
}
|
||||
pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second);
|
||||
pass.Draw(data.vertexCount);
|
||||
|
|
|
@ -3,21 +3,18 @@
|
|||
#include "../common.hpp"
|
||||
#include "../gx.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace aurora::gfx::stream {
|
||||
struct DrawData {
|
||||
PipelineRef pipeline;
|
||||
Range vertRange;
|
||||
Range uniformRange;
|
||||
uint32_t vertexCount;
|
||||
uint32_t uniformSize;
|
||||
BindGroupRef samplerBindGroup;
|
||||
BindGroupRef textureBindGroup;
|
||||
gx::GXBindGroups bindGroups;
|
||||
};
|
||||
|
||||
struct PipelineConfig : public GXPipelineConfig {
|
||||
uint32_t uniformSize;
|
||||
metaforce::EStreamFlags flags;
|
||||
};
|
||||
struct PipelineConfig : public gx::PipelineConfig {};
|
||||
|
||||
struct CachedBindGroup {
|
||||
wgpu::BindGroupLayout layout;
|
||||
|
@ -30,6 +27,7 @@ struct State {
|
|||
wgpu::BindGroupLayout textureLayout;
|
||||
mutable std::unordered_map<uint32_t, CachedBindGroup> uniform;
|
||||
mutable std::unordered_map<uint64_t, wgpu::Sampler> sampler;
|
||||
mutable std::unordered_map<PipelineRef, gx::ShaderInfo> shaderInfo;
|
||||
};
|
||||
|
||||
State construct_state();
|
||||
|
|
|
@ -348,7 +348,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type,
|
|||
const auto vertRange = push_verts(ArrayRef{verts});
|
||||
|
||||
const auto uniform = Uniform{
|
||||
.xf = get_combined_matrix(),
|
||||
.xf = gx::get_combined_matrix(),
|
||||
.color = color,
|
||||
.lod = lod,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue