Skip to content

Commit 24c25dc

Browse files
committed
pull master & resolve conflicts
2 parents b3b1e32 + c92a34c commit 24c25dc

File tree

3 files changed

+127
-56
lines changed

3 files changed

+127
-56
lines changed

include/nbl/builtin/hlsl/text_rendering/msdf.hlsl

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@ This can then be processed using smoothstep to provide anti-alising when renderi
2323
@params:
2424
- msdfSample: sampled SNORM value from the MSDF texture generated by msdfgen library.
2525
26-
- msdfPixelRange: specifies the width of the range around the shape between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.
27-
for example if msdfPixelRange is 4, then the range of distance values are [-2, +2], and it can be computed by snormValue * MSDFPixelRange/2.0
28-
so an snorm value of 1.0 means a distance of 2 pixels outside the shape (in msdf texture space)
29-
This value is set when rendering the MSDF with MSDFgen.
30-
31-
- screenPxRangeValue: the value used to convert the distance values in the msdf texture/atlas to distance in screen space.
32-
In other words it's DistanceInScreenSpace/DistanceInMSDFTextureSpace, the larger the glyph (or more zoomed in) the larger this value is.
33-
In 2D Text Rendering it is computed by `GlyphScreenSpaceSize/GlyphTextureSpaceSize`
34-
where GlyphTextureSpaceSize is the size of the glyph inside the msdf texture/atlas
26+
- pixelRange = msdfPixelRange * screenPxRangeValue, It is the value to convert snorm distance to screen space distance
27+
- msdfPixelRange: specifies the width of the range around the shape between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.
28+
for example if msdfPixelRange is 4, then the range of distance values are [-2, +2], and it can be computed by snormValue * MSDFPixelRange/2.0
29+
so an snorm value of 1.0 means a distance of 2 pixels outside the shape (in msdf texture space)
30+
This value is set when rendering the MSDF with MSDFgen.
31+
- screenPxRangeValue: the value used to convert the distance values in the msdf texture/atlas to distance in screen space.
32+
In other words it's DistanceInScreenSpace/DistanceInMSDFTextureSpace, the larger the glyph (or more zoomed in) the larger this value is.
33+
In 2D Text Rendering it is computed by `GlyphScreenSpaceSize/GlyphTextureSpaceSize`
34+
where GlyphTextureSpaceSize is the size of the glyph inside the msdf texture/atlas
3535
*/
36-
float msdfDistance(float3 msdfSample, float msdfPixelRange, float screenPxRangeValue) {
37-
float texelDist = median(msdfSample.r, msdfSample.g, msdfSample.b) * msdfPixelRange / 2.0f;
38-
return texelDist * screenPxRangeValue;
36+
float msdfDistance(float3 msdfSample, float pixelRange) {
37+
return median(msdfSample.r, msdfSample.g, msdfSample.b) * pixelRange / 2.0f;
3938
}
4039

4140
}

include/nbl/ext/TextRendering/TextRendering.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,15 @@ class TextRenderer : public nbl::core::IReferenceCounted
3434

3535
static constexpr asset::E_FORMAT MSDFTextureFormat = asset::E_FORMAT::EF_R8G8B8A8_SNORM;
3636

37-
// Spits out CPUBuffer containing the image data in SNORM format
38-
core::smart_refctd_ptr<ICPUBuffer> generateShapeMSDF(msdfgen::Shape glyph, uint32_t msdfPixelRange, uint32_t2 msdfExtents, float32_t2 scale, float32_t2 translate);
37+
// Takes the CPUBuffer containing the image data in SNORM format and an offset into it
38+
void generateShapeMSDF(
39+
ICPUBuffer* bufferToFill,
40+
size_t* bufferOffset,
41+
msdfgen::Shape glyph,
42+
float32_t msdfPixelRange,
43+
uint32_t2 msdfExtents,
44+
float32_t2 scale,
45+
float32_t2 translate);
3946

