mirror of https://github.com/AxioDL/metaforce.git
Extract GX shader/pipeline generation into shared API
This commit is contained in:
parent
cd86dbd6ee
commit
7d07892198
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ../)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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",
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue