mirror of https://github.com/AxioDL/metaforce.git
Initial working CGraphics Stream API
This commit is contained in:
parent
d93cf46bc3
commit
33d0d14fda
|
@ -150,10 +150,20 @@ void CGraphics::EndScene() {
|
|||
UpdateFPSCounter();
|
||||
}
|
||||
|
||||
void CGraphics::Render2D(const CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus::CColor& col) {
|
||||
const auto oldProj = g_Proj;
|
||||
CGraphics::SetOrtho(-g_Viewport.x8_width / 2, g_Viewport.x8_width / 2, g_Viewport.xc_height / 2,
|
||||
-g_Viewport.xc_height / 2, -1.f, -10.f);
|
||||
// TODO
|
||||
g_Proj = oldProj;
|
||||
FlushProjection();
|
||||
}
|
||||
|
||||
bool CGraphics::BeginRender2D(const CTexture& tex) { return false; }
|
||||
|
||||
void CGraphics::DoRender2D(const CTexture& tex, s32 x, s32 y, s32 w1, s32 w2, s32 w3, s32 w4, s32 w5,
|
||||
const zeus::CColor& col) {}
|
||||
|
||||
void CGraphics::EndRender2D(bool v) {}
|
||||
|
||||
void CGraphics::SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1) {}
|
||||
|
@ -565,7 +575,5 @@ void CGraphics::StreamVertex(const zeus::CVector3f& pos) {
|
|||
aurora::gfx::stream_vertex(sStreamFlags, pos, sQueuedNormal, sQueuedColor, sQueuedTexCoord);
|
||||
}
|
||||
|
||||
void CGraphics::StreamEnd() {
|
||||
aurora::gfx::stream_end();
|
||||
}
|
||||
void CGraphics::StreamEnd() { aurora::gfx::stream_end(); }
|
||||
} // namespace metaforce
|
||||
|
|
|
@ -155,6 +155,7 @@ public:
|
|||
static void SetCullMode(ERglCullMode);
|
||||
static void BeginScene();
|
||||
static void EndScene();
|
||||
static void Render2D(const CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus::CColor& col);
|
||||
static bool BeginRender2D(const CTexture& tex);
|
||||
static void DoRender2D(const CTexture& tex, s32 x, s32 y, s32 w1, s32 w2, s32 w3, s32 w4, s32 w5,
|
||||
const zeus::CColor& col);
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace metaforce::CTevCombiners {
|
|||
u32 CTevPass::sNextUniquePass = 0;
|
||||
|
||||
void CTevPass::Execute(ERglTevStage stage) const {
|
||||
aurora::gfx::update_tev_stage(stage, x4_colorPass, x14_alphaPass, x24_colorOp, x38_alphaOp);
|
||||
if (*this == skPassThru) {
|
||||
aurora::gfx::disable_tev_stage(stage);
|
||||
} else {
|
||||
aurora::gfx::update_tev_stage(stage, x4_colorPass, x14_alphaPass, x24_colorOp, x38_alphaOp);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 maxTevPasses = 2;
|
||||
|
|
|
@ -159,24 +159,25 @@ void CTexture::Load(GX::TexMapID id, EClampMode clamp) {
|
|||
|
||||
// GXInitObjectData(x20_texObj, image_ptr);
|
||||
// GXLoadObj(x20_texObj, id);
|
||||
aurora::gfx::bind_texture(id, clamp, x20_texObj, 0.f);
|
||||
sLoadedTextures[id] = this;
|
||||
x64_frameAllocated = sCurrentFrameCount;
|
||||
}
|
||||
}
|
||||
|
||||
void CTexture::LoadMipLevel(s32 mip, GX::TexMapID id, EClampMode clamp) {
|
||||
auto image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
|
||||
u32 width = x4_w;
|
||||
u32 height = x6_h;
|
||||
u32 iVar15 = 0;
|
||||
u32 offset = 0;
|
||||
if (mip > 0) {
|
||||
for (u32 i = 0; i < mip; ++i) {
|
||||
offset += ROUND_UP_32(x9_bitsPerPixel * (ROUND_UP_4(width) * ROUND_UP_4(height)));
|
||||
width /= 2;
|
||||
height /= 2;
|
||||
}
|
||||
}
|
||||
void CTexture::LoadMipLevel(float lod, GX::TexMapID id, EClampMode clamp) {
|
||||
// auto image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
|
||||
// u32 width = x4_w;
|
||||
// u32 height = x6_h;
|
||||
// u32 iVar15 = 0;
|
||||
// u32 offset = 0;
|
||||
// if (mip > 0) {
|
||||
// for (u32 i = 0; i < mip; ++i) {
|
||||
// offset += ROUND_UP_32(x9_bitsPerPixel * (ROUND_UP_4(width) * ROUND_UP_4(height)));
|
||||
// width /= 2;
|
||||
// height /= 2;
|
||||
// }
|
||||
// }
|
||||
|
||||
// GXTexObj texObj;
|
||||
// GXInitTexObj(&texObj, image_ptr + offset, width, height, x18_gxFormat);
|
||||
|
@ -186,6 +187,7 @@ void CTexture::LoadMipLevel(s32 mip, GX::TexMapID id, EClampMode clamp) {
|
|||
xa_25_canLoadPalette = false;
|
||||
}
|
||||
// GXLoadTexObj(&texObj, mapId);
|
||||
aurora::gfx::bind_texture(id, clamp, x20_texObj, lod);
|
||||
x64_frameAllocated = sCurrentFrameCount;
|
||||
sLoadedTextures[id] = nullptr;
|
||||
}
|
||||
|
@ -333,7 +335,10 @@ bool CTexture::sMangleMips = false;
|
|||
u32 CTexture::sCurrentFrameCount = 0;
|
||||
u32 CTexture::sTotalAllocatedMemory = 0;
|
||||
|
||||
void CTexture::InvalidateTexMap(GX::TexMapID id) { sLoadedTextures[id] = nullptr; }
|
||||
void CTexture::InvalidateTexMap(GX::TexMapID id) {
|
||||
aurora::gfx::unbind_texture(id);
|
||||
sLoadedTextures[id] = nullptr;
|
||||
}
|
||||
|
||||
CFactoryFnReturn FTextureFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms,
|
||||
CObjectReference* selfRef) {
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
[[nodiscard]] u8* Lock();
|
||||
void UnLock();
|
||||
void Load(GX::TexMapID id, EClampMode clamp);
|
||||
void LoadMipLevel(s32 mip, GX::TexMapID id, EClampMode clamp);
|
||||
void LoadMipLevel(float lod, GX::TexMapID id, EClampMode clamp); // was an s32 mip parameter, adjusted to use lod
|
||||
// void UnloadBitmapData(u32) const;
|
||||
// void TryReloadBitmapData(CResFactory&) const;
|
||||
// void LoadToMRAM() const;
|
||||
|
|
|
@ -278,6 +278,7 @@ enum TextureFormat : uint32_t {
|
|||
|
||||
enum TexMapID {
|
||||
TEXMAP0,
|
||||
TEXMAP1,
|
||||
TEXMAP2,
|
||||
TEXMAP3,
|
||||
TEXMAP4,
|
||||
|
|
|
@ -24,11 +24,12 @@ add_library(aurora STATIC
|
|||
lib/dawn/Hacks.cpp
|
||||
lib/gfx/common.cpp
|
||||
lib/gfx/texture.cpp
|
||||
lib/gfx/stream.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.cpp
|
||||
lib/gfx/texture_convert.cpp
|
||||
lib/gfx/stream/shader.cpp
|
||||
)
|
||||
target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32
|
||||
target_include_directories(aurora PUBLIC include ../)
|
||||
|
|
|
@ -45,7 +45,9 @@ enum class ERglBlendFactor {
|
|||
SrcAlpha = 4,
|
||||
InvSrcAlpha = 5,
|
||||
DstAlpha = 6,
|
||||
InvDstAlpha = 7
|
||||
InvDstAlpha = 7,
|
||||
DstColor = 8,
|
||||
InvDstColor = 9,
|
||||
};
|
||||
|
||||
enum class ERglLogicOp {
|
||||
|
@ -137,7 +139,7 @@ struct CFogState {
|
|||
enum class EStreamFlagBits : u8 {
|
||||
fHasNormal = 0x1,
|
||||
fHasColor = 0x2,
|
||||
fHasTexture = 0x3,
|
||||
fHasTexture = 0x4,
|
||||
};
|
||||
using EStreamFlags = Flags<EStreamFlagBits>;
|
||||
|
||||
|
@ -220,7 +222,9 @@ enum class ZComp : uint8_t {
|
|||
|
||||
[[nodiscard]] bool get_dxt_compression_supported() noexcept;
|
||||
|
||||
void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp) noexcept;
|
||||
void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept;
|
||||
void unbind_texture(GX::TexMapID id) noexcept;
|
||||
void disable_tev_stage(metaforce::ERglTevStage stage) noexcept;
|
||||
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
|
||||
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
||||
const metaforce::CTevCombiners::CTevOp& colorOp,
|
||||
|
|
|
@ -257,7 +257,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
g_window = SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, flags);
|
||||
g_window = SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 960, flags);
|
||||
if (g_window == nullptr) {
|
||||
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
||||
unreachable();
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace aurora::gfx::colored_quad {
|
||||
|
@ -44,4 +46,4 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type,
|
|||
const zeus::CColor& color, const ArrayRef<zeus::CVector3f>& pos);
|
||||
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||
} // namespace aurora::gfx::colored_quad
|
||||
} // namespace aurora::gfx::colored_quad
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
#include "../gpu.hpp"
|
||||
#include "colored_quad/shader.hpp"
|
||||
#include "textured_quad/shader.hpp"
|
||||
#include "movie_player/shader.hpp"
|
||||
#include "stream/shader.hpp"
|
||||
#include "textured_quad/shader.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
|
@ -21,6 +22,7 @@ struct ShaderState {
|
|||
movie_player::State moviePlayer;
|
||||
colored_quad::State coloredQuad;
|
||||
textured_quad::State texturedQuad;
|
||||
stream::State stream;
|
||||
};
|
||||
struct ShaderDrawCommand {
|
||||
ShaderType type;
|
||||
|
@ -28,6 +30,7 @@ struct ShaderDrawCommand {
|
|||
movie_player::DrawData moviePlayer;
|
||||
colored_quad::DrawData coloredQuad;
|
||||
textured_quad::DrawData texturedQuad;
|
||||
stream::DrawData stream;
|
||||
};
|
||||
};
|
||||
struct PipelineCreateCommand {
|
||||
|
@ -36,6 +39,7 @@ struct PipelineCreateCommand {
|
|||
movie_player::PipelineConfig moviePlayer;
|
||||
colored_quad::PipelineConfig coloredQuad;
|
||||
textured_quad::PipelineConfig texturedQuad;
|
||||
stream::PipelineConfig stream;
|
||||
};
|
||||
};
|
||||
enum class CommandType {
|
||||
|
@ -65,6 +69,19 @@ zeus::CMatrix4f g_mv;
|
|||
zeus::CMatrix4f g_mvInv;
|
||||
zeus::CMatrix4f g_proj;
|
||||
metaforce::CFogState g_fogState;
|
||||
// GX state
|
||||
metaforce::ERglCullMode g_cullMode;
|
||||
metaforce::ERglBlendMode g_blendMode;
|
||||
metaforce::ERglBlendFactor g_blendFacSrc;
|
||||
metaforce::ERglBlendFactor g_blendFacDst;
|
||||
metaforce::ERglLogicOp g_blendOp;
|
||||
bool g_depthCompare;
|
||||
bool g_depthUpdate;
|
||||
metaforce::ERglEnum g_depthFunc;
|
||||
std::array<zeus::CColor, 4> g_colorRegs;
|
||||
bool g_alphaUpdate;
|
||||
std::optional<float> g_dstAlpha;
|
||||
zeus::CColor g_clearColor;
|
||||
|
||||
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||
static std::mutex g_pipelineMutex;
|
||||
|
@ -74,6 +91,7 @@ static std::condition_variable g_pipelineCv;
|
|||
static std::unordered_map<PipelineRef, wgpu::RenderPipeline> g_pipelines;
|
||||
static std::deque<std::pair<PipelineRef, NewPipelineCallback>> g_queuedPipelines;
|
||||
static std::unordered_map<BindGroupRef, wgpu::BindGroup> g_cachedBindGroups;
|
||||
static std::unordered_map<SamplerRef, wgpu::Sampler> g_cachedSamplers;
|
||||
std::atomic_uint32_t queuedPipelines;
|
||||
std::atomic_uint32_t createdPipelines;
|
||||
|
||||
|
@ -118,14 +136,29 @@ static void push_draw_command(ShaderDrawCommand data) { g_commands.push_back({Co
|
|||
bool get_dxt_compression_supported() noexcept { return g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC); }
|
||||
|
||||
// GX state
|
||||
void set_cull_mode(metaforce::ERglCullMode mode) noexcept {}
|
||||
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 {}
|
||||
void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept {}
|
||||
void set_gx_reg1_color(const zeus::CColor& color) noexcept {}
|
||||
void set_alpha_update(bool enabled) noexcept {}
|
||||
void set_dst_alpha(bool enabled, float value) noexcept {}
|
||||
void set_clear_color(const zeus::CColor& color) noexcept {}
|
||||
metaforce::ERglLogicOp op) noexcept {
|
||||
g_blendMode = mode;
|
||||
g_blendFacSrc = src;
|
||||
g_blendFacDst = dst;
|
||||
g_blendOp = op;
|
||||
}
|
||||
void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept {
|
||||
g_depthCompare = compare_enable;
|
||||
g_depthFunc = func;
|
||||
g_depthUpdate = update_enable;
|
||||
}
|
||||
void set_gx_reg1_color(const zeus::CColor& color) noexcept { g_colorRegs[1] = color; }
|
||||
void set_alpha_update(bool enabled) noexcept { g_alphaUpdate = enabled; }
|
||||
void set_dst_alpha(bool enabled, float value) noexcept {
|
||||
if (enabled) {
|
||||
g_dstAlpha = value;
|
||||
} else {
|
||||
g_dstAlpha.reset();
|
||||
}
|
||||
}
|
||||
void set_clear_color(const zeus::CColor& color) noexcept { g_clearColor = color; }
|
||||
|
||||
// Model state
|
||||
void set_alpha_discard(bool v) {}
|
||||
|
@ -195,7 +228,6 @@ void queue_colored_quad(CameraFilterType filter_type, ZComp z_comparison, bool z
|
|||
auto data = colored_quad::make_draw_data(g_state.coloredQuad, filter_type, z_comparison, z_test, color, rect, z);
|
||||
push_draw_command({.type = ShaderType::ColoredQuad, .coloredQuad = data});
|
||||
}
|
||||
|
||||
template <>
|
||||
PipelineRef pipeline_ref(colored_quad::PipelineConfig config) {
|
||||
return find_pipeline({.type = ShaderType::ColoredQuad, .coloredQuad = config},
|
||||
|
@ -214,6 +246,20 @@ PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
|||
[=]() { return create_pipeline(g_state.moviePlayer, config); });
|
||||
}
|
||||
|
||||
template <>
|
||||
const stream::State& get_state() {
|
||||
return g_state.stream;
|
||||
}
|
||||
template <>
|
||||
void push_draw_command(stream::DrawData data) {
|
||||
push_draw_command({.type = ShaderType::Stream, .stream = data});
|
||||
}
|
||||
template <>
|
||||
PipelineRef pipeline_ref(stream::PipelineConfig config) {
|
||||
return find_pipeline({.type = ShaderType::Stream, .stream = config},
|
||||
[=]() { return create_pipeline(g_state.stream, config); });
|
||||
}
|
||||
|
||||
static void pipeline_worker() {
|
||||
bool hasMore = false;
|
||||
while (true) {
|
||||
|
@ -276,6 +322,7 @@ void initialize() {
|
|||
g_state.moviePlayer = movie_player::construct_state();
|
||||
g_state.coloredQuad = colored_quad::construct_state();
|
||||
g_state.texturedQuad = textured_quad::construct_state();
|
||||
g_state.stream = stream::construct_state();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
|
@ -336,6 +383,9 @@ void render(const wgpu::RenderPassEncoder& pass) {
|
|||
case ShaderType::MoviePlayer:
|
||||
movie_player::render(g_state.moviePlayer, draw.moviePlayer, pass);
|
||||
break;
|
||||
case ShaderType::Stream:
|
||||
stream::render(g_state.stream, draw.stream, pass);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
@ -392,4 +442,19 @@ const wgpu::BindGroup& find_bind_group(BindGroupRef id) {
|
|||
}
|
||||
return g_cachedBindGroups[id];
|
||||
}
|
||||
|
||||
const wgpu::Sampler& sampler_ref(const wgpu::SamplerDescriptor& descriptor) {
|
||||
const auto id = xxh3_hash(descriptor);
|
||||
if (!g_cachedSamplers.contains(id)) {
|
||||
g_cachedSamplers[id] = g_device.CreateSampler(&descriptor);
|
||||
}
|
||||
return g_cachedSamplers[id];
|
||||
}
|
||||
|
||||
uint32_t align_uniform(uint32_t value) {
|
||||
wgpu::SupportedLimits limits;
|
||||
g_device.GetLimits(&limits); // TODO cache
|
||||
const auto uniform_alignment = limits.limits.minUniformBufferOffsetAlignment;
|
||||
return ALIGN(value, uniform_alignment);
|
||||
}
|
||||
} // namespace aurora::gfx
|
||||
|
|
|
@ -146,6 +146,19 @@ extern zeus::CMatrix4f g_mv;
|
|||
extern zeus::CMatrix4f g_mvInv;
|
||||
extern zeus::CMatrix4f g_proj;
|
||||
extern metaforce::CFogState g_fogState;
|
||||
// GX state
|
||||
extern metaforce::ERglCullMode g_cullMode;
|
||||
extern metaforce::ERglBlendMode g_blendMode;
|
||||
extern metaforce::ERglBlendFactor g_blendFacSrc;
|
||||
extern metaforce::ERglBlendFactor g_blendFacDst;
|
||||
extern metaforce::ERglLogicOp g_blendOp;
|
||||
extern bool g_depthCompare;
|
||||
extern bool g_depthUpdate;
|
||||
extern metaforce::ERglEnum g_depthFunc;
|
||||
extern std::array<zeus::CColor, 4> g_colorRegs;
|
||||
extern bool g_alphaUpdate;
|
||||
extern std::optional<float> g_dstAlpha;
|
||||
extern zeus::CColor g_clearColor;
|
||||
|
||||
extern wgpu::Buffer g_vertexBuffer;
|
||||
extern wgpu::Buffer g_uniformBuffer;
|
||||
|
@ -169,8 +182,10 @@ struct TextureRef {
|
|||
, gameFormat(gameFormat) {}
|
||||
};
|
||||
|
||||
using PipelineRef = uint64_t;
|
||||
using BindGroupRef = uint64_t;
|
||||
using PipelineRef = uint64_t;
|
||||
using SamplerRef = uint64_t;
|
||||
using ShaderRef = uint64_t;
|
||||
using Range = std::pair<uint32_t, uint32_t>;
|
||||
|
||||
enum class ShaderType {
|
||||
|
@ -178,6 +193,7 @@ enum class ShaderType {
|
|||
ColoredQuad,
|
||||
TexturedQuad,
|
||||
MoviePlayer,
|
||||
Stream,
|
||||
};
|
||||
|
||||
void initialize();
|
||||
|
@ -201,6 +217,11 @@ static inline Range push_uniform(const T& data) {
|
|||
return push_uniform(reinterpret_cast<const uint8_t*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename State>
|
||||
const State& get_state();
|
||||
template <typename DrawData>
|
||||
void push_draw_command(DrawData data);
|
||||
|
||||
template <typename PipelineConfig>
|
||||
PipelineRef pipeline_ref(PipelineConfig config);
|
||||
bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass);
|
||||
|
@ -208,5 +229,9 @@ bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass);
|
|||
BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor);
|
||||
const wgpu::BindGroup& find_bind_group(BindGroupRef id);
|
||||
|
||||
static inline zeus::CMatrix4f get_combined_matrix() { return g_proj * g_mv; }
|
||||
const wgpu::Sampler& sampler_ref(const wgpu::SamplerDescriptor& descriptor);
|
||||
|
||||
uint32_t align_uniform(uint32_t value);
|
||||
|
||||
static inline Mat4x4<float> get_combined_matrix() { return g_proj * g_mv; }
|
||||
} // namespace aurora::gfx
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace aurora::gfx::movie_player {
|
||||
|
|
|
@ -1,15 +1,458 @@
|
|||
#include "stream/shader.hpp"
|
||||
|
||||
#include "../gpu.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
namespace aurora::gfx {
|
||||
using namespace fmt::literals;
|
||||
|
||||
static logvisor::Module Log("aurora::gfx::stream");
|
||||
|
||||
struct STevStage {
|
||||
metaforce::CTevCombiners::ColorPass colorPass;
|
||||
metaforce::CTevCombiners::AlphaPass alphaPass;
|
||||
metaforce::CTevCombiners::CTevOp colorOp;
|
||||
metaforce::CTevCombiners::CTevOp alphaOp;
|
||||
|
||||
STevStage(const metaforce::CTevCombiners::ColorPass& colPass, const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
||||
const metaforce::CTevCombiners::CTevOp& colorOp, const metaforce::CTevCombiners::CTevOp& alphaOp)
|
||||
: colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {}
|
||||
};
|
||||
constexpr u32 maxTevStages = 2;
|
||||
//static std::array<STevStage, maxTevStages> sTevStages;
|
||||
static std::array<std::optional<STevStage>, maxTevStages> sTevStages;
|
||||
|
||||
void disable_tev_stage(metaforce::ERglTevStage stage) noexcept { sTevStages[static_cast<size_t>(stage)].reset(); }
|
||||
|
||||
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
|
||||
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
||||
const metaforce::CTevCombiners::CTevOp& colorOp,
|
||||
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {}
|
||||
void stream_begin(GX::Primitive primitive) noexcept {}
|
||||
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {
|
||||
sTevStages[static_cast<size_t>(stage)] = {colPass, alphaPass, colorOp, alphaOp};
|
||||
}
|
||||
|
||||
struct SStreamState {
|
||||
GX::Primitive primitive;
|
||||
metaforce::EStreamFlags flags;
|
||||
uint32_t vertexCount = 0;
|
||||
ByteBuffer vertexBuffer;
|
||||
|
||||
explicit SStreamState(GX::Primitive primitive) noexcept : primitive(primitive) {}
|
||||
};
|
||||
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!"));
|
||||
unreachable();
|
||||
}
|
||||
sStreamState.emplace(primitive);
|
||||
}
|
||||
|
||||
void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, const zeus::CVector3f& nrm,
|
||||
const zeus::CColor& color, const zeus::CVector2f& uv) noexcept {}
|
||||
void stream_end() noexcept {}
|
||||
const zeus::CColor& color, const zeus::CVector2f& uv) noexcept {
|
||||
if (!sStreamState) {
|
||||
Log.report(logvisor::Fatal, FMT_STRING("Stream not started!"));
|
||||
unreachable();
|
||||
}
|
||||
if (sStreamState->flags) {
|
||||
if (sStreamState->flags != flags) {
|
||||
Log.report(logvisor::Fatal, FMT_STRING("Stream changed flags?"));
|
||||
unreachable();
|
||||
}
|
||||
} else {
|
||||
sStreamState->flags = flags;
|
||||
// TODO begin shader construction
|
||||
}
|
||||
sStreamState->vertexBuffer.append(&pos, 12);
|
||||
if (flags & metaforce::EStreamFlagBits::fHasNormal) {
|
||||
sStreamState->vertexBuffer.append(&nrm, 12);
|
||||
}
|
||||
if (flags & metaforce::EStreamFlagBits::fHasColor) {
|
||||
sStreamState->vertexBuffer.append(&color, 16);
|
||||
}
|
||||
if (flags & metaforce::EStreamFlagBits::fHasTexture) {
|
||||
sStreamState->vertexBuffer.append(&uv, 8);
|
||||
}
|
||||
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 = xxh3_hash(sTevStages, 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 : sTevStages) {
|
||||
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 : sTevStages) {
|
||||
if (!stage) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
{
|
||||
std::string op;
|
||||
std::string outReg;
|
||||
switch (stage->colorOp.xc_regId) {
|
||||
case GX::TevRegID::TEVPREV:
|
||||
outReg = "prev";
|
||||
break;
|
||||
default:
|
||||
Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"),
|
||||
magic_enum::enum_name(stage->colorOp.xc_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.xc_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.xc_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]) {
|
||||
Log.report(logvisor::Fatal, FMT_STRING("Stream has texture but no texture bound!"));
|
||||
unreachable();
|
||||
}
|
||||
const auto vertRange = push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size());
|
||||
|
||||
ByteBuffer uniBuf;
|
||||
std::bitset<g_colorRegs.size()> usedColors;
|
||||
{
|
||||
const auto xf = get_combined_matrix();
|
||||
uniBuf.append(&xf, 64);
|
||||
}
|
||||
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) {
|
||||
uniBuf.append(&sTextures[0].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,
|
||||
.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,
|
||||
});
|
||||
|
||||
BindGroupRef samplerBindGroup{};
|
||||
BindGroupRef textureBindGroup{};
|
||||
if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) {
|
||||
const auto& state = get_state<stream::State>();
|
||||
{
|
||||
const std::array samplerEntries{wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.sampler = sampler_ref(sTextures[0].get_descriptor()),
|
||||
}};
|
||||
samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
|
||||
.label = "Stream Sampler Bind Group",
|
||||
.layout = state.samplerLayout,
|
||||
.entryCount = samplerEntries.size(),
|
||||
.entries = samplerEntries.data(),
|
||||
});
|
||||
}
|
||||
{
|
||||
const std::array textureEntries{wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.textureView = sTextures[0].handle.ref->view,
|
||||
}};
|
||||
textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{
|
||||
.label = "Stream Texture Bind Group",
|
||||
.layout = state.textureLayout,
|
||||
.entryCount = textureEntries.size(),
|
||||
.entries = textureEntries.data(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
push_draw_command(stream::DrawData{
|
||||
.pipeline = pipeline,
|
||||
.vertRange = vertRange,
|
||||
.uniformRange = uniRange,
|
||||
.vertexCount = sStreamState->vertexCount,
|
||||
.uniformSize = uniform_size,
|
||||
.samplerBindGroup = samplerBindGroup,
|
||||
.textureBindGroup = textureBindGroup,
|
||||
});
|
||||
|
||||
sStreamState.reset();
|
||||
}
|
||||
} // namespace aurora::gfx
|
||||
|
|
|
@ -0,0 +1,322 @@
|
|||
#include "shader.hpp"
|
||||
|
||||
#include "../../gpu.hpp"
|
||||
#include "../common.hpp"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
namespace aurora::gfx {
|
||||
extern std::unordered_map<ShaderRef, wgpu::ShaderModule> g_streamCachedShaders;
|
||||
} // namespace aurora::gfx
|
||||
|
||||
namespace aurora::gfx::stream {
|
||||
static logvisor::Module Log("aurora::gfx::stream");
|
||||
|
||||
using gpu::g_device;
|
||||
using gpu::g_graphicsConfig;
|
||||
using gpu::utils::make_vertex_state;
|
||||
|
||||
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{
|
||||
.format = wgpu::VertexFormat::Float32x3,
|
||||
.offset = 0,
|
||||
.shaderLocation = 0,
|
||||
};
|
||||
uint64_t offset = 12;
|
||||
uint32_t shaderLocation = 1;
|
||||
if (config.flags & metaforce::EStreamFlagBits::fHasNormal) {
|
||||
attributes[shaderLocation] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x3,
|
||||
.offset = offset,
|
||||
.shaderLocation = shaderLocation,
|
||||
};
|
||||
offset += 12;
|
||||
shaderLocation++;
|
||||
}
|
||||
if (config.flags & metaforce::EStreamFlagBits::fHasColor) {
|
||||
attributes[shaderLocation] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x4,
|
||||
.offset = offset,
|
||||
.shaderLocation = shaderLocation,
|
||||
};
|
||||
offset += 16;
|
||||
shaderLocation++;
|
||||
}
|
||||
if (config.flags & metaforce::EStreamFlagBits::fHasTexture) {
|
||||
attributes[shaderLocation] = wgpu::VertexAttribute{
|
||||
.format = wgpu::VertexFormat::Float32x2,
|
||||
.offset = offset,
|
||||
.shaderLocation = shaderLocation,
|
||||
};
|
||||
offset += 8;
|
||||
shaderLocation++;
|
||||
}
|
||||
const std::array vertexBuffers{wgpu::VertexBufferLayout{
|
||||
.arrayStride = offset,
|
||||
.attributeCount = shaderLocation,
|
||||
.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;
|
||||
} else {
|
||||
const std::array uniformLayoutEntries{wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
|
||||
.buffer =
|
||||
wgpu::BufferBindingLayout{
|
||||
.type = wgpu::BufferBindingType::Uniform,
|
||||
.hasDynamicOffset = true,
|
||||
.minBindingSize = config.uniformSize,
|
||||
},
|
||||
}};
|
||||
const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
|
||||
.label = "Stream Uniform Bind Group Layout",
|
||||
.entryCount = uniformLayoutEntries.size(),
|
||||
.entries = uniformLayoutEntries.data(),
|
||||
};
|
||||
uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor);
|
||||
|
||||
const std::array uniformBindGroupEntries{wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.buffer = g_uniformBuffer,
|
||||
.size = config.uniformSize,
|
||||
}};
|
||||
const auto uniformBindGroupDescriptor = wgpu::BindGroupDescriptor{
|
||||
.label = "Stream Quad Uniform Bind Group",
|
||||
.layout = uniformLayout,
|
||||
.entryCount = uniformBindGroupEntries.size(),
|
||||
.entries = uniformBindGroupEntries.data(),
|
||||
};
|
||||
auto uniformBindGroup = g_device.CreateBindGroup(&uniformBindGroupDescriptor);
|
||||
|
||||
state.uniform.try_emplace(config.uniformSize, uniformLayout, std::move(uniformBindGroup));
|
||||
}
|
||||
|
||||
const std::array bindGroupLayouts{
|
||||
uniformLayout,
|
||||
state.samplerLayout,
|
||||
state.textureLayout,
|
||||
};
|
||||
const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{
|
||||
.label = "Stream Pipeline Layout",
|
||||
.bindGroupLayoutCount =
|
||||
// TODO avoid creating bind group layouts if no tex?
|
||||
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);
|
||||
}
|
||||
|
||||
State construct_state() {
|
||||
const auto samplerBinding = wgpu::SamplerBindingLayout{
|
||||
.type = wgpu::SamplerBindingType::Filtering,
|
||||
};
|
||||
const std::array samplerLayoutEntries{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.sampler = samplerBinding,
|
||||
},
|
||||
};
|
||||
const auto samplerLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
|
||||
.label = "Stream Sampler Bind Group Layout",
|
||||
.entryCount = samplerLayoutEntries.size(),
|
||||
.entries = samplerLayoutEntries.data(),
|
||||
};
|
||||
auto samplerLayout = g_device.CreateBindGroupLayout(&samplerLayoutDescriptor);
|
||||
|
||||
const auto textureBinding = wgpu::TextureBindingLayout{
|
||||
.sampleType = wgpu::TextureSampleType::Float,
|
||||
.viewDimension = wgpu::TextureViewDimension::e2D,
|
||||
};
|
||||
const std::array textureLayoutEntries{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = textureBinding,
|
||||
},
|
||||
};
|
||||
const auto textureLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{
|
||||
.label = "Stream Texture Bind Group Layout",
|
||||
.entryCount = textureLayoutEntries.size(),
|
||||
.entries = textureLayoutEntries.data(),
|
||||
};
|
||||
auto textureLayout = g_device.CreateBindGroupLayout(&textureLayoutDescriptor);
|
||||
|
||||
return {
|
||||
.samplerLayout = samplerLayout,
|
||||
.textureLayout = textureLayout,
|
||||
};
|
||||
}
|
||||
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) {
|
||||
if (!bind_pipeline(data.pipeline, pass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::array offsets{data.uniformRange.first};
|
||||
pass.SetBindGroup(0, state.uniform.at(data.uniformSize).bindGroup, offsets.size(), offsets.data());
|
||||
if (data.samplerBindGroup && data.textureBindGroup) {
|
||||
pass.SetBindGroup(1, find_bind_group(data.samplerBindGroup));
|
||||
pass.SetBindGroup(2, find_bind_group(data.textureBindGroup));
|
||||
}
|
||||
pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second);
|
||||
pass.Draw(data.vertexCount);
|
||||
}
|
||||
} // namespace aurora::gfx::stream
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace aurora::gfx::stream {
|
||||
struct DrawData {
|
||||
PipelineRef pipeline;
|
||||
Range vertRange;
|
||||
Range uniformRange;
|
||||
uint32_t vertexCount;
|
||||
uint32_t uniformSize;
|
||||
BindGroupRef samplerBindGroup;
|
||||
BindGroupRef textureBindGroup;
|
||||
};
|
||||
|
||||
struct PipelineConfig {
|
||||
ShaderRef shader;
|
||||
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 {
|
||||
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::stream
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace aurora::gfx::textured_quad {
|
||||
|
|
|
@ -21,7 +21,7 @@ struct GraphicsConfig {
|
|||
wgpu::TextureFormat colorFormat;
|
||||
wgpu::TextureFormat depthFormat;
|
||||
uint32_t msaaSamples;
|
||||
uint8_t textureAnistropy;
|
||||
uint16_t textureAnistropy;
|
||||
};
|
||||
struct TextureWithSampler {
|
||||
wgpu::Texture texture;
|
||||
|
@ -58,7 +58,7 @@ namespace aurora::gpu::utils {
|
|||
template <auto N>
|
||||
static consteval std::array<wgpu::VertexAttribute, N>
|
||||
make_vertex_attributes(std::array<wgpu::VertexFormat, N> formats) {
|
||||
std::array<wgpu::VertexAttribute, N> attributes{};
|
||||
std::array<wgpu::VertexAttribute, N> attributes;
|
||||
uint64_t offset = 0;
|
||||
for (uint32_t i = 0; i < N; ++i) {
|
||||
auto format = formats[i];
|
||||
|
|
Loading…
Reference in New Issue