4047
TextRenderer()
4148
{
@@ -119,7 +126,7 @@ class FontFace : public nbl::core::IReferenceCounted
119126
// it will place the glyph in the center of msdfExtents considering the margin of msdfPixelRange
120127
// preserves aspect ratio of the glyph corresponding to metrics of the "glyphId"
121128
// use the `getUV` to address the glyph in your texture correctly.
122-
core::smart_refctd_ptr<ICPUBuffer> generateGlyphMSDF(uint32_t msdfPixelRange, uint32_t glyphId, uint32_t2 textureExtents);
129+
core::smart_refctd_ptr<ICPUImage> generateGlyphMSDF(uint32_t baseMSDFPixelRange, uint32_t glyphId, uint32_t2 textureExtents, uint32_t mipLevels);
123130

124131
// transforms uv in glyph space to uv in the actual texture
125132
float32_t2 getUV(float32_t2 uv, float32_t2 glyphSize, uint32_t2 textureExtents, uint32_t msdfPixelRange);

src/nbl/ext/TextRendering/TextRendering.cpp

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,52 @@ namespace ext
1717
namespace TextRendering
1818
{
1919

20-
core::smart_refctd_ptr<ICPUBuffer> TextRenderer::generateShapeMSDF(msdfgen::Shape glyph, uint32_t msdfPixelRange, uint32_t2 msdfExtents, float32_t2 scale, float32_t2 translate)
20+
void TextRenderer::generateShapeMSDF(
21+
ICPUBuffer* bufferToFill,
22+
size_t* bufferOffset,
23+
msdfgen::Shape glyph,
24+
float32_t msdfPixelRange,
25+
uint32_t2 msdfExtents,
26+
float32_t2 scale,
27+
float32_t2 translate)
2128
{
2229
uint32_t glyphW = msdfExtents.x;
2330
uint32_t glyphH = msdfExtents.y;
2431

25-
auto shapeBounds = glyph.getBounds();
32+
size_t& offset = *bufferOffset;
33+
size_t bufferSize = glyphW * glyphH * sizeof(int8_t) * 4;
34+
assert(bufferToFill->getSize() >= bufferSize + offset);
35+
36+
int8_t* data = reinterpret_cast<int8_t*>(bufferToFill->getPointer());
37+
38+
auto floatToSNORM8 = [](const float fl) -> int8_t
39+
{
40+
// we need to invert values because msdfgen assigns positive values for shape interior which is the exact opposite of our convention
41+
return -1 * (int8_t)(std::clamp(fl * 2.0f - 1.0f, -1.0f, 1.0f) * 127.f);
42+
};
2643

2744
msdfgen::edgeColoringSimple(glyph, 3.0);
28-
msdfgen::Bitmap<float, 4> msdfMap(glyphW, glyphH);
2945

30-
msdfgen::generateMTSDF(msdfMap, glyph, msdfPixelRange, { scale.x, scale.y }, { translate.x, translate.y });
46+
auto shapeBounds = glyph.getBounds();
3147

32-
auto cpuBuf = core::make_smart_refctd_ptr<ICPUBuffer>(glyphW * glyphH * sizeof(int8_t) * 4);
33-
int8_t* data = reinterpret_cast<int8_t*>(cpuBuf->getPointer());
48+
msdfgen::Bitmap<float, 4> msdfMap(msdfExtents.x, msdfExtents.y);
3449

35-
auto floatToSNORM8 = [](const float fl) -> int8_t
36-
{
37-
// we need to invert values because msdfgen assigns positive values for shape interior which is the exact opposite of our convention
38-
return -1 * (int8_t)(std::clamp(fl * 2.0f - 1.0f, -1.0f, 1.0f) * 127.f);
39-
};
50+
float32_t pxRange = msdfPixelRange / (min(scale.x, scale.y));
51+
msdfgen::generateMTSDF(msdfMap, glyph, pxRange, { scale.x, scale.y }, { translate.x, translate.y });
4052

41-
for (int y = 0; y < msdfMap.height(); ++y)
53+
for (int y = 0; y < msdfExtents.x; ++y)
4254
{
43-
for (int x = 0; x < msdfMap.width(); ++x)
55+
for (int x = 0; x < msdfExtents.y; ++x)
4456
{
45-
auto pixel = msdfMap(x, glyphH - 1 - y);
46-
data[(x + y * glyphW) * 4 + 0] = floatToSNORM8(pixel[0]);
47-
data[(x + y * glyphW) * 4 + 1] = floatToSNORM8(pixel[1]);
48-
data[(x + y * glyphW) * 4 + 2] = floatToSNORM8(pixel[2]);
49-
data[(x + y * glyphW) * 4 + 3] = floatToSNORM8(pixel[3]);
57+
auto pixel = msdfMap(x, msdfExtents.y - 1 - y);
58+
data[offset + (x + y * msdfExtents.x) * 4 + 0] = floatToSNORM8(pixel[0]);
59+
data[offset + (x + y * msdfExtents.x) * 4 + 1] = floatToSNORM8(pixel[1]);
60+
data[offset + (x + y * msdfExtents.x) * 4 + 2] = floatToSNORM8(pixel[2]);
61+
data[offset + (x + y * msdfExtents.x) * 4 + 3] = floatToSNORM8(pixel[3]);
5062
}
5163
}
5264

53-
return std::move(cpuBuf);
65+
offset += bufferSize;
5466
}
5567

5668
constexpr double FreeTypeFontScaling = 1.0 / 64.0;
@@ -75,33 +87,86 @@ FontFace::GlyphMetrics FontFace::getGlyphMetrics(uint32_t glyphId)
7587
};
7688
}
7789

