pica/shader: extend UnitState for GS

Among four shader units in pica, a special unit can be configured to run both VS and GS program. GSUnitState represents this unit, which extends UnitState (which represents the other three normal units) with extra state for primitive emitting. It uses lots of raw pointers to represent internal structure in order to keep it standard layout type for JIT to access.
This unit doesn't handle triangle winding (inverting) itself; instead, it calls a WindingSetter handler. This will be explained in the following commits
This commit is contained in:
wwylele 2017-07-25 22:30:29 +03:00
parent db309b2423
commit 46c6973d2b
2 changed files with 84 additions and 0 deletions

View file

@ -82,6 +82,44 @@ void UnitState::WriteOutput(const ShaderRegs& config, AttributeBuffer& output) {
}
}
UnitState::UnitState(GSEmitter* emitter) : emitter_ptr(emitter) {}
GSEmitter::GSEmitter() {
handlers = new Handlers;
}
GSEmitter::~GSEmitter() {
delete handlers;
}
void GSEmitter::Emit(Math::Vec4<float24> (&vertex)[16]) {
ASSERT(vertex_id < 3);
std::copy(std::begin(vertex), std::end(vertex), buffer[vertex_id].begin());
if (prim_emit) {
if (winding)
handlers->winding_setter();
for (size_t i = 0; i < buffer.size(); ++i) {
AttributeBuffer output;
unsigned int output_i = 0;
for (unsigned int reg : Common::BitSet<u32>(output_mask)) {
output.attr[output_i++] = buffer[i][reg];
}
handlers->vertex_handler(output);
}
}
}
GSUnitState::GSUnitState() : UnitState(&emitter) {}
void GSUnitState::SetVertexHandler(VertexHandler vertex_handler, WindingSetter winding_setter) {
emitter.handlers->vertex_handler = std::move(vertex_handler);
emitter.handlers->winding_setter = std::move(winding_setter);
}
void GSUnitState::ConfigOutput(const ShaderRegs& config) {
emitter.output_mask = config.output_mask;
}
MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240));
#ifdef ARCHITECTURE_x86_64

View file

@ -6,6 +6,7 @@
#include <array>
#include <cstddef>
#include <functional>
#include <type_traits>
#include <nihstro/shader_bytecode.h>
#include "common/assert.h"
@ -31,6 +32,12 @@ struct AttributeBuffer {
alignas(16) Math::Vec4<float24> attr[16];
};
/// 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 OutputVertex {
Math::Vec4<float24> pos;
Math::Vec4<float24> quat;
@ -60,6 +67,29 @@ ASSERT_POS(tc2, RasterizerRegs::VSOutputAttributes::TEXCOORD2_U);
static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
static_assert(sizeof(OutputVertex) == 24 * sizeof(float), "OutputVertex has invalid size");
/**
* This structure contains state information for primitive emitting in geometry shader.
*/
struct GSEmitter {
std::array<std::array<Math::Vec4<float24>, 16>, 3> buffer;
u8 vertex_id;
bool prim_emit;
bool winding;
u32 output_mask;
// Function objects are hidden behind a raw pointer to make the structure standard layout type,
// for JIT to use offsetof to access other members.
struct Handlers {
VertexHandler vertex_handler;
WindingSetter winding_setter;
} * handlers;
GSEmitter();
~GSEmitter();
void Emit(Math::Vec4<float24> (&vertex)[16]);
};
static_assert(std::is_standard_layout<GSEmitter>::value, "GSEmitter is not standard layout type");
/**
* 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. At the present, Citra only implements a
@ -67,6 +97,7 @@ static_assert(sizeof(OutputVertex) == 24 * sizeof(float), "OutputVertex has inva
* here will make it easier for us to parallelize the shader processing later.
*/
struct UnitState {
explicit UnitState(GSEmitter* emitter = nullptr);
struct Registers {
// The registers are accessed by the shader JIT using SSE instructions, and are therefore
// required to be 16-byte aligned.
@ -82,6 +113,8 @@ struct UnitState {
// TODO: How many bits do these actually have?
s32 address_registers[3];
GSEmitter* emitter_ptr;
static size_t InputOffset(const SourceRegister& reg) {
switch (reg.GetRegisterType()) {
case RegisterType::Input:
@ -125,6 +158,19 @@ struct UnitState {
void WriteOutput(const ShaderRegs& config, AttributeBuffer& output);
};
/**
* 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 GSUnitState : public UnitState {
GSUnitState();
void SetVertexHandler(VertexHandler vertex_handler, WindingSetter winding_setter);
void ConfigOutput(const ShaderRegs& config);
GSEmitter emitter;
};
struct ShaderSetup {
struct {
// The float uniforms are accessed by the shader JIT using SSE instructions, and are