mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30: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
 | ||||
|         case PICA_REG_INDEX(trigger_irq): | ||||
|             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
 | ||||
|         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)); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     u32* read_pointer = (u32*)list; | ||||
|     u32 list_length = size / sizeof(u32); | ||||
|     g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list; | ||||
|     g_state.cmd_list.length = size / sizeof(u32); | ||||
| 
 | ||||
|     while (read_pointer < list + list_length) { | ||||
|         read_pointer += ExecuteCommandBlock(read_pointer); | ||||
|     while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) { | ||||
|         // 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
 | ||||
|     }; | ||||
| 
 | ||||
|     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) { | ||||
|         switch (format) { | ||||
|         case TextureFormat::RGBA8: | ||||
|  | @ -221,6 +240,7 @@ struct Regs { | |||
|         enum class Source : u32 { | ||||
|             PrimaryColor           = 0x0, | ||||
|             PrimaryFragmentColor   = 0x1, | ||||
|             SecondaryFragmentColor = 0x2, | ||||
| 
 | ||||
|             Texture0               = 0x3, | ||||
|             Texture1               = 0x4, | ||||
|  | @ -413,12 +433,8 @@ struct Regs { | |||
|         } alpha_blending; | ||||
| 
 | ||||
|         union { | ||||
|             enum Op { | ||||
|                 Set = 4, | ||||
|             }; | ||||
| 
 | ||||
|             BitField<0, 4, Op> op; | ||||
|         } logic_op; | ||||
|             BitField<0, 4, LogicOp> logic_op; | ||||
|         }; | ||||
| 
 | ||||
|         union { | ||||
|             BitField< 0, 8, u32> r; | ||||
|  | @ -708,7 +724,33 @@ struct Regs { | |||
|         u32 set_value[3]; | ||||
|     } 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 { | ||||
|         List        = 0, | ||||
|  | @ -861,6 +903,7 @@ struct Regs { | |||
|         ADD_FIELD(trigger_draw); | ||||
|         ADD_FIELD(trigger_draw_indexed); | ||||
|         ADD_FIELD(vs_default_attributes_setup); | ||||
|         ADD_FIELD(command_buffer); | ||||
|         ADD_FIELD(triangle_topology); | ||||
|         ADD_FIELD(vs_bool_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_indexed, 0x22f); | ||||
| ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | ||||
| ASSERT_REG_POSITION(command_buffer, 0x238); | ||||
| ASSERT_REG_POSITION(triangle_topology, 0x25e); | ||||
| ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ||||
| ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | ||||
|  | @ -1053,21 +1097,12 @@ private: | |||
|     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 State { | ||||
|     /// Pica registers
 | ||||
|     Regs regs; | ||||
| 
 | ||||
|     /// Vertex shader memory
 | ||||
|     struct { | ||||
|         struct { | ||||
|             Math::Vec4<float24> f[96]; | ||||
|  | @ -1080,6 +1115,13 @@ struct State { | |||
|         std::array<u32, 1024> program_code; | ||||
|         std::array<u32, 1024> swizzle_data; | ||||
|     } vs; | ||||
| 
 | ||||
|     /// Current Pica command list
 | ||||
|     struct { | ||||
|         const u32* head_ptr; | ||||
|         const u32* current_ptr; | ||||
|         u32 length; | ||||
|     } cmd_list; | ||||
| }; | ||||
| 
 | ||||
| /// Initialize Pica state
 | ||||
|  |  | |||
|  | @ -402,11 +402,16 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
| 
 | ||||
|                 auto GetSource = [&](Source source) -> Math::Vec4<u8> { | ||||
|                     switch (source) { | ||||
|                     // TODO: What's the difference between these two?
 | ||||
|                     case Source::PrimaryColor: | ||||
| 
 | ||||
|                     // HACK: Until we implement fragment lighting, use primary_color
 | ||||
|                     case Source::PrimaryFragmentColor: | ||||
|                         return primary_color; | ||||
| 
 | ||||
|                     // HACK: Until we implement fragment lighting, use zero
 | ||||
|                     case Source::SecondaryFragmentColor: | ||||
|                         return {0, 0, 0, 0}; | ||||
| 
 | ||||
|                     case Source::Texture0: | ||||
|                         return texture_color[0]; | ||||
| 
 | ||||
|  | @ -570,6 +575,13 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, | |||
|                     case Operation::Add: | ||||
|                         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: | ||||
|                         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, | ||||
|                                                        const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor, | ||||
|                                                        BlendEquation equation) { | ||||
|                                                        Regs::BlendEquation equation) { | ||||
|                     Math::Vec4<int> result; | ||||
| 
 | ||||
|                     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.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); | ||||
|             } else { | ||||
|                 LOG_CRITICAL(HW_GPU, "logic op: %x", output_merger.logic_op); | ||||
|                 UNIMPLEMENTED(); | ||||
|                 static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 { | ||||
|                     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 = { | ||||
|  |  | |||
|  | @ -135,6 +135,7 @@ void RasterizerOpenGL::Reset() { | |||
|     SyncBlendFuncs(); | ||||
|     SyncBlendColor(); | ||||
|     SyncAlphaTest(); | ||||
|     SyncLogicOp(); | ||||
|     SyncStencilTest(); | ||||
|     SyncDepthTest(); | ||||
| 
 | ||||
|  | @ -249,6 +250,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
|         SyncDepthTest(); | ||||
|         break; | ||||
| 
 | ||||
|     // Logic op
 | ||||
|     case PICA_REG_INDEX(output_merger.logic_op): | ||||
|         SyncLogicOp(); | ||||
|         break; | ||||
| 
 | ||||
|     // TEV stage 0
 | ||||
|     case PICA_REG_INDEX(tev_stage0.color_source1): | ||||
|         SyncTevSources(0, regs.tev_stage0); | ||||
|  | @ -633,6 +639,10 @@ void RasterizerOpenGL::SyncAlphaTest() { | |||
|     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() { | ||||
|     // TODO: Implement stencil test, mask, and op
 | ||||
| } | ||||
|  |  | |||
|  | @ -125,6 +125,9 @@ private: | |||
|     /// Syncs the alpha test states to match the PICA register
 | ||||
|     void SyncAlphaTest(); | ||||
| 
 | ||||
|     /// Syncs the logic op states to match the PICA register
 | ||||
|     void SyncLogicOp(); | ||||
| 
 | ||||
|     /// Syncs the stencil test states to match the PICA register
 | ||||
|     void SyncStencilTest(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,15 +69,16 @@ const char g_fragment_shader_hw[] = R"( | |||
| #define NUM_VTX_ATTR 7 | ||||
| #define NUM_TEV_STAGES 6 | ||||
| 
 | ||||
| #define SOURCE_PRIMARYCOLOR         0x0 | ||||
| #define SOURCE_PRIMARYFRAGMENTCOLOR 0x1 | ||||
| #define SOURCE_TEXTURE0             0x3 | ||||
| #define SOURCE_TEXTURE1             0x4 | ||||
| #define SOURCE_TEXTURE2             0x5 | ||||
| #define SOURCE_TEXTURE3             0x6 | ||||
| #define SOURCE_PREVIOUSBUFFER       0xd | ||||
| #define SOURCE_CONSTANT             0xe | ||||
| #define SOURCE_PREVIOUS             0xf | ||||
| #define SOURCE_PRIMARYCOLOR           0x0 | ||||
| #define SOURCE_PRIMARYFRAGMENTCOLOR   0x1 | ||||
| #define SOURCE_SECONDARYFRAGMENTCOLOR 0x2 | ||||
| #define SOURCE_TEXTURE0               0x3 | ||||
| #define SOURCE_TEXTURE1               0x4 | ||||
| #define SOURCE_TEXTURE2               0x5 | ||||
| #define SOURCE_TEXTURE3               0x6 | ||||
| #define SOURCE_PREVIOUSBUFFER         0xd | ||||
| #define SOURCE_CONSTANT               0xe | ||||
| #define SOURCE_PREVIOUS               0xf | ||||
| 
 | ||||
| #define COLORMODIFIER_SOURCECOLOR         0x0 | ||||
| #define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 | ||||
|  | @ -151,8 +152,11 @@ vec4 GetSource(int source) { | |||
|     if (source == SOURCE_PRIMARYCOLOR) { | ||||
|         return o[2]; | ||||
|     } 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]; | ||||
|     } 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) { | ||||
|         return texture(tex[0], o[3].xy); | ||||
|     } else if (source == SOURCE_TEXTURE1) { | ||||
|  |  | |||
|  | @ -32,6 +32,8 @@ OpenGLState::OpenGLState() { | |||
|     blend.color.blue = 0.0f; | ||||
|     blend.color.alpha = 0.0f; | ||||
| 
 | ||||
|     logic_op = GL_COPY; | ||||
| 
 | ||||
|     for (auto& texture_unit : texture_units) { | ||||
|         texture_unit.enabled_2d = false; | ||||
|         texture_unit.texture_2d = 0; | ||||
|  | @ -99,8 +101,13 @@ void OpenGLState::Apply() { | |||
|     if (blend.enabled != cur_state.blend.enabled) { | ||||
|         if (blend.enabled) { | ||||
|             glEnable(GL_BLEND); | ||||
| 
 | ||||
|             cur_state.logic_op = GL_COPY; | ||||
|             glLogicOp(cur_state.logic_op); | ||||
|             glDisable(GL_COLOR_LOGIC_OP); | ||||
|         } else { | ||||
|             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); | ||||
|     } | ||||
| 
 | ||||
|     if (logic_op != cur_state.logic_op) { | ||||
|         glLogicOp(logic_op); | ||||
|     } | ||||
| 
 | ||||
|     // Textures
 | ||||
|     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) { | ||||
|  |  | |||
|  | @ -42,6 +42,8 @@ public: | |||
|         } color; // GL_BLEND_COLOR
 | ||||
|     } blend; | ||||
| 
 | ||||
|     GLenum logic_op; // GL_LOGIC_OP_MODE
 | ||||
| 
 | ||||
|     // 3 texture units - one for each that is used in PICA fragment shader emulation
 | ||||
|     struct { | ||||
|         bool enabled_2d; // GL_TEXTURE_2D
 | ||||
|  |  | |||
|  | @ -71,6 +71,37 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor 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) { | ||||
|     static const GLenum compare_func_table[] = { | ||||
|         GL_NEVER,    // CompareFunc::Never
 | ||||
|  |  | |||
|  | @ -119,17 +119,13 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
|         switch (instr.opcode.Value().GetInfo().type) { | ||||
|         case OpCode::Type::Arithmetic: | ||||
|         { | ||||
|             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 bool is_inverted = (0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed)); | ||||
| 
 | ||||
|             const int address_offset = (instr.common.address_register_index == 0) | ||||
|                                        ? 0 : state.address_registers[instr.common.address_register_index - 1]; | ||||
| 
 | ||||
|             const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + address_offset); | ||||
|             const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted)); | ||||
|             const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + (!is_inverted * address_offset)); | ||||
|             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_src2 = ((bool)swizzle.negate_src2 != false); | ||||
|  | @ -208,6 +204,15 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
|                 } | ||||
|                 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::DP4: | ||||
|             { | ||||
|  | @ -279,6 +284,16 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
|                 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: | ||||
|                 for (int i = 0; i < 2; ++i) { | ||||
|                     // TODO: Can you restrict to one compare via dest masking?
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue