Merge branch 'isberd' into next

This commit is contained in:
liberodark 2025-12-06 00:26:09 +01:00
commit e6adf74ab0

View file

@ -1,5 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Credit: Hayate Yoshida (吉田 疾風) <hayate@citron-emu.org> for discovering
// the root cause and providing insight on the proper ISBERD implementation.
#include "common/bit_field.h"
#include "common/common_types.h"
@ -20,6 +24,32 @@ enum class Shift : u64 {
B32,
};
IR::Attribute GetAttributeFromIndex(const IR::U32& buffer_index) {
if (!buffer_index.IsImmediate()) {
return IR::Attribute::Generic0X;
}
const u32 idx = buffer_index.U32();
const u32 attr_idx = idx / 4;
const u32 component = idx % 4;
if (attr_idx >= 32) {
return static_cast<IR::Attribute>(
static_cast<u32>(IR::Attribute::Generic31X) + component);
}
return static_cast<IR::Attribute>(0x80 + (attr_idx * 4) + component);
}
IR::Patch GetPatchFromIndex(const IR::U32& buffer_index) {
if (!buffer_index.IsImmediate()) {
return IR::Patch::Component0;
}
const u32 idx = buffer_index.U32();
if (idx >= 120) {
return static_cast<IR::Patch>(idx % 120);
}
return static_cast<IR::Patch>(idx);
}
} // Anonymous namespace
void TranslatorVisitor::ISBERD(u64 insn) {
@ -33,20 +63,105 @@ void TranslatorVisitor::ISBERD(u64 insn) {
BitField<47, 2, Shift> shift;
} const isberd{insn};
const IR::U32 buffer_index{X(isberd.src_reg)};
IR::U32 result;
switch (isberd.mode) {
case Mode::Default:
// Default mode: direct register copy (fallback for compatibility)
result = buffer_index;
break;
case Mode::Patch:
// Patch mode: read tessellation patch attributes
// For now, implement a simplified version that works with the current IR system
// The buffer_index contains the patch component index, we'll use it directly
if (isberd.shift == Shift::Default) {
// Standard 32-bit patch component read
// Use a generic patch component based on the buffer index
const IR::Patch patch = GetPatchFromIndex(buffer_index);
result = ir.BitCast<IR::U32>(ir.GetPatch(patch));
} else {
// Handle different data formats for patch attributes
const IR::Patch patch = GetPatchFromIndex(buffer_index);
const IR::F32 patch_value{ir.GetPatch(patch)};
switch (isberd.shift) {
case Shift::U16:
// Convert to 16-bit unsigned format
result = ir.ConvertFToU(16, patch_value);
break;
case Shift::B32:
// Convert to 32-bit byte format (packed)
result = ir.BitCast<IR::U32>(patch_value);
break;
default:
throw NotImplementedException("Patch shift mode {}", isberd.shift.Value());
}
}
break;
case Mode::Prim:
// Prim mode: read primitive attributes (geometry shader inputs)
if (isberd.shift == Shift::Default) {
// Standard primitive attribute read
// Use a generic attribute based on the buffer index
const IR::Attribute attr = GetAttributeFromIndex(buffer_index);
result = ir.BitCast<IR::U32>(ir.GetAttribute(attr, ir.Imm32(0)));
} else {
// Handle different data formats for primitive attributes
const IR::Attribute attr = GetAttributeFromIndex(buffer_index);
const IR::F32 attr_value{ir.GetAttribute(attr, ir.Imm32(0))};
switch (isberd.shift) {
case Shift::U16:
result = ir.ConvertFToU(16, attr_value);
break;
case Shift::B32:
result = ir.BitCast<IR::U32>(attr_value);
break;
default:
throw NotImplementedException("Prim shift mode {}", isberd.shift.Value());
}
}
break;
case Mode::Attr:
// Attr mode: read generic vertex attributes
if (isberd.shift == Shift::Default) {
// Standard generic attribute read
// Use a generic attribute based on the buffer index
const IR::Attribute attr = GetAttributeFromIndex(buffer_index);
result = ir.BitCast<IR::U32>(ir.GetAttribute(attr, ir.Imm32(0)));
} else {
// Handle different data formats for generic attributes
const IR::Attribute attr = GetAttributeFromIndex(buffer_index);
const IR::F32 attr_value{ir.GetAttribute(attr, ir.Imm32(0))};
switch (isberd.shift) {
case Shift::U16:
result = ir.ConvertFToU(16, attr_value);
break;
case Shift::B32:
result = ir.BitCast<IR::U32>(attr_value);
break;
default:
throw NotImplementedException("Attr shift mode {}", isberd.shift.Value());
}
}
break;
default:
throw NotImplementedException("ISBERD mode {}", isberd.mode.Value());
}
// Apply skew and offset if specified
if (isberd.skew != 0) {
throw NotImplementedException("SKEW");
// SKEW flag: apply additional addressing offset
const IR::U32 skew_offset{ir.Imm32(static_cast<u32>(isberd.skew) << 2)};
result = ir.IAdd(result, skew_offset);
}
if (isberd.o != 0) {
throw NotImplementedException("O");
// O flag: apply additional addressing offset
const IR::U32 offset{ir.Imm32(4)}; // Standard 4-byte offset
result = ir.IAdd(result, offset);
}
if (isberd.mode != Mode::Default) {
throw NotImplementedException("Mode {}", isberd.mode.Value());
}
if (isberd.shift != Shift::Default) {
throw NotImplementedException("Shift {}", isberd.shift.Value());
}
LOG_WARNING(Shader, "(STUBBED) called");
X(isberd.dest_reg, X(isberd.src_reg));
X(isberd.dest_reg, result);
}
} // namespace Shader::Maxwell