Browse Source

Text: flush glyph cache image including padding.

Otherwise the texture may retain garbage data from previous GPU
allocations, causing random artifacts in rendered output.
pull/638/head
Vladimír Vondruš 2 years ago
parent
commit
57ca69bc29
  1. 13
      src/Magnum/Text/AbstractGlyphCache.cpp
  2. 13
      src/Magnum/Text/AbstractGlyphCache.h
  3. 18
      src/Magnum/Text/DistanceFieldGlyphCache.cpp
  4. 292
      src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp
  5. 36
      src/Magnum/Text/Test/GlyphCacheGLTest.cpp

13
src/Magnum/Text/AbstractGlyphCache.cpp

@ -380,18 +380,25 @@ void AbstractGlyphCache::flushImage(const Range3Di& range) {
CORRADE_ASSERT((rangeu.min() <= rangeu.max()).all() && (rangeu.max() <= Vector3ui{state.image.size()}).all(), CORRADE_ASSERT((rangeu.min() <= rangeu.max()).all() && (rangeu.max() <= Vector3ui{state.image.size()}).all(),
"Text::AbstractGlyphCache::flushImage():" << Debug::packed << range << "out of range for size" << Debug::packed << state.image.size(), ); "Text::AbstractGlyphCache::flushImage():" << Debug::packed << range << "out of range for size" << Debug::packed << state.image.size(), );
/* Set the image including padding, to make sure the sampled glyph area
doesn't contain potentially uninitialized GPU memory */
const Vector3i paddedMin = Math::max(Vector3i{0},
range.min() - Vector3i{padding(), 0});
const Vector3i paddedMax = Math::min(size(),
range.max() + Vector3i{padding(), 0});
/** @todo ugh have slicing on images directly already */ /** @todo ugh have slicing on images directly already */
PixelStorage storage; PixelStorage storage;
storage.setRowLength(state.image.size().x()) storage.setRowLength(state.image.size().x())
.setSkip(range.min()); .setSkip(paddedMin);
/* Set image height only if it's an array glyph cache, as otherwise it'd /* Set image height only if it's an array glyph cache, as otherwise it'd
cause errors on ES2 that doesn't support this pixel storage state */ cause errors on ES2 that doesn't support this pixel storage state */
if(state.image.size().z() != 1) if(state.image.size().z() != 1)
storage.setImageHeight(state.image.size().y()); storage.setImageHeight(state.image.size().y());
doSetImage(range.min(), ImageView3D{ doSetImage(paddedMin, ImageView3D{
storage, storage,
state.image.format(), state.image.format(),
range.size(), paddedMax - paddedMin,
state.image.data()}); state.image.data()});
} }

13
src/Magnum/Text/AbstractGlyphCache.h

