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();
|
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; }
|
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,
|
void CGraphics::DoRender2D(const CTexture& tex, s32 x, s32 y, s32 w1, s32 w2, s32 w3, s32 w4, s32 w5,
|
||||||
const zeus::CColor& col) {}
|
const zeus::CColor& col) {}
|
||||||
|
|
||||||
void CGraphics::EndRender2D(bool v) {}
|
void CGraphics::EndRender2D(bool v) {}
|
||||||
|
|
||||||
void CGraphics::SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1) {}
|
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);
|
aurora::gfx::stream_vertex(sStreamFlags, pos, sQueuedNormal, sQueuedColor, sQueuedTexCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGraphics::StreamEnd() {
|
void CGraphics::StreamEnd() { aurora::gfx::stream_end(); }
|
||||||
aurora::gfx::stream_end();
|
|
||||||
}
|
|
||||||
} // namespace metaforce
|
} // namespace metaforce
|
||||||
|
|
|
@ -155,6 +155,7 @@ public:
|
||||||
static void SetCullMode(ERglCullMode);
|
static void SetCullMode(ERglCullMode);
|
||||||
static void BeginScene();
|
static void BeginScene();
|
||||||
static void EndScene();
|
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 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,
|
static void DoRender2D(const CTexture& tex, s32 x, s32 y, s32 w1, s32 w2, s32 w3, s32 w4, s32 w5,
|
||||||
const zeus::CColor& col);
|
const zeus::CColor& col);
|
||||||
|
|
|
@ -4,7 +4,11 @@ namespace metaforce::CTevCombiners {
|
||||||
u32 CTevPass::sNextUniquePass = 0;
|
u32 CTevPass::sNextUniquePass = 0;
|
||||||
|
|
||||||
void CTevPass::Execute(ERglTevStage stage) const {
|
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;
|
constexpr u32 maxTevPasses = 2;
|
||||||
|
|
|
@ -159,24 +159,25 @@ void CTexture::Load(GX::TexMapID id, EClampMode clamp) {
|
||||||
|
|
||||||
// GXInitObjectData(x20_texObj, image_ptr);
|
// GXInitObjectData(x20_texObj, image_ptr);
|
||||||
// GXLoadObj(x20_texObj, id);
|
// GXLoadObj(x20_texObj, id);
|
||||||
|
aurora::gfx::bind_texture(id, clamp, x20_texObj, 0.f);
|
||||||
sLoadedTextures[id] = this;
|
sLoadedTextures[id] = this;
|
||||||
x64_frameAllocated = sCurrentFrameCount;
|
x64_frameAllocated = sCurrentFrameCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTexture::LoadMipLevel(s32 mip, GX::TexMapID id, EClampMode clamp) {
|
void CTexture::LoadMipLevel(float lod, GX::TexMapID id, EClampMode clamp) {
|
||||||
auto image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
|
// auto image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get();
|
||||||
u32 width = x4_w;
|
// u32 width = x4_w;
|
||||||
u32 height = x6_h;
|
// u32 height = x6_h;
|
||||||
u32 iVar15 = 0;
|
// u32 iVar15 = 0;
|
||||||
u32 offset = 0;
|
// u32 offset = 0;
|
||||||
if (mip > 0) {
|
// if (mip > 0) {
|
||||||
for (u32 i = 0; i < mip; ++i) {
|
// for (u32 i = 0; i < mip; ++i) {
|
||||||
offset += ROUND_UP_32(x9_bitsPerPixel * (ROUND_UP_4(width) * ROUND_UP_4(height)));
|
// offset += ROUND_UP_32(x9_bitsPerPixel * (ROUND_UP_4(width) * ROUND_UP_4(height)));
|
||||||
width /= 2;
|
// width /= 2;
|
||||||
height /= 2;
|
// height /= 2;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// GXTexObj texObj;
|
// GXTexObj texObj;
|
||||||
// GXInitTexObj(&texObj, image_ptr + offset, width, height, x18_gxFormat);
|
// 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;
|
xa_25_canLoadPalette = false;
|
||||||
}
|
}
|
||||||
// GXLoadTexObj(&texObj, mapId);
|
// GXLoadTexObj(&texObj, mapId);
|
||||||
|
aurora::gfx::bind_texture(id, clamp, x20_texObj, lod);
|
||||||
x64_frameAllocated = sCurrentFrameCount;
|
x64_frameAllocated = sCurrentFrameCount;
|
||||||
sLoadedTextures[id] = nullptr;
|
sLoadedTextures[id] = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -333,7 +335,10 @@ bool CTexture::sMangleMips = false;
|
||||||
u32 CTexture::sCurrentFrameCount = 0;
|
u32 CTexture::sCurrentFrameCount = 0;
|
||||||
u32 CTexture::sTotalAllocatedMemory = 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,
|
CFactoryFnReturn FTextureFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms,
|
||||||
CObjectReference* selfRef) {
|
CObjectReference* selfRef) {
|
||||||
|
|
|
@ -90,7 +90,7 @@ public:
|
||||||
[[nodiscard]] u8* Lock();
|
[[nodiscard]] u8* Lock();
|
||||||
void UnLock();
|
void UnLock();
|
||||||
void Load(GX::TexMapID id, EClampMode clamp);
|
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 UnloadBitmapData(u32) const;
|
||||||
// void TryReloadBitmapData(CResFactory&) const;
|
// void TryReloadBitmapData(CResFactory&) const;
|
||||||
// void LoadToMRAM() const;
|
// void LoadToMRAM() const;
|
||||||
|
|
|
@ -278,6 +278,7 @@ enum TextureFormat : uint32_t {
|
||||||
|
|
||||||
enum TexMapID {
|
enum TexMapID {
|
||||||
TEXMAP0,
|
TEXMAP0,
|
||||||
|
TEXMAP1,
|
||||||
TEXMAP2,
|
TEXMAP2,
|
||||||
TEXMAP3,
|
TEXMAP3,
|
||||||
TEXMAP4,
|
TEXMAP4,
|
||||||
|
|
|
@ -24,11 +24,12 @@ add_library(aurora STATIC
|
||||||
lib/dawn/Hacks.cpp
|
lib/dawn/Hacks.cpp
|
||||||
lib/gfx/common.cpp
|
lib/gfx/common.cpp
|
||||||
lib/gfx/texture.cpp
|
lib/gfx/texture.cpp
|
||||||
|
lib/gfx/stream.cpp
|
||||||
|
lib/gfx/texture_convert.cpp
|
||||||
lib/gfx/movie_player/shader.cpp
|
lib/gfx/movie_player/shader.cpp
|
||||||
lib/gfx/textured_quad/shader.cpp
|
lib/gfx/textured_quad/shader.cpp
|
||||||
lib/gfx/colored_quad/shader.cpp
|
lib/gfx/colored_quad/shader.cpp
|
||||||
lib/gfx/stream.cpp
|
lib/gfx/stream/shader.cpp
|
||||||
lib/gfx/texture_convert.cpp
|
|
||||||
)
|
)
|
||||||
target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32
|
target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32
|
||||||
target_include_directories(aurora PUBLIC include ../)
|
target_include_directories(aurora PUBLIC include ../)
|
||||||
|
|
|
@ -45,7 +45,9 @@ enum class ERglBlendFactor {
|
||||||
SrcAlpha = 4,
|
SrcAlpha = 4,
|
||||||
InvSrcAlpha = 5,
|
InvSrcAlpha = 5,
|
||||||
DstAlpha = 6,
|
DstAlpha = 6,
|
||||||
InvDstAlpha = 7
|
InvDstAlpha = 7,
|
||||||
|
DstColor = 8,
|
||||||
|
InvDstColor = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ERglLogicOp {
|
enum class ERglLogicOp {
|
||||||
|
@ -137,7 +139,7 @@ struct CFogState {
|
||||||
enum class EStreamFlagBits : u8 {
|
enum class EStreamFlagBits : u8 {
|
||||||
fHasNormal = 0x1,
|
fHasNormal = 0x1,
|
||||||
fHasColor = 0x2,
|
fHasColor = 0x2,
|
||||||
fHasTexture = 0x3,
|
fHasTexture = 0x4,
|
||||||
};
|
};
|
||||||
using EStreamFlags = Flags<EStreamFlagBits>;
|
using EStreamFlags = Flags<EStreamFlagBits>;
|
||||||
|
|
||||||
|
@ -220,7 +222,9 @@ enum class ZComp : uint8_t {
|
||||||
|
|
||||||
[[nodiscard]] bool get_dxt_compression_supported() noexcept;
|
[[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,
|
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
|
||||||
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
||||||
const metaforce::CTevCombiners::CTevOp& colorOp,
|
const metaforce::CTevCombiners::CTevOp& colorOp,
|
||||||
|
|
|
@ -257,7 +257,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv)
|
||||||
default:
|
default:
|
||||||
break;
|
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) {
|
if (g_window == nullptr) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
||||||
unreachable();
|
unreachable();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "../common.hpp"
|
#include "../common.hpp"
|
||||||
|
|
||||||
namespace aurora::gfx::colored_quad {
|
namespace aurora::gfx::colored_quad {
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
#include "../gpu.hpp"
|
#include "../gpu.hpp"
|
||||||
#include "colored_quad/shader.hpp"
|
#include "colored_quad/shader.hpp"
|
||||||
#include "textured_quad/shader.hpp"
|
|
||||||
#include "movie_player/shader.hpp"
|
#include "movie_player/shader.hpp"
|
||||||
|
#include "stream/shader.hpp"
|
||||||
|
#include "textured_quad/shader.hpp"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -21,6 +22,7 @@ struct ShaderState {
|
||||||
movie_player::State moviePlayer;
|
movie_player::State moviePlayer;
|
||||||
colored_quad::State coloredQuad;
|
colored_quad::State coloredQuad;
|
||||||
textured_quad::State texturedQuad;
|
textured_quad::State texturedQuad;
|
||||||
|
stream::State stream;
|
||||||
};
|
};
|
||||||
struct ShaderDrawCommand {
|
struct ShaderDrawCommand {
|
||||||
ShaderType type;
|
ShaderType type;
|
||||||
|
@ -28,6 +30,7 @@ struct ShaderDrawCommand {
|
||||||
movie_player::DrawData moviePlayer;
|
movie_player::DrawData moviePlayer;
|
||||||
colored_quad::DrawData coloredQuad;
|
colored_quad::DrawData coloredQuad;
|
||||||
textured_quad::DrawData texturedQuad;
|
textured_quad::DrawData texturedQuad;
|
||||||
|
stream::DrawData stream;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
struct PipelineCreateCommand {
|
struct PipelineCreateCommand {
|
||||||
|
@ -36,6 +39,7 @@ struct PipelineCreateCommand {
|
||||||
movie_player::PipelineConfig moviePlayer;
|
movie_player::PipelineConfig moviePlayer;
|
||||||
colored_quad::PipelineConfig coloredQuad;
|
colored_quad::PipelineConfig coloredQuad;
|
||||||
textured_quad::PipelineConfig texturedQuad;
|
textured_quad::PipelineConfig texturedQuad;
|
||||||
|
stream::PipelineConfig stream;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
enum class CommandType {
|
enum class CommandType {
|
||||||
|
@ -65,6 +69,19 @@ zeus::CMatrix4f g_mv;
|
||||||
zeus::CMatrix4f g_mvInv;
|
zeus::CMatrix4f g_mvInv;
|
||||||
zeus::CMatrix4f g_proj;
|
zeus::CMatrix4f g_proj;
|
||||||
metaforce::CFogState g_fogState;
|
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()>;
|
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||||
static std::mutex g_pipelineMutex;
|
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::unordered_map<PipelineRef, wgpu::RenderPipeline> g_pipelines;
|
||||||
static std::deque<std::pair<PipelineRef, NewPipelineCallback>> g_queuedPipelines;
|
static std::deque<std::pair<PipelineRef, NewPipelineCallback>> g_queuedPipelines;
|
||||||
static std::unordered_map<BindGroupRef, wgpu::BindGroup> g_cachedBindGroups;
|
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 queuedPipelines;
|
||||||
std::atomic_uint32_t createdPipelines;
|
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); }
|
bool get_dxt_compression_supported() noexcept { return g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC); }
|
||||||
|
|
||||||
// GX state
|
// 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,
|
void set_blend_mode(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor src, metaforce::ERglBlendFactor dst,
|
||||||
metaforce::ERglLogicOp op) noexcept {}
|
metaforce::ERglLogicOp op) noexcept {
|
||||||
void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept {}
|
g_blendMode = mode;
|
||||||
void set_gx_reg1_color(const zeus::CColor& color) noexcept {}
|
g_blendFacSrc = src;
|
||||||
void set_alpha_update(bool enabled) noexcept {}
|
g_blendFacDst = dst;
|
||||||
void set_dst_alpha(bool enabled, float value) noexcept {}
|
g_blendOp = op;
|
||||||
void set_clear_color(const zeus::CColor& color) noexcept {}
|
}
|
||||||
|
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
|
// Model state
|
||||||
void set_alpha_discard(bool v) {}
|
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);
|
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});
|
push_draw_command({.type = ShaderType::ColoredQuad, .coloredQuad = data});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
PipelineRef pipeline_ref(colored_quad::PipelineConfig config) {
|
PipelineRef pipeline_ref(colored_quad::PipelineConfig config) {
|
||||||
return find_pipeline({.type = ShaderType::ColoredQuad, .coloredQuad = 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); });
|
[=]() { 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() {
|
static void pipeline_worker() {
|
||||||
bool hasMore = false;
|
bool hasMore = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -276,6 +322,7 @@ void initialize() {
|
||||||
g_state.moviePlayer = movie_player::construct_state();
|
g_state.moviePlayer = movie_player::construct_state();
|
||||||
g_state.coloredQuad = colored_quad::construct_state();
|
g_state.coloredQuad = colored_quad::construct_state();
|
||||||
g_state.texturedQuad = textured_quad::construct_state();
|
g_state.texturedQuad = textured_quad::construct_state();
|
||||||
|
g_state.stream = stream::construct_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
|
@ -336,6 +383,9 @@ void render(const wgpu::RenderPassEncoder& pass) {
|
||||||
case ShaderType::MoviePlayer:
|
case ShaderType::MoviePlayer:
|
||||||
movie_player::render(g_state.moviePlayer, draw.moviePlayer, pass);
|
movie_player::render(g_state.moviePlayer, draw.moviePlayer, pass);
|
||||||
break;
|
break;
|
||||||
|
case ShaderType::Stream:
|
||||||
|
stream::render(g_state.stream, draw.stream, pass);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
@ -392,4 +442,19 @@ const wgpu::BindGroup& find_bind_group(BindGroupRef id) {
|
||||||
}
|
}
|
||||||
return g_cachedBindGroups[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
|
} // namespace aurora::gfx
|
||||||
|
|
|
@ -146,6 +146,19 @@ extern zeus::CMatrix4f g_mv;
|
||||||
extern zeus::CMatrix4f g_mvInv;
|
extern zeus::CMatrix4f g_mvInv;
|
||||||
extern zeus::CMatrix4f g_proj;
|
extern zeus::CMatrix4f g_proj;
|
||||||
extern metaforce::CFogState g_fogState;
|
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_vertexBuffer;
|
||||||
extern wgpu::Buffer g_uniformBuffer;
|
extern wgpu::Buffer g_uniformBuffer;
|
||||||
|
@ -169,8 +182,10 @@ struct TextureRef {
|
||||||
, gameFormat(gameFormat) {}
|
, gameFormat(gameFormat) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
using PipelineRef = uint64_t;
|
|
||||||
using BindGroupRef = 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>;
|
using Range = std::pair<uint32_t, uint32_t>;
|
||||||
|
|
||||||
enum class ShaderType {
|
enum class ShaderType {
|
||||||
|
@ -178,6 +193,7 @@ enum class ShaderType {
|
||||||
ColoredQuad,
|
ColoredQuad,
|
||||||
TexturedQuad,
|
TexturedQuad,
|
||||||
MoviePlayer,
|
MoviePlayer,
|
||||||
|
Stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
void initialize();
|
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));
|
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>
|
template <typename PipelineConfig>
|
||||||
PipelineRef pipeline_ref(PipelineConfig config);
|
PipelineRef pipeline_ref(PipelineConfig config);
|
||||||
bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass);
|
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);
|
BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor);
|
||||||
const wgpu::BindGroup& find_bind_group(BindGroupRef id);
|
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
|
} // namespace aurora::gfx
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "../common.hpp"
|
#include "../common.hpp"
|
||||||
|
|
||||||
namespace aurora::gfx::movie_player {
|
namespace aurora::gfx::movie_player {
|
||||||
|
|
|
@ -1,15 +1,458 @@
|
||||||
|
#include "stream/shader.hpp"
|
||||||
|
|
||||||
|
#include "../gpu.hpp"
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
namespace aurora::gfx {
|
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;
|
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,
|
void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass,
|
||||||
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
const metaforce::CTevCombiners::AlphaPass& alphaPass,
|
||||||
const metaforce::CTevCombiners::CTevOp& colorOp,
|
const metaforce::CTevCombiners::CTevOp& colorOp,
|
||||||
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {}
|
const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept {
|
||||||
void stream_begin(GX::Primitive primitive) 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,
|
void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, const zeus::CVector3f& nrm,
|
||||||
const zeus::CColor& color, const zeus::CVector2f& uv) noexcept {}
|
const zeus::CColor& color, const zeus::CVector2f& uv) noexcept {
|
||||||
void stream_end() 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
|
} // 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"
|
#include "../common.hpp"
|
||||||
|
|
||||||
namespace aurora::gfx::textured_quad {
|
namespace aurora::gfx::textured_quad {
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct GraphicsConfig {
|
||||||
wgpu::TextureFormat colorFormat;
|
wgpu::TextureFormat colorFormat;
|
||||||
wgpu::TextureFormat depthFormat;
|
wgpu::TextureFormat depthFormat;
|
||||||
uint32_t msaaSamples;
|
uint32_t msaaSamples;
|
||||||
uint8_t textureAnistropy;
|
uint16_t textureAnistropy;
|
||||||
};
|
};
|
||||||
struct TextureWithSampler {
|
struct TextureWithSampler {
|
||||||
wgpu::Texture texture;
|
wgpu::Texture texture;
|
||||||
|
@ -58,7 +58,7 @@ namespace aurora::gpu::utils {
|
||||||
template <auto N>
|
template <auto N>
|
||||||
static consteval std::array<wgpu::VertexAttribute, N>
|
static consteval std::array<wgpu::VertexAttribute, N>
|
||||||
make_vertex_attributes(std::array<wgpu::VertexFormat, N> formats) {
|
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;
|
uint64_t offset = 0;
|
||||||
for (uint32_t i = 0; i < N; ++i) {
|
for (uint32_t i = 0; i < N; ++i) {
|
||||||
auto format = formats[i];
|
auto format = formats[i];
|
||||||
|
|
Loading…
Reference in New Issue