From a2afcb3f77d6e7c816dce9b736d378f99ea147aa Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:27:23 -0600 Subject: [PATCH] update parallel rdp --- parallel-rdp-standalone/COMMIT | 2 +- .../parallel-rdp/rdp_device.cpp | 5 ++ .../parallel-rdp/rdp_device.hpp | 8 ++- .../parallel-rdp/rdp_renderer.cpp | 58 +++++++++++++++++++ .../parallel-rdp/rdp_renderer.hpp | 27 +++++++++ 5 files changed, 96 insertions(+), 4 deletions(-) diff --git a/parallel-rdp-standalone/COMMIT b/parallel-rdp-standalone/COMMIT index 573fca8..41a11ea 100644 --- a/parallel-rdp-standalone/COMMIT +++ b/parallel-rdp-standalone/COMMIT @@ -1 +1 @@ -e6cf0e5ca257135acbcacfbdacd7cd3fdf3fb408 +07c3d161dbeb78121be5eebdab7c810b72af822a diff --git a/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp b/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp index b51a22c..6e9c78b 100644 --- a/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp +++ b/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp @@ -227,6 +227,11 @@ bool CommandProcessor::device_is_supported() const return is_supported; } +void CommandProcessor::set_validation_interface(ValidationInterface *iface) +{ + renderer.set_validation_interface(iface); +} + void CommandProcessor::clear_hidden_rdram() { clear_buffer(*hidden_rdram, 0x03030303); diff --git a/parallel-rdp-standalone/parallel-rdp/rdp_device.hpp b/parallel-rdp-standalone/parallel-rdp/rdp_device.hpp index d7ec48c..2ad7640 100644 --- a/parallel-rdp-standalone/parallel-rdp/rdp_device.hpp +++ b/parallel-rdp-standalone/parallel-rdp/rdp_device.hpp @@ -124,6 +124,8 @@ public: ~CommandProcessor(); + void set_validation_interface(ValidationInterface *iface); + bool device_is_supported() const; // Synchronization. @@ -210,10 +212,10 @@ private: std::unique_ptr shader_bank; #endif - CommandRing ring; - - VideoInterface vi; + // Tear-down order is important here. Renderer renderer; + VideoInterface vi; + CommandRing ring; void clear_hidden_rdram(); void clear_tmem(); diff --git a/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp b/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp index 41e9cff..9ae7d0a 100644 --- a/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp +++ b/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp @@ -120,6 +120,11 @@ void Renderer::set_device(Vulkan::Device *device_) device = device_; } +void Renderer::set_validation_interface(ValidationInterface *iface) +{ + validation_iface = iface; +} + bool Renderer::init_caps() { auto &features = device->get_device_features(); @@ -1404,8 +1409,50 @@ void Renderer::fixup_triangle_setup(TriangleSetup &setup) const setup.flags |= TRIANGLE_SETUP_FILL_COPY_RASTER_BIT; } +void Renderer::validate_draw_state() const +{ + if ((stream.static_raster_state.flags & RASTERIZATION_FILL_BIT) != 0) + { + if (fb.fmt == FBFormat::I4) + { + validation_iface->report_rdp_crash(ValidationError::Fill4bpp, + "Attempted to use Fill mode on 4bpp surface."); + } + + if ((stream.depth_blend_state.flags & DEPTH_BLEND_DEPTH_TEST_BIT) != 0) + { + validation_iface->report_rdp_crash(ValidationError::FillDepthTest, + "Attempted to use Fill mode with depth test."); + } + + if ((stream.depth_blend_state.flags & DEPTH_BLEND_IMAGE_READ_ENABLE_BIT) != 0) + { + validation_iface->report_rdp_crash(ValidationError::FillImageReadEnable, + "Attempted to use Fill mode with image read enable."); + } + + if ((stream.depth_blend_state.flags & DEPTH_BLEND_DEPTH_UPDATE_BIT) != 0 && + !constants.use_prim_depth) + { + validation_iface->report_rdp_crash(ValidationError::FillDepthWrite, + "Attempted to use Fill mode with depth write enabled."); + } + } + else if ((stream.static_raster_state.flags & RASTERIZATION_COPY_BIT) != 0) + { + if (fb.fmt == FBFormat::RGBA8888) + { + validation_iface->report_rdp_crash(ValidationError::Copy32bpp, + "Attempted to use Copy mode on 32bpp surface."); + } + } +} + void Renderer::draw_shaded_primitive(TriangleSetup &setup, const AttributeSetup &attr) { + if (validation_iface) + validate_draw_state(); + fixup_triangle_setup(setup); unsigned num_tiles = compute_conservative_max_num_tiles(setup); @@ -3040,6 +3087,15 @@ bool Renderer::tmem_upload_needs_flush(uint32_t addr) const void Renderer::load_tile(uint32_t tile, const LoadTileInfo &info) { + if (validation_iface && info.mode == UploadMode::TLUT) + { + if ((info.thi >> 2) > (info.tlo >> 2)) + { + validation_iface->report_rdp_crash(ValidationError::InvalidMultilineLoadTlut, + "Attempting to load multiple lines in TLUT."); + } + } + if (tmem_upload_needs_flush(info.tex_addr)) flush_queues(); @@ -3171,6 +3227,8 @@ void Renderer::load_tile_iteration(uint32_t tile, const LoadTileInfo &info, uint if (info.size == TextureSize::Bpp4) { LOGE("4-bit VRAM pointer crashes the RDP.\n"); + if (validation_iface) + validation_iface->report_rdp_crash(ValidationError::LoadTile4bpp, "4-bit VRAM pointer crashes the RDP."); return; } diff --git a/parallel-rdp-standalone/parallel-rdp/rdp_renderer.hpp b/parallel-rdp-standalone/parallel-rdp/rdp_renderer.hpp index 2303653..26a02e4 100644 --- a/parallel-rdp-standalone/parallel-rdp/rdp_renderer.hpp +++ b/parallel-rdp-standalone/parallel-rdp/rdp_renderer.hpp @@ -72,6 +72,28 @@ struct RendererOptions bool super_sampled_readback_dither = false; }; +enum class ValidationError +{ + Fill4bpp, + LoadTile4bpp, + InvalidMultilineLoadTlut, + FillDepthTest, + FillDepthWrite, + FillImageReadEnable, + Copy32bpp +}; + +class ValidationInterface +{ +public: + virtual ~ValidationInterface() = default; + // Validation errors may be called from a thread as errors are encountered. + // Reports situations that would cause fatal error on a real RDP. + // We only opt to report these situations rather than deliberately crashing the renderer. + // Handling crashes is only relevant during development of N64 homebrew. + virtual void report_rdp_crash(ValidationError err, const char *msg) = 0; +}; + class Renderer : public Vulkan::DebugChannelInterface { public: @@ -79,6 +101,8 @@ public: ~Renderer(); void set_device(Vulkan::Device *device); + void set_validation_interface(ValidationInterface *iface); + // If coherent is false, RDRAM is a buffer split into data in lower half and writemask state in upper half, each part being size large. // offset must be 0 in this case. void set_rdram(Vulkan::Buffer *buffer, uint8_t *host_rdram, size_t offset, size_t size, bool coherent); @@ -136,11 +160,14 @@ private: CommandProcessor &processor; Vulkan::Device *device = nullptr; Vulkan::Buffer *rdram = nullptr; + ValidationInterface *validation_iface = nullptr; Vulkan::BufferHandle upscaling_reference_rdram; Vulkan::BufferHandle upscaling_multisampled_rdram; Vulkan::BufferHandle upscaling_multisampled_hidden_rdram; + void validate_draw_state() const; + struct { uint8_t *host_rdram = nullptr;