Merge pull request #12705 from JosJuice/get-span-for-address

Memmap: Replace GetPointer with GetSpanForAddress
This commit is contained in:
Tilka 2024-04-20 21:16:54 +01:00 committed by GitHub
commit f1e40f73bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 274 additions and 124 deletions

View File

@ -126,6 +126,7 @@ add_library(common
SmallVector.h
SocketContext.cpp
SocketContext.h
SpanUtils.h
SPSCQueue.h
StringLiteral.h
StringUtil.cpp

View File

@ -0,0 +1,41 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <cstddef>
#include <span>
#include <utility>
#include "Common/CommonTypes.h"
namespace Common
{
// Like std::span::subspan, except undefined behavior is replaced with returning a 0-length span.
template <class T>
[[nodiscard]] constexpr std::span<T> SafeSubspan(std::span<T> span, size_t offset,
size_t count = std::dynamic_extent)
{
if (count == std::dynamic_extent || offset > span.size())
return span.subspan(std::min(offset, span.size()));
else
return span.subspan(offset, std::min(count, span.size() - offset));
}
// Default-constructs an object of type T, then copies data into it from the specified offset in
// the specified span. Out-of-bounds reads will be skipped, meaning that specifying a too large
// offset results in the object partially or entirely remaining default constructed.
template <class T>
[[nodiscard]] T SafeSpanRead(std::span<const u8> span, size_t offset)
{
static_assert(std::is_trivially_copyable<T>());
const std::span<const u8> subspan = SafeSubspan(span, offset);
T result{};
std::memcpy(&result, subspan.data(), std::min(subspan.size(), sizeof(result)));
return result;
}
} // namespace Common

View File

@ -697,7 +697,7 @@ void FifoPlayer::LoadTextureMemory()
{
static_assert(static_cast<size_t>(TMEM_SIZE) == static_cast<size_t>(FifoDataFile::TEX_MEM_SIZE),
"TMEM_SIZE matches the size of texture memory in FifoDataFile");
std::memcpy(texMem, m_File->GetTexMem(), FifoDataFile::TEX_MEM_SIZE);
std::memcpy(s_tex_mem.data(), m_File->GetTexMem(), FifoDataFile::TEX_MEM_SIZE);
}
void FifoPlayer::WriteCP(u32 address, u16 value)

View File

@ -291,7 +291,7 @@ void FifoRecorder::RecordInitialVideoMemory()
g_main_cp_state.FillCPMemoryArray(cpmem);
SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, texMem);
SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, s_tex_mem.data());
}
void FifoRecorder::StopRecording()

View File

@ -12,6 +12,7 @@
#include <array>
#include <cstring>
#include <memory>
#include <span>
#include <tuple>
#include "Common/ChunkFile.h"
@ -400,22 +401,23 @@ void MemoryManager::Clear()
u8* MemoryManager::GetPointerForRange(u32 address, size_t size) const
{
// Make sure we don't have a range spanning 2 separate banks
if (size >= GetExRamSizeReal())
std::span<u8> span = GetSpanForAddress(address);
if (span.data() == nullptr)
{
// The address isn't in a valid memory region.
// A panic alert has already been raised by GetPointer, so let's not raise another one.
return nullptr;
}
if (span.size() < size)
{
// The start address is in a valid region, but the end address is beyond the end of that region.
PanicAlertFmt("Oversized range in GetPointerForRange. {:x} bytes at {:#010x}", size, address);
return nullptr;
}
// Check that the beginning and end of the range are valid
u8* pointer = GetPointer(address);
if (pointer == nullptr || (size != 0 && GetPointer(address + u32(size) - 1) == nullptr))
{
// A panic alert has already been raised by GetPointer
return nullptr;
}
return pointer;
return span.data();
}
void MemoryManager::CopyFromEmu(void* data, u32 address, size_t size) const
@ -487,24 +489,27 @@ std::string MemoryManager::GetString(u32 em_address, size_t size)
}
}
u8* MemoryManager::GetPointer(u32 address) const
std::span<u8> MemoryManager::GetSpanForAddress(u32 address) const
{
// TODO: Should we be masking off more bits here? Can all devices access
// EXRAM?
address &= 0x3FFFFFFF;
if (address < GetRamSizeReal())
return m_ram + address;
return std::span(m_ram + address, GetRamSizeReal() - address);
if (m_exram)
{
if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal())
return m_exram + (address & GetExRamMask());
{
return std::span(m_exram + (address & GetExRamMask()),
GetExRamSizeReal() - (address & GetExRamMask()));
}
}
auto& ppc_state = m_system.GetPPCState();
PanicAlertFmt("Unknown Pointer {:#010x} PC {:#010x} LR {:#010x}", address, ppc_state.pc,
LR(ppc_state));
return nullptr;
return {};
}
u8 MemoryManager::Read_U8(u32 address) const

