Mercurial > games > semicongine
changeset 1409:5a56f8ac328b
did: improve descriptor-set handling + last fixes for new font/text rendering api
author | sam <sam@basx.dev> |
---|---|
date | Mon, 23 Dec 2024 00:32:07 +0700 |
parents | 17d960ff6a24 |
children | 99d5b42cf32d |
files | semicongine/rendering/shaders.nim semicongine/text.nim semicongine/text/font.nim tests/test_text.nim |
diffstat | 4 files changed, 108 insertions(+), 110 deletions(-) [+] |
line wrap: on
line diff
--- a/semicongine/rendering/shaders.nim Sun Dec 22 22:32:12 2024 +0700 +++ b/semicongine/rendering/shaders.nim Mon Dec 23 00:32:07 2024 +0700 @@ -472,6 +472,17 @@ nil, addr(result[value.getCustomPragmaVal(DescriptorSet)]), ) + # create empty descriptor sets for unused sets + for i in 0 ..< result.len: + if not result[i].Valid: + var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + bindingCount: 0, + pBindings: nil, + ) + checkVkResult vkCreateDescriptorSetLayout( + vulkan.device, addr(layoutCreateInfo), nil, addr(result[i]) + ) proc createPipeline*[TShader]( renderPass: RenderPass, @@ -488,10 +499,6 @@ (result.vertexShaderModule, result.fragmentShaderModule) = compileShader(shader) result.descriptorSetLayouts = createDescriptorSetLayouts[TShader]() - var layouts: seq[VkDescriptorSetLayout] - for l in result.descriptorSetLayouts: - if l.Valid: - layouts.add l # TODO: only add pushConstants if shader actually uses them let pushConstant = VkPushConstantRange( @@ -502,8 +509,8 @@ let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - setLayoutCount: layouts.len.uint32, - pSetLayouts: layouts.ToCPointer, + setLayoutCount: result.descriptorSetLayouts.len.uint32, + pSetLayouts: result.descriptorSetLayouts.ToCPointer, pushConstantRangeCount: 1, pPushConstantRanges: addr(pushConstant), )
--- a/semicongine/text.nim Sun Dec 22 22:32:12 2024 +0700 +++ b/semicongine/text.nim Mon Dec 23 00:32:07 2024 +0700 @@ -15,63 +15,19 @@ import ./image import ./contrib/algorithms/texture_packing -const - NEWLINE = Rune('\n') - SPACE = Rune(' ') - type - TextAlignment* = enum - Left - Center - Right - GlyphQuad[MaxGlyphs: static int] = object pos: array[MaxGlyphs, Vec4f] # vertex offsets to glyph center: [left, bottom, right, top] uv: array[MaxGlyphs, Vec4f] # [left, bottom, right, top] + TextRendering* = object + aspectRatio*: float32 + GlyphDescriptorSet*[MaxGlyphs: static int] = object fontAtlas*: Image[Gray] glyphquads*: GPUValue[GlyphQuad[MaxGlyphs], StorageBuffer] - FontObj*[MaxGlyphs: static int] = object - advance*: Table[Rune, float32] - kerning*: Table[(Rune, Rune), float32] - lineAdvance*: float32 - lineHeight*: float32 # like lineAdvance - lineGap - ascent*: float32 # from baseline to highest glyph - descent*: float32 # from baseline to highest glyph - xHeight*: float32 # from baseline to height of lowercase x - descriptorSet*: DescriptorSetData[GlyphDescriptorSet[MaxGlyphs]] - descriptorGlyphIndex: Table[Rune, uint16] - fallbackCharacter: Rune - - Font*[MaxGlyphs: static int] = ref FontObj[MaxGlyphs] - - TextHandle* = distinct int - Text = object - bufferOffset: int - text: seq[Rune] - position: Vec3f = vec3() - alignment: TextAlignment = Left - anchor: Vec2f = vec2() - scale: float32 = 0 - color: Vec4f = vec4(1, 1, 1, 1) - capacity: int - - TextBuffer*[MaxGlyphs: static int] = object - cursor: int - font*: Font[MaxGlyphs] - baseScale*: float32 - position*: GPUArray[Vec3f, VertexBufferMapped] - color*: GPUArray[Vec4f, VertexBufferMapped] - scale*: GPUArray[float32, VertexBufferMapped] - glyphIndex*: GPUArray[uint16, VertexBufferMapped] - texts: seq[Text] - - TextRendering* = object - aspectRatio*: float32 - GlyphShader*[MaxGlyphs: static int] = object position {.InstanceAttribute.}: Vec3f color {.InstanceAttribute.}: Vec4f @@ -82,7 +38,7 @@ fragmentUv {.Pass.}: Vec2f fragmentColor {.PassFlat.}: Vec4f outColor {.ShaderOutput.}: Vec4f - glyphData {.DescriptorSet: 0.}: GlyphDescriptorSet[MaxGlyphs] + glyphData {.DescriptorSet: 3.}: GlyphDescriptorSet[MaxGlyphs] vertexCode* = """ const int[6] indices = int[](0, 1, 2, 2, 3, 0); @@ -109,6 +65,47 @@ outColor = vec4(fragmentColor.rgb, fragmentColor.a * a); }""" + FontObj*[MaxGlyphs: static int] = object + advance*: Table[Rune, float32] + kerning*: Table[(Rune, Rune), float32] + lineAdvance*: float32 + lineHeight*: float32 # like lineAdvance - lineGap + ascent*: float32 # from baseline to highest glyph + descent*: float32 # from baseline to highest glyph + xHeight*: float32 # from baseline to height of lowercase x + descriptorSet*: DescriptorSetData[GlyphDescriptorSet[MaxGlyphs]] + descriptorGlyphIndex: Table[Rune, uint16] + fallbackCharacter: Rune + + Font*[MaxGlyphs: static int] = ref FontObj[MaxGlyphs] + + TextHandle* = distinct int + + TextAlignment* = enum + Left + Center + Right + + Text = object + bufferOffset: int + text: seq[Rune] + position: Vec3f = vec3() + alignment: TextAlignment = Left + anchor: Vec2f = vec2() + scale: float32 = 0 + color: Vec4f = vec4(1, 1, 1, 1) + capacity: int + + TextBuffer*[MaxGlyphs: static int] = object + cursor: int + font*: Font[MaxGlyphs] + baseScale*: float32 + position*: GPUArray[Vec3f, VertexBufferMapped] + color*: GPUArray[Vec4f, VertexBufferMapped] + scale*: GPUArray[float32, VertexBufferMapped] + glyphIndex*: GPUArray[uint16, VertexBufferMapped] + texts: seq[Text] + proc `=copy`[MaxGlyphs: static int]( dest: var FontObj[MaxGlyphs], source: FontObj[MaxGlyphs] ) {.error.} @@ -120,16 +117,16 @@ include ./text/font func initTextBuffer*[MaxGlyphs: static int]( - font: Font[MaxGlyphs], maxCharacters: int, baseScale = 1'f32 + font: Font[MaxGlyphs], bufferSize: int, baseScale = 1'f32 ): TextBuffer[MaxGlyphs] = result.cursor = 0 result.font = font result.baseScale = baseScale - result.position.data.setLen(maxCharacters) - result.scale.data.setLen(maxCharacters) - result.color.data.setLen(maxCharacters) - result.glyphIndex.data.setLen(maxCharacters) - result.texts.setLen(maxCharacters) # waste a lot of memory? + result.position.data.setLen(bufferSize) + result.scale.data.setLen(bufferSize) + result.color.data.setLen(bufferSize) + result.glyphIndex.data.setLen(bufferSize) + result.texts.setLen(bufferSize) # waste a lot of memory? iterator splitLines(text: seq[Rune]): seq[Rune] = var current = newSeq[Rune]() @@ -141,21 +138,24 @@ current.add c yield current -proc width(font: Font, text: seq[Rune], scale: float32): float32 = +proc width*(font: Font, text: seq[Rune]): float32 = for i in 0 ..< text.len: if not (i == text.len - 1 and text[i].isWhiteSpace): if text[i] in font.advance: - result += font.advance[text[i]] * scale + result += font.advance[text[i]] else: - result += font.advance[font.fallbackCharacter] * scale + result += font.advance[font.fallbackCharacter] if i < text.len - 1: - result += font.kerning.getOrDefault((text[i], text[i + 1]), 0) * scale + result += font.kerning.getOrDefault((text[i], text[i + 1]), 0) return result -proc textDimension*(font: Font, text: seq[Rune], scale: float32): Vec2f = +proc width*(font: Font, text: string): float32 = + width(font, text.toRunes) + +proc textDimension*(font: Font, text: seq[Rune]): Vec2f = let nLines = text.countIt(it == Rune('\n')).float32 - let h = (nLines * font.lineAdvance * scale + font.lineHeight * scale) - let w = max(splitLines(text).toSeq.mapIt(width(font, it, scale))) + let h = (nLines * font.lineAdvance + font.lineHeight) + let w = max(splitLines(text).toSeq.mapIt(width(font, it))) return vec2(w, h) @@ -172,7 +172,7 @@ capacity = textbuffer.texts[i].capacity globalScale = scale * textbuffer.baseScale - box = textDimension(textbuffer.font, text, globalScale) + box = textDimension(textbuffer.font, text) * globalScale xH = textbuffer.font.xHeight * globalScale aratio = getAspectRatio() origin = vec3( @@ -181,7 +181,7 @@ textbuffer.font.lineHeight * globalScale * 0.5, position.z, ) - lineWidths = splitLines(text).toSeq.mapIt(width(textbuffer.font, it, globalScale)) + lineWidths = splitLines(text).toSeq.mapIt(width(textbuffer.font, it) * globalScale) maxWidth = box.x var
--- a/semicongine/text/font.nim Sun Dec 22 22:32:12 2024 +0700 +++ b/semicongine/text/font.nim Mon Dec 23 00:32:07 2024 +0700 @@ -176,23 +176,19 @@ lineHeightPixels, ) -func textWidth*(theText: seq[Rune] | string, font: FontObj): float32 = - var text = when theText is string: theText.toRunes else: theText - var currentWidth = 0'f32 - var lineWidths: seq[float32] - for i in 0 ..< text.len: - if text[i] == NEWLINE: - lineWidths.add currentWidth - currentWidth = 0'f32 - else: - if not (i == text.len - 1 and text[i].isWhiteSpace): - currentWidth += font.advance[text[i]] - if i < text.len - 1: - currentWidth += font.kerning[(text[i], text[i + 1])] - lineWidths.add currentWidth - return lineWidths.max +proc upload*(font: Font, renderdata: var RenderData) = + assignBuffers(renderdata, font.descriptorSet) + uploadImages(renderdata, font.descriptorSet) + +proc addToPipeline*(font: Font, renderdata: RenderData, pipeline: Pipeline) = + initDescriptorSet(renderdata, pipeline.layout(3), font.descriptorSet) -func WordWrapped*(text: seq[Rune], font: FontObj, maxWidth: float32): seq[Rune] = +proc bindTo*(font: Font, pipeline: Pipeline, commandbuffer: VkCommandBuffer) = + bindDescriptorSet(commandbuffer, font.descriptorSet, 3, pipeline) + +#[ +# needs to be adjusted to work correctly with new metrics code in text.nim +func wordWrapped*(text: seq[Rune], font: FontObj, maxWidth: float32): seq[Rune] = var remaining: seq[seq[Rune]] = @[@[]] for c in text: if c == SPACE: @@ -236,3 +232,4 @@ result.add currentLine return result + ]#
--- a/tests/test_text.nim Sun Dec 22 22:32:12 2024 +0700 +++ b/tests/test_text.nim Mon Dec 23 00:32:07 2024 +0700 @@ -22,11 +22,10 @@ renderPass = vulkan.swapchain.renderPass ) var textbuffer = font.initTextBuffer(1000, baseScale = 0.1) + assignBuffers(renderdata, textbuffer) - assignBuffers(renderdata, textbuffer) - assignBuffers(renderdata, font.descriptorSet) - uploadImages(renderdata, font.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font.descriptorSet) + font.upload(renderdata) + font.addToPipeline(renderdata, pipeline) discard textbuffer.add("Hello semicongine!", vec3()) @@ -37,7 +36,7 @@ textbuffer.refresh() withNextFrame(framebuffer, commandbuffer): - bindDescriptorSet(commandbuffer, font.descriptorSet, 0, pipeline) + font.bindTo(pipeline, commandbuffer) withRenderPass( vulkan.swapchain.renderPass, framebuffer, @@ -64,15 +63,12 @@ renderPass = vulkan.swapchain.renderPass ) - assignBuffers(renderdata, font1.descriptorSet) - assignBuffers(renderdata, font2.descriptorSet) - assignBuffers(renderdata, font3.descriptorSet) - uploadImages(renderdata, font1.descriptorSet) - uploadImages(renderdata, font2.descriptorSet) - uploadImages(renderdata, font3.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font1.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font2.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font3.descriptorSet) + font1.upload(renderdata) + font2.upload(renderdata) + font3.upload(renderdata) + font1.addToPipeline(renderdata, pipeline) + font2.addToPipeline(renderdata, pipeline) + font3.addToPipeline(renderdata, pipeline) var textbuffer1 = font1.initTextBuffer(10, baseScale = 0.1) var textbuffer2 = font2.initTextBuffer(10, baseScale = 0.1) @@ -111,11 +107,11 @@ vec4(0, 0, 0, 0), ): withPipeline(commandbuffer, pipeline): - bindDescriptorSet(commandbuffer, font1.descriptorSet, 0, pipeline) + bindDescriptorSet(commandbuffer, font1.descriptorSet, 3, pipeline) renderTextBuffer(commandbuffer, pipeline, textbuffer1) - bindDescriptorSet(commandbuffer, font2.descriptorSet, 0, pipeline) + bindDescriptorSet(commandbuffer, font2.descriptorSet, 3, pipeline) renderTextBuffer(commandbuffer, pipeline, textbuffer2) - bindDescriptorSet(commandbuffer, font3.descriptorSet, 0, pipeline) + bindDescriptorSet(commandbuffer, font3.descriptorSet, 3, pipeline) renderTextBuffer(commandbuffer, pipeline, textbuffer3) # cleanup @@ -131,9 +127,8 @@ renderPass = vulkan.swapchain.renderPass ) - assignBuffers(renderdata, font.descriptorSet) - uploadImages(renderdata, font.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font.descriptorSet) + font.upload(renderdata) + font.addToPipeline(renderdata, pipeline) var textbuffer = font.initTextBuffer(1000, baseScale = 0.1) assignBuffers(renderdata, textbuffer) @@ -161,7 +156,7 @@ textbuffer.refresh() withNextFrame(framebuffer, commandbuffer): - bindDescriptorSet(commandbuffer, font.descriptorSet, 0, pipeline) + bindDescriptorSet(commandbuffer, font.descriptorSet, 3, pipeline) withRenderPass( vulkan.swapchain.renderPass, framebuffer, @@ -186,9 +181,8 @@ renderPass = vulkan.swapchain.renderPass ) - assignBuffers(renderdata, font.descriptorSet) - uploadImages(renderdata, font.descriptorSet) - initDescriptorSet(renderdata, pipeline.layout(0), font.descriptorSet) + font.upload(renderdata) + font.addToPipeline(renderdata, pipeline) var textbuffer = font.initTextBuffer(3000, baseScale = 0.1) assignBuffers(renderdata, textbuffer) @@ -211,7 +205,7 @@ withNextFrame(framebuffer, commandbuffer): if windowWasResized(): textbuffer.refresh() - bindDescriptorSet(commandbuffer, font.descriptorSet, 0, pipeline) + bindDescriptorSet(commandbuffer, font.descriptorSet, 3, pipeline) withRenderPass( vulkan.swapchain.renderPass, framebuffer,