AzaharUWP/src/video_core/pica/shader_unit.h
Wunk 0ea9f23f0c shader_jit: Fix/optimize conditional evaluation (#234)
* 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.
2024-11-07 14:47:27 +01:00

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