diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 7b81cd2c6d..af48ba7e13 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -126,6 +126,7 @@ add_library(common SmallVector.h SocketContext.cpp SocketContext.h + SpanUtils.h SPSCQueue.h StringLiteral.h StringUtil.cpp diff --git a/Source/Core/Common/SpanUtils.h b/Source/Core/Common/SpanUtils.h new file mode 100644 index 0000000000..d09193b314 --- /dev/null +++ b/Source/Core/Common/SpanUtils.h @@ -0,0 +1,41 @@ +// Copyright 2024 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +namespace Common +{ + +// Like std::span::subspan, except undefined behavior is replaced with returning a 0-length span. +template +[[nodiscard]] constexpr std::span SafeSubspan(std::span 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 +[[nodiscard]] T SafeSpanRead(std::span span, size_t offset) +{ + static_assert(std::is_trivially_copyable()); + + const std::span subspan = SafeSubspan(span, offset); + T result{}; + std::memcpy(&result, subspan.data(), std::min(subspan.size(), sizeof(result))); + return result; +} + +} // namespace Common diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp index e035990504..39377d7cde 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp @@ -697,7 +697,7 @@ void FifoPlayer::LoadTextureMemory() { static_assert(static_cast(TMEM_SIZE) == static_cast(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) diff --git a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp index bea5b94519..c354a5fdf7 100644 --- a/Source/Core/Core/FifoPlayer/FifoRecorder.cpp +++ b/Source/Core/Core/FifoPlayer/FifoRecorder.cpp @@ -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() diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index cd6fcfc161..beec3e9a2d 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -153,6 +153,7 @@ + diff --git a/Source/Core/VideoBackends/Software/TextureSampler.cpp b/Source/Core/VideoBackends/Software/TextureSampler.cpp index f9182441bb..ceb6c02256 100644 --- a/Source/Core/VideoBackends/Software/TextureSampler.cpp +++ b/Source/Core/VideoBackends/Software/TextureSampler.cpp @@ -9,6 +9,7 @@ #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" +#include "Common/SpanUtils.h" #include "Core/HW/Memmap.h" #include "Core/System.h" @@ -124,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 image_src; + std::span 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 { @@ -138,16 +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; - // TODO: For memory safety, we need to check the size of this span - std::span span = memory.GetSpanForAddress(imageBase); - imageSrc = span.data(); + 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 tlut = TexDecoder_GetTmemSpan(tlutAddress); // reduce sample location and texture size to mip level // move texture pointer to mip location @@ -171,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--; @@ -205,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)); } @@ -256,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 diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 6e4bb981a8..7eb6c88656 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -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); @@ -596,7 +596,7 @@ static void BPWritten(PixelShaderManager& pixel_shader_manager, XFStateManager& auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - memory.CopyFromEmu(texMem + tmem_addr_even, src_addr, bytes_read); + 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 +615,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; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index d6fec6adc4..7184ea725e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -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); diff --git a/Source/Core/VideoCommon/TextureDecoder.h b/Source/Core/VideoCommon/TextureDecoder.h index dcc7513d43..2296c1bfed 100644 --- a/Source/Core/VideoCommon/TextureDecoder.h +++ b/Source/Core/VideoCommon/TextureDecoder.h @@ -3,16 +3,20 @@ #pragma once +#include +#include #include + #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 s_tex_mem; enum class TextureFormat { @@ -171,6 +175,11 @@ static inline bool CanReinterpretTextureOnGPU(TextureFormat from_format, Texture } } +inline std::span TexDecoder_GetTmemSpan(size_t offset = 0) +{ + return Common::SafeSubspan(std::span(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 src, int s, int t, int imageWidth, + TextureFormat texformat, std::span tlut, TLUTFormat tlutfmt); +void TexDecoder_DecodeTexelRGBA8FromTmem(u8* dst, std::span src_ar, + std::span 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); diff --git a/Source/Core/VideoCommon/TextureDecoder_Common.cpp b/Source/Core/VideoCommon/TextureDecoder_Common.cpp index 01af14a649..299a36e4d4 100644 --- a/Source/Core/VideoCommon/TextureDecoder_Common.cpp +++ b/Source/Core/VideoCommon/TextureDecoder_Common.cpp @@ -2,11 +2,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include +#include #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 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 src, int s, int t, int imageWidth, + TextureFormat texformat, std::span 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(src, offset) >> rs) & 0xF; + u16 pixel = Common::SafeSpanRead(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(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(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(src, base + blkOff); + u16 pixel = Common::SafeSpanRead(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(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(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(src, offset)) & 0x3FFF; + u16 pixel = Common::SafeSpanRead(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(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(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(src, offset); + dst[0] = Common::SafeSpanRead(src, offset + 1); + dst[1] = Common::SafeSpanRead(src, offset + 32); + dst[2] = Common::SafeSpanRead(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(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 src_ar, + std::span 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(src_ar, offset_ar); // A + dst[0] = Common::SafeSpanRead(src_ar, offset_ar + 1); // R + dst[1] = Common::SafeSpanRead(src_gb, offset_gb); // G + dst[2] = Common::SafeSpanRead(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) { diff --git a/Source/Core/VideoCommon/TextureInfo.cpp b/Source/Core/VideoCommon/TextureInfo.cpp index a1bccb0ebe..5ee23f3671 100644 --- a/Source/Core/VideoCommon/TextureInfo.cpp +++ b/Source/Core/VideoCommon/TextureInfo.cpp @@ -9,6 +9,9 @@ #include #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" @@ -27,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 tlut_data = TexDecoder_GetTmemSpan(tlutaddr); std::optional mip_count; const bool has_mipmaps = tex.texMode0.mipmap_filter != MipMode::None; @@ -42,25 +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(); - // TODO: For memory safety, we need to check the size of this span - std::span span = memory.GetSpanForAddress(address); - return TextureInfo(stage, span.data(), 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 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 data, std::span tlut_data, + u32 address, TextureFormat texture_format, TLUTFormat tlut_format, + u32 width, u32 height, bool from_tmem, std::span tmem_odd, + std::span tmem_even, std::optional 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); @@ -77,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; @@ -90,13 +107,17 @@ TextureInfo::TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 addre std::min(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 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)); } } @@ -109,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; @@ -133,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]; @@ -142,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)) { @@ -159,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; @@ -172,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; @@ -271,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* src_data, std::span* tmem_even, + std::span* tmem_odd) { m_raw_width = std::max(parent.GetRawWidth() >> level, 1u); m_raw_height = std::max(parent.GetRawHeight() >> level, 1u); @@ -281,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* 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 @@ -296,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; diff --git a/Source/Core/VideoCommon/TextureInfo.h b/Source/Core/VideoCommon/TextureInfo.h index 940b09b3cb..49c76810c5 100644 --- a/Source/Core/VideoCommon/TextureInfo.h +++ b/Source/Core/VideoCommon/TextureInfo.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -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 data, std::span 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 tmem_odd, std::span tmem_even, std::optional 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* src_data, + std::span* tmem_even, std::span* 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; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index ba6adbe6f7..91492321f4 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -344,7 +344,7 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, { memset(reinterpret_cast(&g_main_cp_state), 0, sizeof(g_main_cp_state)); memset(reinterpret_cast(&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; diff --git a/Source/Core/VideoCommon/VideoState.cpp b/Source/Core/VideoCommon/VideoState.cpp index 594aca1598..d5f53f4963 100644 --- a/Source/Core/VideoCommon/VideoState.cpp +++ b/Source/Core/VideoCommon/VideoState.cpp @@ -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