@ -648,10 +648,15 @@ class MAGNUM_TEXT_EXPORT AbstractGlyphCache {
* @m_since_latest * @m_since_latest
* *
* Call after copying glyph data to @ref image() in order to reflect * Call after copying glyph data to @ref image() in order to reflect
* the updates to the GPU-side data. The @p layer and @p range is * the updates to the GPU-side data. The @p range is expected to be in
* expected to be in bounds for @ref size(). You can use * bounds for @ref size(). You can use @ref Math::join() on rectangles
* @ref Math::join() on rectangles passed to @ref addGlyph() to * passed to @ref addGlyph() to calculate the area that spans all
* calculate the area that spans all glyphs that were added. * glyphs that were added.
*
* The function assumes the @p range excludes @ref padding(). The image
* data get copied to the GPU including the padding to make sure the
* padded glyph area doesn't contain leftovers of uninitialized GPU
* memory.
*/ */
void flushImage(const Range3Di& range); void flushImage(const Range3Di& range);

18
src/Magnum/Text/DistanceFieldGlyphCache.cpp

@ -112,24 +112,14 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView
#endif #endif
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
{ {
/* Create an image view that includes the distance field radius as /* The image range was already expanded to include the padding in
well, to be sure the edges are processed appropriately as well */ flushImage() */
/** @todo this feels more like a hack than a real solution, any better
idea? Tried to make TextureTools::DistanceField pad the processing
area on its own, but eventually gave up as it would (a) made the
processing do more work than necessary in many cases, with no easy
way to opt out of that, (b) wasn't really working if the input
didn't have a black strip around the edge on platforms that don't
support border clamp, (c) required the input texture to have
certain wrapping mode set */
CORRADE_INTERNAL_ASSERT(image.storage().skip().xy() == offset); CORRADE_INTERNAL_ASSERT(image.storage().skip().xy() == offset);
#ifdef CORRADE_NO_ASSERT #ifdef CORRADE_NO_ASSERT
static_cast<void>(offset); static_cast<void>(offset);
#endif #endif
const Vector2i paddedMin = Math::max(Vector2i{0}, const Vector2i paddedMin = image.storage().skip().xy();
image.storage().skip().xy() - padding()); const Vector2i paddedMax = image.size() + image.storage().skip().xy();
const Vector2i paddedMax = Math::min(size().xy(),
image.size() + image.storage().skip().xy() + padding());
/* TextureTools::DistanceField expects the input size and output /* TextureTools::DistanceField expects the input size and output
rectangle size ratio to be a multiple of 2 in order for the shader rectangle size ratio to be a multiple of 2 in order for the shader

292
src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp

@ -140,6 +140,14 @@ struct AbstractGlyphCacheTest: TestSuite::Tester {
#endif #endif
}; };
const struct {
const char* name;
Vector2i padding;
} FlushImageData[]{
{"", {}},
{"with padding", {2, 3}},
};
const struct { const struct {
const char* name; const char* name;
GlyphCacheFeatures features; GlyphCacheFeatures features;
@ -205,25 +213,33 @@ AbstractGlyphCacheTest::AbstractGlyphCacheTest() {
&AbstractGlyphCacheTest::insertNot2D, &AbstractGlyphCacheTest::insertNot2D,
&AbstractGlyphCacheTest::insertMultiFont, &AbstractGlyphCacheTest::insertMultiFont,
#endif #endif
});
&AbstractGlyphCacheTest::flushImage, addInstancedTests({&AbstractGlyphCacheTest::flushImage,
&AbstractGlyphCacheTest::flushImageWholeArea, &AbstractGlyphCacheTest::flushImageWholeArea,
&AbstractGlyphCacheTest::flushImageLayer, &AbstractGlyphCacheTest::flushImageLayer,
&AbstractGlyphCacheTest::flushImage2D, &AbstractGlyphCacheTest::flushImage2D,
&AbstractGlyphCacheTest::flushImage2DPassthrough2D, &AbstractGlyphCacheTest::flushImage2DPassthrough2D},
&AbstractGlyphCacheTest::flushImageNotImplemented, Containers::arraySize(FlushImageData));
&AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented,
&AbstractGlyphCacheTest::flushImageOutOfRange,
&AbstractGlyphCacheTest::flushImage2DNot2D,
#ifdef MAGNUM_BUILD_DEPRECATED addTests({&AbstractGlyphCacheTest::flushImageNotImplemented,
&AbstractGlyphCacheTest::setImage, &AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented});
&AbstractGlyphCacheTest::setImageOutOfRange,
addInstancedTests({&AbstractGlyphCacheTest::flushImageOutOfRange},
Containers::arraySize(FlushImageData));
addTests({&AbstractGlyphCacheTest::flushImage2DNot2D});
#ifdef MAGNUM_BUILD_DEPRECATED
addInstancedTests({&AbstractGlyphCacheTest::setImage},
Containers::arraySize(FlushImageData));
addTests({&AbstractGlyphCacheTest::setImageOutOfRange,
&AbstractGlyphCacheTest::setImageInvalidFormat, &AbstractGlyphCacheTest::setImageInvalidFormat,
&AbstractGlyphCacheTest::setImageNot2D, &AbstractGlyphCacheTest::setImageNot2D});
#endif #endif
&AbstractGlyphCacheTest::processedImage}); addTests({&AbstractGlyphCacheTest::processedImage});
addInstancedTests({&AbstractGlyphCacheTest::processedImageNotSupported}, addInstancedTests({&AbstractGlyphCacheTest::processedImageNotSupported},
Containers::arraySize(ProcessedImageNotSupportedData)); Containers::arraySize(ProcessedImageNotSupportedData));
@ -1014,6 +1030,9 @@ void AbstractGlyphCacheTest::insertMultiFont() {
#endif #endif
void AbstractGlyphCacheTest::flushImage() { void AbstractGlyphCacheTest::flushImage() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache; using AbstractGlyphCache::AbstractGlyphCache;
@ -1021,27 +1040,56 @@ void AbstractGlyphCacheTest::flushImage() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override { void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true; called = true;
char pixels0[]{ CORRADE_COMPARE(offset, (Vector3i{15, 30, 3} - Vector3i{padding(), 0}));
'a', 'b', 'c', 0, CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 2} + Vector3i{2*padding(), 0}));
'd', 'e', 'f', 0,
}; if(padding().isZero()) {
char pixels1[]{ char pixels0[]{
'0', '1', '2', 0, 'a', 'b', 'c', 0,
'3', '4', '5', 0, 'd', 'e', 'f', 0,
}; };
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3})); char pixels1[]{
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 2})); '0', '1', '2', 0,
CORRADE_COMPARE_AS(image.pixels<Byte>()[0], '3', '4', '5', 0,
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels0}), };
DebugTools::CompareImage); CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
CORRADE_COMPARE_AS(image.pixels<Byte>()[1], (ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels0}),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels1}), DebugTools::CompareImage);
DebugTools::CompareImage); CORRADE_COMPARE_AS(image.pixels<Byte>()[1],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels1}),
DebugTools::CompareImage);
} else {
char pixels0[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
char pixels1[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, '0', '1', '2', 0, 0, 0,
0, 0, '3', '4', '5', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels0}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS(image.pixels<Byte>()[1],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels1}),
DebugTools::CompareImage);
}
} }
bool called = false; bool called = false;
/* Padding should have no effect on any of this */ } cache{PixelFormat::R8Snorm, {45, 35, 5}, data.padding};
} cache{PixelFormat::R8Snorm, {45, 35, 5}, {2, 3}};
/* Capture correct function name */ /* Capture correct function name */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
@ -1062,8 +1110,12 @@ void AbstractGlyphCacheTest::flushImage() {
} }
void AbstractGlyphCacheTest::flushImageWholeArea() { void AbstractGlyphCacheTest::flushImageWholeArea() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like above, but calling flushImage() with the whole size to test bounds /* Like above, but calling flushImage() with the whole size to test bounds
checking */ checking. The padding doesn't affect the call in this case -- the actual
range is always the whole image. */
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache; using AbstractGlyphCache::AbstractGlyphCache;
@ -1091,8 +1143,7 @@ void AbstractGlyphCacheTest::flushImageWholeArea() {
} }
bool called = false; bool called = false;
/* Padding should have no effect on any of this */ } cache{PixelFormat::R8Snorm, {3, 2, 2}, data.padding};
} cache{PixelFormat::R8Snorm, {3, 2, 2}, {2, 3}};
/* Capture correct function name */ /* Capture correct function name */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
@ -1113,6 +1164,9 @@ void AbstractGlyphCacheTest::flushImageWholeArea() {
} }
void AbstractGlyphCacheTest::flushImageLayer() { void AbstractGlyphCacheTest::flushImageLayer() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Single slice subset of flushImage() */ /* Single slice subset of flushImage() */
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
@ -1122,20 +1176,36 @@ void AbstractGlyphCacheTest::flushImageLayer() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override { void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true; called = true;
char pixels[]{ CORRADE_COMPARE(offset, (Vector3i{15, 30, 3} - Vector3i{padding(), 0}));
'a', 'b', 'c', 0, CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1} + Vector3i{2*padding(), 0}));
'd', 'e', 'f', 0,
}; if(padding().isZero()) {
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3})); char pixels[]{
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1})); 'a', 'b', 'c', 0,
CORRADE_COMPARE_AS(image.pixels<Byte>()[0], 'd', 'e', 'f', 0,
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}), };
DebugTools::CompareImage); CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
} }
bool called = false; bool called = false;
/* Padding should have no effect on any of this */ } cache{PixelFormat::R8Snorm, {45, 35, 5}, data.padding};
} cache{PixelFormat::R8Snorm, {45, 35, 5}, {2, 3}};
/* Capture correct function name */ /* Capture correct function name */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
@ -1153,6 +1223,9 @@ void AbstractGlyphCacheTest::flushImageLayer() {
} }
void AbstractGlyphCacheTest::flushImage2D() { void AbstractGlyphCacheTest::flushImage2D() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like flushImageLayer() but reduced to two dimensions */ /* Like flushImageLayer() but reduced to two dimensions */
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
@ -1162,20 +1235,36 @@ void AbstractGlyphCacheTest::flushImage2D() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override { void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true; called = true;
char pixels[]{ CORRADE_COMPARE(offset, (Vector3i{15, 30, 0} - Vector3i{padding(), 0}));
'a', 'b', 'c', 0, CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1} + Vector3i{2*padding(), 0}));
'd', 'e', 'f', 0,
}; if(padding().isZero()) {
CORRADE_COMPARE(offset, (Vector3i{15, 30, 0})); char pixels[]{
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1})); 'a', 'b', 'c', 0,
CORRADE_COMPARE_AS(image.pixels<Byte>()[0], 'd', 'e', 'f', 0,
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}), };
DebugTools::CompareImage); CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
} }
bool called = false; bool called = false;
/* Padding should have no effect on any of this */ } cache{PixelFormat::R8Snorm, {45, 35}, data.padding};
} cache{PixelFormat::R8Snorm, {45, 35}, {2, 3}};
/* Capture correct function name */ /* Capture correct function name */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
@ -1193,6 +1282,9 @@ void AbstractGlyphCacheTest::flushImage2D() {
} }
void AbstractGlyphCacheTest::flushImage2DPassthrough2D() { void AbstractGlyphCacheTest::flushImage2DPassthrough2D() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like flushImage2D() but with 2D doSetImage() */ /* Like flushImage2D() but with 2D doSetImage() */
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
@ -1202,20 +1294,36 @@ void AbstractGlyphCacheTest::flushImage2DPassthrough2D() {
void doSetImage(const Vector2i& offset, const ImageView2D& image) override { void doSetImage(const Vector2i& offset, const ImageView2D& image) override {
called = true; called = true;
char pixels[]{ CORRADE_COMPARE(offset, (Vector2i{15, 30}) - padding());
'a', 'b', 'c', 0, CORRADE_COMPARE(image.size(), (Vector2i{3, 2}) + 2*padding());
'd', 'e', 'f', 0,
}; if(padding().isZero()) {
CORRADE_COMPARE(offset, (Vector2i{15, 30})); char pixels[]{
CORRADE_COMPARE(image.size(), (Vector2i{3, 2})); 'a', 'b', 'c', 0,
CORRADE_COMPARE_AS(image.pixels<Byte>(), 'd', 'e', 'f', 0,
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}), };
DebugTools::CompareImage); CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
} }
bool called = false; bool called = false;
/* Padding should have no effect on any of this */ } cache{PixelFormat::R8Snorm, {45, 35}, data.padding};
} cache{PixelFormat::R8Snorm, {45, 35}, {2, 3}};
/* Capture correct function name */ /* Capture correct function name */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
@ -1271,10 +1379,13 @@ void AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented() {
} }
void AbstractGlyphCacheTest::flushImageOutOfRange() { void AbstractGlyphCacheTest::flushImageOutOfRange() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
/* The padding should have no effect on this */ /* The padding should not have any effect on the check */
DummyGlyphCache cache{PixelFormat::R32F, {1024, 512, 8}, {2, 3}}; DummyGlyphCache cache{PixelFormat::R32F, {1024, 512, 8}, data.padding};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
@ -1322,6 +1433,9 @@ void AbstractGlyphCacheTest::flushImage2DNot2D() {
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
void AbstractGlyphCacheTest::setImage() { void AbstractGlyphCacheTest::setImage() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
struct: AbstractGlyphCache { struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache; using AbstractGlyphCache::AbstractGlyphCache;
@ -1330,20 +1444,38 @@ void AbstractGlyphCacheTest::setImage() {
void doSetImage(const Vector2i& offset, const ImageView2D& image) override { void doSetImage(const Vector2i& offset, const ImageView2D& image) override {
called = true; called = true;
char pixels[]{ CORRADE_COMPARE(offset, (Vector2i{15, 30}) - padding());
'a', 'b', 'c', 0, CORRADE_COMPARE(image.size(), (Vector2i{3, 2}) + 2*padding());
'd', 'e', 'f', 0,
}; if(padding().isZero()) {
CORRADE_COMPARE(offset, (Vector2i{15, 30})); char pixels[]{
CORRADE_COMPARE_AS(image, 'a', 'b', 'c', 0,
(ImageView2D{PixelFormat::R8Unorm, {3, 2}, pixels}), 'd', 'e', 'f', 0,
DebugTools::CompareImage); };
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
} }
bool called = false; bool called = false;
/* Deliberately using the deprecated PixelFormat-less constructor to verify /* Deliberately using the deprecated PixelFormat-less constructor to verify
that passing a R8Unorm image "just works" */ that passing a R8Unorm image "just works" */
} cache{{45, 35}}; } cache{{45, 35}, data.padding};
CORRADE_IGNORE_DEPRECATED_POP CORRADE_IGNORE_DEPRECATED_POP
/* Capture correct function name */ /* Capture correct function name */

36
src/Magnum/Text/Test/GlyphCacheGLTest.cpp

@ -132,6 +132,11 @@ const UnsignedByte ExpectedData[]{
void GlyphCacheGLTest::setImage() { void GlyphCacheGLTest::setImage() {
GlyphCache cache{{16, 8}}; GlyphCache cache{{16, 8}};
/* Fill the texture with non-zero data to verify the padding gets uploaded
as well */
cache.texture().setSubImage(0, {}, Image2D{PixelFormat::R8Unorm, {16, 8}, Containers::Array<char>{DirectInit, 16*8, '\xcd'}});
MAGNUM_VERIFY_NO_GL_ERROR();
Utility::copy( Utility::copy(
Containers::StridedArrayView2D<const UnsignedByte>{InputData, {4, 8}}, Containers::StridedArrayView2D<const UnsignedByte>{InputData, {4, 8}},
cache.image().pixels<UnsignedByte>()[0].sliceSize({4, 8}, {4, 8})); cache.image().pixels<UnsignedByte>()[0].sliceSize({4, 8}, {4, 8}));
@ -143,24 +148,45 @@ void GlyphCacheGLTest::setImage() {
MutableImageView2D actual{actual3.format(), actual3.size().xy(), actual3.data()}; MutableImageView2D actual{actual3.format(), actual3.size().xy(), actual3.data()};
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
ImageView2D expected{PixelFormat::R8Unorm, {16, 8}, ExpectedData}; /* The CPU-side image is zero-initialized, what was set above in the
texture isn't present there */
CORRADE_COMPARE_AS(actual, CORRADE_COMPARE_AS(actual,
expected, (ImageView2D{PixelFormat::R8Unorm, {16, 8}, ExpectedData}),
DebugTools::CompareImage); DebugTools::CompareImage);
#ifdef MAGNUM_TARGET_GLES2 #ifdef MAGNUM_TARGET_GLES2
CORRADE_SKIP("Luminance format used on GLES2 isn't usable for framebuffer reading, can't verify texture contents."); CORRADE_SKIP("Luminance format used on GLES2 isn't usable for framebuffer reading, can't verify texture contents.");
#else #else
/* Verify the actual texture. It should be the same as above. On GLES we /* The actual texture has just the slice updated, the rest stays. On GLES
cannot really verify that the size matches, but at least something. */ we cannot really verify that the size matches, but at least
something. */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
Image2D image = cache.texture().image(0, {PixelFormat::R8Unorm}); Image2D image = cache.texture().image(0, {PixelFormat::R8Unorm});
#else #else
Image2D image = DebugTools::textureSubImage(cache.texture(), 0, {{}, {16, 8}}, {PixelFormat::R8Unorm}); Image2D image = DebugTools::textureSubImage(cache.texture(), 0, {{}, {16, 8}}, {PixelFormat::R8Unorm});
#endif #endif
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
const UnsignedByte expectedTextureData[]{
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
};
CORRADE_COMPARE_AS(image, CORRADE_COMPARE_AS(image,
expected, (ImageView2D{PixelFormat::R8Unorm, {16, 8}, expectedTextureData}),
DebugTools::CompareImage); DebugTools::CompareImage);
#endif #endif
} }

Loading…
Cancel
Save