Extract GX shader/pipeline generation into shared API

This commit is contained in:
Luke Street 2022-03-06 15:58:06 -05:00
parent cd86dbd6ee
commit 7d07892198
15 changed files with 798 additions and 526 deletions

View File

@ -6,6 +6,8 @@
#include "Graphics/CCubeSurface.hpp"
#include "Graphics/CModel.hpp"
#include <aurora/model.hpp>
namespace metaforce {
static u32 sReflectionType = 0;
static u32 sLastMaterialUnique = UINT32_MAX;
@ -65,8 +67,8 @@ void CCubeMaterial::SetCurrent(const CModelFlags& flags, const CCubeSurface& sur
}
sLastMaterialUnique = groupIdx;
CCubeMaterialVatFlags vatFlags = SBig(*reinterpret_cast<const u32*>(materialDataCur));
// SetVtxDescv_Compressed(vatFlags);
u32 vatFlags = SBig(*reinterpret_cast<const u32*>(materialDataCur));
aurora::gfx::model::set_vtx_desc_compressed(vatFlags);
materialDataCur += 8;
bool packedLightMaps = matFlags.IsSet(CCubeMaterialFlagBits::fLightmapUvArray);

View File

@ -6,6 +6,8 @@
#include "Graphics/CGraphics.hpp"
#include "Graphics/CModel.hpp"
#include <aurora/model.hpp>
namespace metaforce {
bool CCubeModel::sRenderModelBlack = false;
bool CCubeModel::sRenderModelShadow = false;
@ -156,7 +158,8 @@ void CCubeModel::DrawFlat(TVectorRef positions, TVectorRef normals, ESurfaceSele
const auto* surface = x38_firstUnsortedSurf;
while (surface != nullptr) {
const auto mat = GetMaterialByIndex(surface->GetMaterialIndex());
// TODO draw
aurora::gfx::model::set_vtx_desc_compressed(mat.GetVertexDesc());
aurora::gfx::model::queue_surface(surface->GetDisplayList(), surface->GetDisplayListSize());
surface = surface->GetNextSurface();
}
}
@ -164,14 +167,19 @@ void CCubeModel::DrawFlat(TVectorRef positions, TVectorRef normals, ESurfaceSele
const auto* surface = x3c_firstSortedSurf;
while (surface != nullptr) {
const auto mat = GetMaterialByIndex(surface->GetMaterialIndex());
// TODO draw
aurora::gfx::model::set_vtx_desc_compressed(mat.GetVertexDesc());
aurora::gfx::model::queue_surface(surface->GetDisplayList(), surface->GetDisplayListSize());
surface = surface->GetNextSurface();
}
}
}
void CCubeModel::DrawNormal(TVectorRef positions, TVectorRef normals, ESurfaceSelection surfaces) {
// TODO bunch of CGX stuff, what is it doing?
CGraphics::SetDepthWriteMode(true, ERglEnum::LEqual, true);
CGraphics::SetTevOp(ERglTevStage::Stage0, CTevCombiners::skPassZero);
CGraphics::SetTevOp(ERglTevStage::Stage1, CTevCombiners::skPassThru);
// TODO update fog
CGraphics::SetBlendMode(ERglBlendMode::Blend, ERglBlendFactor::Zero, ERglBlendFactor::One, ERglLogicOp::Clear);
DrawFlat(positions, normals, surfaces);
}
@ -201,7 +209,7 @@ void CCubeModel::DrawSurface(const CCubeSurface& surface, const CModelFlags& fla
auto mat = GetMaterialByIndex(surface.GetMaterialIndex());
if (!mat.GetFlags().IsSet(CCubeMaterialFlagBits::fShadowOccluderMesh) || sDrawingOccluders) {
mat.SetCurrent(flags, surface, *this);
// TODO draw
aurora::gfx::model::queue_surface(surface.GetDisplayList(), surface.GetDisplayListSize());
}
}
@ -249,10 +257,10 @@ void CCubeModel::DisableShadowMaps() { sRenderModelShadow = false; }
void CCubeModel::SetArraysCurrent() {
if (x0_modelInstance.GetVertexPointer() != nullptr) {
// TODO set vertices active
aurora::gfx::model::set_vertex_buffer(*x0_modelInstance.GetVertexPointer());
}
if (x0_modelInstance.GetNormalPointer() != nullptr) {
// TODO set normals active
aurora::gfx::model::set_normal_buffer(*x0_modelInstance.GetNormalPointer());
}
SetStaticArraysCurrent();
}
@ -273,26 +281,26 @@ void CCubeModel::SetRenderModelBlack(bool v) {
}
void CCubeModel::SetSkinningArraysCurrent(TVectorRef positions, TVectorRef normals) {
// TODO activate vertices, normals & colors
aurora::gfx::model::set_vertex_buffer(*positions);
aurora::gfx::model::set_normal_buffer(*normals);
// colors unused
SetStaticArraysCurrent();
}
void CCubeModel::SetStaticArraysCurrent() {
// TODO activate colors
// colors unused
const auto* packedTexCoords = x0_modelInstance.GetPackedTCPointer();
const auto* texCoords = x0_modelInstance.GetTCPointer();
if (packedTexCoords == nullptr) {
sUsingPackedLightmaps = false;
}
if (sUsingPackedLightmaps) {
// TODO activate packed TCs for texture 0
aurora::gfx::model::set_tex0_tc_buffer(*packedTexCoords);
} else if (texCoords != nullptr) {
// TODO activate TCs for texture 0
aurora::gfx::model::set_tex0_tc_buffer(*packedTexCoords);
}
if (texCoords != nullptr) {
for (int i = 1; i < 8; ++i) {
// TODO activate TCs for textures 1-7
}
aurora::gfx::model::set_tc_buffer(*texCoords);
}
CCubeMaterial::KillCachedViewDepState();
}
@ -300,9 +308,9 @@ void CCubeModel::SetStaticArraysCurrent() {
void CCubeModel::SetUsingPackedLightmaps(bool v) {
sUsingPackedLightmaps = v;
if (v) {
// TODO activate packed TCs for texture 0
aurora::gfx::model::set_tex0_tc_buffer(*x0_modelInstance.GetPackedTCPointer());
} else {
// TODO activate TCs for texture 0
aurora::gfx::model::set_tex0_tc_buffer(*x0_modelInstance.GetTCPointer());
}
}

View File

@ -19,6 +19,10 @@ const CTevPass skPassThru{
{GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_RASC},
{GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_RASA},
};
const CTevPass skPassZero{
{GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO},
{GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO},
};
const CTevPass sTevPass805a5698{
{GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_RASC, GX::TevColorArg::CC_C0, GX::TevColorArg::CC_ZERO},
{GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_RASA, GX::TevAlphaArg::CA_A0, GX::TevAlphaArg::CA_ZERO},

View File

@ -29,6 +29,7 @@ public:
};
extern const CTevPass skPassThru;
extern const CTevPass skPassZero;
extern const CTevPass sTevPass805a5698;
extern const CTevPass sTevPass805a5e70;
extern const CTevPass sTevPass805a5ebc;

View File

@ -145,7 +145,7 @@ void CTexture::UnLock() {
void CTexture::Load(GX::TexMapID id, EClampMode clamp) {
if (sLoadedTextures[id] != this || xa_29_canLoadObj) {
auto* image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
// auto* image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
CountMemory();
if (HasPalette()) {
x10_graphicsPalette->Load();
@ -202,8 +202,8 @@ void CTexture::MakeSwappable() {
const u8* CTexture::GetConstBitMapData(s32 mip) const {
u32 buffOffset = 0;
if (x8_mips > 0) {
for (u32 i = 0; i < x8_mips; ++i) {
if (x8_mips > mip) {
for (u32 i = 0; i < mip; ++i) {
buffOffset += (x9_bitsPerPixel >> 3) * (x4_w >> (i & 0x3f)) * (x6_h >> (i & 0x3f));
}
}

View File

@ -26,11 +26,13 @@ add_library(aurora STATIC
lib/gfx/texture.cpp
lib/gfx/stream.cpp
lib/gfx/gx.cpp
lib/gfx/gx_shader.cpp
lib/gfx/texture_convert.cpp
lib/gfx/movie_player/shader.cpp
lib/gfx/textured_quad/shader.cpp
lib/gfx/colored_quad/shader.cpp
lib/gfx/stream/shader.cpp
lib/gfx/model/shader.cpp
)
target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32
target_include_directories(aurora PUBLIC include ../)

View File

@ -1,5 +1,13 @@
#pragma once
namespace aurora::gfx::model {
#include "common.hpp"
}
namespace aurora::gfx::model {
void set_vertex_buffer(const std::vector<zeus::CVector3f>& data) noexcept;
void set_normal_buffer(const std::vector<zeus::CVector3f>& norm) noexcept;
void set_tex0_tc_buffer(const std::vector<zeus::CVector2f>& tcs) noexcept; // Tex coords for TEX0
void set_tc_buffer(const std::vector<zeus::CVector2f>& tcs) noexcept; // Tex coords for the TEX1-7
void set_vtx_desc_compressed(u32 vtxDesc) noexcept;
void queue_surface(const u8* dlStart, u32 dlSize) noexcept;
} // namespace aurora::gfx::model

View File

@ -25,14 +25,22 @@ std::array<zeus::CColor, GX::MAX_KCOLOR> g_kcolors;
bool g_alphaUpdate;
std::optional<float> g_dstAlpha;
zeus::CColor g_clearColor = zeus::skClear;
bool g_alphaDiscard;
std::array<SChannelState, 2> g_colorChannels;
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;
// 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)]; }
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 {
@ -69,9 +77,7 @@ void set_dst_alpha(bool enabled, float value) noexcept {
}
}
void set_clear_color(const zeus::CColor& color) noexcept { g_clearColor = color; }
void set_alpha_discard(bool v) {
// TODO
}
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;
@ -138,13 +144,217 @@ void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept {
g_colorChannels[id - GX::COLOR0A0].matColor = color;
}
void load_light(GX::LightID id, const Light& light) noexcept {
g_lights[id] = light;
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:
return wgpu::BlendFactor::Zero;
case metaforce::ERglBlendFactor::One:
return wgpu::BlendFactor::One;
case metaforce::ERglBlendFactor::SrcColor:
return wgpu::BlendFactor::Src;
case metaforce::ERglBlendFactor::InvSrcColor:
return wgpu::BlendFactor::OneMinusSrc;
case metaforce::ERglBlendFactor::SrcAlpha:
return wgpu::BlendFactor::SrcAlpha;
case metaforce::ERglBlendFactor::InvSrcAlpha:
return wgpu::BlendFactor::OneMinusSrcAlpha;
case metaforce::ERglBlendFactor::DstAlpha:
return wgpu::BlendFactor::DstAlpha;
case metaforce::ERglBlendFactor::InvDstAlpha:
return wgpu::BlendFactor::OneMinusDstAlpha;
case metaforce::ERglBlendFactor::DstColor:
return wgpu::BlendFactor::Dst;
case metaforce::ERglBlendFactor::InvDstColor:
return wgpu::BlendFactor::OneMinusDst;
}
}
void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept {
g_lights[id] = ambient;
static inline wgpu::CompareFunction to_compare_function(metaforce::ERglEnum func) {
switch (func) {
case metaforce::ERglEnum::Never:
return wgpu::CompareFunction::Never;
case metaforce::ERglEnum::Less:
return wgpu::CompareFunction::Less;
case metaforce::ERglEnum::Equal:
return wgpu::CompareFunction::Equal;
case metaforce::ERglEnum::LEqual:
return wgpu::CompareFunction::LessEqual;
case metaforce::ERglEnum::Greater:
return wgpu::CompareFunction::Greater;
case metaforce::ERglEnum::NEqual:
return wgpu::CompareFunction::NotEqual;
case metaforce::ERglEnum::GEqual:
return wgpu::CompareFunction::GreaterEqual;
case metaforce::ERglEnum::Always:
return wgpu::CompareFunction::Always;
}
}
void set_light_state(std::bitset<MaxLights> bits) noexcept {
g_lightState = bits;
static inline wgpu::BlendState to_blend_state(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor srcFac,
metaforce::ERglBlendFactor dstFac, std::optional<float> dstAlpha) {
if (mode != metaforce::ERglBlendMode::Blend) {
Log.report(logvisor::Fatal, FMT_STRING("How to {}?"), magic_enum::enum_name(mode));
}
const auto colorBlendComponent = wgpu::BlendComponent{
.operation = wgpu::BlendOperation::Add,
.srcFactor = to_blend_factor(srcFac),
.dstFactor = to_blend_factor(dstFac),
};
auto alphaBlendComponent = colorBlendComponent;
if (dstAlpha) {
alphaBlendComponent = wgpu::BlendComponent{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::Zero,
.dstFactor = wgpu::BlendFactor::Constant,
};
}
return {
.color = colorBlendComponent,
.alpha = alphaBlendComponent,
};
}
static inline wgpu::ColorWriteMask to_write_mask(bool alphaUpdate) {
auto writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue;
if (alphaUpdate) {
writeMask = writeMask | wgpu::ColorWriteMask::Alpha;
}
return writeMask;
}
static inline wgpu::PrimitiveState to_primitive_state(GX::Primitive gx_prim, metaforce::ERglCullMode e_cullMode) {
wgpu::PrimitiveTopology primitive = wgpu::PrimitiveTopology::TriangleList;
switch (gx_prim) {
case GX::TRIANGLES:
break;
case GX::TRIANGLESTRIP:
primitive = wgpu::PrimitiveTopology::TriangleStrip;
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unsupported primitive type {}"), gx_prim);
unreachable();
}
wgpu::FrontFace frontFace = wgpu::FrontFace::CCW;
wgpu::CullMode cullMode = wgpu::CullMode::None;
switch (e_cullMode) {
case metaforce::ERglCullMode::Front:
frontFace = wgpu::FrontFace::CW;
cullMode = wgpu::CullMode::Front;
break;
case metaforce::ERglCullMode::Back:
cullMode = wgpu::CullMode::Back;
break;
default:
break;
}
return {
.topology = primitive,
.frontFace = frontFace,
.cullMode = cullMode,
};
}
wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers,
const GXPipelineConfig& config, zstring_view label) noexcept {
using gpu::g_graphicsConfig;
const auto depthStencil = wgpu::DepthStencilState{
.format = g_graphicsConfig.depthFormat,
.depthWriteEnabled = config.depthUpdate,
.depthCompare = to_compare_function(config.depthFunc),
};
const auto blendState = to_blend_state(config.blendMode, config.blendFacSrc, config.blendFacDst, config.dstAlpha);
const std::array colorTargets{wgpu::ColorTargetState{
.format = g_graphicsConfig.colorFormat,
.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 descriptor = wgpu::RenderPipelineDescriptor{
.label = label.c_str(),
.layout = std::move(layout),
.vertex =
{
.module = shader,
.entryPoint = "vs_main",
.bufferCount = static_cast<uint32_t>(vtxBuffers.size()),
.buffers = vtxBuffers.data(),
},
.primitive = to_primitive_state(config.primitive, config.cullMode),
.depthStencil = &depthStencil,
.multisample =
wgpu::MultisampleState{
.count = g_graphicsConfig.msaaSamples,
},
.fragment = &fragmentState,
};
return gpu::g_device.CreateRenderPipeline(&descriptor);
}
void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive,
std::bitset<maxTextures> enabledTextures) noexcept {
for (size_t idx = 0; const auto& item : g_tevStages) {
// Copy until disabled TEV stage (indicating end)
if (!item) {
break;
}
config.shaderConfig.tevStages[idx++] = item;
}
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,
.depthFunc = g_depthFunc,
.cullMode = g_cullMode,
.blendMode = g_blendMode,
.blendFacSrc = g_blendFacSrc,
.blendFacDst = g_blendFacDst,
.blendOp = g_blendOp,
.dstAlpha = g_dstAlpha,
.depthCompare = g_depthCompare,
.depthUpdate = g_depthUpdate,
.alphaUpdate = g_alphaUpdate,
};
}
wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept {
wgpu::AddressMode mode;
switch (clampMode) {
case metaforce::EClampMode::Clamp:
mode = wgpu::AddressMode::ClampToEdge;
break;
case metaforce::EClampMode::Repeat:
mode = wgpu::AddressMode::Repeat;
break;
case metaforce::EClampMode::Mirror:
mode = wgpu::AddressMode::MirrorRepeat;
break;
}
return {
.label = "Generated Sampler",
.addressModeU = mode,
.addressModeV = mode,
.addressModeW = mode,
// TODO logic from CTexture?
.magFilter = wgpu::FilterMode::Linear,
.minFilter = wgpu::FilterMode::Linear,
.mipmapFilter = wgpu::FilterMode::Linear,
.maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy,
};
}
} // namespace aurora::gfx

View File

@ -22,6 +22,7 @@ extern std::array<zeus::CColor, GX::MAX_KCOLOR> g_kcolors;
extern bool g_alphaUpdate;
extern std::optional<float> g_dstAlpha;
extern zeus::CColor g_clearColor;
extern bool g_alphaDiscard;
struct SChannelState {
zeus::CColor matColor;
@ -43,18 +44,56 @@ struct STevStage {
GX::TexMapID texMapId = GX::TEXMAP_NULL;
GX::ChannelID channelId = GX::COLOR_NULL;
STevStage(const metaforce::CTevCombiners::ColorPass& colPass, const metaforce::CTevCombiners::AlphaPass& alphaPass,
const metaforce::CTevCombiners::CTevOp& colorOp, const metaforce::CTevCombiners::CTevOp& alphaOp)
constexpr STevStage(const metaforce::CTevCombiners::ColorPass& colPass,
const metaforce::CTevCombiners::AlphaPass& alphaPass,
const metaforce::CTevCombiners::CTevOp& colorOp,
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept
: colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {}
};
struct STextureBind {
aurora::gfx::TextureHandle handle;
metaforce::EClampMode clampMode;
float lod;
STextureBind() noexcept = default;
STextureBind(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;
operator bool() const noexcept { return handle; }
};
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;
static inline XXH64_hash_t hash_tev_stages(XXH64_hash_t seed = 0) {
const auto end = std::find_if(g_tevStages.cbegin(), g_tevStages.cend(), [](const auto& a) { return !a; });
return xxh3_hash(g_tevStages.data(), (end - g_tevStages.begin()) * sizeof(STevStage), seed);
}
static inline Mat4x4<float> get_combined_matrix() noexcept { return g_proj * g_mv; }
static inline Mat4x4<float> get_combined_matrix() { return g_proj * g_mv; }
const STextureBind& get_texture(GX::TexMapID id) noexcept;
struct GXShaderConfig {
std::array<std::optional<STevStage>, maxTevStages> tevStages;
bool alphaDiscard;
bool denormalizedVertexAttributes;
bool denormalizedNorm;
bool denormalizedColor;
std::bitset<maxTextures> boundTextures;
};
struct GXPipelineConfig {
GXShaderConfig shaderConfig;
GX::Primitive primitive;
metaforce::ERglEnum depthFunc;
metaforce::ERglCullMode cullMode;
metaforce::ERglBlendMode blendMode;
metaforce::ERglBlendFactor blendFacSrc, blendFacDst;
metaforce::ERglLogicOp blendOp;
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

View File

@ -0,0 +1,249 @@
#include "common.hpp"
#include "../gpu.hpp"
#include "gx.hpp"
namespace aurora::gfx {
using namespace fmt::literals;
static logvisor::Module Log("aurora::gfx::gx");
std::unordered_map<ShaderRef, wgpu::ShaderModule> g_gxCachedShaders;
static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) {
switch (arg) {
case GX::CC_CPREV:
return "prev.rgb";
case GX::CC_APREV:
return "prev.a";
case GX::CC_C0:
case GX::CC_A0:
case GX::CC_C1:
case GX::CC_A1:
case GX::CC_C2:
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_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_ZERO:
return "0.0";
}
}
static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) {
switch (arg) {
case GX::CA_APREV:
return "prev.a";
case GX::CA_A0:
case GX::CA_A1:
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_ZERO:
return "0.0";
}
}
static std::string_view tev_op(GX::TevOp op) {
switch (op) {
case GX::TEV_ADD:
return "+";
case GX::TEV_SUB:
return "-";
default:
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), op);
unreachable();
}
}
static std::string_view tev_bias(GX::TevBias bias) {
switch (bias) {
case GX::TB_ZERO:
return " + 0.0";
case GX::TB_ADDHALF:
return " + 0.5";
case GX::TB_SUBHALF:
return " - 0.5";
}
}
static std::string_view tev_scale(GX::TevScale scale) {
switch (scale) {
case GX::CS_SCALE_1:
return " * 1.0";
case GX::CS_SCALE_2:
return " * 2.0";
case GX::CS_SCALE_4:
return " * 4.0";
case GX::CS_DIVIDE_2:
return " / 2.0";
}
}
wgpu::ShaderModule build_shader(const GXShaderConfig& config) {
const auto hash = xxh3_hash(config);
if (g_gxCachedShaders.contains(hash)) {
return g_gxCachedShaders[hash];
}
std::string uniBufAttrs;
std::string sampBindings;
std::string texBindings;
std::string vtxOutAttrs;
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++;
}
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:
outReg = "prev";
break;
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));
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:
outReg = "prev.a";
break;
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));
fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op);
}
idx++;
}
const auto shaderSource =
fmt::format(FMT_STRING(R"""(
struct Uniform {{
xf: mat4x4<f32>;{uniBufAttrs}
}};
@group(0) @binding(0)
var<uniform> ubuf: Uniform;{sampBindings}{texBindings}
struct VertexOutput {{
@builtin(position) pos: vec4<f32>;{vtxOutAttrs}
}};
@stage(vertex)
fn vs_main(
@location(0) in_pos: vec3<f32>{vtxInAttrs}
) -> VertexOutput {{
var out: VertexOutput;
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);{vtxXfrAttrs}
return out;
}}
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
var prev: vec4<f32>;{fragmentFn}
return prev;
}}
)"""),
"uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings,
"vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs,
"fragmentFn"_a = fragmentFn);
Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource);
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.source = shaderSource.c_str();
const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{
.nextInChain = &wgslDescriptor,
.label = "GX Shader",
};
auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor);
g_gxCachedShaders.emplace(hash, shader);
return shader;
}
} // namespace aurora::gfx

