mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
GPU: Properly implement memory fills.
This commit is contained in:
parent
745b42d236
commit
0da6a7e234
4 changed files with 76 additions and 31 deletions
|
@ -369,18 +369,18 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
|
||||||
{
|
{
|
||||||
auto& params = command.memory_fill;
|
auto& params = command.memory_fill;
|
||||||
WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)),
|
WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)),
|
||||||
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…
Reference in a new issue