View File

@ -5,6 +5,7 @@
#include <array>
#include <memory>
#include <span>
#include <string>
#include <vector>
@ -105,10 +106,16 @@ public:
// Routines to access physically addressed memory, designed for use by
// emulated hardware outside the CPU. Use "Device_" prefix.
std::string GetString(u32 em_address, size_t size = 0);
// WARNING: Incrementing the pointer returned by GetPointer is unsafe without additional bounds
// checks. New code should use other functions instead, like GetPointerForRange or CopyFromEmu.
u8* GetPointer(u32 address) const;
// If the specified guest address is within a valid memory region, returns a span starting at the
// host address corresponding to the specified address and ending where the memory region ends.
// Otherwise, returns a 0-length span starting at nullptr.
std::span<u8> GetSpanForAddress(u32 address) const;
// If the specified range is within a single valid memory region, returns a pointer to the start
// of the corresponding range in host memory. Otherwise, returns nullptr.
u8* GetPointerForRange(u32 address, size_t size) const;
void CopyFromEmu(void* data, u32 address, size_t size) const;
void CopyToEmu(u32 address, const void* data, size_t size);
void Memset(u32 address, u8 value, size_t size);

View File

@ -153,6 +153,7 @@
<ClInclude Include="Common\SFMLHelper.h" />
<ClInclude Include="Common\SmallVector.h" />
<ClInclude Include="Common\SocketContext.h" />
<ClInclude Include="Common\SpanUtils.h" />
<ClInclude Include="Common\SPSCQueue.h" />
<ClInclude Include="Common\StringLiteral.h" />
<ClInclude Include="Common\StringUtil.h" />

View File

@ -5,9 +5,11 @@
#include <algorithm>
#include <cmath>
#include <span>
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/SpanUtils.h"
#include "Core/HW/Memmap.h"
#include "Core/System.h"
@ -123,13 +125,13 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
const TextureFormat texfmt = ti0.format;
const TLUTFormat tlutfmt = texTlut.tlut_format;
const u8* imageSrc;
const u8* imageSrcOdd = nullptr;
std::span<const u8> image_src;
std::span<const u8> image_src_odd;
if (texUnit.texImage1.cache_manually_managed)
{
imageSrc = &texMem[texUnit.texImage1.tmem_even * TMEM_LINE_SIZE];
image_src = TexDecoder_GetTmemSpan(texUnit.texImage1.tmem_even * TMEM_LINE_SIZE);
if (texfmt == TextureFormat::RGBA8)
imageSrcOdd = &texMem[texUnit.texImage2.tmem_odd * TMEM_LINE_SIZE];
image_src_odd = TexDecoder_GetTmemSpan(texUnit.texImage2.tmem_odd * TMEM_LINE_SIZE);
}
else
{
@ -137,14 +139,14 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
auto& memory = system.GetMemory();
const u32 imageBase = texUnit.texImage3.image_base << 5;
imageSrc = memory.GetPointer(imageBase);
image_src = memory.GetSpanForAddress(imageBase);
}
int image_width_minus_1 = ti0.width;
int image_height_minus_1 = ti0.height;
const int tlutAddress = texTlut.tmem_offset << 9;
const u8* tlut = &texMem[tlutAddress];
const std::span<const u8> tlut = TexDecoder_GetTmemSpan(tlutAddress);
// reduce sample location and texture size to mip level
// move texture pointer to mip location
@ -168,7 +170,7 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
mipHeight = std::max(mipHeight, fmtHeight);
const u32 size = (mipWidth * mipHeight * fmtDepth) >> 1;
imageSrc += size;
image_src = Common::SafeSubspan(image_src, size);
mipWidth >>= 1;
mipHeight >>= 1;
mip--;
@ -202,37 +204,37 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
{
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageS, imageT, image_width_minus_1, texfmt,
TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageT, image_width_minus_1, texfmt,
tlut, tlutfmt);
SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageSPlus1, imageT, image_width_minus_1, texfmt,
tlut, tlutfmt);
TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageT, image_width_minus_1,
texfmt, tlut, tlutfmt);
AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageS, imageTPlus1, image_width_minus_1, texfmt,
tlut, tlutfmt);
TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageTPlus1, image_width_minus_1,
texfmt, tlut, tlutfmt);
AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageSPlus1, imageTPlus1, image_width_minus_1,
TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageTPlus1, image_width_minus_1,
texfmt, tlut, tlutfmt);
AddTexel(sampledTex, texel, (fractS) * (fractT));
}
else
{
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, imageSrc, imageSrcOdd, imageS, imageT,
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageT,
image_width_minus_1);
SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, imageSrc, imageSrcOdd, imageSPlus1, imageT,
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1, imageT,
image_width_minus_1);
AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, imageSrc, imageSrcOdd, imageS, imageTPlus1,
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageTPlus1,
image_width_minus_1);
AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, imageSrc, imageSrcOdd, imageSPlus1,
TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1,
imageTPlus1, image_width_minus_1);
AddTexel(sampledTex, texel, (fractS) * (fractT));
}
@ -253,11 +255,15 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
WrapCoord(&imageT, tm0.wrap_t, image_height_minus_1 + 1);
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
TexDecoder_DecodeTexel(sample, imageSrc, imageS, imageT, image_width_minus_1, texfmt, tlut,
{
TexDecoder_DecodeTexel(sample, image_src, imageS, imageT, image_width_minus_1, texfmt, tlut,
tlutfmt);
}
else
TexDecoder_DecodeTexelRGBA8FromTmem(sample, imageSrc, imageSrcOdd, imageS, imageT,
{
TexDecoder_DecodeTexelRGBA8FromTmem(sample, image_src, image_src_odd, imageS, imageT,
image_width_minus_1);
}
}
}
} // namespace TextureSampler