View File

@ -0,0 +1,180 @@
#include "shader.hpp"
#include "../../gpu.hpp"
#include "../common.hpp"
#include <aurora/model.hpp>
#include <magic_enum.hpp>
enum class VtxDescAttr : u8 {
Position = 0,
Normal = 2,
Color0 = 4,
Color1 = 8,
Tex0 = 10,
Tex1 = 12,
Tex2 = 14,
Tex3 = 16,
Tex4 = 18,
Tex5 = 20,
Tex6 = 22,
PnMatIdx = 24,
Tex0MatIdx = 25,
Tex1MatIdx = 26,
Tex2MatIdx = 27,
Tex3MatIdx = 28,
Tex4MatIdx = 29,
Tex5MatIdx = 30,
Tex6MatIdx = 31,
};
enum class VtxDescAttrType : u8 { None = 0, Direct = 1, Index8 = 2, Index16 = 3 };
class VtxDescFlags {
u32 m_flags = 0;
public:
constexpr VtxDescFlags() noexcept = default;
constexpr VtxDescFlags(u32 flags) noexcept : m_flags(flags){};
[[nodiscard]] constexpr VtxDescAttrType GetAttributeType(VtxDescAttr attribute) const noexcept {
return VtxDescAttrType((m_flags >> u32(attribute)) & 0x3);
}
[[nodiscard]] constexpr VtxDescAttrType GetDirectAttributeType(VtxDescAttr attribute) const noexcept {
return VtxDescAttrType((m_flags >> u32(attribute)) & 0x1);
}
constexpr void SetAttributeType(VtxDescAttr attribute, VtxDescAttrType type) noexcept {
m_flags &= ~(u32(0x3) << u32(attribute));
m_flags |= u32(type) << u32(attribute);
}
};
namespace aurora::gfx::model {
static logvisor::Module Log("aurora::gfx::model");
static const std::vector<zeus::CVector3f>* vtxData;
static const std::vector<zeus::CVector3f>* nrmData;
static const std::vector<zeus::CVector2f>* tex0TcData;
static const std::vector<zeus::CVector2f>* tcData;
void set_vertex_buffer(const std::vector<zeus::CVector3f>& data) noexcept { vtxData = &data; }
void set_normal_buffer(const std::vector<zeus::CVector3f>& norm) noexcept { nrmData = &norm; }
void set_tex0_tc_buffer(const std::vector<zeus::CVector2f>& tcs) noexcept { tex0TcData = &tcs; }
void set_tc_buffer(const std::vector<zeus::CVector2f>& tcs) noexcept { tcData = &tcs; }
struct DlVert {
s16 pos;
s16 norm;
// colors ignored
std::array<s16, 7> uvs;
// pn_mtx_idx ignored
// tex_mtx_idxs ignored
s16 _pad;
};
enum class VertexFormat : u8 {
F32F32,
S16F32,
S16S16,
};
static VtxDescFlags sVtxDescFlags;
void set_vtx_desc_compressed(u32 vtxDesc) noexcept { sVtxDescFlags = vtxDesc; }
static inline std::pair<DlVert, size_t> readVert(const u8* data) noexcept {
DlVert out{};
size_t offset = 0;
const auto read8 = [data, &offset](VtxDescAttrType type) -> s8 {
if (type == VtxDescAttrType::Direct) {
s8 v = static_cast<s8>(data[offset]);
++offset;
return v;
}
return 0;
};
const auto read16 = [data, &offset](VtxDescAttrType type) -> s16 {
if (type == VtxDescAttrType::Index16) {
s16 v = metaforce::SBig(*reinterpret_cast<const u16*>(data + offset));
offset += 2;
return v;
}
return 0;
};
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::PnMatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex0MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex1MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex2MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex3MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex4MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex5MatIdx));
read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex6MatIdx));
out.pos = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Position));
out.norm = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Normal));
read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Color0));
read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Color1));
out.uvs[0] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex0));
out.uvs[1] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex1));
out.uvs[2] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex2));
out.uvs[3] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex3));
out.uvs[4] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex4));
out.uvs[5] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex5));
out.uvs[6] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex6));
return {out, offset};
}
void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
// Log.report(logvisor::Info, FMT_STRING("DL size {}"), dlSize);
std::vector<DlVert> verts;
std::vector<u32> indices;
size_t offset = 0;
while (offset < dlSize) {
const auto header = dlStart[offset];
const auto primitive = static_cast<GX::Primitive>(header & 0xF8);
const auto vtxFmt = static_cast<VertexFormat>(header & 0x3);
const auto vtxCount = metaforce::SBig(*reinterpret_cast<const u16*>(dlStart + offset + 1));
// Log.report(logvisor::Info, FMT_STRING("DL header prim {}, fmt {}, vtx count {}"), primitive,
// magic_enum::enum_name(vtxFmt), vtxCount);
offset += 3;
if (primitive == 0) {
break;
}
if (primitive != GX::TRIANGLES && primitive != GX::TRIANGLESTRIP && primitive != GX::TRIANGLEFAN) {
Log.report(logvisor::Fatal, FMT_STRING("queue_surface: unsupported primitive type {}"), primitive);
unreachable();
}
const u32 idxStart = indices.size();
const u16 vertsStart = verts.size();
verts.reserve(vertsStart + vtxCount);
if (vtxCount > 3 && (primitive == GX::TRIANGLEFAN || primitive == GX::TRIANGLESTRIP)) {
indices.reserve(idxStart + (u32(vtxCount) - 3) * 3 + 3);
} else {
indices.reserve(idxStart + vtxCount);
}
auto curVert = vertsStart;
for (int v = 0; v < vtxCount; ++v) {
const auto [vert, read] = readVert(dlStart + offset);
verts.push_back(vert);
offset += read;
if (primitive == GX::TRIANGLES || v < 3) {
// pass
} else if (primitive == GX::TRIANGLEFAN) {
indices.push_back(vertsStart);
indices.push_back(curVert - 1);
} else if (primitive == GX::TRIANGLESTRIP) {
if ((v & 1) == 0) {
indices.push_back(curVert - 2);
indices.push_back(curVert - 1);
} else {
indices.push_back(curVert - 1);
indices.push_back(curVert - 2);
}
}
indices.push_back(curVert);
++curVert;
}
}
// Log.report(logvisor::Info, FMT_STRING("Read {} verts, {} indices"), verts.size(), indices.size());
}
} // namespace aurora::gfx::model

