mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #822 from bunnei/pica-improvements
Pica improvements
This commit is contained in:
		
						commit
						8852fc6a87
					
				
					 10 changed files with 261 additions and 71 deletions
				
			
		|  | @ -56,7 +56,17 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | ||||||
|         // Trigger IRQ
 |         // Trigger IRQ
 | ||||||
|         case PICA_REG_INDEX(trigger_irq): |         case PICA_REG_INDEX(trigger_irq): | ||||||
|             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); |             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); | ||||||
|             return; |             break; | ||||||
|  | 
 | ||||||
|  |         case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c): | ||||||
|  |         case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): | ||||||
|  |         { | ||||||
|  |             unsigned index = id - PICA_REG_INDEX(command_buffer.trigger[0]); | ||||||
|  |             u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index)); | ||||||
|  |             g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr; | ||||||
|  |             g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         // It seems like these trigger vertex rendering
 |         // It seems like these trigger vertex rendering
 | ||||||
|         case PICA_REG_INDEX(trigger_draw): |         case PICA_REG_INDEX(trigger_draw): | ||||||
|  | @ -363,38 +373,34 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | ||||||
|         g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); |         g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { |  | ||||||
|     const CommandHeader& header = *(const CommandHeader*)(&first_command_word[1]); |  | ||||||
| 
 |  | ||||||
|     u32* read_pointer = (u32*)first_command_word; |  | ||||||
| 
 |  | ||||||
|     const u32 write_mask = ((header.parameter_mask & 0x1) ? (0xFFu <<  0) : 0u) | |  | ||||||
|                            ((header.parameter_mask & 0x2) ? (0xFFu <<  8) : 0u) | |  | ||||||
|                            ((header.parameter_mask & 0x4) ? (0xFFu << 16) : 0u) | |  | ||||||
|                            ((header.parameter_mask & 0x8) ? (0xFFu << 24) : 0u); |  | ||||||
| 
 |  | ||||||
|     WritePicaReg(header.cmd_id, *read_pointer, write_mask); |  | ||||||
|     read_pointer += 2; |  | ||||||
| 
 |  | ||||||
|     for (unsigned int i = 1; i < 1+header.extra_data_length; ++i) { |  | ||||||
|         u32 cmd = header.cmd_id + ((header.group_commands) ? i : 0); |  | ||||||
|         WritePicaReg(cmd, *read_pointer, write_mask); |  | ||||||
|         ++read_pointer; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // align read pointer to 8 bytes
 |  | ||||||
|     if ((first_command_word - read_pointer) % 2) |  | ||||||
|         ++read_pointer; |  | ||||||
| 
 |  | ||||||
|     return read_pointer - first_command_word; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ProcessCommandList(const u32* list, u32 size) { | void ProcessCommandList(const u32* list, u32 size) { | ||||||
|     u32* read_pointer = (u32*)list; |     g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list; | ||||||
|     u32 list_length = size / sizeof(u32); |     g_state.cmd_list.length = size / sizeof(u32); | ||||||
| 
 | 
 | ||||||
|     while (read_pointer < list + list_length) { |     while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) { | ||||||
|         read_pointer += ExecuteCommandBlock(read_pointer); |         // Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF
 | ||||||
|  |         static const u32 expand_bits_to_bytes[] = { | ||||||
|  |             0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, | ||||||
|  |             0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, | ||||||
|  |             0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, | ||||||
|  |             0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Align read pointer to 8 bytes
 | ||||||
|  |         if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0) | ||||||
|  |             ++g_state.cmd_list.current_ptr; | ||||||
|  | 
 | ||||||
|  |         u32 value = *g_state.cmd_list.current_ptr++; | ||||||
|  |         const CommandHeader header = { *g_state.cmd_list.current_ptr++ }; | ||||||
|  |         const u32 write_mask = expand_bits_to_bytes[header.parameter_mask]; | ||||||
|  |         u32 cmd = header.cmd_id; | ||||||
|  | 
 | ||||||
|  |         WritePicaReg(cmd, value, write_mask); | ||||||
|  | 
 | ||||||
|  |         for (unsigned i = 0; i < header.extra_data_length; ++i) { | ||||||
|  |             u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0); | ||||||
|  |             WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, write_mask); | ||||||
|  |          } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -162,6 +162,25 @@ struct Regs { | ||||||
|         ETC1A4       = 13,  // compressed
 |         ETC1A4       = 13,  // compressed
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     enum class LogicOp : u32 { | ||||||
|  |         Clear        =  0, | ||||||
|  |         And          =  1, | ||||||
|  |         AndReverse   =  2, | ||||||
|  |         Copy         =  3, | ||||||
|  |         Set          =  4, | ||||||
|  |         CopyInverted =  5, | ||||||
|  |         NoOp         =  6, | ||||||
|  |         Invert       =  7, | ||||||
|  |         Nand         =  8, | ||||||
|  |         Or           =  9, | ||||||
|  |         Nor          = 10, | ||||||
|  |         Xor          = 11, | ||||||
|  |         Equiv        = 12, | ||||||
|  |         AndInverted  = 13, | ||||||
|  |         OrReverse    = 14, | ||||||
|  |         OrInverted   = 15, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     static unsigned NibblesPerPixel(TextureFormat format) { |     static unsigned NibblesPerPixel(TextureFormat format) { | ||||||
|         switch (format) { |         switch (format) { | ||||||
|         case TextureFormat::RGBA8: |         case TextureFormat::RGBA8: | ||||||
|  | @ -221,6 +240,7 @@ struct Regs { | ||||||
|         enum class Source : u32 { |         enum class Source : u32 { | ||||||
|             PrimaryColor           = 0x0, |             PrimaryColor           = 0x0, | ||||||
|             PrimaryFragmentColor   = 0x1, |             PrimaryFragmentColor   = 0x1, | ||||||
|  |             SecondaryFragmentColor = 0x2, | ||||||
| 
 | 
 | ||||||
|             Texture0               = 0x3, |             Texture0               = 0x3, | ||||||
|             Texture1               = 0x4, |             Texture1               = 0x4, | ||||||
|  | @ -413,12 +433,8 @@ struct Regs { | ||||||
|         } alpha_blending; |         } alpha_blending; | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|             enum Op { |             BitField<0, 4, LogicOp> logic_op; | ||||||
|                 Set = 4, |         }; | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             BitField<0, 4, Op> op; |  | ||||||
|         } logic_op; |  | ||||||
| 
 | 
 | ||||||
|         union { |         union { | ||||||
|             BitField< 0, 8, u32> r; |             BitField< 0, 8, u32> r; | ||||||
|  | @ -708,7 +724,33 @@ struct Regs { | ||||||
|         u32 set_value[3]; |         u32 set_value[3]; | ||||||
|     } vs_default_attributes_setup; |     } vs_default_attributes_setup; | ||||||
| 
 | 
 | ||||||
|     INSERT_PADDING_WORDS(0x28); |     INSERT_PADDING_WORDS(0x2); | ||||||
|  | 
 | ||||||
|  |     struct { | ||||||
|  |         // There are two channels that can be used to configure the next command buffer, which
 | ||||||
|  |         // can be then executed by writing to the "trigger" registers. There are two reasons why a
 | ||||||
|  |         // game might use this feature:
 | ||||||
|  |         //  1) With this, an arbitrary number of additional command buffers may be executed in
 | ||||||
|  |         //     sequence without requiring any intervention of the CPU after the initial one is
 | ||||||
|  |         //     kicked off.
 | ||||||
|  |         //  2) Games can configure these registers to provide a command list subroutine mechanism.
 | ||||||
|  | 
 | ||||||
|  |         BitField< 0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer
 | ||||||
|  |         BitField< 0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer
 | ||||||
|  |         u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to
 | ||||||
|  | 
 | ||||||
|  |         unsigned GetSize(unsigned index) const { | ||||||
|  |             ASSERT(index < 2); | ||||||
|  |             return 8 * size[index]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         PAddr GetPhysicalAddress(unsigned index) const { | ||||||
|  |             ASSERT(index < 2); | ||||||
|  |             return (PAddr)(8 * addr[index]); | ||||||
|  |         } | ||||||
|  |     } command_buffer; | ||||||
|  | 
 | ||||||
|  |     INSERT_PADDING_WORDS(0x20); | ||||||
| 
 | 
 | ||||||
|     enum class TriangleTopology : u32 { |     enum class TriangleTopology : u32 { | ||||||
|         List        = 0, |         List        = 0, | ||||||
|  | @ -861,6 +903,7 @@ struct Regs { | ||||||
|         ADD_FIELD(trigger_draw); |         ADD_FIELD(trigger_draw); | ||||||
|         ADD_FIELD(trigger_draw_indexed); |         ADD_FIELD(trigger_draw_indexed); | ||||||
|         ADD_FIELD(vs_default_attributes_setup); |         ADD_FIELD(vs_default_attributes_setup); | ||||||
|  |         ADD_FIELD(command_buffer); | ||||||
|         ADD_FIELD(triangle_topology); |         ADD_FIELD(triangle_topology); | ||||||
|         ADD_FIELD(vs_bool_uniforms); |         ADD_FIELD(vs_bool_uniforms); | ||||||
|         ADD_FIELD(vs_int_uniforms); |         ADD_FIELD(vs_int_uniforms); | ||||||
|  | @ -938,6 +981,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228); | ||||||
| ASSERT_REG_POSITION(trigger_draw, 0x22e); | ASSERT_REG_POSITION(trigger_draw, 0x22e); | ||||||
| ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | ||||||
| ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | ||||||
|  | ASSERT_REG_POSITION(command_buffer, 0x238); | ||||||
| ASSERT_REG_POSITION(triangle_topology, 0x25e); | ASSERT_REG_POSITION(triangle_topology, 0x25e); | ||||||
| ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ||||||
| ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | ||||||
|  | @ -1053,21 +1097,12 @@ private: | ||||||
|     float value; |     float value; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| union CommandHeader { |  | ||||||
|     CommandHeader(u32 h) : hex(h) {} |  | ||||||
| 
 |  | ||||||
|     u32 hex; |  | ||||||
| 
 |  | ||||||
|     BitField< 0, 16, u32> cmd_id; |  | ||||||
|     BitField<16,  4, u32> parameter_mask; |  | ||||||
|     BitField<20, 11, u32> extra_data_length; |  | ||||||
|     BitField<31,  1, u32> group_commands; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Struct used to describe current Pica state
 | /// Struct used to describe current Pica state
 | ||||||
| struct State { | struct State { | ||||||
|  |     /// Pica registers
 | ||||||
|     Regs regs; |     Regs regs; | ||||||
| 
 | 
 | ||||||
|  |     /// Vertex shader memory
 | ||||||
|     struct { |     struct { | ||||||
|         struct { |         struct { | ||||||
|             Math::Vec4<float24> f[96]; |             Math::Vec4<float24> f[96]; | ||||||
|  | @ -1080,6 +1115,13 @@ struct State { | ||||||
|         std::array<u32, 1024> program_code; |         std::array<u32, 1024> program_code; | ||||||
|         std::array<u32, 1024> swizzle_data; |         std::array<u32, 1024> swizzle_data; | ||||||
|     } vs; |     } vs; | ||||||
|  | 
 | ||||||
|  |     /// Current Pica command list
 | ||||||
|  |     struct { | ||||||
|  |         const u32* head_ptr; | ||||||
|  |         const u32* current_ptr; | ||||||
|  |         u32 length; | ||||||
|  |     } cmd_list; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Initialize Pica state
 | /// Initialize Pica state
 | ||||||
|  |  | ||||||
|  | @ -402,11 +402,16 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | ||||||
| 
 | 
 | ||||||
|                 auto GetSource = [&](Source source) -> Math::Vec4<u8> { |                 auto GetSource = [&](Source source) -> Math::Vec4<u8> { | ||||||
|                     switch (source) { |                     switch (source) { | ||||||
|                     // TODO: What's the difference between these two?
 |  | ||||||
|                     case Source::PrimaryColor: |                     case Source::PrimaryColor: | ||||||
|  | 
 | ||||||
|  |                     // HACK: Until we implement fragment lighting, use primary_color
 | ||||||
|                     case Source::PrimaryFragmentColor: |                     case Source::PrimaryFragmentColor: | ||||||
|                         return primary_color; |                         return primary_color; | ||||||
| 
 | 
 | ||||||
|  |                     // HACK: Until we implement fragment lighting, use zero
 | ||||||
|  |                     case Source::SecondaryFragmentColor: | ||||||
|  |                         return {0, 0, 0, 0}; | ||||||
|  | 
 | ||||||
|                     case Source::Texture0: |                     case Source::Texture0: | ||||||
|                         return texture_color[0]; |                         return texture_color[0]; | ||||||
| 
 | 
 | ||||||
|  | @ -570,6 +575,13 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | ||||||
|                     case Operation::Add: |                     case Operation::Add: | ||||||
|                         return std::min(255, input[0] + input[1]); |                         return std::min(255, input[0] + input[1]); | ||||||
| 
 | 
 | ||||||
|  |                     case Operation::AddSigned: | ||||||
|  |                     { | ||||||
|  |                         // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
 | ||||||
|  |                         auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128; | ||||||
|  |                         return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255)); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                     case Operation::Lerp: |                     case Operation::Lerp: | ||||||
|                         return (input[0] * input[2] + input[1] * (255 - input[2])) / 255; |                         return (input[0] * input[2] + input[1] * (255 - input[2])) / 255; | ||||||
| 
 | 
 | ||||||
|  | @ -808,10 +820,9 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 using BlendEquation = Regs::BlendEquation; |  | ||||||
|                 static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor, |                 static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor, | ||||||
|                                                        const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor, |                                                        const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor, | ||||||
|                                                        BlendEquation equation) { |                                                        Regs::BlendEquation equation) { | ||||||
|                     Math::Vec4<int> result; |                     Math::Vec4<int> result; | ||||||
| 
 | 
 | ||||||
|                     auto src_result = (src  *  srcfactor).Cast<int>(); |                     auto src_result = (src  *  srcfactor).Cast<int>(); | ||||||
|  | @ -866,8 +877,63 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | ||||||
|                 blend_output     = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); |                 blend_output     = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); | ||||||
|                 blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); |                 blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); | ||||||
|             } else { |             } else { | ||||||
|                 LOG_CRITICAL(HW_GPU, "logic op: %x", output_merger.logic_op); |                 static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 { | ||||||
|                 UNIMPLEMENTED(); |                     switch (op) { | ||||||
|  |                     case Regs::LogicOp::Clear: | ||||||
|  |                         return 0; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::And: | ||||||
|  |                         return src & dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::AndReverse: | ||||||
|  |                         return src & ~dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Copy: | ||||||
|  |                         return src; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Set: | ||||||
|  |                         return 255; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::CopyInverted: | ||||||
|  |                         return ~src; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::NoOp: | ||||||
|  |                         return dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Invert: | ||||||
|  |                         return ~dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Nand: | ||||||
|  |                         return ~(src & dest); | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Or: | ||||||
|  |                         return src | dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Nor: | ||||||
|  |                         return ~(src | dest); | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Xor: | ||||||
|  |                         return src ^ dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::Equiv: | ||||||
|  |                         return ~(src ^ dest); | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::AndInverted: | ||||||
|  |                         return ~src & dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::OrReverse: | ||||||
|  |                         return src | ~dest; | ||||||
|  | 
 | ||||||
|  |                     case Regs::LogicOp::OrInverted: | ||||||
|  |                         return ~src | dest; | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 blend_output = Math::MakeVec( | ||||||
|  |                     LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op), | ||||||
|  |                     LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op), | ||||||
|  |                     LogicOp(combiner_output.b(), dest.b(), output_merger.logic_op), | ||||||
|  |                     LogicOp(combiner_output.a(), dest.a(), output_merger.logic_op)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const Math::Vec4<u8> result = { |             const Math::Vec4<u8> result = { | ||||||
|  |  | ||||||
|  | @ -135,6 +135,7 @@ void RasterizerOpenGL::Reset() { | ||||||
|     SyncBlendFuncs(); |     SyncBlendFuncs(); | ||||||
|     SyncBlendColor(); |     SyncBlendColor(); | ||||||
|     SyncAlphaTest(); |     SyncAlphaTest(); | ||||||
|  |     SyncLogicOp(); | ||||||
|     SyncStencilTest(); |     SyncStencilTest(); | ||||||
|     SyncDepthTest(); |     SyncDepthTest(); | ||||||
| 
 | 
 | ||||||
|  | @ -249,6 +250,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | ||||||
|         SyncDepthTest(); |         SyncDepthTest(); | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|  |     // Logic op
 | ||||||
|  |     case PICA_REG_INDEX(output_merger.logic_op): | ||||||
|  |         SyncLogicOp(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|     // TEV stage 0
 |     // TEV stage 0
 | ||||||
|     case PICA_REG_INDEX(tev_stage0.color_source1): |     case PICA_REG_INDEX(tev_stage0.color_source1): | ||||||
|         SyncTevSources(0, regs.tev_stage0); |         SyncTevSources(0, regs.tev_stage0); | ||||||
|  | @ -633,6 +639,10 @@ void RasterizerOpenGL::SyncAlphaTest() { | ||||||
|     glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); |     glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RasterizerOpenGL::SyncLogicOp() { | ||||||
|  |     state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RasterizerOpenGL::SyncStencilTest() { | void RasterizerOpenGL::SyncStencilTest() { | ||||||
|     // TODO: Implement stencil test, mask, and op
 |     // TODO: Implement stencil test, mask, and op
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -125,6 +125,9 @@ private: | ||||||
|     /// Syncs the alpha test states to match the PICA register
 |     /// Syncs the alpha test states to match the PICA register
 | ||||||
|     void SyncAlphaTest(); |     void SyncAlphaTest(); | ||||||
| 
 | 
 | ||||||
|  |     /// Syncs the logic op states to match the PICA register
 | ||||||
|  |     void SyncLogicOp(); | ||||||
|  | 
 | ||||||
|     /// Syncs the stencil test states to match the PICA register
 |     /// Syncs the stencil test states to match the PICA register
 | ||||||
|     void SyncStencilTest(); |     void SyncStencilTest(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -69,15 +69,16 @@ const char g_fragment_shader_hw[] = R"( | ||||||
| #define NUM_VTX_ATTR 7 | #define NUM_VTX_ATTR 7 | ||||||
| #define NUM_TEV_STAGES 6 | #define NUM_TEV_STAGES 6 | ||||||
| 
 | 
 | ||||||
| #define SOURCE_PRIMARYCOLOR         0x0 | #define SOURCE_PRIMARYCOLOR           0x0 | ||||||
| #define SOURCE_PRIMARYFRAGMENTCOLOR 0x1 | #define SOURCE_PRIMARYFRAGMENTCOLOR   0x1 | ||||||
| #define SOURCE_TEXTURE0             0x3 | #define SOURCE_SECONDARYFRAGMENTCOLOR 0x2 | ||||||
| #define SOURCE_TEXTURE1             0x4 | #define SOURCE_TEXTURE0               0x3 | ||||||
| #define SOURCE_TEXTURE2             0x5 | #define SOURCE_TEXTURE1               0x4 | ||||||
| #define SOURCE_TEXTURE3             0x6 | #define SOURCE_TEXTURE2               0x5 | ||||||
| #define SOURCE_PREVIOUSBUFFER       0xd | #define SOURCE_TEXTURE3               0x6 | ||||||
| #define SOURCE_CONSTANT             0xe | #define SOURCE_PREVIOUSBUFFER         0xd | ||||||
| #define SOURCE_PREVIOUS             0xf | #define SOURCE_CONSTANT               0xe | ||||||
|  | #define SOURCE_PREVIOUS               0xf | ||||||
| 
 | 
 | ||||||
| #define COLORMODIFIER_SOURCECOLOR         0x0 | #define COLORMODIFIER_SOURCECOLOR         0x0 | ||||||
| #define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 | #define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 | ||||||
|  | @ -151,8 +152,11 @@ vec4 GetSource(int source) { | ||||||
|     if (source == SOURCE_PRIMARYCOLOR) { |     if (source == SOURCE_PRIMARYCOLOR) { | ||||||
|         return o[2]; |         return o[2]; | ||||||
|     } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) { |     } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) { | ||||||
|         // HACK: Uses color value, but should really use fragment lighting output
 |         // HACK: Until we implement fragment lighting, use primary_color
 | ||||||
|         return o[2]; |         return o[2]; | ||||||
|  |     } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) { | ||||||
|  |         // HACK: Until we implement fragment lighting, use zero
 | ||||||
|  |         return vec4(0.0, 0.0, 0.0, 0.0); | ||||||
|     } else if (source == SOURCE_TEXTURE0) { |     } else if (source == SOURCE_TEXTURE0) { | ||||||
|         return texture(tex[0], o[3].xy); |         return texture(tex[0], o[3].xy); | ||||||
|     } else if (source == SOURCE_TEXTURE1) { |     } else if (source == SOURCE_TEXTURE1) { | ||||||
|  |  | ||||||
|  | @ -32,6 +32,8 @@ OpenGLState::OpenGLState() { | ||||||
|     blend.color.blue = 0.0f; |     blend.color.blue = 0.0f; | ||||||
|     blend.color.alpha = 0.0f; |     blend.color.alpha = 0.0f; | ||||||
| 
 | 
 | ||||||
|  |     logic_op = GL_COPY; | ||||||
|  | 
 | ||||||
|     for (auto& texture_unit : texture_units) { |     for (auto& texture_unit : texture_units) { | ||||||
|         texture_unit.enabled_2d = false; |         texture_unit.enabled_2d = false; | ||||||
|         texture_unit.texture_2d = 0; |         texture_unit.texture_2d = 0; | ||||||
|  | @ -99,8 +101,13 @@ void OpenGLState::Apply() { | ||||||
|     if (blend.enabled != cur_state.blend.enabled) { |     if (blend.enabled != cur_state.blend.enabled) { | ||||||
|         if (blend.enabled) { |         if (blend.enabled) { | ||||||
|             glEnable(GL_BLEND); |             glEnable(GL_BLEND); | ||||||
|  | 
 | ||||||
|  |             cur_state.logic_op = GL_COPY; | ||||||
|  |             glLogicOp(cur_state.logic_op); | ||||||
|  |             glDisable(GL_COLOR_LOGIC_OP); | ||||||
|         } else { |         } else { | ||||||
|             glDisable(GL_BLEND); |             glDisable(GL_BLEND); | ||||||
|  |             glEnable(GL_COLOR_LOGIC_OP); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -118,6 +125,10 @@ void OpenGLState::Apply() { | ||||||
|         glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func); |         glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (logic_op != cur_state.logic_op) { | ||||||
|  |         glLogicOp(logic_op); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Textures
 |     // Textures
 | ||||||
|     for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { |     for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { | ||||||
|         if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d) { |         if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d) { | ||||||
|  |  | ||||||
|  | @ -42,6 +42,8 @@ public: | ||||||
|         } color; // GL_BLEND_COLOR
 |         } color; // GL_BLEND_COLOR
 | ||||||
|     } blend; |     } blend; | ||||||
| 
 | 
 | ||||||
|  |     GLenum logic_op; // GL_LOGIC_OP_MODE
 | ||||||
|  | 
 | ||||||
|     // 3 texture units - one for each that is used in PICA fragment shader emulation
 |     // 3 texture units - one for each that is used in PICA fragment shader emulation
 | ||||||
|     struct { |     struct { | ||||||
|         bool enabled_2d; // GL_TEXTURE_2D
 |         bool enabled_2d; // GL_TEXTURE_2D
 | ||||||
|  |  | ||||||
|  | @ -71,6 +71,37 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { | ||||||
|     return blend_func_table[(unsigned)factor]; |     return blend_func_table[(unsigned)factor]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | inline GLenum LogicOp(Pica::Regs::LogicOp op) { | ||||||
|  |     static const GLenum logic_op_table[] = { | ||||||
|  |         GL_CLEAR,           // Clear
 | ||||||
|  |         GL_AND,             // And
 | ||||||
|  |         GL_AND_REVERSE,     // AndReverse
 | ||||||
|  |         GL_COPY,            // Copy
 | ||||||
|  |         GL_SET,             // Set
 | ||||||
|  |         GL_COPY_INVERTED,   // CopyInverted
 | ||||||
|  |         GL_NOOP,            // NoOp
 | ||||||
|  |         GL_INVERT,          // Invert
 | ||||||
|  |         GL_NAND,            // Nand
 | ||||||
|  |         GL_OR,              // Or
 | ||||||
|  |         GL_NOR,             // Nor
 | ||||||
|  |         GL_XOR,             // Xor
 | ||||||
|  |         GL_EQUIV,           // Equiv
 | ||||||
|  |         GL_AND_INVERTED,    // AndInverted
 | ||||||
|  |         GL_OR_REVERSE,      // OrReverse
 | ||||||
|  |         GL_OR_INVERTED,     // OrInverted
 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Range check table for input
 | ||||||
|  |     if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) { | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); | ||||||
|  |         UNREACHABLE(); | ||||||
|  | 
 | ||||||
|  |         return GL_COPY; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return logic_op_table[(unsigned)op]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| inline GLenum CompareFunc(Pica::Regs::CompareFunc func) { | inline GLenum CompareFunc(Pica::Regs::CompareFunc func) { | ||||||
|     static const GLenum compare_func_table[] = { |     static const GLenum compare_func_table[] = { | ||||||
|         GL_NEVER,    // CompareFunc::Never
 |         GL_NEVER,    // CompareFunc::Never
 | ||||||
|  |  | ||||||
|  | @ -119,17 +119,13 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|         switch (instr.opcode.Value().GetInfo().type) { |         switch (instr.opcode.Value().GetInfo().type) { | ||||||
|         case OpCode::Type::Arithmetic: |         case OpCode::Type::Arithmetic: | ||||||
|         { |         { | ||||||
|             bool is_inverted = 0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed); |             const bool is_inverted = (0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed)); | ||||||
|             // TODO: We don't really support this properly: For instance, the address register
 |  | ||||||
|             //       offset needs to be applied to SRC2 instead, etc.
 |  | ||||||
|             //       For now, we just abort in this situation.
 |  | ||||||
|             ASSERT_MSG(!is_inverted, "Bad condition..."); |  | ||||||
| 
 | 
 | ||||||
|             const int address_offset = (instr.common.address_register_index == 0) |             const int address_offset = (instr.common.address_register_index == 0) | ||||||
|                                        ? 0 : state.address_registers[instr.common.address_register_index - 1]; |                                        ? 0 : state.address_registers[instr.common.address_register_index - 1]; | ||||||
| 
 | 
 | ||||||
|             const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + address_offset); |             const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + (!is_inverted * address_offset)); | ||||||
|             const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted)); |             const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted) + ( is_inverted * address_offset)); | ||||||
| 
 | 
 | ||||||
|             const bool negate_src1 = ((bool)swizzle.negate_src1 != false); |             const bool negate_src1 = ((bool)swizzle.negate_src1 != false); | ||||||
|             const bool negate_src2 = ((bool)swizzle.negate_src2 != false); |             const bool negate_src2 = ((bool)swizzle.negate_src2 != false); | ||||||
|  | @ -208,6 +204,15 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|  |             case OpCode::Id::MIN: | ||||||
|  |                 for (int i = 0; i < 4; ++i) { | ||||||
|  |                     if (!swizzle.DestComponentEnabled(i)) | ||||||
|  |                         continue; | ||||||
|  | 
 | ||||||
|  |                     dest[i] = std::min(src1[i], src2[i]); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|             case OpCode::Id::DP3: |             case OpCode::Id::DP3: | ||||||
|             case OpCode::Id::DP4: |             case OpCode::Id::DP4: | ||||||
|             { |             { | ||||||
|  | @ -279,6 +284,16 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             case OpCode::Id::SLT: | ||||||
|  |             case OpCode::Id::SLTI: | ||||||
|  |                 for (int i = 0; i < 4; ++i) { | ||||||
|  |                     if (!swizzle.DestComponentEnabled(i)) | ||||||
|  |                         continue; | ||||||
|  | 
 | ||||||
|  |                     dest[i] = (src1[i] < src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|             case OpCode::Id::CMP: |             case OpCode::Id::CMP: | ||||||
|                 for (int i = 0; i < 2; ++i) { |                 for (int i = 0; i < 2; ++i) { | ||||||
|                     // TODO: Can you restrict to one compare via dest masking?
 |                     // TODO: Can you restrict to one compare via dest masking?
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue