diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 178e5383a..80e9688a9 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -398,6 +398,33 @@ bool MemorySystem::IsValidPhysicalAddress(const PAddr paddr) const { return GetPhysicalPointer(paddr) != nullptr; } +PAddr MemorySystem::ClampPhysicalAddress(PAddr base, PAddr address) const { + struct MemoryArea { + PAddr paddr_base; + u32 size; + }; + + constexpr std::array memory_areas = { + MemoryArea{VRAM_PADDR, VRAM_SIZE}, + MemoryArea{DSP_RAM_PADDR, DSP_RAM_SIZE}, + MemoryArea{FCRAM_PADDR, FCRAM_N3DS_SIZE}, + MemoryArea{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE}, + }; + + const auto area = + std::ranges::find_if(memory_areas, [&](const MemoryArea& area) { + return base >= area.paddr_base && base <= area.paddr_base + area.size; + }); + + if (area == memory_areas.end()) { + LOG_ERROR(HW_Memory, "Unknown base address used for clamping {:#08X} at PC {:#08X}", base, + Core::GetRunningCore().GetPC()); + return address; + } + + return std::clamp(address, area->paddr_base, area->paddr_base + area->size); +} + u8* MemorySystem::GetPointer(const VAddr vaddr) { u8* page_pointer = impl->current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { @@ -457,22 +484,22 @@ MemoryRef MemorySystem::GetPhysicalRef(PAddr address) const { u32 size; }; - static constexpr MemoryArea memory_areas[] = { - {VRAM_PADDR, VRAM_SIZE}, - {DSP_RAM_PADDR, DSP_RAM_SIZE}, - {FCRAM_PADDR, FCRAM_N3DS_SIZE}, - {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE}, + constexpr std::array memory_areas = { + MemoryArea{VRAM_PADDR, VRAM_SIZE}, + MemoryArea{DSP_RAM_PADDR, DSP_RAM_SIZE}, + MemoryArea{FCRAM_PADDR, FCRAM_N3DS_SIZE}, + MemoryArea{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE}, }; const auto area = - std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { + std::ranges::find_if(memory_areas, [&](const MemoryArea& area) { // Note: the region end check is inclusive because the user can pass in an address that // represents an open right bound return address >= area.paddr_base && address <= area.paddr_base + area.size; }); - if (area == std::end(memory_areas)) { - LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x{:08X} at PC 0x{:08X}", address, + if (area == memory_areas.end()) { + LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ {:#08X} at PC {:#08X}", address, Core::GetRunningCore().GetPC()); return nullptr; } diff --git a/src/core/memory.h b/src/core/memory.h index 01b3777b0..c2aef30aa 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -350,8 +350,12 @@ public: u8* GetPointer(VAddr vaddr); const u8* GetPointer(VAddr vaddr) const; + /// Returns true if the address refers to a valid memory region bool IsValidPhysicalAddress(PAddr paddr) const; + /// Clamps the address to the boundaries of the memory region pointed by base + PAddr ClampPhysicalAddress(PAddr base, PAddr address) const; + /// Gets offset in FCRAM from a pointer inside FCRAM range u32 GetFCRAMOffset(const u8* pointer) const; diff --git a/src/video_core/rasterizer_cache/morton_swizzle.h b/src/video_core/rasterizer_cache/morton_swizzle.h index 723cd33cb..73c31b216 100644 --- a/src/video_core/rasterizer_cache/morton_swizzle.h +++ b/src/video_core/rasterizer_cache/morton_swizzle.h @@ -73,7 +73,7 @@ static void MortonCopy(u32 stride, u32 height, u8* linear_buffer, PAddr base, PA const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size); const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size); - const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size); + PAddr aligned_end = base + Common::AlignDown(end - base, tile_size); ASSERT(!morton_to_linear || (aligned_start == start && aligned_end == end)); @@ -81,9 +81,12 @@ static void MortonCopy(u32 stride, u32 height, u8* linear_buffer, PAddr base, PA u32 x = (begin_pixel_index % (stride * 8)) / 8; u32 y = (begin_pixel_index / (stride * 8)) * 8; + // In OpenGL the texture origin is in the bottom left corner as opposed to other + // APIs that have it at the top left. To avoid flipping texture coordinates in + // the shader we read/write the linear buffer backwards linear_buffer += ((height - 8 - y) * stride + x) * aligned_bytes_per_pixel; - auto linearbuf_next_tile = [&] { + auto linear_next_tile = [&] { x = (x + 8) % stride; linear_buffer += 8 * aligned_bytes_per_pixel; if (!x) { @@ -94,36 +97,38 @@ static void MortonCopy(u32 stride, u32 height, u8* linear_buffer, PAddr base, PA u8* tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start); + // If during a texture download the start coordinate is inside a tile, swizzle + // the tile to a temporary buffer and copy the part we are interested in if (start < aligned_start && !morton_to_linear) { std::array tmp_buf; - MortonCopyTile(stride, &tmp_buf[0], linear_buffer); - std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start], + MortonCopyTile(stride, tmp_buf.data(), linear_buffer); + std::memcpy(tile_buffer, tmp_buf.data() + start - aligned_down_start, std::min(aligned_start, end) - start); tile_buffer += aligned_start - start; - linearbuf_next_tile(); + linear_next_tile(); } - const u8* const buffer_end = tile_buffer + aligned_end - aligned_start; - PAddr current_paddr = aligned_start; + // Pokemon Super Mystery Dungeon will try to use textures that go beyond + // the end address of VRAM. Clamp the address to the end of VRAM if that happens + // TODO: Move this to the rasterizer cache + if (const u32 clamped_end = VideoCore::g_memory->ClampPhysicalAddress(aligned_start, aligned_end); + clamped_end != aligned_end) { + LOG_ERROR(Render_OpenGL, "Out of bound texture read address {:#x}, clamping to {:#x}", aligned_end, clamped_end); + aligned_end = clamped_end; + } + + const u8* buffer_end = tile_buffer + aligned_end - aligned_start; while (tile_buffer < buffer_end) { - // Pokemon Super Mystery Dungeon will try to use textures that go beyond - // the end address of VRAM. Stop reading if reaches invalid address - if (!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr) || - !VideoCore::g_memory->IsValidPhysicalAddress(current_paddr + tile_size)) { - LOG_ERROR(Render_OpenGL, "Out of bound texture"); - break; - } MortonCopyTile(stride, tile_buffer, linear_buffer); tile_buffer += tile_size; - current_paddr += tile_size; - linearbuf_next_tile(); + linear_next_tile(); } if (end > std::max(aligned_start, aligned_end) && !morton_to_linear) { std::array tmp_buf; - MortonCopyTile(stride, &tmp_buf[0], linear_buffer); - std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end); + MortonCopyTile(stride, tmp_buf.data(), linear_buffer); + std::memcpy(tile_buffer, tmp_buf.data(), end - aligned_end); } }