View File

@ -401,7 +401,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
static_assert(MAX_LOADABLE_TMEM_ADDR + MAX_TMEM_LINE_COUNT < TMEM_SIZE);
auto& memory = system.GetMemory();
memory.CopyFromEmu(texMem + tmem_addr, addr, tmem_transfer_count);
memory.CopyFromEmu(s_tex_mem.data() + tmem_addr, addr, tmem_transfer_count);
if (OpcodeDecoder::g_record_fifo_data)
system.GetFifoRecorder().UseMemory(addr, tmem_transfer_count, MemoryUpdate::Type::TMEM);
@ -590,13 +590,16 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
if (tmem_cfg.preload_tile_info.type != 3)
{
bytes_read = tmem_cfg.preload_tile_info.count * TMEM_LINE_SIZE;
if (tmem_addr_even + bytes_read > TMEM_SIZE)
bytes_read = TMEM_SIZE - tmem_addr_even;
if (tmem_addr_even < TMEM_SIZE)
{
bytes_read = tmem_cfg.preload_tile_info.count * TMEM_LINE_SIZE;
if (tmem_addr_even + bytes_read > TMEM_SIZE)
bytes_read = TMEM_SIZE - tmem_addr_even;
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
memory.CopyFromEmu(texMem + tmem_addr_even, src_addr, bytes_read);
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
memory.CopyFromEmu(s_tex_mem.data() + tmem_addr_even, src_addr, bytes_read);
}
}
else // RGBA8 tiles (and CI14, but that might just be stupid libogc!)
{
@ -615,9 +618,10 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager&
break;
}
memory.CopyFromEmu(texMem + tmem_addr_even, src_addr + bytes_read, TMEM_LINE_SIZE);
memory.CopyFromEmu(texMem + tmem_addr_odd, src_addr + bytes_read + TMEM_LINE_SIZE,
memory.CopyFromEmu(s_tex_mem.data() + tmem_addr_even, src_addr + bytes_read,
TMEM_LINE_SIZE);
memory.CopyFromEmu(s_tex_mem.data() + tmem_addr_odd,
src_addr + bytes_read + TMEM_LINE_SIZE, TMEM_LINE_SIZE);
tmem_addr_even += TMEM_LINE_SIZE;
tmem_addr_odd += TMEM_LINE_SIZE;
bytes_read += TMEM_LINE_SIZE * 2;

View File

@ -1319,6 +1319,9 @@ TCacheEntry* TextureCacheBase::LoadImpl(const TextureInfo& texture_info, bool fo
RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
const TextureInfo& texture_info)
{
if (!texture_info.IsDataValid())
return {};
// Hash assigned to texcache entry (also used to generate filenames used for texture dumping and
// custom texture lookup)
u64 base_hash = TEXHASH_INVALID;
@ -1337,12 +1340,6 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
// TODO: the texture cache lookup is based on address, but a texture from tmem has no reason
// to have a unique and valid address. This could result in a regular texture and a tmem
// texture aliasing onto the same texture cache entry.
if (!texture_info.GetData())
{
ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}",
texture_info.GetRawAddress());
return {};
}
// If we are recording a FifoLog, keep track of what memory we read. FifoRecorder does
// its own memory modification tracking independent of the texture hashing below.
@ -1916,7 +1913,7 @@ RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height
entry->frameCount = FRAMECOUNT_INVALID;
if (!g_ActiveConfig.UseGPUTextureDecoding() ||
!DecodeTextureOnGPU(entry, 0, src_data, total_size, entry->format.texfmt, width, height,
width, height, stride, texMem, entry->format.tlutfmt))
width, height, stride, s_tex_mem.data(), entry->format.tlutfmt))
{
const u32 decoded_size = width * height * sizeof(u32);
CheckTempSize(decoded_size);

View File

@ -3,16 +3,20 @@
#pragma once
#include <array>
#include <span>
#include <tuple>
#include "Common/CommonTypes.h"
#include "Common/EnumFormatter.h"
#include "Common/SpanUtils.h"
enum
{
TMEM_SIZE = 1024 * 1024,
TMEM_LINE_SIZE = 32,
};
alignas(16) extern u8 texMem[TMEM_SIZE];
alignas(16) extern std::array<u8, TMEM_SIZE> s_tex_mem;
enum class TextureFormat
{
@ -171,6 +175,11 @@ static inline bool CanReinterpretTextureOnGPU(TextureFormat from_format, Texture
}
}
inline std::span<u8> TexDecoder_GetTmemSpan(size_t offset = 0)
{
return Common::SafeSubspan(std::span<u8>(s_tex_mem), offset);
}
int TexDecoder_GetTexelSizeInNibbles(TextureFormat format);
int TexDecoder_GetTextureSizeInBytes(int width, int height, TextureFormat format);
int TexDecoder_GetBlockWidthInTexels(TextureFormat format);
@ -184,8 +193,10 @@ void TexDecoder_Decode(u8* dst, const u8* src, int width, int height, TextureFor
const u8* tlut, TLUTFormat tlutfmt);
void TexDecoder_DecodeRGBA8FromTmem(u8* dst, const u8* src_ar, const u8* src_gb, int width,
int height);
void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth,
TextureFormat texformat, const u8* tlut, TLUTFormat tlutfmt);
void TexDecoder_DecodeTexel(u8* dst, std::span<const u8> src, int s, int t, int imageWidth,
TextureFormat texformat, std::span<const u8> tlut, TLUTFormat tlutfmt);
void TexDecoder_DecodeTexelRGBA8FromTmem(u8* dst, std::span<const u8> src_ar,
std::span<const u8> src_gb, int s, int t, int imageWidth);
void TexDecoder_DecodeTexelRGBA8FromTmem(u8* dst, const u8* src_ar, const u8* src_gb, int s, int t,
int imageWidth);
void TexDecoder_DecodeXFB(u8* dst, const u8* src, u32 width, u32 height, u32 stride);

