forked from moonpower/azahar-UWP
* shader_jit: Add conditional unit-tests
Tests all permutations of X, Y, AND, OR with each possible input value.
* video_core: Fix shader-interpreter conditional-code initialization
Rather than reserving the incoming state of the conditional codes, the
shader-interpreter was setting them both to false. In pretty much all
cases, the initial state of a shaderunit can be zero-initialized
statically. Just running the interpreter shouldn't necessarily reset the
conditional codes though. The JIT loads incoming conditional codes
while the shader-interpreter resets them to false. This makes the
interpreter match the behavior of the shader-jit.
* shader_jit_a64: Fix/optimize conditional evaluation
Fix some of the regressions introduced by the previous optimization.
EOR does not support a constant of `0` in its immediate. In these cases
the COND{0,1} registers can be utilized immediately.
* shader_jit_x64: Fix conditional evaluation extended-bit hazard
The unit test seems to have identified a bug in the x64 jit too. The x64
jit was doing 32-bit comparisons despite the condition flags being 8-bit
values and is sensitive to garbage being in the upper 24 bits of the
register. This is fixed by using the proper 8-bit register types rather
than the 32-bit ones(`eax,`ebx` -> `al`, `bl`).
* shader_jit_x64: Zero-extend conditional-code bytes
`mov` was doing a partial update of bits within the register, allowing
garbage to be introduced in the upper bits of the register.
121 lines
3.5 KiB
C++
121 lines
3.5 KiB
C++
// Copyright 2023 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <span>
|
|
#include <boost/serialization/base_object.hpp>
|
|
|
|
#include "video_core/pica/output_vertex.h"
|
|
|
|
namespace Pica {
|
|
|
|
/// Handler type for receiving vertex outputs from vertex shader or geometry shader
|
|
using VertexHandler = std::function<void(const AttributeBuffer&)>;
|
|
|
|
/// Handler type for signaling to invert the vertex order of the next triangle
|
|
using WindingSetter = std::function<void()>;
|
|
|
|
struct ShaderRegs;
|
|
struct GeometryEmitter;
|
|
|
|
/**
|
|
* This structure contains the state information that needs to be unique for a shader unit. The 3DS
|
|
* has four shader units that process shaders in parallel.
|
|
*/
|
|
struct ShaderUnit {
|
|
explicit ShaderUnit(GeometryEmitter* emitter = nullptr);
|
|
~ShaderUnit();
|
|
|
|
void LoadInput(const ShaderRegs& config, const AttributeBuffer& input);
|
|
|
|
void WriteOutput(const ShaderRegs& config, AttributeBuffer& output);
|
|
|
|
static constexpr std::size_t InputOffset(s32 register_index) {
|
|
return offsetof(ShaderUnit, input) + register_index * sizeof(Common::Vec4<f24>);
|
|
}
|
|
|
|
static constexpr std::size_t OutputOffset(s32 register_index) {
|
|
return offsetof(ShaderUnit, output) + register_index * sizeof(Common::Vec4<f24>);
|
|
}
|
|
|
|
static constexpr std::size_t TemporaryOffset(s32 register_index) {
|
|
return offsetof(ShaderUnit, temporary) + register_index * sizeof(Common::Vec4<f24>);
|
|
}
|
|
|
|
public:
|
|
s32 address_registers[3] = {};
|
|
bool conditional_code[2] = {};
|
|
alignas(16) std::array<Common::Vec4<f24>, 16> input = {};
|
|
alignas(16) std::array<Common::Vec4<f24>, 16> temporary = {};
|
|
alignas(16) std::array<Common::Vec4<f24>, 16> output = {};
|
|
GeometryEmitter* emitter_ptr;
|
|
|
|
private:
|
|
friend class boost::serialization::access;
|
|
template <class Archive>
|
|
void serialize(Archive& ar, const u32 file_version) {
|
|
ar& input;
|
|
ar& temporary;
|
|
ar& output;
|
|
ar& conditional_code;
|
|
ar& address_registers;
|
|
}
|
|
};
|
|
|
|
struct Handlers {
|
|
VertexHandler vertex_handler;
|
|
WindingSetter winding_setter;
|
|
};
|
|
|
|
/// This structure contains state information for primitive emitting in geometry shader.
|
|
struct GeometryEmitter {
|
|
void Emit(std::span<Common::Vec4<f24>, 16> output_regs);
|
|
|
|
public:
|
|
std::array<AttributeBuffer, 3> buffer;
|
|
u8 vertex_id;
|
|
bool prim_emit;
|
|
bool winding;
|
|
u32 output_mask;
|
|
Handlers* handlers;
|
|
|
|
private:
|
|
friend class boost::serialization::access;
|
|
template <class Archive>
|
|
void serialize(Archive& ar, const u32 file_version) {
|
|
ar& buffer;
|
|
ar& vertex_id;
|
|
ar& prim_emit;
|
|
ar& winding;
|
|
ar& output_mask;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This is an extended shader unit state that represents the special unit that can run both vertex
|
|
* shader and geometry shader. It contains an additional primitive emitter and utilities for
|
|
* geometry shader.
|
|
*/
|
|
struct GeometryShaderUnit : public ShaderUnit {
|
|
GeometryShaderUnit();
|
|
~GeometryShaderUnit();
|
|
|
|
void SetVertexHandlers(VertexHandler vertex_handler, WindingSetter winding_setter);
|
|
void ConfigOutput(const ShaderRegs& config);
|
|
|
|
GeometryEmitter emitter;
|
|
|
|
private:
|
|
friend class boost::serialization::access;
|
|
template <class Archive>
|
|
void serialize(Archive& ar, const u32 file_version) {
|
|
ar& boost::serialization::base_object<ShaderUnit>(*this);
|
|
ar& emitter;
|
|
}
|
|
};
|
|
|
|
} // namespace Pica
|