mirror of
https://github.com/gopher64/gopher64.git
synced 2026-11-12 09:09:01 +01:00
update framebuffer check (#1344)
This commit is contained in:
1 parent
3a1e09cae4
commit
80cfbc43e2
10 files changed
+250
-144
No files matched your search
@@ -209,7 +209,7 @@ fn main() {
|
||||
.allowlist_function("rdp_translate_busy_until")
|
||||
.allowlist_function("rdp_onscreen_message")
|
||||
.allowlist_function("rdp_check_callback")
|
||||
.allowlist_function("rdp_check_framebuffers")
|
||||
.allowlist_function("rdp_check_rdram")
|
||||
.allowlist_function("rdp_state_size")
|
||||
.allowlist_function("rdp_save_state")
|
||||
.allowlist_function("rdp_load_state")
|
||||
|
||||
+123
-129
@@ -1,6 +1,7 @@
|
||||
#include "interface.hpp"
|
||||
#include "../retroachievements/retroachievements.h"
|
||||
#include "rdp_device.hpp"
|
||||
#include "rdram_tracker.hpp"
|
||||
#include "spirv.hpp"
|
||||
#include "spirv_crt.hpp"
|
||||
#include "wsi.hpp"
|
||||
@@ -72,14 +73,15 @@ enum user_event_codes {
|
||||
typedef struct {
|
||||
uint32_t depthbuffer_address;
|
||||
uint32_t framebuffer_address;
|
||||
uint32_t framebuffer_y_offset;
|
||||
uint32_t texture_address;
|
||||
uint32_t framebuffer_pixel_size;
|
||||
uint32_t framebuffer_width;
|
||||
uint32_t texture_pixel_size;
|
||||
uint32_t texture_width;
|
||||
uint32_t framebuffer_height;
|
||||
bool depth_buffer_enabled;
|
||||
// scissor as inclusive pixel bounds (empty when x1 < x0 or y1 < y0)
|
||||
int32_t clip_x0, clip_y0, clip_x1, clip_y1;
|
||||
bool depth_compare;
|
||||
bool depth_update;
|
||||
} FrameBufferInfo;
|
||||
|
||||
typedef struct {
|
||||
@@ -120,8 +122,76 @@ static GFX_INFO gfx_info;
|
||||
static const uint32_t *fragment_spirv;
|
||||
static size_t fragment_size;
|
||||
|
||||
static std::vector<bool> rdram_dirty;
|
||||
static uint64_t sync_signal;
|
||||
// RDRAM handed to parallel-rdp that the GPU may not have finished with yet
|
||||
static RdramTracker rdram_tracker;
|
||||
|
||||
// the timeline value that will cover commands enqueued now
|
||||
static uint64_t mark_stamp() { return processor->get_timeline_value() + 1; }
|
||||
|
||||
// A primitive covering pixels [x0, x1] x [y0, y1] before scissoring.
|
||||
// parallel-rdp only writes back pixels a primitive actually touched, so the
|
||||
// RDRAM involved is the primitive's footprint inside the scissor. The color
|
||||
// buffer always counts as written (that also covers blender reads).
|
||||
static void mark_primitive(int32_t x0, int32_t x1, int32_t y0, int32_t y1) {
|
||||
auto &fb = rdp_device.frame_buffer_info;
|
||||
x0 = std::max(x0, fb.clip_x0);
|
||||
x1 = std::min({x1, fb.clip_x1, int32_t(fb.framebuffer_width) - 1});
|
||||
y0 = std::max(y0, fb.clip_y0);
|
||||
y1 = std::min(y1, fb.clip_y1);
|
||||
if (x1 < x0 || y1 < y0)
|
||||
return;
|
||||
|
||||
// parallel-rdp stores I4 color buffers as one byte per pixel
|
||||
uint32_t bytes =
|
||||
fb.framebuffer_pixel_size ? 1u << (fb.framebuffer_pixel_size - 1) : 1u;
|
||||
uint64_t stamp = mark_stamp();
|
||||
rdram_tracker.mark_rect(fb.framebuffer_address, fb.framebuffer_width, bytes,
|
||||
x0, x1, y0, y1, RdramTracker::Access::Write, stamp);
|
||||
|
||||
if (fb.depth_update)
|
||||
rdram_tracker.mark_rect(fb.depthbuffer_address, fb.framebuffer_width, 2, x0,
|
||||
x1, y0, y1, RdramTracker::Access::Write, stamp);
|
||||
else if (fb.depth_compare)
|
||||
rdram_tracker.mark_rect(fb.depthbuffer_address, fb.framebuffer_width, 2, x0,
|
||||
x1, y0, y1, RdramTracker::Access::Read, stamp);
|
||||
}
|
||||
|
||||
// texels [first, first + count) of the current texture image
|
||||
static void mark_texels(uint32_t first, uint32_t count) {
|
||||
auto &fb = rdp_device.frame_buffer_info;
|
||||
uint64_t bits = 4u << fb.texture_pixel_size;
|
||||
uint64_t start = first * bits / 8;
|
||||
uint64_t end = ((uint64_t(first) + count) * bits + 7) / 8;
|
||||
rdram_tracker.mark(fb.texture_address + start, end - start,
|
||||
RdramTracker::Access::Read, mark_stamp());
|
||||
}
|
||||
|
||||
// mirrors the RDRAM reads of parallel-rdp's Renderer::load_tile
|
||||
static void mark_texture_load(RDP::Op op, uint32_t w1, uint32_t w2) {
|
||||
auto &fb = rdp_device.frame_buffer_info;
|
||||
uint32_t sl = (w1 >> 12) & 0xFFF, tl = w1 & 0xFFF;
|
||||
uint32_t sh = (w2 >> 12) & 0xFFF, th = w2 & 0xFFF;
|
||||
|
||||
if (op == RDP::Op::LoadBlock) {
|
||||
// a single run of texels; sl/sh/tl are integers, low bits of w2 are dxt
|
||||
uint32_t count = (sh - sl + 1) & 0xFFF;
|
||||
if (count == 0 || count > 2048)
|
||||
return;
|
||||
mark_texels(tl * fb.texture_width + sl, count);
|
||||
return;
|
||||
}
|
||||
|
||||
// LoadTile / LoadTLut: 10.2 coordinates, fetched one row at a time
|
||||
sl >>= 2;
|
||||
tl >>= 2;
|
||||
sh >>= 2;
|
||||
th >>= 2;
|
||||
uint32_t count = (sh - sl + 1) & 0xFFF;
|
||||
if (th < tl || count == 0)
|
||||
return;
|
||||
for (uint32_t t = tl; t <= th; t++)
|
||||
mark_texels(t * fb.texture_width + sl, count);
|
||||
}
|
||||
|
||||
static TTF_Font *message_font;
|
||||
static std::queue<Message> messages;
|
||||
@@ -337,11 +407,7 @@ JoystickEvent get_joystick_event() {
|
||||
return joystick_event;
|
||||
}
|
||||
|
||||
void rdp_idle() {
|
||||
processor->idle();
|
||||
sync_signal = 0;
|
||||
rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false);
|
||||
}
|
||||
void rdp_idle() { processor->idle(); }
|
||||
|
||||
static void rdp_new_processor() {
|
||||
RDP::CommandProcessorFlags flags =
|
||||
@@ -373,8 +439,8 @@ static void rdp_new_processor() {
|
||||
LOGE("Failed to get hidden_rdram\n");
|
||||
}
|
||||
|
||||
sync_signal = 0;
|
||||
rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false);
|
||||
// the new processor's timeline starts over
|
||||
rdram_tracker.reset(gfx_info.RDRAM_SIZE);
|
||||
}
|
||||
|
||||
static ImageHandle create_message_image(Vulkan::Device &device, int width,
|
||||
@@ -758,25 +824,19 @@ CALL_BACK rdp_check_callback() {
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void rdp_check_framebuffers(uint32_t address, uint32_t length) {
|
||||
if (sync_signal) {
|
||||
address >>= 3;
|
||||
length = (length + 7) >> 3;
|
||||
void rdp_check_rdram(uint32_t address, uint32_t length, bool write) {
|
||||
uint64_t completed = processor->get_completed_timeline();
|
||||
// fast path: everything the RDP has touched is already done
|
||||
if (rdram_tracker.max_stamp() <= completed)
|
||||
return;
|
||||
|
||||
if (address >= rdram_dirty.size())
|
||||
return;
|
||||
|
||||
uint32_t end_addr =
|
||||
std::min(address + length, static_cast<uint32_t>(rdram_dirty.size()));
|
||||
|
||||
auto it = std::find(rdram_dirty.begin() + address,
|
||||
rdram_dirty.begin() + end_addr, true);
|
||||
if (it != rdram_dirty.begin() + end_addr) {
|
||||
processor->wait_for_timeline(sync_signal);
|
||||
rdram_dirty.assign(gfx_info.RDRAM_SIZE >> 3, false);
|
||||
sync_signal = 0;
|
||||
}
|
||||
}
|
||||
uint64_t stamp = rdram_tracker.conflict(address, length, write);
|
||||
if (stamp <= completed)
|
||||
return;
|
||||
// commands since the last signal haven't been submitted to the GPU yet
|
||||
if (stamp > processor->get_timeline_value())
|
||||
processor->signal_timeline();
|
||||
processor->wait_for_timeline(stamp);
|
||||
}
|
||||
|
||||
size_t rdp_state_size() {
|
||||
@@ -1043,9 +1103,8 @@ uint64_t rdp_process_commands(uint64_t now) {
|
||||
: uint64_t(sh >= sl ? ((sh - sl) >> 2) + 1 : 0) *
|
||||
uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0);
|
||||
// LoadBlock is one contiguous transfer; LoadTile fetches row by row
|
||||
uint64_t rows = command == 0x33
|
||||
? 1
|
||||
: uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0);
|
||||
uint64_t rows =
|
||||
command == 0x33 ? 1 : uint64_t(th >= tl ? ((th - tl) >> 2) + 1 : 0);
|
||||
uint32_t bits = 4u << rdp_device.frame_buffer_info.texture_pixel_size;
|
||||
rdp_add_busy(RDP_PRIMITIVE_SETUP_CLOCKS + (texels * bits + 63) / 64 +
|
||||
rows * RDP_SCANLINE_CLOCKS);
|
||||
@@ -1059,99 +1118,28 @@ uint64_t rdp_process_commands(uint64_t now) {
|
||||
case RDP::Op::ShadeTriangle:
|
||||
case RDP::Op::ShadeZBufferTriangle:
|
||||
case RDP::Op::ShadeTextureTriangle:
|
||||
case RDP::Op::ShadeTextureZBufferTriangle:
|
||||
case RDP::Op::ShadeTextureZBufferTriangle: {
|
||||
// s11.2 Y range, rows [yh, yl); X is only bounded by the scissor
|
||||
int32_t yl = sign_extend_14(w1), yh = sign_extend_14(w2);
|
||||
mark_primitive(0, INT32_MAX, yh >> 2, (yl - 1) >> 2);
|
||||
} break;
|
||||
case RDP::Op::TextureRectangle:
|
||||
case RDP::Op::TextureRectangleFlip:
|
||||
case RDP::Op::FillRectangle: {
|
||||
uint32_t offset_address =
|
||||
(rdp_device.frame_buffer_info.framebuffer_address +
|
||||
pixel_size(rdp_device.frame_buffer_info.framebuffer_pixel_size,
|
||||
rdp_device.frame_buffer_info.framebuffer_y_offset *
|
||||
rdp_device.frame_buffer_info.framebuffer_width)) >>
|
||||
3;
|
||||
if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) {
|
||||
uint32_t end_addr = std::min(
|
||||
offset_address +
|
||||
((pixel_size(
|
||||
rdp_device.frame_buffer_info.framebuffer_pixel_size,
|
||||
rdp_device.frame_buffer_info.framebuffer_width *
|
||||
rdp_device.frame_buffer_info.framebuffer_height) +
|
||||
7) >>
|
||||
3),
|
||||
static_cast<uint32_t>(rdram_dirty.size()));
|
||||
std::fill(rdram_dirty.begin() + offset_address,
|
||||
rdram_dirty.begin() + end_addr, true);
|
||||
}
|
||||
|
||||
if (rdp_device.frame_buffer_info.depth_buffer_enabled) {
|
||||
offset_address =
|
||||
(rdp_device.frame_buffer_info.depthbuffer_address +
|
||||
pixel_size(2,
|
||||
rdp_device.frame_buffer_info.framebuffer_y_offset *
|
||||
rdp_device.frame_buffer_info.framebuffer_width)) >>
|
||||
3;
|
||||
if (offset_address < rdram_dirty.size() &&
|
||||
!rdram_dirty[offset_address]) {
|
||||
uint32_t end_addr = std::min(
|
||||
offset_address +
|
||||
((pixel_size(
|
||||
2,
|
||||
rdp_device.frame_buffer_info.framebuffer_width *
|
||||
rdp_device.frame_buffer_info.framebuffer_height) +
|
||||
7) >>
|
||||
3),
|
||||
static_cast<uint32_t>(rdram_dirty.size()));
|
||||
std::fill(rdram_dirty.begin() + offset_address,
|
||||
rdram_dirty.begin() + end_addr, true);
|
||||
}
|
||||
}
|
||||
// 10.2 coordinates; copy/fill modes include the last row and column
|
||||
int32_t xl = (w1 >> 12) & 0xFFF, yl = w1 & 0xFFF;
|
||||
int32_t xh = (w2 >> 12) & 0xFFF, yh = w2 & 0xFFF;
|
||||
bool copy_fill = rdp_device.cycle_type >= 2;
|
||||
if (copy_fill)
|
||||
yl |= 3;
|
||||
mark_primitive(xh >> 2, copy_fill ? xl >> 2 : (xl - 1) >> 2, yh >> 2,
|
||||
(yl - 1) >> 2);
|
||||
} break;
|
||||
case RDP::Op::LoadTLut:
|
||||
case RDP::Op::LoadTile: {
|
||||
uint32_t upper_left_t = (w1 & 0xFFF) >> 2;
|
||||
uint32_t offset_address =
|
||||
(rdp_device.frame_buffer_info.texture_address +
|
||||
pixel_size(rdp_device.frame_buffer_info.texture_pixel_size,
|
||||
upper_left_t *
|
||||
rdp_device.frame_buffer_info.texture_width)) >>
|
||||
3;
|
||||
if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) {
|
||||
uint32_t lower_right_t = (w2 & 0xFFF) >> 2;
|
||||
uint32_t end_addr = std::min(
|
||||
offset_address +
|
||||
((pixel_size(rdp_device.frame_buffer_info.texture_pixel_size,
|
||||
(lower_right_t - upper_left_t) *
|
||||
rdp_device.frame_buffer_info.texture_width) +
|
||||
7) >>
|
||||
3),
|
||||
static_cast<uint32_t>(rdram_dirty.size()));
|
||||
std::fill(rdram_dirty.begin() + offset_address,
|
||||
rdram_dirty.begin() + end_addr, true);
|
||||
}
|
||||
} break;
|
||||
case RDP::Op::LoadBlock: {
|
||||
uint32_t upper_left_s = ((w1 >> 12) & 0xFFF);
|
||||
uint32_t upper_left_t = (w1 & 0xFFF);
|
||||
uint32_t offset_address =
|
||||
(rdp_device.frame_buffer_info.texture_address +
|
||||
pixel_size(rdp_device.frame_buffer_info.texture_pixel_size,
|
||||
upper_left_s +
|
||||
upper_left_t *
|
||||
rdp_device.frame_buffer_info.texture_width)) >>
|
||||
3;
|
||||
if (offset_address < rdram_dirty.size() && !rdram_dirty[offset_address]) {
|
||||
uint32_t lower_right_s = ((w2 >> 12) & 0xFFF);
|
||||
uint32_t end_addr = std::min(
|
||||
offset_address +
|
||||
((pixel_size(rdp_device.frame_buffer_info.texture_pixel_size,
|
||||
lower_right_s - upper_left_s) +
|
||||
7) >>
|
||||
3),
|
||||
static_cast<uint32_t>(rdram_dirty.size()));
|
||||
std::fill(rdram_dirty.begin() + offset_address,
|
||||
rdram_dirty.begin() + end_addr, true);
|
||||
}
|
||||
} break;
|
||||
case RDP::Op::LoadTile:
|
||||
case RDP::Op::LoadBlock:
|
||||
mark_texture_load(RDP::Op(command), w1, w2);
|
||||
break;
|
||||
case RDP::Op::SetColorImage:
|
||||
rdp_device.frame_buffer_info.framebuffer_address = (w2 & 0x00FFFFFF);
|
||||
rdp_device.frame_buffer_info.framebuffer_pixel_size = (w1 >> 19) & 0x3;
|
||||
@@ -1177,20 +1165,26 @@ uint64_t rdp_process_commands(uint64_t now) {
|
||||
rdp_device.scissor_field = (w2 >> 25) & 1;
|
||||
rdp_device.scissor_keep_odd = (w2 >> 24) & 1;
|
||||
|
||||
rdp_device.frame_buffer_info.framebuffer_y_offset = upper_left_y;
|
||||
rdp_device.frame_buffer_info.framebuffer_height =
|
||||
lower_right_y - upper_left_y;
|
||||
// parallel-rdp draws subpixel rows/columns in [hi, lo) of the 10.2
|
||||
// scissor, so the last pixel touched is (lo - 1) >> 2
|
||||
auto &fb = rdp_device.frame_buffer_info;
|
||||
fb.clip_x0 = int32_t(upper_left_x);
|
||||
fb.clip_y0 = int32_t(upper_left_y);
|
||||
fb.clip_x1 = (int32_t((w2 >> 12) & 0xFFF) - 1) >> 2;
|
||||
fb.clip_y1 = (int32_t(w2 & 0xFFF) - 1) >> 2;
|
||||
} break;
|
||||
case RDP::Op::SetOtherModes: {
|
||||
uint8_t cycle_type = (w1 >> 20) & 3;
|
||||
rdp_device.cycle_type = cycle_type;
|
||||
uint8_t depth_read_write = (w2 >> 4) & 3;
|
||||
rdp_device.frame_buffer_info.depth_buffer_enabled =
|
||||
((cycle_type & 2) == 0) && (depth_read_write != 0);
|
||||
// z_compare_en is bit 4, z_update_en is bit 5; ignored in copy/fill
|
||||
bool depth_allowed = (cycle_type & 2) == 0;
|
||||
rdp_device.frame_buffer_info.depth_compare =
|
||||
depth_allowed && (w2 & (1u << 4));
|
||||
rdp_device.frame_buffer_info.depth_update =
|
||||
depth_allowed && (w2 & (1u << 5));
|
||||
} break;
|
||||
case RDP::Op::SyncFull:
|
||||
sync_signal = processor->signal_timeline();
|
||||
|
||||
processor->signal_timeline();
|
||||
// SyncFull itself adds RDP_SYNC_CLOCKS, so busy_until > now here
|
||||
interrupt_timer = rdp_device.busy_until - now;
|
||||
break;
|
||||
|
||||
@@ -63,7 +63,7 @@ uint64_t rdp_process_commands(uint64_t now);
|
||||
void rdp_translate_busy_until(uint64_t old_count, uint64_t new_count);
|
||||
void rdp_idle();
|
||||
void rdp_onscreen_message(const char *message, MESSAGE_LENGTH milliseconds);
|
||||
void rdp_check_framebuffers(uint32_t address, uint32_t length);
|
||||
void rdp_check_rdram(uint32_t address, uint32_t length, bool write);
|
||||
size_t rdp_state_size();
|
||||
void rdp_save_state(uint8_t *state);
|
||||
void rdp_load_state(GFX_INFO _gfx_info, const uint8_t *state,
|
||||
|
||||
Submodule parallel-rdp/parallel-rdp-standalone updated: 9ba7406d4d...9105dc073f.
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
// Tracks RDRAM that parallel-rdp may still read or write on the GPU, so an
|
||||
// emulated access that overlaps it can wait first.
|
||||
//
|
||||
// Every 8-byte block holds a stamp: the timeline value that covers the last
|
||||
// RDP command reading or writing it (0 if never touched). The tracker knows
|
||||
// nothing about the GPU; callers pass the stamp when marking, and compare the
|
||||
// stamp returned by conflict() against the GPU's timeline. Stamps never need
|
||||
// clearing: once the GPU completes a value, every block stamped with it or
|
||||
// earlier is free.
|
||||
class RdramTracker {
|
||||
public:
|
||||
enum class Access { Read, Write };
|
||||
|
||||
// forget all stamps, e.g. when the timeline starts over; rdram_size fixes
|
||||
// the trackable range
|
||||
void reset(size_t rdram_size) {
|
||||
write_stamps.assign(rdram_size >> 3, 0);
|
||||
read_stamps.assign(rdram_size >> 3, 0);
|
||||
newest = 0;
|
||||
}
|
||||
|
||||
// the highest stamp ever marked; once the GPU completes it nothing can
|
||||
// conflict
|
||||
uint64_t max_stamp() const { return newest; }
|
||||
|
||||
// the RDP accesses [address, address + length) in commands covered by
|
||||
// timeline value `stamp`. Stamps only grow, so a later mark always wins.
|
||||
void mark(uint64_t address, uint64_t length, Access access, uint64_t stamp) {
|
||||
size_t first, last;
|
||||
if (!block_range(address, length, first, last))
|
||||
return;
|
||||
// timeline values stay far below 2^32 in practice (about one per SyncFull)
|
||||
uint32_t s = uint32_t(stamp);
|
||||
newest = std::max(newest, s);
|
||||
auto &stamps = access == Access::Write ? write_stamps : read_stamps;
|
||||
std::fill(stamps.begin() + first, stamps.begin() + last + 1, s);
|
||||
}
|
||||
|
||||
// the RDP accesses pixels [x0, x1] x [y0, y1] of an image `width` pixels
|
||||
// wide at `base`
|
||||
void mark_rect(uint32_t base, uint32_t width, uint32_t bytes_per_pixel,
|
||||
int32_t x0, int32_t x1, int32_t y0, int32_t y1, Access access,
|
||||
uint64_t stamp) {
|
||||
if (x1 < x0 || y1 < y0)
|
||||
return;
|
||||
uint64_t stride = uint64_t(width) * bytes_per_pixel;
|
||||
if (x0 == 0 && uint32_t(x1) + 1 == width) {
|
||||
mark(base + y0 * stride, (y1 - y0 + 1) * stride, access, stamp);
|
||||
return;
|
||||
}
|
||||
for (int32_t y = y0; y <= y1; y++)
|
||||
mark(base + y * stride + uint64_t(x0) * bytes_per_pixel,
|
||||
uint64_t(x1 - x0 + 1) * bytes_per_pixel, access, stamp);
|
||||
}
|
||||
|
||||
// The timeline value an emulated access to [address, address + length) has
|
||||
// to wait for, 0 if none. Emulated reads conflict with RDP writes; emulated
|
||||
// writes conflict with RDP reads and writes.
|
||||
uint64_t conflict(uint64_t address, uint64_t length, bool write) const {
|
||||
size_t first, last;
|
||||
if (!block_range(address, length, first, last))
|
||||
return 0;
|
||||
uint32_t stamp = *std::max_element(write_stamps.begin() + first,
|
||||
write_stamps.begin() + last + 1);
|
||||
if (write)
|
||||
stamp =
|
||||
std::max(stamp, *std::max_element(read_stamps.begin() + first,
|
||||
read_stamps.begin() + last + 1));
|
||||
return stamp;
|
||||
}
|
||||
|
||||
private:
|
||||
// the 8-byte blocks [first, last] covering [address, address + length)
|
||||
bool block_range(uint64_t address, uint64_t length, size_t &first,
|
||||
size_t &last) const {
|
||||
uint64_t blocks = write_stamps.size();
|
||||
first = address >> 3;
|
||||
if (length == 0 || first >= blocks)
|
||||
return false;
|
||||
last = std::min((address + length - 1) >> 3, blocks - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> write_stamps; // color / depth buffer writes
|
||||
std::vector<uint32_t> read_stamps; // texture loads, depth compare
|
||||
uint32_t newest = 0;
|
||||
};
|
||||
+4
-1
@@ -65,6 +65,9 @@ fn dma_read(device: &mut device::Device) {
|
||||
length += 1;
|
||||
}
|
||||
|
||||
// check RDP before PI reads from RDRAM
|
||||
ui::video::check_rdram_read(dram_addr, length);
|
||||
|
||||
let cycles = (handler.read)(device, cart_addr, dram_addr, length);
|
||||
|
||||
device::events::create_event(device, device::events::EVENT_TYPE_PI, cycles);
|
||||
@@ -92,7 +95,7 @@ fn dma_write(device: &mut device::Device) {
|
||||
}
|
||||
|
||||
// check RDP before PI writes to RDRAM
|
||||
ui::video::check_framebuffers(dram_addr, length);
|
||||
ui::video::check_rdram_write(dram_addr, length);
|
||||
|
||||
let cycles = (handler.write)(device, cart_addr, dram_addr, length);
|
||||
|
||||
|
||||
+11
-7
@@ -58,7 +58,7 @@ pub fn read_mem(
|
||||
);
|
||||
let masked_address = address as usize & RDRAM_MASK;
|
||||
|
||||
ui::video::check_framebuffers(masked_address as u32, 4);
|
||||
ui::video::check_rdram_read(masked_address as u32, 4);
|
||||
|
||||
u32::from_ne_bytes(
|
||||
device
|
||||
@@ -72,13 +72,15 @@ pub fn read_mem(
|
||||
}
|
||||
|
||||
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
||||
ui::video::check_framebuffers(address as u32, 4);
|
||||
let masked_address = address as usize & RDRAM_MASK;
|
||||
|
||||
ui::video::check_rdram_write(masked_address as u32, 4);
|
||||
|
||||
let mut data = u32::from_ne_bytes(
|
||||
device
|
||||
.rdram
|
||||
.mem
|
||||
.get(address as usize..(address + 4) as usize)
|
||||
.get(masked_address..masked_address + 4)
|
||||
.unwrap_or(&[0; 4])
|
||||
.try_into()
|
||||
.unwrap_or_default(),
|
||||
@@ -87,7 +89,7 @@ pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u3
|
||||
device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut(address as usize..(address + 4) as usize)
|
||||
.get_mut(masked_address..masked_address + 4)
|
||||
.unwrap_or(&mut [0; 4])
|
||||
.copy_from_slice(&data.to_ne_bytes());
|
||||
}
|
||||
@@ -107,13 +109,15 @@ pub fn write_mem_repeat(device: &mut device::Device, address: u64, value: u32, m
|
||||
);
|
||||
}
|
||||
|
||||
ui::video::check_framebuffers(address as u32, repeat_length);
|
||||
let masked_address = address as usize & RDRAM_MASK;
|
||||
|
||||
for i in (0..repeat_length as u64).step_by(4) {
|
||||
ui::video::check_rdram_write(masked_address as u32, repeat_length);
|
||||
|
||||
for i in (0..repeat_length as usize).step_by(4) {
|
||||
device
|
||||
.rdram
|
||||
.mem
|
||||
.get_mut((address + i) as usize..(address + i) as usize + 4)
|
||||
.get_mut(masked_address + i..masked_address + i + 4)
|
||||
.unwrap_or(&mut [0; 4])
|
||||
.copy_from_slice(&value.to_ne_bytes());
|
||||
}
|
||||
|
||||
@@ -148,8 +148,10 @@ fn do_dma(device: &mut device::Device, dma: RspDma) {
|
||||
let mut dram_addr = dma.dramaddr & 0xfffff8;
|
||||
let offset = dma.memaddr & 0x1000;
|
||||
|
||||
ui::video::check_framebuffers(dram_addr, count * (length + skip) - skip);
|
||||
let dram_length = count * (length + skip) - skip;
|
||||
if dma.dir == DmaDir::Read {
|
||||
// SP read DMA: DMEM/IMEM -> RDRAM
|
||||
ui::video::check_rdram_write(dram_addr, dram_length);
|
||||
let mut j = 0;
|
||||
while j < count {
|
||||
let mut i = 0;
|
||||
@@ -174,6 +176,7 @@ fn do_dma(device: &mut device::Device, dma: RspDma) {
|
||||
j += 1;
|
||||
}
|
||||
} else {
|
||||
ui::video::check_rdram_read(dram_addr, dram_length);
|
||||
let mut j = 0;
|
||||
while j < count {
|
||||
let mut i = 0;
|
||||
|
||||
+3
-1
@@ -83,6 +83,8 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
|
||||
fn copy_pif_rdram(device: &mut device::Device) {
|
||||
let dram_addr = device.si.regs[SI_DRAM_ADDR_REG] as usize & device::rdram::RDRAM_MASK;
|
||||
if device.si.dma_dir == DmaDir::Write {
|
||||
// check RDP before SI reads from RDRAM
|
||||
ui::video::check_rdram_read(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32);
|
||||
let mut i = 0;
|
||||
while i < device::pif::PIF_RAM_SIZE {
|
||||
let data = u32::from_ne_bytes(
|
||||
@@ -99,7 +101,7 @@ fn copy_pif_rdram(device: &mut device::Device) {
|
||||
}
|
||||
} else if device.si.dma_dir == DmaDir::Read {
|
||||
// check RDP before SI writes to RDRAM
|
||||
ui::video::check_framebuffers(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32);
|
||||
ui::video::check_rdram_write(dram_addr as u32, device::pif::PIF_RAM_SIZE as u32);
|
||||
let mut i = 0;
|
||||
while i < device::pif::PIF_RAM_SIZE {
|
||||
let data = u32::from_be_bytes(device.pif.ram[i..i + 4].try_into().unwrap());
|
||||
|
||||
+8
-2
@@ -297,8 +297,14 @@ pub fn translate_rdp_busy_until(old_count: u64, new_count: u64) {
|
||||
unsafe { rdp_translate_busy_until(old_count, new_count) }
|
||||
}
|
||||
|
||||
pub fn check_framebuffers(address: u32, length: u32) {
|
||||
unsafe { rdp_check_framebuffers(address, length) }
|
||||
// emulated read: wait if the RDP has queued writes to this range
|
||||
pub fn check_rdram_read(address: u32, length: u32) {
|
||||
unsafe { rdp_check_rdram(address, length, false) }
|
||||
}
|
||||
|
||||
// emulated write: wait if the RDP has queued reads or writes to this range
|
||||
pub fn check_rdram_write(address: u32, length: u32) {
|
||||
unsafe { rdp_check_rdram(address, length, true) }
|
||||
}
|
||||
|
||||
pub fn onscreen_message(message: &str, milliseconds: MESSAGE_LENGTH) {
|
||||
|
||||
Reference in new issue
Block a user