cemu: 2 CWE-407 defects, MOAD 0002-0005 CLEAN
This commit is contained in:
parent
7b1f81c260
commit
65578228e4
6 changed files with 279 additions and 0 deletions
28
defects/cemu-0001/patch/cemu-0001.patch
Normal file
28
defects/cemu-0001/patch/cemu-0001.patch
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
--- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h
|
||||
+++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h
|
||||
@@ -19,7 +19,7 @@ class LatteTextureViewVk : public LatteTextureView
|
||||
LatteTextureVk* GetBaseImage() const { return (LatteTextureVk*)baseTexture; }
|
||||
|
||||
- void AddDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { if (std::find(list_descriptorSets.begin(), list_descriptorSets.end(), dsInfo) == list_descriptorSets.end()) list_descriptorSets.emplace_back(dsInfo); };
|
||||
- void RemoveDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.erase(std::remove(list_descriptorSets.begin(), list_descriptorSets.end(), dsInfo), list_descriptorSets.end()); };
|
||||
+ void AddDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.insert(dsInfo); };
|
||||
+ void RemoveDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.erase(dsInfo); };
|
||||
|
||||
private:
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
VkDevice m_device;
|
||||
VkFormat m_format;
|
||||
- std::vector<struct VkDescriptorSetInfo*> list_descriptorSets; // list of descriptors sets referencing this view
|
||||
+ std::unordered_set<struct VkDescriptorSetInfo*> list_descriptorSets; // list of descriptors sets referencing this view
|
||||
|
||||
uint64 m_uniqueId;
|
||||
};
|
||||
--- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp
|
||||
+++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp
|
||||
@@ -75,8 +75,8 @@ LatteTextureViewVk::~LatteTextureViewVk()
|
||||
{
|
||||
- while (!list_descriptorSets.empty())
|
||||
- delete list_descriptorSets[0];
|
||||
+ while (!list_descriptorSets.empty())
|
||||
+ delete *list_descriptorSets.begin();
|
||||
BIN
defects/cemu-0001/test/cemu_0001_test
Executable file
BIN
defects/cemu-0001/test/cemu_0001_test
Executable file
Binary file not shown.
97
defects/cemu-0001/test/cemu_0001_test.cpp
Normal file
97
defects/cemu-0001/test/cemu_0001_test.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// cemu-0001: LatteTextureViewVk::list_descriptorSets vector -> unordered_set
|
||||
// Tests that AddDescriptorSetReference is O(1) amortized, not O(D).
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
// Simulates the original defect: std::vector with linear scan for uniqueness
|
||||
struct DefectRef {
|
||||
std::vector<int*> refs;
|
||||
|
||||
void add(int* p) {
|
||||
for (auto* r : refs)
|
||||
if (r == p) return;
|
||||
refs.push_back(p);
|
||||
}
|
||||
|
||||
void remove(int* p) {
|
||||
refs.erase(std::remove(refs.begin(), refs.end(), p), refs.end());
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed implementation: unordered_set, O(1) amortized
|
||||
struct FixedRef {
|
||||
std::unordered_set<int*> refs;
|
||||
|
||||
void add(int* p) {
|
||||
refs.insert(p);
|
||||
}
|
||||
|
||||
void remove(int* p) {
|
||||
refs.erase(p);
|
||||
}
|
||||
};
|
||||
|
||||
static long long bench_ns(auto fn) {
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
fn();
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count();
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Correctness test: both implementations maintain unique set
|
||||
DefectRef defect;
|
||||
FixedRef fixed;
|
||||
|
||||
const int N = 500;
|
||||
int ptrs[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
defect.add(&ptrs[i]);
|
||||
fixed.add(&ptrs[i]);
|
||||
}
|
||||
// Duplicate add should not grow the collection
|
||||
for (int i = 0; i < N; i++) {
|
||||
defect.add(&ptrs[i]);
|
||||
fixed.add(&ptrs[i]);
|
||||
}
|
||||
assert(defect.refs.size() == N);
|
||||
assert(fixed.refs.size() == N);
|
||||
|
||||
// Remove half
|
||||
for (int i = 0; i < N / 2; i++) {
|
||||
defect.remove(&ptrs[i]);
|
||||
fixed.remove(&ptrs[i]);
|
||||
}
|
||||
assert(defect.refs.size() == N / 2);
|
||||
assert(fixed.refs.size() == N / 2);
|
||||
|
||||
// Performance: add D = 1000 unique pointers to each, measure time
|
||||
const int D = 1000;
|
||||
int ds_ptrs[D];
|
||||
|
||||
DefectRef defect2;
|
||||
auto t_defect = bench_ns([&]() {
|
||||
for (int i = 0; i < D; i++)
|
||||
defect2.add(&ds_ptrs[i]);
|
||||
});
|
||||
|
||||
FixedRef fixed2;
|
||||
auto t_fixed = bench_ns([&]() {
|
||||
for (int i = 0; i < D; i++)
|
||||
fixed2.add(&ds_ptrs[i]);
|
||||
});
|
||||
|
||||
double ratio = (double)t_defect / (double)t_fixed;
|
||||
printf("cemu-0001: defect=%lldns fixed=%lldns ratio=%.1fx (D=%d)\n",
|
||||
(long long)t_defect, (long long)t_fixed, ratio, D);
|
||||
|
||||
// Fixed should be significantly faster for large D
|
||||
assert(ratio > 2.0 && "Fixed implementation not measurably faster — check test");
|
||||
|
||||
printf("cemu-0001: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
50
defects/cemu-0002/patch/cemu-0002.patch
Normal file
50
defects/cemu-0002/patch/cemu-0002.patch
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
--- a/src/Cafe/GraphicPack/GraphicPack2.h
|
||||
+++ b/src/Cafe/GraphicPack/GraphicPack2.h
|
||||
@@ -35,10 +35,10 @@ struct FILTER_SETTINGS
|
||||
sint32 width = -1;
|
||||
sint32 height = -1;
|
||||
sint32 depth = -1;
|
||||
MEM1_FILTER inMEM1 = MEM1_FILTER::BOTH;
|
||||
- std::vector<sint32> format_whitelist{};
|
||||
- std::vector<sint32> format_blacklist{};
|
||||
- std::vector<sint32> tilemode_whitelist{};
|
||||
- std::vector<sint32> tilemode_blacklist{};
|
||||
+ std::unordered_set<sint32> format_whitelist{};
|
||||
+ std::unordered_set<sint32> format_blacklist{};
|
||||
+ std::unordered_set<sint32> tilemode_whitelist{};
|
||||
+ std::unordered_set<sint32> tilemode_blacklist{};
|
||||
} filter_settings;
|
||||
--- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp
|
||||
+++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp
|
||||
@@ -1243,13 +1243,13 @@ void LatteTexture_init()
|
||||
- if (!rule.filter_settings.format_whitelist.empty() && std::find(rule.filter_settings.format_whitelist.begin(), rule.filter_settings.format_whitelist.end(), (uint32)format) == rule.filter_settings.format_whitelist.end())
|
||||
+ if (!rule.filter_settings.format_whitelist.empty() && rule.filter_settings.format_whitelist.find((sint32)format) == rule.filter_settings.format_whitelist.end())
|
||||
continue;
|
||||
|
||||
- if (!rule.filter_settings.format_blacklist.empty() && std::find(rule.filter_settings.format_blacklist.begin(), rule.filter_settings.format_blacklist.end(), (uint32)format) != rule.filter_settings.format_blacklist.end())
|
||||
+ if (!rule.filter_settings.format_blacklist.empty() && rule.filter_settings.format_blacklist.find((sint32)format) != rule.filter_settings.format_blacklist.end())
|
||||
continue;
|
||||
|
||||
- if (!rule.filter_settings.tilemode_whitelist.empty() && std::find(rule.filter_settings.tilemode_whitelist.begin(), rule.filter_settings.tilemode_whitelist.end(), (int)tileMode) == rule.filter_settings.tilemode_whitelist.end())
|
||||
+ if (!rule.filter_settings.tilemode_whitelist.empty() && rule.filter_settings.tilemode_whitelist.find((sint32)tileMode) == rule.filter_settings.tilemode_whitelist.end())
|
||||
continue;
|
||||
|
||||
- if (!rule.filter_settings.tilemode_blacklist.empty() && std::find(rule.filter_settings.tilemode_blacklist.begin(), rule.filter_settings.tilemode_blacklist.end(), (int)tileMode) != rule.filter_settings.tilemode_blacklist.end())
|
||||
+ if (!rule.filter_settings.tilemode_blacklist.empty() && rule.filter_settings.tilemode_blacklist.find((sint32)tileMode) != rule.filter_settings.tilemode_blacklist.end())
|
||||
continue;
|
||||
--- a/src/Cafe/GraphicPack/GraphicPack2.cpp
|
||||
+++ b/src/Cafe/GraphicPack/GraphicPack2.cpp
|
||||
@@ -978,12 +978,16 @@ void GraphicPack2::_loadTextureRules(IniParser& parser)
|
||||
- rule.filter_settings.format_whitelist = ParseList<sint32>(parser, rules, "formats");
|
||||
- rule.filter_settings.format_blacklist = ParseList<sint32>(parser, rules, "formatsExcluded");
|
||||
- rule.filter_settings.tilemode_whitelist = ParseList<sint32>(parser, rules, "tilemodes");
|
||||
- rule.filter_settings.tilemode_blacklist = ParseList<sint32>(parser, rules, "tilemodesExcluded");
|
||||
+ auto fmt_wl = ParseList<sint32>(parser, rules, "formats");
|
||||
+ rule.filter_settings.format_whitelist = std::unordered_set<sint32>(fmt_wl.begin(), fmt_wl.end());
|
||||
+ auto fmt_bl = ParseList<sint32>(parser, rules, "formatsExcluded");
|
||||
+ rule.filter_settings.format_blacklist = std::unordered_set<sint32>(fmt_bl.begin(), fmt_bl.end());
|
||||
+ auto tm_wl = ParseList<sint32>(parser, rules, "tilemodes");
|
||||
+ rule.filter_settings.tilemode_whitelist = std::unordered_set<sint32>(tm_wl.begin(), tm_wl.end());
|
||||
+ auto tm_bl = ParseList<sint32>(parser, rules, "tilemodesExcluded");
|
||||
+ rule.filter_settings.tilemode_blacklist = std::unordered_set<sint32>(tm_bl.begin(), tm_bl.end());
|
||||
BIN
defects/cemu-0002/test/cemu_0002_test
Executable file
BIN
defects/cemu-0002/test/cemu_0002_test
Executable file
Binary file not shown.
104
defects/cemu-0002/test/cemu_0002_test.cpp
Normal file
104
defects/cemu-0002/test/cemu_0002_test.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// cemu-0002: GraphicPack2 FILTER_SETTINGS format/tilemode list membership O(F) -> O(1)
|
||||
// Per-texture-creation std::find in active graphic pack rules replaced with unordered_set lookup.
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
// Simulates defect: vector + std::find membership check, called per texture creation
|
||||
struct DefectFilter {
|
||||
std::vector<int> format_whitelist;
|
||||
std::vector<int> tilemode_whitelist;
|
||||
|
||||
bool matches(int format, int tilemode) const {
|
||||
if (!format_whitelist.empty()) {
|
||||
if (std::find(format_whitelist.begin(), format_whitelist.end(), format)
|
||||
== format_whitelist.end())
|
||||
return false;
|
||||
}
|
||||
if (!tilemode_whitelist.empty()) {
|
||||
if (std::find(tilemode_whitelist.begin(), tilemode_whitelist.end(), tilemode)
|
||||
== tilemode_whitelist.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed: unordered_set membership, O(1) per lookup
|
||||
struct FixedFilter {
|
||||
std::unordered_set<int> format_whitelist;
|
||||
std::unordered_set<int> tilemode_whitelist;
|
||||
|
||||
bool matches(int format, int tilemode) const {
|
||||
if (!format_whitelist.empty()) {
|
||||
if (format_whitelist.find(format) == format_whitelist.end())
|
||||
return false;
|
||||
}
|
||||
if (!tilemode_whitelist.empty()) {
|
||||
if (tilemode_whitelist.find(tilemode) == tilemode_whitelist.end())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static long long bench_ns(auto fn) {
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
fn();
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count();
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Build filters with F = 200 format entries and T = 50 tilemode entries
|
||||
// (large texture packs may enumerate many formats and tile modes)
|
||||
const int F = 200;
|
||||
const int T = 50;
|
||||
const int TEXTURES = 10000; // simulate 10k texture creations
|
||||
|
||||
DefectFilter defect;
|
||||
FixedFilter fixed;
|
||||
|
||||
for (int i = 0; i < F; i++) {
|
||||
defect.format_whitelist.push_back(i * 3 + 1); // scattered to avoid trivial branch pred
|
||||
fixed.format_whitelist.insert(i * 3 + 1);
|
||||
}
|
||||
for (int i = 0; i < T; i++) {
|
||||
defect.tilemode_whitelist.push_back(i * 5 + 2);
|
||||
fixed.tilemode_whitelist.insert(i * 5 + 2);
|
||||
}
|
||||
|
||||
// Correctness: both should agree on membership
|
||||
for (int f = 0; f < F * 4; f++) {
|
||||
for (int t = 0; t < T * 6; t++) {
|
||||
bool d = defect.matches(f, t);
|
||||
bool x = fixed.matches(f, t);
|
||||
assert(d == x && "mismatch between defect and fixed");
|
||||
}
|
||||
}
|
||||
|
||||
// Performance: TEXTURES texture lookups, scattered format/tilemode queries
|
||||
volatile bool sink = false;
|
||||
|
||||
auto t_defect = bench_ns([&]() {
|
||||
for (int i = 0; i < TEXTURES; i++)
|
||||
sink ^= defect.matches(i % (F * 4), i % (T * 6));
|
||||
});
|
||||
|
||||
auto t_fixed = bench_ns([&]() {
|
||||
for (int i = 0; i < TEXTURES; i++)
|
||||
sink ^= fixed.matches(i % (F * 4), i % (T * 6));
|
||||
});
|
||||
|
||||
double ratio = (double)t_defect / (double)t_fixed;
|
||||
printf("cemu-0002: defect=%lldns fixed=%lldns ratio=%.1fx (F=%d T=%d textures=%d)\n",
|
||||
(long long)t_defect, (long long)t_fixed, ratio, F, T, TEXTURES);
|
||||
|
||||
assert(ratio > 1.5 && "Fixed implementation not measurably faster — check test");
|
||||
|
||||
printf("cemu-0002: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue