Skip to content

Commit 2274bfd

Browse files
committed
Merge branch 'rc/1.64.0' into release
2 parents 74c68fe + 46ccb6b commit 2274bfd

File tree

5 files changed

+263
-6
lines changed

5 files changed

+263
-6
lines changed

filament/src/details/Texture.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,9 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept {
653653
FILAMENT_CHECK_PRECONDITION(formatMipmappable)
654654
<< "Texture format " << (unsigned)mFormat << " is not mipmappable.";
655655

656-
FILAMENT_CHECK_PRECONDITION(any(mUsage & TextureUsage::GEN_MIPMAPPABLE))
656+
auto const& featureFlags = downcast(engine).features.engine.debug;
657+
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(any(mUsage & TextureUsage::GEN_MIPMAPPABLE),
658+
featureFlags.assert_texture_can_generate_mipmap)
657659
<< "Texture usage does not have GEN_MIPMAPPABLE set";
658660

659661
if (mLevelCount < 2 || (mWidth == 1 && mHeight == 1)) {

libs/filamat/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ if (IS_HOST_PLATFORM)
162162
tests/test_filamat.cpp
163163
tests/test_argBufferFixup.cpp
164164
tests/test_clipDistanceFixup.cpp
165+
tests/test_line_dictionary.cpp
165166
tests/test_includes.cpp)
166167

167168
add_executable(${TARGET} ${SRCS})

libs/filamat/src/eiff/LineDictionary.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@
3333

3434
namespace filamat {
3535

36+
namespace {
37+
bool isWordChar(char const c) {
38+
// Note: isalnum is locale-dependent, which can be problematic.
39+
// For our purpose, we define word characters as ASCII alphanumeric characters plus underscore.
40+
// This is safe for UTF-8 strings, as any byte of a multi-byte character will not be
41+
// in these ranges.
42+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
43+
}
44+
} // anonymous namespace
45+
3646
LineDictionary::LineDictionary() = default;
3747

3848
LineDictionary::~LineDictionary() noexcept {
@@ -43,16 +53,15 @@ std::string const& LineDictionary::getString(index_t const index) const noexcept
4353
return *mStrings[index];
4454
}
4555

46-
size_t LineDictionary::getDictionaryLineCount() const {
47-
return mStrings.size();
48-
}
4956
std::vector<LineDictionary::index_t> LineDictionary::getIndices(
5057
std::string_view const& line) const noexcept {
5158
std::vector<index_t> result;
5259
std::vector<std::string_view> const sublines = splitString(line);
5360
for (std::string_view const& subline : sublines) {
5461
if (auto iter = mLineIndices.find(subline); iter != mLineIndices.end()) {
5562
result.push_back(iter->second.index);
63+
} else {
64+
return {};
5665
}
5766
}
5867
return result;
@@ -108,7 +117,7 @@ std::pair<size_t, size_t> LineDictionary::findPattern(
108117
const size_t line_len = line.length();
109118
for (size_t i = offset; i < line_len; ++i) {
110119
// A pattern must be a whole word (or at the start of the string).
111-
if (i > 0 && std::isalnum(line[i - 1])) {
120+
if (i > 0 && isWordChar(line[i - 1])) {
112121
continue;
113122
}
114123

@@ -121,6 +130,12 @@ std::pair<size_t, size_t> LineDictionary::findPattern(
121130
while (j < line_len && (j < startOfDigits + 6) && std::isdigit(line[j])) {
122131
j++;
123132
}
133+
// We have a potential match (prefix + digits).
134+
// Check if the character after the match is also a word character.
135+
if (j < line_len && isWordChar(line[j])) {
136+
// It is, so this is not a valid boundary. Continue searching.
137+
break; // breaks from the inner loop (kPatterns)
138+
}
124139
// We have a full pattern match (prefix + digits).
125140
return { i, j - i };
126141
}

libs/filamat/src/eiff/LineDictionary.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ class LineDictionary {
4646
void addText(std::string_view text) noexcept;
4747

4848
// Returns the total number of unique lines stored in the dictionary.
49-
size_t getDictionaryLineCount() const;
49+
size_t getDictionaryLineCount() const {
50+
return mStrings.size();
51+
}
5052

5153
// Checks if the dictionary is empty.
5254
bool isEmpty() const noexcept {
@@ -62,6 +64,19 @@ class LineDictionary {
6264
// Prints statistics about the dictionary to the given output stream.
6365
void printStatistics(utils::io::ostream& stream) const noexcept;
6466

67+
// conveniences...
68+
size_t size() const {
69+
return getDictionaryLineCount();
70+
}
71+
72+
bool empty() const noexcept {
73+
return isEmpty();
74+
}
75+
76+
std::string const& operator[](index_t const index) const noexcept {
77+
return getString(index);
78+
}
79+
6580
private:
6681
// Adds a single line to the dictionary.
6782
void addLine(std::string_view line) noexcept;
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <gtest/gtest.h>
18+
19+
#include "eiff/LineDictionary.h"
20+
21+
#include <string>
22+
23+
using namespace filamat;
24+
25+
TEST(LineDictionary, splitString) {
26+
LineDictionary dictionary;
27+
const std::string text = "first line hp_copy_123456 second line";
28+
dictionary.addText(text);
29+
EXPECT_EQ(dictionary.size(), 3);
30+
EXPECT_EQ(dictionary[0], "first line ");
31+
EXPECT_EQ(dictionary[1], "hp_copy_123456");
32+
EXPECT_EQ(dictionary[2], " second line");
33+
}
34+
35+
TEST(LineDictionary, Empty) {
36+
LineDictionary const dictionary;
37+
EXPECT_TRUE(dictionary.empty());
38+
EXPECT_EQ(dictionary.size(), 0);
39+
}
40+
41+
TEST(LineDictionary, AddTextSimple) {
42+
LineDictionary dictionary;
43+
dictionary.addText("Hello world\n");
44+
EXPECT_FALSE(dictionary.empty());
45+
EXPECT_EQ(dictionary.size(), 1);
46+
EXPECT_EQ(dictionary[0], "Hello world\n");
47+
}
48+
49+
TEST(LineDictionary, AddTextMultipleLines) {
50+
LineDictionary dictionary;
51+
dictionary.addText("First line\nSecond line\n");
52+
EXPECT_EQ(dictionary.size(), 2);
53+
EXPECT_EQ(dictionary[0], "First line\n");
54+
EXPECT_EQ(dictionary[1], "Second line\n");
55+
}
56+
57+
TEST(LineDictionary, AddTextDuplicateLines) {
58+
LineDictionary dictionary;
59+
dictionary.addText("Same line\nSame line\n");
60+
EXPECT_EQ(dictionary.size(), 1);
61+
EXPECT_EQ(dictionary[0], "Same line\n");
62+
}
63+
64+
TEST(LineDictionary, GetIndices) {
65+
LineDictionary dictionary;
66+
dictionary.addText("Line one\nLine two\nLine one\n");
67+
auto const indicesOne = dictionary.getIndices("Line one\n");
68+
ASSERT_EQ(indicesOne.size(), 1);
69+
EXPECT_EQ(indicesOne[0], 0);
70+
71+
auto const indicesTwo = dictionary.getIndices("Line two\n");
72+
ASSERT_EQ(indicesTwo.size(), 1);
73+
EXPECT_EQ(indicesTwo[0], 1);
74+
}
75+
76+
TEST(LineDictionary, SplitLogicNoPattern) {
77+
LineDictionary dictionary;
78+
dictionary.addText("A simple line with no patterns.");
79+
EXPECT_EQ(dictionary.size(), 1);
80+
EXPECT_EQ(dictionary[0], "A simple line with no patterns.");
81+
}
82+
83+
TEST(LineDictionary, SplitLogicHpPattern) {
84+
LineDictionary dictionary;
85+
dictionary.addText("some_var = hp_copy_123;");
86+
EXPECT_EQ(dictionary.size(), 3);
87+
EXPECT_EQ(dictionary[0], "some_var = ");
88+
EXPECT_EQ(dictionary[1], "hp_copy_123");
89+
EXPECT_EQ(dictionary[2], ";");
90+
}
91+
92+
TEST(LineDictionary, SplitLogicMpPattern) {
93+
LineDictionary dictionary;
94+
dictionary.addText("another_var = mp_copy_4567;");
95+
EXPECT_EQ(dictionary.size(), 3);
96+
EXPECT_EQ(dictionary[0], "another_var = ");
97+
EXPECT_EQ(dictionary[1], "mp_copy_4567");
98+
EXPECT_EQ(dictionary[2], ";");
99+
}
100+
101+
TEST(LineDictionary, SplitLogicUnderscorePattern) {
102+
LineDictionary dictionary;
103+
dictionary.addText("var_1 = 0;");
104+
EXPECT_EQ(dictionary.size(), 1);
105+
EXPECT_EQ(dictionary[0], "var_1 = 0;");
106+
}
107+
108+
TEST(LineDictionary, SplitLogicMultiplePatterns) {
109+
LineDictionary dictionary;
110+
dictionary.addText("hp_copy_1 mp_copy_2 _3");
111+
EXPECT_EQ(dictionary.size(), 4);
112+
EXPECT_EQ(dictionary[0], "hp_copy_1");
113+
EXPECT_EQ(dictionary[1], " ");
114+
EXPECT_EQ(dictionary[2], "mp_copy_2");
115+
EXPECT_EQ(dictionary[3], "_3");
116+
}
117+
118+
TEST(LineDictionary, SplitLogicInvalidPattern) {
119+
LineDictionary dictionary;
120+
dictionary.addText("hp_copy_ a_b_c");
121+
EXPECT_EQ(dictionary.size(), 1);
122+
EXPECT_EQ(dictionary[0], "hp_copy_ a_b_c");
123+
}
124+
125+
TEST(LineDictionary, SplitLogicPatternFollowedByWordChar) {
126+
LineDictionary dictionary;
127+
dictionary.addText("hp_copy_99rest");
128+
EXPECT_EQ(dictionary.size(), 1);
129+
EXPECT_EQ(dictionary[0], "hp_copy_99rest");
130+
}
131+
132+
TEST(LineDictionary, SplitLogicPatternPrecededByWordChar) {
133+
LineDictionary dictionary;
134+
dictionary.addText("rest_of_it_hp_copy_99");
135+
EXPECT_EQ(dictionary.size(), 1);
136+
EXPECT_EQ(dictionary[0], "rest_of_it_hp_copy_99");
137+
}
138+
139+
TEST(LineDictionary, SplitLogicPatternNotFollowedByWordChar) {
140+
LineDictionary dictionary;
141+
dictionary.addText("hp_copy_99;");
142+
EXPECT_EQ(dictionary.size(), 2);
143+
EXPECT_EQ(dictionary[0], "hp_copy_99");
144+
EXPECT_EQ(dictionary[1], ";");
145+
}
146+
147+
TEST(LineDictionary, AddEmptyText) {
148+
LineDictionary dictionary;
149+
dictionary.addText("");
150+
EXPECT_TRUE(dictionary.empty());
151+
}
152+
153+
TEST(LineDictionary, GetIndicesMultiple) {
154+
LineDictionary dictionary;
155+
dictionary.addText("A _1 B _2");
156+
auto const indices = dictionary.getIndices("A _1");
157+
ASSERT_EQ(indices.size(), 2);
158+
EXPECT_EQ(indices[0], 0);
159+
EXPECT_EQ(indices[1], 1);
160+
}
161+
162+
TEST(LineDictionary, GetIndicesMultiplePatternsInARow) {
163+
LineDictionary dictionary;
164+
dictionary.addText("hp_copy_1 hp_copy_2");
165+
auto const indices = dictionary.getIndices("hp_copy_1 hp_copy_2");
166+
ASSERT_EQ(indices.size(), 3);
167+
EXPECT_EQ(indices[0], 0);
168+
EXPECT_EQ(indices[1], 1);
169+
EXPECT_EQ(indices[2], 2);
170+
}
171+
172+
TEST(LineDictionary, GetIndicesSamePatternMultipleTimes) {
173+
LineDictionary dictionary;
174+
dictionary.addText("hp_copy_1 hp_copy_1");
175+
auto const indices = dictionary.getIndices("hp_copy_1 hp_copy_1");
176+
ASSERT_EQ(indices.size(), 3);
177+
EXPECT_EQ(indices[0], 0);
178+
EXPECT_EQ(indices[1], 1);
179+
EXPECT_EQ(indices[2], 0);
180+
}
181+
182+
TEST(LineDictionary, GetIndicesWithExistingDictionary) {
183+
LineDictionary dictionary;
184+
dictionary.addText("unrelated_string");
185+
dictionary.addText("hp_copy_1");
186+
dictionary.addText("another_string");
187+
dictionary.addText(" ");
188+
auto const indices = dictionary.getIndices("hp_copy_1 hp_copy_1");
189+
ASSERT_EQ(indices.size(), 3);
190+
EXPECT_EQ(indices[0], 1);
191+
EXPECT_EQ(indices[1], 3);
192+
EXPECT_EQ(indices[2], 1);
193+
}
194+
195+
TEST(LineDictionary, GetIndicesWithAdjacentPatterns) {
196+
LineDictionary dictionary;
197+
dictionary.addText("hp_copy_1hp_copy_2");
198+
auto const indices = dictionary.getIndices("hp_copy_1hp_copy_2");
199+
ASSERT_EQ(indices.size(), 1);
200+
EXPECT_EQ(indices[0], 0);
201+
}
202+
203+
TEST(LineDictionary, GetIndicesWithAdjacentPatternsNotInDictionary) {
204+
LineDictionary dictionary;
205+
dictionary.addText("hp_copy_1");
206+
dictionary.addText("hp_copy_2");
207+
auto const indices = dictionary.getIndices("hp_copy_1hp_copy_2");
208+
ASSERT_EQ(indices.size(), 0);
209+
}
210+
211+
TEST(LineDictionary, GetIndicesWithMixedContent) {
212+
LineDictionary dictionary;
213+
dictionary.addText("hp_copy_1");
214+
dictionary.addText(" ");
215+
dictionary.addText("mp_copy_2");
216+
217+
// The query string contains patterns that are in the dictionary,
218+
// but also content that is not.
219+
auto const indices = dictionary.getIndices("prefix hp_copy_1 mp_copy_2 suffix");
220+
221+
// Since not all substrings of the query string are in the dictionary,
222+
// getIndices should return an empty vector.
223+
ASSERT_EQ(indices.size(), 0);
224+
}

0 commit comments

Comments
 (0)