maxwell_3d: Implement a safer CB data upload

This makes constant buffer uploads safer and more accurate by updating the GPU memory as soon as the CB Data method is invoked. The previous implementation was deferring the updates until a different maxwell 3d method was detected, then writing all CB data at once.
This commit is contained in:
ameerj 2022-03-14 19:11:41 -04:00
parent 0331b8d799
commit 5119a57614
2 changed files with 12 additions and 70 deletions

View file

@ -195,7 +195,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
return StartCBData(method); return ProcessCBData(argument);
case MAXWELL3D_REG_INDEX(cb_bind[0]): case MAXWELL3D_REG_INDEX(cb_bind[0]):
return ProcessCBBind(0); return ProcessCBBind(0);
case MAXWELL3D_REG_INDEX(cb_bind[1]): case MAXWELL3D_REG_INDEX(cb_bind[1]):
@ -248,14 +248,6 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
} }
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
if (method == cb_data_state.current) {
regs.reg_array[method] = method_argument;
ProcessCBData(method_argument);
return;
} else if (cb_data_state.current != null_cb_data) {
FinishCBData();
}
// It is an error to write to a register other than the current macro's ARG register before it // It is an error to write to a register other than the current macro's ARG register before it
// has finished execution. // has finished execution.
if (executing_macro != 0) { if (executing_macro != 0) {
@ -302,7 +294,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 13:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 14:
case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15: case MAXWELL3D_REG_INDEX(const_buffer.cb_data) + 15:
ProcessCBMultiData(method, base_start, amount); ProcessCBMultiData(base_start, amount);
break; break;
default: default:
for (std::size_t i = 0; i < amount; i++) { for (std::size_t i = 0; i < amount; i++) {
@ -587,46 +579,7 @@ void Maxwell3D::ProcessCBBind(size_t stage_index) {
rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size); rasterizer->BindGraphicsUniformBuffer(stage_index, bind_data.index, gpu_addr, size);
} }
void Maxwell3D::ProcessCBData(u32 value) { void Maxwell3D::ProcessCBMultiData(const u32* start_base, u32 amount) {
const u32 id = cb_data_state.id;
cb_data_state.buffer[id][cb_data_state.counter] = value;
// Increment the current buffer position.
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
cb_data_state.counter++;
}
void Maxwell3D::StartCBData(u32 method) {
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
cb_data_state.start_pos = regs.const_buffer.cb_pos;
cb_data_state.id = method - first_cb_data;
cb_data_state.current = method;
cb_data_state.counter = 0;
ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]);
}
void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) {
if (cb_data_state.current != method) {
if (cb_data_state.current != null_cb_data) {
FinishCBData();
}
constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data);
cb_data_state.start_pos = regs.const_buffer.cb_pos;
cb_data_state.id = method - first_cb_data;
cb_data_state.current = method;
cb_data_state.counter = 0;
}
const std::size_t id = cb_data_state.id;
const std::size_t size = amount;
std::size_t i = 0;
for (; i < size; i++) {
cb_data_state.buffer[id][cb_data_state.counter] = start_base[i];
cb_data_state.counter++;
}
// Increment the current buffer position.
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount;
}
void Maxwell3D::FinishCBData() {
// Write the input value to the current const buffer at the current position. // Write the input value to the current const buffer at the current position.
const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); const GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
ASSERT(buffer_address != 0); ASSERT(buffer_address != 0);
@ -634,14 +587,16 @@ void Maxwell3D::FinishCBData() {
// Don't allow writing past the end of the buffer. // Don't allow writing past the end of the buffer.
ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size); ASSERT(regs.const_buffer.cb_pos <= regs.const_buffer.cb_size);
const GPUVAddr address{buffer_address + cb_data_state.start_pos}; const GPUVAddr address{buffer_address + regs.const_buffer.cb_pos};
const std::size_t size = regs.const_buffer.cb_pos - cb_data_state.start_pos; const size_t copy_size = amount * sizeof(u32);
memory_manager.WriteBlock(address, start_base, copy_size);
const u32 id = cb_data_state.id; // Increment the current buffer position.
memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size); regs.const_buffer.cb_pos += static_cast<u32>(copy_size);
}
cb_data_state.id = null_cb_data; void Maxwell3D::ProcessCBData(u32 value) {
cb_data_state.current = null_cb_data; ProcessCBMultiData(&value, 1);
} }
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {

View file

@ -1520,10 +1520,8 @@ private:
void ProcessSyncPoint(); void ProcessSyncPoint();
/// Handles a write to the CB_DATA[i] register. /// Handles a write to the CB_DATA[i] register.
void StartCBData(u32 method);
void ProcessCBData(u32 value); void ProcessCBData(u32 value);
void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount); void ProcessCBMultiData(const u32* start_base, u32 amount);
void FinishCBData();
/// Handles a write to the CB_BIND register. /// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index); void ProcessCBBind(size_t stage_index);
@ -1555,17 +1553,6 @@ private:
/// Interpreter for the macro codes uploaded to the GPU. /// Interpreter for the macro codes uploaded to the GPU.
std::unique_ptr<MacroEngine> macro_engine; std::unique_ptr<MacroEngine> macro_engine;
static constexpr u32 null_cb_data = 0xFFFFFFFF;
struct CBDataState {
static constexpr size_t inline_size = 0x4000;
std::array<std::array<u32, inline_size>, 16> buffer;
u32 current{null_cb_data};
u32 id{null_cb_data};
u32 start_pos{};
u32 counter{};
};
CBDataState cb_data_state;
Upload::State upload_state; Upload::State upload_state;
bool execute_on{true}; bool execute_on{true};