mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	GPU: Properly implement memory fills.
This commit is contained in:
		
							parent
							
								
									745b42d236
								
							
						
					
					
						commit
						0da6a7e234
					
				
					 4 changed files with 76 additions and 31 deletions
				
			
		|  | @ -372,15 +372,15 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | ||||||
|                          Memory::VirtualToPhysicalAddress(params.start1) >> 3); |                          Memory::VirtualToPhysicalAddress(params.start1) >> 3); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), | ||||||
|                          Memory::VirtualToPhysicalAddress(params.end1) >> 3); |                          Memory::VirtualToPhysicalAddress(params.end1) >> 3); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].size)), params.end1 - params.start1); |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), params.value1); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value)), params.value1); |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), params.control1); | ||||||
| 
 | 
 | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), | ||||||
|                          Memory::VirtualToPhysicalAddress(params.start2) >> 3); |                          Memory::VirtualToPhysicalAddress(params.start2) >> 3); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), | ||||||
|                          Memory::VirtualToPhysicalAddress(params.end2) >> 3); |                          Memory::VirtualToPhysicalAddress(params.end2) >> 3); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].size)), params.end2 - params.start2); |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), params.value2); | ||||||
|         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value)), params.value2); |         WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), params.control2); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -109,9 +109,13 @@ struct Command { | ||||||
|             u32 start1; |             u32 start1; | ||||||
|             u32 value1; |             u32 value1; | ||||||
|             u32 end1; |             u32 end1; | ||||||
|  | 
 | ||||||
|             u32 start2; |             u32 start2; | ||||||
|             u32 value2; |             u32 value2; | ||||||
|             u32 end2; |             u32 end2; | ||||||
|  | 
 | ||||||
|  |             u16 control1; | ||||||
|  |             u16 control2; | ||||||
|         } memory_fill; |         } memory_fill; | ||||||
| 
 | 
 | ||||||
|         struct { |         struct { | ||||||
|  |  | ||||||
|  | @ -67,23 +67,38 @@ inline void Write(u32 addr, const T data) { | ||||||
|     switch (index) { |     switch (index) { | ||||||
| 
 | 
 | ||||||
|     // Memory fills are triggered once the fill value is written.
 |     // Memory fills are triggered once the fill value is written.
 | ||||||
|     // NOTE: This is not verified.
 |     case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].trigger, 0x00004 + 0x3): | ||||||
|     case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].value, 0x00004 + 0x3): |     case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].trigger, 0x00008 + 0x3): | ||||||
|     case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].value, 0x00008 + 0x3): |  | ||||||
|     { |     { | ||||||
|         const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].value)); |         const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].trigger)); | ||||||
|         const auto& config = g_regs.memory_fill_config[is_second_filler]; |         auto& config = g_regs.memory_fill_config[is_second_filler]; | ||||||
| 
 | 
 | ||||||
|         // TODO: Not sure if this check should be done at GSP level instead
 |         if (config.address_start && config.trigger) { | ||||||
|         if (config.address_start) { |             u8* start = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetStartAddress())); | ||||||
|             // TODO: Not sure if this algorithm is correct, particularly because it doesn't use the size member at all
 |             u8* end = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetEndAddress())); | ||||||
|             u32* start = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetStartAddress())); | 
 | ||||||
|             u32* end = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetEndAddress())); |             if (config.fill_24bit) { | ||||||
|             for (u32* ptr = start; ptr < end; ++ptr) |                 // fill with 24-bit values
 | ||||||
|                 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
 |                 for (u8* ptr = start; ptr < end; ptr += 3) { | ||||||
|  |                     ptr[0] = config.value_24bit_b; | ||||||
|  |                     ptr[1] = config.value_24bit_g; | ||||||
|  |                     ptr[2] = config.value_24bit_r; | ||||||
|  |                 } | ||||||
|  |             } else if (config.fill_32bit) { | ||||||
|  |                 // fill with 32-bit values
 | ||||||
|  |                 for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) | ||||||
|  |                     *ptr = config.value_32bit; | ||||||
|  |             } else { | ||||||
|  |                 // fill with 16-bit values
 | ||||||
|  |                 for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) | ||||||
|  |                     *ptr = config.value_16bit; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); |             LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); | ||||||
| 
 | 
 | ||||||
|  |             config.trigger = 0; | ||||||
|  |             config.finished = 1; | ||||||
|  | 
 | ||||||
|             if (!is_second_filler) { |             if (!is_second_filler) { | ||||||
|                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); |                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0); | ||||||
|             } else { |             } else { | ||||||
|  |  | ||||||
|  | @ -84,9 +84,35 @@ struct Regs { | ||||||
| 
 | 
 | ||||||
|     struct { |     struct { | ||||||
|         u32 address_start; |         u32 address_start; | ||||||
|         u32 address_end; // ?
 |         u32 address_end; | ||||||
|         u32 size; | 
 | ||||||
|         u32 value; // ?
 |         union { | ||||||
|  |             u32 value_32bit; | ||||||
|  | 
 | ||||||
|  |             BitField<0, 16, u32> value_16bit; | ||||||
|  | 
 | ||||||
|  |             // TODO: Verify component order
 | ||||||
|  |             BitField< 0, 8, u32> value_24bit_r; | ||||||
|  |             BitField< 8, 8, u32> value_24bit_g; | ||||||
|  |             BitField<16, 8, u32> value_24bit_b; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         union { | ||||||
|  |             u32 control; | ||||||
|  | 
 | ||||||
|  |             // Setting this field to 1 triggers the memory fill.
 | ||||||
|  |             // This field also acts as a status flag, and gets reset to 0 upon completion.
 | ||||||
|  |             BitField<0, 1, u32> trigger; | ||||||
|  | 
 | ||||||
|  |             // Set to 1 upon completion.
 | ||||||
|  |             BitField<0, 1, u32> finished; | ||||||
|  | 
 | ||||||
|  |             // 0: fill with 16- or 32-bit wide values; 1: fill with 24-bit wide values
 | ||||||
|  |             BitField<8, 1, u32> fill_24bit; | ||||||
|  | 
 | ||||||
|  |             // 0: fill with 16-bit wide values; 1: fill with 32-bit wide values
 | ||||||
|  |             BitField<9, 1, u32> fill_32bit; | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|         inline u32 GetStartAddress() const { |         inline u32 GetStartAddress() const { | ||||||
|             return DecodeAddressRegister(address_start); |             return DecodeAddressRegister(address_start); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue