mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 04:04:18 +02:00
Incomplete primitives no longer emit indices: quads drop a one- or two-vertex tail and draw a three-vertex tail as a triangle, triangle lists ignore leftover vertices, and fans or strips under three vertices emit nothing. Draws with no complete primitive are skipped. Previously an incomplete quad indexed vertices that belonged to the next merged draw, or past the end of the buffer. Merging now stops before the 16-bit index offset would wrap, never folds triangles into a single-instance line or point draw, and breaks after GXInvalidateVtxCache or a vertex-format switch, so the next draw uploads fresh arrays and uses its own format's shader. From upstream patchzyy/Wiicompiled 6f14bde (#244, KartPad batch). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wg7mB8ogCWmp9GH19Uc82B
112 lines
5.2 KiB
C++
112 lines
5.2 KiB
C++
#pragma once
|
|
|
|
#include "../gfx/common.hpp"
|
|
#include "shader_info.hpp"
|
|
#include "../gfx/stereo_replay.hpp"
|
|
#include <optional>
|
|
|
|
namespace aurora::gx {
|
|
struct DrawData {
|
|
gfx::PipelineRef pipeline;
|
|
// Same GX state with exact fragment-depth export enabled. Bound only when
|
|
// this draw is actually reprojected onto the VR virtual screen.
|
|
gfx::PipelineRef exactScreenDepthPipeline;
|
|
// Eye depth/stencil format siblings. Shader modules are shared with mono.
|
|
gfx::PipelineRef stereoPipeline = 0;
|
|
gfx::PipelineRef stereoScreenPipeline = 0;
|
|
// Constant-alpha (kCompositeSourceBlend) siblings of pipeline and stereoPipeline, set only
|
|
// on the perspective draws of a pipeline an eye re-issues at a composite
|
|
// (note_composite_source_pipeline).
|
|
gfx::PipelineRef compositeSourcePipeline = 0;
|
|
gfx::PipelineRef stereoCompositeSourcePipeline = 0;
|
|
gfx::Range vertRange;
|
|
gfx::Range idxRange;
|
|
gfx::Range uniformRange;
|
|
std::array<gfx::Range, MaxInterpolatedFrames> interpolatedUniformRanges;
|
|
std::array<gfx::Range, AURORA_STEREO_EYE_COUNT> stereoUniformRanges;
|
|
gfx::Range previousUniformRange;
|
|
UniformReplayLayout uniformReplayLayout;
|
|
uint32_t vtxCount;
|
|
uint32_t indexCount;
|
|
uint32_t instanceCount;
|
|
bool expandedPrimitive;
|
|
GXBindGroups bindGroups;
|
|
uint32_t dstAlpha;
|
|
// Valid only for simple orthographic rectangles/lines (textured or not).
|
|
// Recorded before merging so VR can omit desktop split masks without
|
|
// modifying GX.
|
|
std::optional<gfx::stereo_replay::SubviewRect> screenRect;
|
|
};
|
|
|
|
constexpr uint32_t GXPipelineConfigVersion = 21;
|
|
|
|
// PipelineConfig::stereoStencil: the eye's Depth24PlusStencil8 target, and the constant-alpha
|
|
// blend an eye uses to re-issue a composite's source draws (see UniformReplayLayout).
|
|
constexpr uint32_t kStereoStencilFormat = 1u;
|
|
constexpr uint32_t kCompositeSourceBlend = 2u;
|
|
|
|
constexpr GXFogType effective_pipeline_fog_type(GXFogType fogType, GXZTexOp zTextureOp, bool zCompLocBeforeTex,
|
|
GXBlendMode blendMode, GXLogicOp logicOp) noexcept {
|
|
const bool usesLateZTexture = zTextureOp != GX_ZT_DISABLE && !zCompLocBeforeTex;
|
|
const bool usesUnsupportedLogicFog = blendMode == GX_BM_LOGIC && logicOp != GX_LO_OR;
|
|
return usesLateZTexture && usesUnsupportedLogicFog ? GX_FOG_NONE : fogType;
|
|
}
|
|
|
|
struct PipelineConfig {
|
|
uint32_t version = GXPipelineConfigVersion;
|
|
uint32_t msaaSamples = 1;
|
|
// Bits of kStereoStencilFormat and kCompositeSourceBlend. One field, so the on-disk
|
|
// pipeline recipes recorded before the second bit existed stay valid.
|
|
uint32_t stereoStencil = 0;
|
|
ShaderConfig shaderConfig;
|
|
GXCompare depthFunc;
|
|
GXCullMode cullMode;
|
|
GXBlendMode blendMode;
|
|
GXBlendFactor blendFacSrc, blendFacDst;
|
|
GXLogicOp blendOp;
|
|
GXPixelFmt pixelFmt;
|
|
uint32_t dstAlpha;
|
|
bool depthCompare, depthUpdate, alphaUpdate, colorUpdate;
|
|
};
|
|
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
|
|
|
inline bool valid_pipeline_config(const PipelineConfig& config) noexcept {
|
|
const auto in_range = [](auto value, auto maximum) {
|
|
using Value = decltype(value);
|
|
return static_cast<std::underlying_type_t<Value>>(value) >= 0 &&
|
|
static_cast<std::underlying_type_t<Value>>(value) <= static_cast<std::underlying_type_t<Value>>(maximum);
|
|
};
|
|
const bool validSamples =
|
|
config.msaaSamples == 1 || config.msaaSamples == 2 || config.msaaSamples == 4 || config.msaaSamples == 8;
|
|
return config.version == GXPipelineConfigVersion && validSamples && config.stereoStencil <= (kStereoStencilFormat | kCompositeSourceBlend) && in_range(config.depthFunc, GX_ALWAYS) &&
|
|
in_range(config.cullMode, GX_CULL_ALL) && in_range(config.blendMode, GX_BM_SUBTRACT) &&
|
|
in_range(config.blendFacSrc, GX_BL_INVDSTALPHA) && in_range(config.blendFacDst, GX_BL_INVDSTALPHA) &&
|
|
in_range(config.blendOp, GX_LO_SET) && in_range(config.pixelFmt, GX_PF_YUV420);
|
|
}
|
|
|
|
wgpu::RenderPipeline create_pipeline([[maybe_unused]] const PipelineConfig& config);
|
|
void clear_shader_module_cache();
|
|
|
|
// Per-pass encoder state carried across the draws of one render pass, so the replay loop can elide Dawn calls that
|
|
// would re-bind what is already bound.
|
|
struct DrawEncodeState {
|
|
gfx::PipelineRef currentPipeline = UINTPTR_MAX;
|
|
// The bind group currently occupying slot 2.
|
|
WGPUBindGroup boundTextureBindGroup = nullptr;
|
|
// The pass-wide index buffer binding is established lazily by the first indexed draw; every draw then addresses it
|
|
// with firstIndex instead of a per-draw SetIndexBuffer.
|
|
bool indexBufferBound = false;
|
|
};
|
|
|
|
void render(const DrawData& data, const wgpu::RenderPassEncoder& pass, DrawEncodeState& state,
|
|
bool requireReadyPipeline, const gfx::Range* uniformRangeOverride = nullptr,
|
|
gfx::PipelineRef pipelineOverride = 0, const wgpu::Color* blendConstantOverride = nullptr);
|
|
|
|
// Gives the draws of `pipeline` their constant-alpha siblings from its next draw on: an eye
|
|
// re-issues them at a composite (gfx::link_composite_source). Called with the renderer lock
|
|
// the draw paths hold, as the composite is recorded.
|
|
void note_composite_source_pipeline(gfx::PipelineRef pipeline) noexcept;
|
|
|
|
void queue_surface(const u8* dlStart, uint32_t dlSize, bool bigEndian) noexcept;
|
|
} // namespace aurora::gx
|