diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 67ed19ba8..b12468d3a 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -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 (&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(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 diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index e156f6aef..caec96043 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include "common/assert.h" @@ -31,6 +32,12 @@ struct AttributeBuffer { alignas(16) Math::Vec4 attr[16]; }; +/// Handler type for receiving vertex outputs from vertex shader or geometry shader +using VertexHandler = std::function; + +/// Handler type for signaling to invert the vertex order of the next triangle +using WindingSetter = std::function; + struct OutputVertex { Math::Vec4 pos; Math::Vec4 quat; @@ -60,6 +67,29 @@ ASSERT_POS(tc2, RasterizerRegs::VSOutputAttributes::TEXCOORD2_U); static_assert(std::is_pod::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, 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 (&vertex)[16]); +}; +static_assert(std::is_standard_layout::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