78-
core::smart_refctd_ptr<ICPUBuffer> FontFace::generateGlyphMSDF(uint32_t msdfPixelRange, uint32_t glyphId, uint32_t2 textureExtents)
90+
core::smart_refctd_ptr<ICPUImage> FontFace::generateGlyphMSDF(uint32_t baseMSDFPixelRange, uint32_t glyphId, uint32_t2 textureExtents, uint32_t mipLevels)
7991
{
80-
auto shape = generateGlyphShape(glyphId);
92+
ICPUImage::SCreationParams imgParams;
93+
{
94+
imgParams.flags = static_cast<ICPUImage::E_CREATE_FLAGS>(0u); // no flags
95+
imgParams.type = ICPUImage::ET_2D;
96+
imgParams.format = TextRenderer::MSDFTextureFormat;
97+
imgParams.extent = { textureExtents.x, textureExtents.y, 1 };
98+
imgParams.mipLevels = mipLevels;
99+
imgParams.arrayLayers = 1u;
100+
imgParams.samples = ICPUImage::ESCF_1_BIT;
101+
}
81102

82-
// Empty shapes should've been filtered sooner
83-
assert(!shape.contours.empty());
103+
uint32_t bufferSize = 0u;
104+
for (uint32_t i = 0; i < mipLevels; i++)
105+
{
106+
uint32_t mipW = textureExtents.x / (1 << i);
107+
uint32_t mipH = textureExtents.y / (1 << i);
108+
bufferSize += mipW * mipH * sizeof(uint8_t) * 4;
109+
}
84110

85-
auto shapeBounds = shape.getBounds();
111+
auto image = ICPUImage::create(std::move(imgParams));
112+
auto buffer = core::make_smart_refctd_ptr<ICPUBuffer>(bufferSize);
113+
auto regions = core::make_refctd_dynamic_array<core::smart_refctd_dynamic_array<IImage::SBufferCopy>>(mipLevels);
86114

87-
float32_t2 frameSize = float32_t2(
88-
(shapeBounds.r - shapeBounds.l),
89-
(shapeBounds.t - shapeBounds.b)
90-
);
115+
size_t bufferOffset = 0ull;
116+
for (uint32_t i = 0; i < mipLevels; i++)
117+
{
118+
// we need to generate a msdfgen per mip map, because the msdf generate call consumes the shape
119+
// and we can't deep clone it
120+
auto shape = generateGlyphShape(glyphId);
121+
122+
// Empty shapes should've been filtered sooner
123+
assert(!shape.contours.empty());
124+
125+
uint32_t mipW = textureExtents.x / (1 << i);
126+
uint32_t mipH = textureExtents.y / (1 << i);
127+
128+
auto& region = regions->begin()[i];
129+
region.bufferOffset = bufferOffset;
130+
region.bufferRowLength = mipW;
131+
region.bufferImageHeight = mipH;
132+
region.imageSubresource.aspectMask = asset::IImage::E_ASPECT_FLAGS::EAF_COLOR_BIT;
133+
region.imageSubresource.mipLevel = i;
134+
region.imageSubresource.baseArrayLayer = 0u;
135+
region.imageSubresource.layerCount = 1u;
136+
region.imageOffset = { 0u,0u,0u };
137+
region.imageExtent = { mipW, mipH, 1u };
138+
139+
auto shapeBounds = shape.getBounds();
140+
141+
float32_t2 mipExtents = float32_t2(float(mipW), float(mipH));
142+
float32_t mipPixelRange = (float32_t)baseMSDFPixelRange / pow(2.0, double(i)); // TODO: Pixel range should be float
143+
144+
float32_t2 frameSize = float32_t2(
145+
(shapeBounds.r - shapeBounds.l),
146+
(shapeBounds.t - shapeBounds.b)
147+
);
148+
149+
const float32_t2 margin = float32_t2(mipPixelRange * 2);
150+
const float32_t2 nonUniformScale = (mipExtents - margin) / frameSize;
151+
const float32_t uniformScale = core::min(nonUniformScale.x, nonUniformScale.y);
152+
153+
// Center before: ((shapeBounds.l + shapeBounds.r) * 0.5, (shapeBounds.t + shapeBounds.b) * 0.5)
154+
// Center after: msdfExtents / 2.0
155+
// Transformation implementation: Center after = (Center before + Translation) * Scale
156+
// Plugging in the values and solving for translate yields:
157+
// Translate = (msdfExtents / (2 * scale)) - ((shapeBounds.l + shapeBounds.r) * 0.5, (shapeBounds.t + shapeBounds.b) * 0.5)
158+
const float32_t2 shapeSpaceCenter = float32_t2(shapeBounds.l + shapeBounds.r, shapeBounds.t + shapeBounds.b) * float32_t2(0.5);
159+
const float32_t2 translate = mipExtents / (float32_t2(2.0) * uniformScale) - shapeSpaceCenter;
160+
161+
// We are using `baseMSDFPixelRange`, because we still need larger range for smaller mips when aa feather is relatively large.
162+
// WARNING: HWTrilinear filtering will not give correct results.
163+
//because now the baseMSDFPixelRange is being used for all mips as it's wrong to mix/lerp values that have different scales (pixel ranges)
164+
m_textRenderer->generateShapeMSDF(buffer.get(), &bufferOffset, shape, baseMSDFPixelRange, mipExtents, float32_t2(uniformScale, uniformScale), translate);
165+
}
166+
assert(bufferOffset <= buffer->getCreationParams().size);
167+
image->setBufferAndRegions(std::move(buffer), std::move(regions));
91168

92-
const float32_t2 margin = float32_t2(msdfPixelRange * 2.0f);
93-
const float32_t2 nonUniformScale = (float32_t2(textureExtents) - margin) / frameSize;
94-
const float32_t uniformScale = core::min(nonUniformScale.x, nonUniformScale.y);
95-
96-
// Center before: ((shapeBounds.l + shapeBounds.r) * 0.5, (shapeBounds.t + shapeBounds.b) * 0.5)
97-
// Center after: msdfExtents / 2.0
98-
// Transformation implementation: Center after = (Center before + Translation) * Scale
99-
// Plugging in the values and solving for translate yields:
100-
// Translate = (msdfExtents / (2 * scale)) - ((shapeBounds.l + shapeBounds.r) * 0.5, (shapeBounds.t + shapeBounds.b) * 0.5)
101-
const float32_t2 shapeSpaceCenter = float32_t2(shapeBounds.l + shapeBounds.r, shapeBounds.t + shapeBounds.b) * float32_t2(0.5);
102-
const float32_t2 translate = float32_t2(textureExtents) / (float32_t2(2.0) * uniformScale) - shapeSpaceCenter;
103-
104-
return m_textRenderer->generateShapeMSDF(shape, msdfPixelRange, textureExtents, float32_t2(uniformScale, uniformScale), translate);
169+
return std::move(image);
105170
}
106171

107172
float32_t2 FontFace::getUV(float32_t2 uv, float32_t2 glyphSize, uint32_t2 textureExtents, uint32_t msdfPixelRange)

0 commit comments

Comments
 (0)