View File

@ -2,11 +2,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <span>
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/SpanUtils.h"
#include "Common/Swap.h"
#include "VideoCommon/LookUpTables.h"
@ -19,7 +22,7 @@ static bool TexFmt_Overlay_Center = false;
// TRAM
// STATE_TO_SAVE
alignas(16) u8 texMem[TMEM_SIZE];
alignas(16) std::array<u8, TMEM_SIZE> s_tex_mem;
int TexDecoder_GetTexelSizeInNibbles(TextureFormat format)
{
@ -356,8 +359,8 @@ static inline u32 DecodePixel_Paletted(u16 pixel, TLUTFormat tlutfmt)
}
}
void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth,
TextureFormat texformat, const u8* tlut_, TLUTFormat tlutfmt)
void TexDecoder_DecodeTexel(u8* dst, std::span<const u8> src, int s, int t, int imageWidth,
TextureFormat texformat, std::span<const u8> tlut_, TLUTFormat tlutfmt)
{
/* General formula for computing texture offset
//
@ -385,10 +388,10 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
int rs = (blkOff & 1) ? 0 : 4;
u32 offset = base + (blkOff >> 1);
u8 val = (*(src + offset) >> rs) & 0xF;
u16* tlut = (u16*)tlut_;
u8 val = (Common::SafeSpanRead<u8>(src, offset) >> rs) & 0xF;
u16 pixel = Common::SafeSpanRead<u16>(tlut_, sizeof(u16) * val);
*((u32*)dst) = DecodePixel_Paletted(tlut[val], tlutfmt);
*((u32*)dst) = DecodePixel_Paletted(pixel, tlutfmt);
}
break;
case TextureFormat::I4:
@ -404,7 +407,7 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
int rs = (blkOff & 1) ? 0 : 4;
u32 offset = base + (blkOff >> 1);
u8 val = (*(src + offset) >> rs) & 0xF;
u8 val = (Common::SafeSpanRead<u8>(src, offset) >> rs) & 0xF;
val = Convert4To8(val);
dst[0] = val;
dst[1] = val;
@ -422,7 +425,7 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u16 blkT = t & 3;
u32 blkOff = (blkT << 3) + blkS;
u8 val = *(src + base + blkOff);
u8 val = Common::SafeSpanRead<u8>(src, base + blkOff);
dst[0] = val;
dst[1] = val;
dst[2] = val;
@ -439,10 +442,10 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u16 blkT = t & 3;
u32 blkOff = (blkT << 3) + blkS;
u8 val = *(src + base + blkOff);
u16* tlut = (u16*)tlut_;
u8 val = Common::SafeSpanRead<u8>(src, base + blkOff);
u16 pixel = Common::SafeSpanRead<u16>(tlut_, sizeof(u16) * val);
*((u32*)dst) = DecodePixel_Paletted(tlut[val], tlutfmt);
*((u32*)dst) = DecodePixel_Paletted(pixel, tlutfmt);
}
break;
case TextureFormat::IA4:
@ -455,7 +458,7 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u16 blkT = t & 3;
u32 blkOff = (blkT << 3) + blkS;
u8 val = *(src + base + blkOff);
u8 val = Common::SafeSpanRead<u8>(src, base + blkOff);
const u8 a = Convert4To8(val >> 4);
const u8 l = Convert4To8(val & 0xF);
dst[0] = l;
@ -475,9 +478,9 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1;
const u16* valAddr = (u16*)(src + offset);
u16 val = Common::SafeSpanRead<u16>(src, offset);
*((u32*)dst) = DecodePixel_IA8(*valAddr);
*((u32*)dst) = DecodePixel_IA8(val);
}
break;
case TextureFormat::C14X2:
@ -491,12 +494,10 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1;
const u16* valAddr = (u16*)(src + offset);
u16 val = Common::swap16(Common::SafeSpanRead<u16>(src, offset)) & 0x3FFF;
u16 pixel = Common::SafeSpanRead<u16>(tlut_, sizeof(u16) * val);
u16 val = Common::swap16(*valAddr) & 0x3FFF;
u16* tlut = (u16*)tlut_;
*((u32*)dst) = DecodePixel_Paletted(tlut[val], tlutfmt);
*((u32*)dst) = DecodePixel_Paletted(pixel, tlutfmt);
}
break;
case TextureFormat::RGB565:
@ -510,9 +511,9 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1;
const u16* valAddr = (u16*)(src + offset);
u16 val = Common::SafeSpanRead<u16>(src, offset);
*((u32*)dst) = DecodePixel_RGB565(Common::swap16(*valAddr));
*((u32*)dst) = DecodePixel_RGB565(Common::swap16(val));
}
break;
case TextureFormat::RGB5A3:
@ -526,9 +527,9 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1;
const u16* valAddr = (u16*)(src + offset);
u16 val = Common::SafeSpanRead<u16>(src, offset);
*((u32*)dst) = DecodePixel_RGB5A3(Common::swap16(*valAddr));
*((u32*)dst) = DecodePixel_RGB5A3(Common::swap16(val));
}
break;
case TextureFormat::RGBA8:
@ -542,12 +543,11 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 blkOff = (blkT << 2) + blkS;
u32 offset = (base + blkOff) << 1;
const u8* valAddr = src + offset;
dst[3] = valAddr[0];
dst[0] = valAddr[1];
dst[1] = valAddr[32];
dst[2] = valAddr[33];
dst[3] = Common::SafeSpanRead<u8>(src, offset);
dst[0] = Common::SafeSpanRead<u8>(src, offset + 1);
dst[1] = Common::SafeSpanRead<u8>(src, offset + 32);
dst[2] = Common::SafeSpanRead<u8>(src, offset + 33);
}
break;
case TextureFormat::CMPR:
@ -565,10 +565,10 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u32 offset = (base + blkOff) << 3;
const DXTBlock* dxtBlock = (const DXTBlock*)(src + offset);
DXTBlock dxtBlock = Common::SafeSpanRead<DXTBlock>(src, offset);
u16 c1 = Common::swap16(dxtBlock->color1);
u16 c2 = Common::swap16(dxtBlock->color2);
u16 c1 = Common::swap16(dxtBlock.color1);
u16 c2 = Common::swap16(dxtBlock.color2);
int blue1 = Convert5To8(c1 & 0x1F);
int blue2 = Convert5To8(c2 & 0x1F);
int green1 = Convert6To8((c1 >> 5) & 0x3F);
@ -579,7 +579,7 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
u16 ss = s & 3;
u16 tt = t & 3;
int colorSel = dxtBlock->lines[tt];
int colorSel = dxtBlock.lines[tt];
int rs = 6 - (ss << 1);
colorSel = (colorSel >> rs) & 3;
colorSel |= c1 > c2 ? 0 : 4;
@ -640,6 +640,28 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
}
}
void TexDecoder_DecodeTexelRGBA8FromTmem(u8* dst, std::span<const u8> src_ar,
std::span<const u8> src_gb, int s, int t, int imageWidth)
{
u16 sBlk = s >> 2;
u16 tBlk = t >> 2;
u16 widthBlks =
(imageWidth >> 2) + 1; // TODO: Looks wrong. Shouldn't this be ((imageWidth-1)>>2)+1 ?
u32 base_ar = (tBlk * widthBlks + sBlk) << 4;
u32 base_gb = (tBlk * widthBlks + sBlk) << 4;
u16 blkS = s & 3;
u16 blkT = t & 3;
u32 blk_off = (blkT << 2) + blkS;
u32 offset_ar = (base_ar + blk_off) << 1;
u32 offset_gb = (base_gb + blk_off) << 1;
dst[3] = Common::SafeSpanRead<u8>(src_ar, offset_ar); // A
dst[0] = Common::SafeSpanRead<u8>(src_ar, offset_ar + 1); // R
dst[1] = Common::SafeSpanRead<u8>(src_gb, offset_gb); // G
dst[2] = Common::SafeSpanRead<u8>(src_gb, offset_gb + 1); // B
}
void TexDecoder_DecodeTexelRGBA8FromTmem(u8* dst, const u8* src_ar, const u8* src_gb, int s, int t,
int imageWidth)
{

View File

@ -3,10 +3,15 @@
#include "VideoCommon/TextureInfo.h"
#include <span>
#include <fmt/format.h>
#include <xxhash.h>
#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/SpanUtils.h"
#include "Core/HW/Memmap.h"
#include "Core/System.h"
#include "VideoCommon/BPMemory.h"
@ -25,7 +30,7 @@ TextureInfo TextureInfo::FromStage(u32 stage)
const u32 address = (tex.texImage3.image_base /* & 0x1FFFFF*/) << 5;
const u32 tlutaddr = tex.texTlut.tmem_offset << 9;
const u8* tlut_ptr = &texMem[tlutaddr];
std::span<const u8> tlut_data = TexDecoder_GetTmemSpan(tlutaddr);
std::optional<u32> mip_count;
const bool has_mipmaps = tex.texMode0.mipmap_filter != MipMode::None;
@ -40,23 +45,24 @@ TextureInfo TextureInfo::FromStage(u32 stage)
if (from_tmem)
{
return TextureInfo(stage, &texMem[tmem_address_even], tlut_ptr, address, texture_format,
tlut_format, width, height, true, &texMem[tmem_address_odd],
&texMem[tmem_address_even], mip_count);
return TextureInfo(stage, TexDecoder_GetTmemSpan(tmem_address_even), tlut_data, address,
texture_format, tlut_format, width, height, true,
TexDecoder_GetTmemSpan(tmem_address_odd),
TexDecoder_GetTmemSpan(tmem_address_even), mip_count);
}
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return TextureInfo(stage, memory.GetPointer(address), tlut_ptr, address, texture_format,
tlut_format, width, height, false, nullptr, nullptr, mip_count);
return TextureInfo(stage, memory.GetSpanForAddress(address), tlut_data, address, texture_format,
tlut_format, width, height, false, {}, {}, mip_count);
}
TextureInfo::TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 address,
TextureFormat texture_format, TLUTFormat tlut_format, u32 width,
u32 height, bool from_tmem, const u8* tmem_odd, const u8* tmem_even,
std::optional<u32> mip_count)
: m_ptr(ptr), m_tlut_ptr(tlut_ptr), m_address(address), m_from_tmem(from_tmem),
m_tmem_odd(tmem_odd), m_texture_format(texture_format), m_tlut_format(tlut_format),
TextureInfo::TextureInfo(u32 stage, std::span<const u8> data, std::span<const u8> tlut_data,
u32 address, TextureFormat texture_format, TLUTFormat tlut_format,
u32 width, u32 height, bool from_tmem, std::span<const u8> tmem_odd,
std::span<const u8> tmem_even, std::optional<u32> mip_count)
: m_ptr(data.data()), m_tlut_ptr(tlut_data.data()), m_address(address), m_from_tmem(from_tmem),
m_tmem_odd(tmem_odd.data()), m_texture_format(texture_format), m_tlut_format(tlut_format),
m_raw_width(width), m_raw_height(height), m_stage(stage)
{
const bool is_palette_texture = IsColorIndexed(m_texture_format);
@ -73,6 +79,21 @@ TextureInfo::TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 addre
m_texture_size =
TexDecoder_GetTextureSizeInBytes(m_expanded_width, m_expanded_height, m_texture_format);
if (data.size() < m_texture_size)
{
ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}", GetRawAddress());
m_data_valid = false;
}
else if (m_palette_size && tlut_data.size() < *m_palette_size)
{
ERROR_LOG_FMT(VIDEO, "Trying to use an invalid TLUT address {:#010x}", GetRawAddress());
m_data_valid = false;
}
else
{
m_data_valid = true;
}
if (mip_count)
{
m_mipmaps_enabled = true;
@ -86,13 +107,17 @@ TextureInfo::TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 addre
std::min<u32>(MathUtil::IntLog2(std::max(width, height)) + 1, raw_mip_count + 1) - 1;
// load mips
const u8* src_data = m_ptr + GetTextureSize();
if (tmem_even)
tmem_even += GetTextureSize();
std::span<const u8> src_data = Common::SafeSubspan(data, GetTextureSize());
tmem_even = Common::SafeSubspan(tmem_even, GetTextureSize());
for (u32 i = 0; i < limited_mip_count; i++)
{
MipLevel mip_level(i + 1, *this, m_from_tmem, src_data, tmem_even, tmem_odd);
MipLevel mip_level(i + 1, *this, m_from_tmem, &src_data, &tmem_even, &tmem_odd);
if (!mip_level.IsDataValid())
{
ERROR_LOG_FMT(VIDEO, "Trying to use an invalid mipmap address {:#010x}", GetRawAddress());
break;
}
m_mip_levels.push_back(std::move(mip_level));
}
}
@ -105,7 +130,7 @@ std::string TextureInfo::NameDetails::GetFullName() const
TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
{
if (!m_ptr)
if (!IsDataValid())
return NameDetails{};
const u8* tlut = m_tlut_ptr;
@ -129,7 +154,6 @@ TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
}
break;
case 256 * 2:
{
for (size_t i = 0; i < m_texture_size; i++)
{
const u32 texture_byte = m_ptr[i];
@ -138,7 +162,6 @@ TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
max = std::max(max, texture_byte);
}
break;
}
case 16384 * 2:
for (size_t i = 0; i < m_texture_size; i += sizeof(u16))
{
@ -155,6 +178,8 @@ TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
tlut += 2 * min;
}
DEBUG_ASSERT(tlut_size <= m_palette_size.value_or(0));
const u64 tex_hash = XXH64(m_ptr, m_texture_size, 0);
const u64 tlut_hash = tlut_size ? XXH64(tlut, tlut_size, 0) : 0;
@ -168,6 +193,11 @@ TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
return result;
}
bool TextureInfo::IsDataValid() const
{
return m_data_valid;
}
const u8* TextureInfo::GetData() const
{
return m_ptr;
@ -267,7 +297,8 @@ const TextureInfo::MipLevel* TextureInfo::GetMipMapLevel(u32 level) const
}
TextureInfo::MipLevel::MipLevel(u32 level, const TextureInfo& parent, bool from_tmem,
const u8*& src_data, const u8*& ptr_even, const u8*& ptr_odd)
std::span<const u8>* src_data, std::span<const u8>* tmem_even,
std::span<const u8>* tmem_odd)
{
m_raw_width = std::max(parent.GetRawWidth() >> level, 1u);
m_raw_height = std::max(parent.GetRawHeight() >> level, 1u);
@ -277,9 +308,11 @@ TextureInfo::MipLevel::MipLevel(u32 level, const TextureInfo& parent, bool from_
m_texture_size = TexDecoder_GetTextureSizeInBytes(m_expanded_width, m_expanded_height,
parent.GetTextureFormat());
const u8*& ptr = from_tmem ? ((level % 2) ? ptr_odd : ptr_even) : src_data;
m_ptr = ptr;
ptr += m_texture_size;
std::span<const u8>* data = from_tmem ? ((level % 2) ? tmem_odd : tmem_even) : src_data;
m_ptr = data->data();
m_data_valid = data->size() >= m_texture_size;
*data = Common::SafeSubspan(*data, m_texture_size);
}
u32 TextureInfo::GetFullLevelSize() const
@ -292,6 +325,11 @@ u32 TextureInfo::GetFullLevelSize() const
return m_texture_size + all_mips_size;
}
bool TextureInfo::MipLevel::IsDataValid() const
{
return m_data_valid;
}
const u8* TextureInfo::MipLevel::GetData() const
{
return m_ptr;

View File

@ -4,6 +4,7 @@
#pragma once
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <vector>
@ -17,9 +18,9 @@ class TextureInfo
{
public:
static TextureInfo FromStage(u32 stage);
TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 address,
TextureInfo(u32 stage, std::span<const u8> data, std::span<const u8> tlut_data, u32 address,
TextureFormat texture_format, TLUTFormat tlut_format, u32 width, u32 height,
bool from_tmem, const u8* tmem_odd, const u8* tmem_even,
bool from_tmem, std::span<const u8> tmem_odd, std::span<const u8> tmem_even,
std::optional<u32> mip_count);
struct NameDetails
@ -33,6 +34,8 @@ public:
};
NameDetails CalculateTextureName() const;
bool IsDataValid() const;
const u8* GetData() const;
const u8* GetTlutAddress() const;
@ -61,11 +64,12 @@ public:
class MipLevel
{
public:
MipLevel(u32 level, const TextureInfo& parent, bool from_tmem, const u8*& src_data,
const u8*& ptr_even, const u8*& ptr_odd);
MipLevel(u32 level, const TextureInfo& parent, bool from_tmem, std::span<const u8>* src_data,
std::span<const u8>* tmem_even, std::span<const u8>* tmem_odd);
bool IsDataValid() const;
const u8* GetData() const;
u32 GetTextureSize() const;
u32 GetExpandedWidth() const;
@ -75,6 +79,8 @@ public:
u32 GetRawHeight() const;
private:
bool m_data_valid;
const u8* m_ptr;
u32 m_texture_size = 0;
@ -99,6 +105,8 @@ private:
u32 m_address;
bool m_data_valid;
bool m_from_tmem;
const u8* m_tmem_odd;

View File

@ -91,26 +91,35 @@ void UpdateVertexArrayPointers()
// Note: Only array bases 0 through 11 are used by the Vertex loaders.
// 12 through 15 are used for loading data into xfmem.
// We also only update the array base if the vertex description states we are going to use it.
// TODO: For memory safety, we need to check the sizes returned by GetSpanForAddress
if (IsIndexed(g_main_cp_state.vtx_desc.low.Position))
{
cached_arraybases[CPArray::Position] =
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Position]);
memory.GetSpanForAddress(g_main_cp_state.array_bases[CPArray::Position]).data();
}
if (IsIndexed(g_main_cp_state.vtx_desc.low.Normal))
{
cached_arraybases[CPArray::Normal] =
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Normal]);
memory.GetSpanForAddress(g_main_cp_state.array_bases[CPArray::Normal]).data();
}
for (u8 i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
{
if (IsIndexed(g_main_cp_state.vtx_desc.low.Color[i]))
{
cached_arraybases[CPArray::Color0 + i] =
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Color0 + i]);
memory.GetSpanForAddress(g_main_cp_state.array_bases[CPArray::Color0 + i]).data();
}
}
for (u8 i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
{
if (IsIndexed(g_main_cp_state.vtx_desc.high.TexCoord[i]))
{
cached_arraybases[CPArray::TexCoord0 + i] =
memory.GetPointer(g_main_cp_state.array_bases[CPArray::TexCoord0 + i]);
memory.GetSpanForAddress(g_main_cp_state.array_bases[CPArray::TexCoord0 + i]).data();
}
}
g_bases_dirty = false;

View File

@ -344,7 +344,7 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr<AbstractGfx> gfx,
{
memset(reinterpret_cast<u8*>(&g_main_cp_state), 0, sizeof(g_main_cp_state));
memset(reinterpret_cast<u8*>(&g_preprocess_cp_state), 0, sizeof(g_preprocess_cp_state));
memset(texMem, 0, TMEM_SIZE);
s_tex_mem.fill(0);
// do not initialize again for the config window
m_initialized = true;

View File

@ -57,7 +57,7 @@ void VideoCommon_DoState(PointerWrap& p)
p.DoMarker("XF Memory");
// Texture decoder
p.DoArray(texMem);
p.DoArray(s_tex_mem);
p.DoMarker("texMem");
// TMEM