View File

@ -0,0 +1,38 @@
#pragma once
#include "../common.hpp"
#include "../gx.hpp"
namespace aurora::gfx::model {
struct DrawData {
PipelineRef pipeline;
Range vertRange;
Range uniformRange;
uint32_t vertexCount;
uint32_t uniformSize;
BindGroupRef samplerBindGroup;
BindGroupRef textureBindGroup;
};
struct PipelineConfig : GXPipelineConfig {
ShaderRef shader;
uint32_t uniformSize;
};
struct CachedBindGroup {
wgpu::BindGroupLayout layout;
wgpu::BindGroup bindGroup;
CachedBindGroup(wgpu::BindGroupLayout layout, wgpu::BindGroup&& group)
: layout(std::move(layout)), bindGroup(std::move(group)) {}
};
struct State {
wgpu::BindGroupLayout samplerLayout;
wgpu::BindGroupLayout textureLayout;
mutable std::unordered_map<uint32_t, CachedBindGroup> uniform;
mutable std::unordered_map<uint64_t, wgpu::Sampler> sampler;
};
State construct_state();
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config);
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
} // namespace aurora::gfx::model

View File

@ -4,13 +4,7 @@
#include "common.hpp"
#include "gx.hpp"
#include <utility>
#include <magic_enum.hpp>
namespace aurora::gfx {
using namespace fmt::literals;
static logvisor::Module Log("aurora::gfx::stream");
struct SStreamState {
@ -23,51 +17,6 @@ struct SStreamState {
};
static std::optional<SStreamState> sStreamState;
constexpr u32 maxTextures = 8;
struct STextureBind {
aurora::gfx::TextureHandle handle;
metaforce::EClampMode clampMode;
float lod;
STextureBind() noexcept = default;
STextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept
: handle(std::move(handle)), clampMode(clampMode), lod(lod) {}
void reset() noexcept { handle.reset(); };
wgpu::SamplerDescriptor get_descriptor() const noexcept {
wgpu::AddressMode mode;
switch (clampMode) {
case metaforce::EClampMode::Clamp:
mode = wgpu::AddressMode::ClampToEdge;
break;
case metaforce::EClampMode::Repeat:
mode = wgpu::AddressMode::Repeat;
break;
case metaforce::EClampMode::Mirror:
mode = wgpu::AddressMode::MirrorRepeat;
break;
}
return {
.label = "Generated Sampler",
.addressModeU = mode,
.addressModeV = mode,
.addressModeW = mode,
// TODO logic from CTexture?
.magFilter = wgpu::FilterMode::Linear,
.minFilter = wgpu::FilterMode::Linear,
.mipmapFilter = wgpu::FilterMode::Linear,
.maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy,
};
}
operator bool() const noexcept { return handle; }
};
static std::array<STextureBind, maxTextures> sTextures;
void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept {
sTextures[static_cast<size_t>(id)] = {tex, clamp, lod};
}
void unbind_texture(GX::TexMapID id) noexcept { sTextures[static_cast<size_t>(id)].reset(); }
void stream_begin(GX::Primitive primitive) noexcept {
if (sStreamState) {
Log.report(logvisor::Fatal, FMT_STRING("Stream began twice!"));
@ -89,7 +38,6 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co
}
} else {
sStreamState->flags = flags;
// TODO begin shader construction
}
sStreamState->vertexBuffer.append(&pos, 12);
if (flags & metaforce::EStreamFlagBits::fHasNormal) {
@ -104,259 +52,9 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co
sStreamState->vertexCount++;
}
static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) {
switch (arg) {
case GX::CC_CPREV:
return "prev.rgb";
case GX::CC_APREV:
return "prev.a";
case GX::CC_C0:
case GX::CC_A0:
case GX::CC_C1:
case GX::CC_A1:
case GX::CC_C2:
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_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_ZERO:
return "0.0";
}
}
static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) {
switch (arg) {
case GX::CA_APREV:
return "prev.a";
case GX::CA_A0:
case GX::CA_A1:
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_ZERO:
return "0.0";
}
}
static std::string_view tev_op(GX::TevOp op) {
switch (op) {
case GX::TEV_ADD:
return "+";
case GX::TEV_SUB:
return "-";
default:
Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), op);
unreachable();
}
}
static std::string_view tev_bias(GX::TevBias bias) {
switch (bias) {
case GX::TB_ZERO:
return " + 0.0";
case GX::TB_ADDHALF:
return " + 0.5";
case GX::TB_SUBHALF:
return " - 0.5";
}
}
static std::string_view tev_scale(GX::TevScale scale) {
switch (scale) {
case GX::CS_SCALE_1:
return " * 1.0";
case GX::CS_SCALE_2:
return " * 2.0";
case GX::CS_SCALE_4:
return " * 4.0";
case GX::CS_DIVIDE_2:
return " / 2.0";
}
}
std::unordered_map<ShaderRef, wgpu::ShaderModule> g_streamCachedShaders;
static ShaderRef generate_shader() {
auto flags = sStreamState->flags;
const auto hash = hash_tev_stages(static_cast<metaforce::EStreamFlags::MaskType>(flags));
if (g_streamCachedShaders.contains(hash)) {
return hash;
}
std::string uniBufAttrs;
if (flags & metaforce::EStreamFlagBits::fHasTexture) {
uniBufAttrs += fmt::format(FMT_STRING("\n tex0_lod: f32;"));
}
std::string sampBindings;
if (flags & metaforce::EStreamFlagBits::fHasTexture) {
sampBindings +=
"\n"
"@group(1) @binding(0)\n"
"var tex0_samp: sampler;";
}
std::string texBindings;
if (flags & metaforce::EStreamFlagBits::fHasTexture) {
texBindings +=
"\n"
"@group(2) @binding(0)\n"
"var tex0: texture_2d<f32>;";
}
std::string vtxOutAttrs;
std::string vtxInAttrs;
std::string vtxXfrAttrs;
{
size_t idx = 0;
if (flags & metaforce::EStreamFlagBits::fHasNormal) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3<f32>;"), idx);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3<f32>"), ++idx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;"));
}
if (flags & metaforce::EStreamFlagBits::fHasColor) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4<f32>;"), idx);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4<f32>"), ++idx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;"));
}
if (flags & metaforce::EStreamFlagBits::fHasTexture) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex0_uv: vec2<f32>;"), idx);
vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_uv: vec2<f32>"), ++idx);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex0_uv = in_uv;"));
}
}
std::string fragmentFn;
bool hasRast = false;
for (size_t idx = 0; const auto& stage : g_tevStages) {
if (!stage) {
idx++;
continue;
}
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 : g_tevStages) {
if (!stage) {
idx++;
continue;
}
{
std::string op;
std::string outReg;
switch (stage->colorOp.x10_regId) {
case GX::TevRegID::TEVPREV:
outReg = "prev";
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"),
magic_enum::enum_name(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));
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:
outReg = "prev.a";
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("TODO: alphaOp outReg {}"),
magic_enum::enum_name(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));
fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op);
}
idx++;
}
const auto shaderSource =
fmt::format(FMT_STRING(R"""(
struct Uniform {{
xf: mat4x4<f32>;{uniBufAttrs}
}};
@group(0) @binding(0)
var<uniform> ubuf: Uniform;{sampBindings}{texBindings}
struct VertexOutput {{
@builtin(position) pos: vec4<f32>;{vtxOutAttrs}
}};
@stage(vertex)
fn vs_main(
@location(0) in_pos: vec3<f32>{vtxInAttrs}
) -> VertexOutput {{
var out: VertexOutput;
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);{vtxXfrAttrs}
return out;
}}
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
var prev: vec4<f32>;{fragmentFn}
return prev;
}}
)"""),
"uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings,
"vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs,
"fragmentFn"_a = fragmentFn);
Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource);
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.source = shaderSource.c_str();
const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{
.nextInChain = &wgslDescriptor,
.label = "Generated Shader",
};
auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor);
g_streamCachedShaders.emplace(hash, std::move(shader));
return hash;
}
void stream_end() noexcept {
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture && !sTextures[0]) {
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();
}
@ -369,28 +67,21 @@ void stream_end() noexcept {
uniBuf.append(&xf, 64);
}
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) {
uniBuf.append(&sTextures[0].lod, 4);
uniBuf.append(&tex.lod, 4);
}
const auto uniRange = push_uniform(uniBuf.data(), uniBuf.size());
const auto shaderRef = generate_shader();
const auto uniform_size = align_uniform(uniBuf.size());
const auto pipeline = pipeline_ref(stream::PipelineConfig{
.shader = shaderRef,
stream::PipelineConfig config{
.uniformSize = uniform_size,
.primitive = sStreamState->primitive,
.flags = sStreamState->flags,
.depthCompare = g_depthCompare,
.depthUpdate = g_depthUpdate,
.depthFunc = g_depthFunc,
.cullMode = g_cullMode,
.blendMode = g_blendMode,
.blendFacSrc = g_blendFacSrc,
.blendFacDst = g_blendFacDst,
.blendOp = g_blendOp,
.dstAlpha = g_dstAlpha,
});
};
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 pipeline = pipeline_ref(config);
BindGroupRef samplerBindGroup{};
BindGroupRef textureBindGroup{};
@ -399,7 +90,7 @@ void stream_end() noexcept {
{
const std::array samplerEntries{wgpu::BindGroupEntry{
.binding = 0,
.sampler = sampler_ref(sTextures[0].get_descriptor()),
.sampler = sampler_ref(tex.get_descriptor()),
}};
samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "Stream Sampler Bind Group",
@ -411,7 +102,7 @@ void stream_end() noexcept {
{
const std::array textureEntries{wgpu::BindGroupEntry{
.binding = 0,
.textureView = sTextures[0].handle.ref->view,
.textureView = tex.handle.ref->view,
}};
textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
.label = "Stream Texture Bind Group",

View File

@ -4,9 +4,10 @@
#include "../common.hpp"
#include <magic_enum.hpp>
#include <utility>
namespace aurora::gfx {
extern std::unordered_map<ShaderRef, wgpu::ShaderModule> g_streamCachedShaders;
extern std::unordered_map<ShaderRef, wgpu::ShaderModule> g_gxCachedShaders;
} // namespace aurora::gfx
namespace aurora::gfx::stream {
@ -16,31 +17,6 @@ using gpu::g_device;
using gpu::g_graphicsConfig;
using gpu::utils::make_vertex_state;
static wgpu::BlendFactor to_blend_factor(metaforce::ERglBlendFactor fac) {
switch (fac) {
case metaforce::ERglBlendFactor::Zero:
return wgpu::BlendFactor::Zero;
case metaforce::ERglBlendFactor::One:
return wgpu::BlendFactor::One;
case metaforce::ERglBlendFactor::SrcColor:
return wgpu::BlendFactor::Src;
case metaforce::ERglBlendFactor::InvSrcColor:
return wgpu::BlendFactor::OneMinusSrc;
case metaforce::ERglBlendFactor::SrcAlpha:
return wgpu::BlendFactor::SrcAlpha;
case metaforce::ERglBlendFactor::InvSrcAlpha:
return wgpu::BlendFactor::OneMinusSrcAlpha;
case metaforce::ERglBlendFactor::DstAlpha:
return wgpu::BlendFactor::DstAlpha;
case metaforce::ERglBlendFactor::InvDstAlpha:
return wgpu::BlendFactor::OneMinusDstAlpha;
case metaforce::ERglBlendFactor::DstColor:
return wgpu::BlendFactor::Dst;
case metaforce::ERglBlendFactor::InvDstColor:
return wgpu::BlendFactor::OneMinusDst;
}
}
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
std::array<wgpu::VertexAttribute, 4> attributes{};
attributes[0] = wgpu::VertexAttribute{
@ -83,115 +59,6 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
.attributes = attributes.data(),
}};
wgpu::CompareFunction depthCompare;
switch (config.depthFunc) {
case metaforce::ERglEnum::Never:
depthCompare = wgpu::CompareFunction::Never;
break;
case metaforce::ERglEnum::Less:
depthCompare = wgpu::CompareFunction::Less;
break;
case metaforce::ERglEnum::Equal:
depthCompare = wgpu::CompareFunction::Equal;
break;
case metaforce::ERglEnum::LEqual:
depthCompare = wgpu::CompareFunction::LessEqual;
break;
case metaforce::ERglEnum::Greater:
depthCompare = wgpu::CompareFunction::Greater;
break;
case metaforce::ERglEnum::NEqual:
depthCompare = wgpu::CompareFunction::NotEqual;
break;
case metaforce::ERglEnum::GEqual:
depthCompare = wgpu::CompareFunction::GreaterEqual;
break;
case metaforce::ERglEnum::Always:
depthCompare = wgpu::CompareFunction::Always;
break;
}
const auto depthStencil = wgpu::DepthStencilState{
.format = g_graphicsConfig.depthFormat,
.depthWriteEnabled = config.depthUpdate,
.depthCompare = depthCompare,
};
if (config.blendMode != metaforce::ERglBlendMode::Blend) {
Log.report(logvisor::Fatal, FMT_STRING("How to {}?"), magic_enum::enum_name(config.blendMode));
}
const auto colorBlendComponent = wgpu::BlendComponent{
.operation = wgpu::BlendOperation::Add,
.srcFactor = to_blend_factor(config.blendFacSrc),
.dstFactor = to_blend_factor(config.blendFacDst),
};
auto alphaBlendComponent = colorBlendComponent;
if (config.dstAlpha) {
alphaBlendComponent = wgpu::BlendComponent{
.operation = wgpu::BlendOperation::Add,
.srcFactor = wgpu::BlendFactor::Zero,
.dstFactor = wgpu::BlendFactor::Constant,
};
}
const auto blendState = wgpu::BlendState{
.color = colorBlendComponent,
.alpha = alphaBlendComponent,
};
auto writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue;
if (config.alphaUpdate) {
writeMask = writeMask | wgpu::ColorWriteMask::Alpha;
}
const std::array colorTargets{wgpu::ColorTargetState{
.format = g_graphicsConfig.colorFormat,
.blend = &blendState,
.writeMask = writeMask,
}};
const auto& shader = g_streamCachedShaders[config.shader];
const auto fragmentState = wgpu::FragmentState{
.module = shader,
.entryPoint = "fs_main",
.targetCount = colorTargets.size(),
.targets = colorTargets.data(),
};
wgpu::PrimitiveTopology primitive;
switch (config.primitive) {
case GX::POINTS:
primitive = wgpu::PrimitiveTopology::PointList;
break;
case GX::LINES:
primitive = wgpu::PrimitiveTopology::LineList;
break;
case GX::LINESTRIP:
primitive = wgpu::PrimitiveTopology::LineStrip;
break;
case GX::TRIANGLES:
primitive = wgpu::PrimitiveTopology::TriangleList;
break;
case GX::TRIANGLESTRIP:
primitive = wgpu::PrimitiveTopology::TriangleStrip;
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unsupported primitive type {}"), magic_enum::enum_name(config.primitive));
unreachable();
}
wgpu::FrontFace frontFace;
wgpu::CullMode cullMode;
switch (config.cullMode) {
case metaforce::ERglCullMode::Front:
frontFace = wgpu::FrontFace::CW;
cullMode = wgpu::CullMode::Front;
break;
case metaforce::ERglCullMode::Back:
frontFace = wgpu::FrontFace::CCW;
cullMode = wgpu::CullMode::Back;
break;
default:
frontFace = wgpu::FrontFace::CCW;
cullMode = wgpu::CullMode::None;
break;
}
wgpu::BindGroupLayout uniformLayout;
if (state.uniform.contains(config.uniformSize)) {
uniformLayout = state.uniform.at(config.uniformSize).layout;
@ -237,30 +104,12 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli
const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{
.label = "Stream Pipeline Layout",
.bindGroupLayoutCount =
// TODO avoid creating bind group layouts if no tex?
static_cast<uint32_t>(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1),
static_cast<uint32_t>(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1),
.bindGroupLayouts = bindGroupLayouts.data(),
};
auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor);
const auto pipelineDescriptor = wgpu::RenderPipelineDescriptor{
.label = "Stream Pipeline",
.layout = pipelineLayout,
.vertex = make_vertex_state(shader, vertexBuffers),
.primitive =
wgpu::PrimitiveState{
.topology = primitive,
.frontFace = frontFace,
.cullMode = cullMode,
},
.depthStencil = &depthStencil,
.multisample =
wgpu::MultisampleState{
.count = g_graphicsConfig.msaaSamples,
},
.fragment = &fragmentState,
};
return g_device.CreateRenderPipeline(&pipelineDescriptor);
return build_pipeline(pipelineLayout, vertexBuffers, config, "Stream Pipeline");
}
State construct_state() {

View File

@ -1,6 +1,7 @@
#pragma once
#include "../common.hpp"
#include "../gx.hpp"
namespace aurora::gfx::stream {
struct DrawData {
@ -13,19 +14,9 @@ struct DrawData {
BindGroupRef textureBindGroup;
};
struct PipelineConfig {
ShaderRef shader;
struct PipelineConfig : public GXPipelineConfig {
uint32_t uniformSize;
GX::Primitive primitive;
metaforce::EStreamFlags flags;
bool depthCompare, depthUpdate, alphaUpdate;
metaforce::ERglEnum depthFunc;
metaforce::ERglCullMode cullMode;
metaforce::ERglBlendMode blendMode;
metaforce::ERglBlendFactor blendFacSrc, blendFacDst;
metaforce::ERglLogicOp blendOp;
std::optional<float> dstAlpha;
};
struct CachedBindGroup {