Initial working CGraphics Stream API

This commit is contained in:
Luke Street 2022-03-04 22:36:54 -05:00
parent d93cf46bc3
commit 33d0d14fda
18 changed files with 976 additions and 44 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -278,6 +278,7 @@ enum TextureFormat : uint32_t {
enum TexMapID {
TEXMAP0,
TEXMAP1,
TEXMAP2,
TEXMAP3,
TEXMAP4,

View File

@ -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 ../)

View File

@ -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,

View File

@ -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();

View File

@ -1,3 +1,5 @@
#pragma once
#include "../common.hpp"
namespace aurora::gfx::colored_quad {

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,5 @@
#pragma once
#include "../common.hpp"
namespace aurora::gfx::movie_player {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,5 @@
#pragma once
#include "../common.hpp"
namespace aurora::gfx::textured_quad {

View File

@ -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];