Mercurial > games > semicongine
changeset 1420:6f81a41603d9
did: start working on big restructuring
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.editorconfig Thu Jan 09 01:00:58 2025 +0700 @@ -0,0 +1,2 @@ +[*.nim] +autocommit = false
--- a/semicongine.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine.nim Thu Jan 09 01:00:58 2025 +0700 @@ -13,12 +13,8 @@ import ./semicongine/image export image -import ./semicongine/events import ./semicongine/rendering -import ./semicongine/rendering/vulkan/api -export events export rendering -export api import ./semicongine/storage import ./semicongine/input @@ -46,3 +42,15 @@ export texture_packing export collision export noise + +#### Main engine object + +proc initEngine*(appName: string) = + engine_obj_internal = Engine() + engine_obj_internal.vulkan = initVulkan(appName) + + # start audio + engine_obj_internal.mixer = createShared(Mixer) + engine_obj_internal.mixer[] = initMixer() + engine_obj_internal.audiothread.createThread(audioWorker, engine_obj_internal.mixer) + engine_obj_internal.initialized = true
--- a/semicongine/audio.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio.nim Thu Jan 09 01:00:58 2025 +0700 @@ -6,5 +6,3 @@ import ./audio/resources export resources - -startMixerThread()
--- a/semicongine/audio/generators.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio/generators.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,6 +1,6 @@ import std/math -import ./mixer_module +import ../core proc sinewave(f: float): proc(x: float): float = proc ret(x: float): float =
--- a/semicongine/audio/mixer_module.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio/mixer_module.nim Thu Jan 09 01:00:58 2025 +0700 @@ -5,10 +5,9 @@ import std/monotimes import std/strformat import std/tables -import std/logging import std/times -import ../core/globals +import ../core const NBUFFERS = 32 # it seems that some alsa hardware has a problem with smaller buffers than 512 @@ -17,48 +16,12 @@ else: const BUFFERSAMPLECOUNT = 256 -type - Level* = 0'f .. 1'f - Sample* = array[2, int16] - SoundData* = seq[Sample] - - Playback = object - sound: SoundData - position: int - loop: bool - levelLeft: Level - levelRight: Level - paused: bool - - Track = object - playing: Table[uint64, Playback] - level: Level - targetLevel: Level - fadeTime: float - fadeStep: float - -proc `=copy`(dest: var Playback, source: Playback) {.error.} -proc `=copy`(dest: var Track, source: Track) {.error.} - when defined(windows): include ./platform/windows when defined(linux): include ./platform/linux -type Mixer* = object - playbackCounter: uint64 - tracks: Table[string, Track] - sounds*: Table[string, SoundData] - level: Level - device: NativeSoundDevice - lock: Lock - buffers: seq[SoundData] - currentBuffer: int - lastUpdate: MonoTime - -proc `=copy`(dest: var Mixer, source: Mixer) {.error.} - -proc initMixer(): Mixer = +proc initMixer*(): Mixer = result = Mixer(tracks: initTable[string, Track](), level: 1'f) result.tracks[""] = Track(level: 1) result.lock.initLock() @@ -82,7 +45,7 @@ warn "sound with name '", name, "' was already loaded, overwriting" mixer.sounds[name] = sound -proc addTrack*(mixer: var Mixer, name: string, level: Level = 1'f) = +proc addTrack*(mixer: var Mixer, name: string, level: AudioLevel = 1'f) = if name in mixer.tracks: warn "track with name '", name, "' was already loaded, overwriting" mixer.lock.withLock: @@ -94,7 +57,7 @@ track = "", stopOtherSounds = false, loop = false, - levelLeft, levelRight: Level, + levelLeft, levelRight: AudioLevel, ): uint64 = assert track in mixer.tracks, &"Track '{track}' does not exists" assert soundName in mixer.sounds, soundName & " not loaded" @@ -118,7 +81,7 @@ track = "", stopOtherSounds = false, loop = false, - level: Level = 1'f, + level: AudioLevel = 1'f, ): uint64 = play( mixer = mixer, @@ -135,32 +98,34 @@ for track in mixer.tracks.mvalues: track.playing.clear() -proc getLevel*(mixer: var Mixer): Level = +proc getLevel*(mixer: var Mixer): AudioLevel = mixer.level -proc getLevel*(mixer: var Mixer, track: string): Level = +proc getLevel*(mixer: var Mixer, track: string): AudioLevel = mixer.tracks[track].level -proc getLevel*(mixer: var Mixer, playbackId: uint64): (Level, Level) = +proc getLevel*(mixer: var Mixer, playbackId: uint64): (AudioLevel, AudioLevel) = for track in mixer.tracks.mvalues: if playbackId in track.playing: return (track.playing[playbackId].levelLeft, track.playing[playbackId].levelRight) -proc setLevel*(mixer: var Mixer, level: Level) = +proc setLevel*(mixer: var Mixer, level: AudioLevel) = mixer.level = level -proc setLevel*(mixer: var Mixer, track: string, level: Level) = +proc setLevel*(mixer: var Mixer, track: string, level: AudioLevel) = mixer.lock.withLock: mixer.tracks[track].level = level -proc setLevel*(mixer: var Mixer, playbackId: uint64, levelLeft, levelRight: Level) = +proc setLevel*( + mixer: var Mixer, playbackId: uint64, levelLeft, levelRight: AudioLevel +) = mixer.lock.withLock: for track in mixer.tracks.mvalues: if playbackId in track.playing: track.playing[playbackId].levelLeft = levelLeft track.playing[playbackId].levelRight = levelRight -proc setLevel*(mixer: var Mixer, playbackId: uint64, level: Level) = +proc setLevel*(mixer: var Mixer, playbackId: uint64, level: AudioLevel) = setLevel(mixer, playbackId, level, level) proc stop*(mixer: var Mixer, track: string) = @@ -210,7 +175,7 @@ proc unpause*(mixer: var Mixer, playbackId: uint64) = mixer.pause(playbackId, false) -proc fadeTo*(mixer: var Mixer, track: string, level: Level, time: float) = +proc fadeTo*(mixer: var Mixer, track: string, level: AudioLevel, time: float) = mixer.tracks[track].targetLevel = level mixer.tracks[track].fadeTime = time mixer.tracks[track].fadeStep = level.float - mixer.tracks[track].level.float / time @@ -231,7 +196,7 @@ return true return false -func applyLevel(sample: Sample, levelLeft, levelRight: Level): Sample = +func applyLevel(sample: Sample, levelLeft, levelRight: AudioLevel): Sample = [int16(float(sample[0]) * levelLeft), int16(float(sample[1]) * levelRight)] func clip(value: int32): int16 = @@ -241,7 +206,7 @@ func mix(a, b: Sample): Sample = [clip(int32(a[0]) + int32(b[0])), clip(int32(a[1]) + int32(b[1]))] -proc updateSoundBuffer(mixer: var Mixer) = +proc updateSoundBuffer*(mixer: var Mixer) = let t = getMonoTime() let dt = (t - mixer.lastUpdate).inNanoseconds.float64 / 1_000_000_000'f64 @@ -251,8 +216,9 @@ for track in mixer.tracks.mvalues: if track.fadeTime > 0: track.fadeTime -= dt - track.level = - (track.level.float64 + track.fadeStep.float64 * dt).clamp(Level.low, Level.high) + track.level = (track.level.float64 + track.fadeStep.float64 * dt).clamp( + AudioLevel.low, AudioLevel.high + ) if track.fadeTime <= 0: track.level = track.targetLevel # mix @@ -327,11 +293,7 @@ mixer.lock.deinitLock() mixer.device.CloseSoundDevice() -var - mixer* = createShared(Mixer) - audiothread: Thread[void] - -proc audioWorker() {.thread.} = +proc audioWorker*(mixer: ptr Mixer) {.thread.} = mixer[].setupDevice() onThreadDestruction( proc() = @@ -340,14 +302,3 @@ ) while true: mixer[].updateSoundBuffer() - -# for thread priority (really necessary?) -when defined(windows): - import ../thirdparty/winim/winim/inc/winbase -when defined(linux): - import std/posix - -proc startMixerThread*() = - mixer[] = initMixer() - audiothread.createThread(audioWorker) - debug "Created audio thread"
--- a/semicongine/audio/platform/linux.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio/platform/linux.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,23 +1,3 @@ -# alsa API -type - OpenMode* {.size: sizeof(culong).} = enum - SND_PCM_BLOCK = 0x00000000 # added by semicongine, for clarity - SND_PCM_NONBLOCK = 0x00000001 - - StreamMode* {.size: sizeof(cint).} = enum - SND_PCM_STREAM_PLAYBACK = 0 - - AccessMode* {.size: sizeof(cint).} = enum - SND_PCM_ACCESS_RW_INTERLEAVED = 3 - - PCMFormat* {.size: sizeof(cint).} = enum - SND_PCM_FORMAT_S16_LE = 2 - - snd_pcm_p* = ptr object - snd_pcm_hw_params_p* = ptr object - snd_pcm_uframes_t* = culong - snd_pcm_sframes_t* = clong - {.pragma: alsafunc, importc, cdecl, dynlib: "libasound.so.2".} proc snd_pcm_open*( pcm_ref: ptr snd_pcm_p, name: cstring, streamMode: StreamMode, openmode: OpenMode @@ -68,10 +48,6 @@ # required for engine: -type NativeSoundDevice* = object - handle: snd_pcm_p - buffers: seq[ptr SoundData] - proc OpenSoundDevice*( sampleRate: uint32, buffers: seq[ptr SoundData] ): NativeSoundDevice =
--- a/semicongine/audio/platform/windows.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio/platform/windows.nim Thu Jan 09 01:00:58 2025 +0700 @@ -2,8 +2,6 @@ import ../../thirdparty/winim/winim/inc/[mmsystem, windef] -# import ../../thirdparty/winim/winim/inc/[windef, winuser, wincon, winbase] - template CheckWinMMResult*(call: untyped) = let value = call if value < 0: @@ -11,10 +9,6 @@ Exception, "Windows multimedia error: " & astToStr(call) & " returned " & $value ) -type NativeSoundDevice* = object - handle: HWAVEOUT - buffers: seq[WAVEHDR] - proc OpenSoundDevice*( sampleRate: uint32, buffers: seq[ptr SoundData] ): NativeSoundDevice =
--- a/semicongine/audio/resources.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/audio/resources.nim Thu Jan 09 01:00:58 2025 +0700 @@ -2,12 +2,8 @@ import std/os import std/streams import std/strformat -import std/strutils import ../core -import ../resources - -import ./mixer_module type Encoding {.size: sizeof(uint32).} = enum
--- a/semicongine/build.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/build.nim Thu Jan 09 01:00:58 2025 +0700 @@ -4,7 +4,7 @@ import std/os import std/strutils -include ./core/globals +include ./core/constants const BLENDER_CONVERT_SCRIPT = currentSourcePath().parentDir().parentDir().joinPath( "tools/blender_gltf_converter.py"
--- a/semicongine/contrib/algorithms/texture_packing.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/contrib/algorithms/texture_packing.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,6 +1,7 @@ import std/algorithm import std/strformat +import ../../core import ../../image type Rect = tuple[i: int, x, y, w, h: uint32]
--- a/semicongine/core.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/core.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,4 +1,6 @@ +import std/dynlib import std/hashes +import std/locks import std/macros import std/math import std/monotimes @@ -8,10 +10,22 @@ import std/strformat import std/tables import std/times +import std/unicode import std/typetraits +import std/logging + +include ./rendering/vulkan/api + include ./core/utils include ./core/buildconfig include ./core/vector include ./core/matrix -include ./core/globals +include ./core/constants +include ./core/types + +var engine_obj_internal*: Engine + +proc engine*(): Engine = + assert engine_obj_internal.initialized, "Engine has not been initialized yet" + return engine_obj_internal
--- a/semicongine/core/buildconfig.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/core/buildconfig.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,5 +1,3 @@ -import std/logging - const ENGINENAME = "semiconginev2" # checks required build options:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semicongine/core/constants.nim Thu Jan 09 01:00:58 2025 +0700 @@ -0,0 +1,2 @@ +const RESOURCEROOT* = "resources" +const AUDIO_SAMPLE_RATE* = 44100
--- a/semicongine/core/globals.nim Wed Jan 01 19:36:55 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -const RESOURCEROOT* = "resources" -const AUDIO_SAMPLE_RATE* = 44100
--- a/semicongine/core/matrix.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/core/matrix.nim Thu Jan 09 01:00:58 2025 +0700 @@ -545,7 +545,7 @@ ] ) -func asMat3(m: Mat4): auto = +func asMat3*(m: Mat4): auto = Mat3( data: [m[0, 0], m[0, 1], m[0, 2], m[1, 0], m[1, 1], m[1, 2], m[2, 0], m[2, 1], m[2, 2]]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semicongine/core/types.nim Thu Jan 09 01:00:58 2025 +0700 @@ -0,0 +1,459 @@ +const + INFLIGHTFRAMES* = 2'u32 + MAX_DESCRIPTORSETS* = 4 + BUFFER_ALIGNMENT* = 64'u64 # align offsets inside buffers along this alignment + # ca. 100mb per block, seems reasonable + MEMORY_BLOCK_ALLOCATION_SIZE* = 100_000_000'u64 + # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block + BUFFER_ALLOCATION_SIZE* = 9_000_000'u64 + SURFACE_FORMAT* = VK_FORMAT_B8G8R8A8_SRGB + DEPTH_FORMAT* = VK_FORMAT_D32_SFLOAT + PUSH_CONSTANT_SIZE* = 128 + +# some declaration needed for platform-types +type + AudioLevel* = 0'f .. 1'f + Sample* = array[2, int16] + SoundData* = seq[Sample] + DescriptorSetIndex* = range[0 .. MAX_DESCRIPTORSETS - 1] + +# custom pragmas to classify shader attributes +template VertexAttribute*() {.pragma.} +template InstanceAttribute*() {.pragma.} +template PushConstant*() {.pragma.} +template Pass*() {.pragma.} +template PassFlat*() {.pragma.} +template ShaderOutput*() {.pragma.} +template DescriptorSet*(index: DescriptorSetIndex) {.pragma.} + +when defined(windows): + include ../platform/windows/types +when defined(linux): + include ../platform/linux/types + +type + # === rendering === + SupportedGPUType* = + float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | + TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | + TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | + TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | + TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | + TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | + TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[ + float32 + ] | TMat43[float64] | TMat4[float32] | TMat4[float64] + + VulkanObject* = object # populated through InitVulkan proc + instance*: VkInstance + device*: VkDevice + physicalDevice*: VkPhysicalDevice + surface*: VkSurfaceKHR + window*: NativeWindow + graphicsQueueFamily*: uint32 + graphicsQueue*: VkQueue + debugMessenger*: VkDebugUtilsMessengerEXT + # populated through the initSwapchain proc + swapchain*: Swapchain + # unclear as of yet + anisotropy*: float32 = 0 # needs to be enable during device creation + fullscreen_internal*: bool + + RenderPass* = ref object + vk*: VkRenderPass + samples*: VkSampleCountFlagBits + depthBuffer*: bool + + Swapchain* = ref object + # parameters to initSwapchain, required for swapchain recreation + renderPass*: RenderPass + vSync*: bool + tripleBuffering*: bool + # populated through initSwapchain proc + vk*: VkSwapchainKHR + width*: uint32 + height*: uint32 + framebuffers*: seq[VkFramebuffer] + framebufferViews*: seq[VkImageView] + currentFramebufferIndex*: uint32 + commandBufferPool*: VkCommandPool + # depth buffer stuff, if enabled + depthImage*: VkImage + depthImageView*: VkImageView + depthMemory*: VkDeviceMemory + # MSAA stuff, if enabled + msaaImage*: VkImage + msaaImageView*: VkImageView + msaaMemory*: VkDeviceMemory + # frame-in-flight handling + currentFiF*: range[0 .. (INFLIGHTFRAMES - 1).int] + queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence] + imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] + renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] + commandBuffers*: array[INFLIGHTFRAMES.int, VkCommandBuffer] + oldSwapchain*: Swapchain + oldSwapchainCounter*: int # swaps until old swapchain will be destroyed + + # shader related types + DescriptorSetData*[T: object] = object + data*: T + vk*: array[INFLIGHTFRAMES.int, VkDescriptorSet] + + Pipeline*[TShader] = object + vk*: VkPipeline + vertexShaderModule*: VkShaderModule + fragmentShaderModule*: VkShaderModule + layout*: VkPipelineLayout + descriptorSetLayouts*: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] + + # memory/buffer related types + BufferType* = enum + VertexBuffer + VertexBufferMapped + IndexBuffer + IndexBufferMapped + UniformBuffer + UniformBufferMapped + StorageBuffer + StorageBufferMapped + + MemoryBlock* = object + vk*: VkDeviceMemory + size*: uint64 + rawPointer*: pointer # if not nil, this is mapped memory + offsetNextFree*: uint64 + + Buffer* = object + vk*: VkBuffer + size*: uint64 + rawPointer*: pointer # if not nil, buffer is using mapped memory + offsetNextFree*: uint64 + memoryOffset*: uint64 + memory*: VkDeviceMemory + + GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object + # TODO: when using mapped buffer memory, directly write values to mapped location + # instead of using data as buffer + data*: seq[T] + buffer*: Buffer + offset*: uint64 + + GPUValue*[T: object, TBuffer: static BufferType] = object + data*: T + buffer*: Buffer + offset*: uint64 + + GPUData* = GPUArray | GPUValue + + RenderDataObject = object + descriptorPool*: VkDescriptorPool + memory*: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]] + buffers*: array[BufferType, seq[Buffer]] + images*: seq[VkImage] + imageViews*: seq[VkImageView] + samplers*: seq[VkSampler] + + RenderData* = ref RenderDataObject + + # === audio === + Playback* = object + sound*: SoundData + position*: int + loop*: bool + levelLeft*: AudioLevel + levelRight*: AudioLevel + paused*: bool + + Track* = object + playing*: Table[uint64, Playback] + level*: AudioLevel + targetLevel*: AudioLevel + fadeTime*: float + fadeStep*: float + + Mixer* = object + playbackCounter*: uint64 + tracks*: Table[string, Track] + sounds*: Table[string, SoundData] + level*: AudioLevel + device*: NativeSoundDevice + lock*: Lock + buffers*: seq[SoundData] + currentBuffer*: int + lastUpdate*: MonoTime + + # === input + window handling === + EventType* = enum + Quit + ResizedWindow + MinimizedWindow + RestoredWindow + KeyPressed + KeyReleased + MousePressed + MouseReleased + MouseWheel + GotFocus + LostFocus + + Key* {.size: sizeof(cint), pure.} = enum + UNKNOWN + Escape + F1 + F2 + F3 + F4 + F5 + F6 + F7 + F8 + F9 + F10 + F11 + F12 + NumberRowExtra1 + `1` + `2` + `3` + `4` + `5` + `6` + `7` + `8` + `9` + `0` + NumberRowExtra2 + NumberRowExtra3 # tilde, minus, plus + A + B + C + D + E + F + G + H + I + J + K + L + M + N + O + P + Q + R + S + T + U + V + W + X + Y + Z + Tab + CapsLock + ShiftL + ShiftR + CtrlL + CtrlR + SuperL + SuperR + AltL + AltR + Space + Enter + Backspace + LetterRow1Extra1 + LetterRow1Extra2 # open bracket, close brackt, backslash + LetterRow2Extra1 + LetterRow2Extra2 + LetterRow2Extra3 # semicolon, quote + LetterRow3Extra1 + LetterRow3Extra2 + LetterRow3Extra3 # comma, period, slash + Up + Down + Left + Right + PageUp + PageDown + Home + End + Insert + Delete + PrintScreen + ScrollLock + Pause + + MouseButton* {.size: sizeof(cint), pure.} = enum + UNKNOWN + Mouse1 + Mouse2 + Mouse3 # Left, middle, right + + Event* = object + case eventType*: EventType + of KeyPressed, KeyReleased: + key*: Key + of MousePressed, MouseReleased: + button*: MouseButton + of MouseWheel: + amount*: float32 + of GotFocus: + discard + of LostFocus: + discard + else: + discard + + # === images === + Gray* = TVec1[uint8] + BGRA* = TVec4[uint8] + PixelType* = Gray | BGRA + + ImageObject*[T: PixelType, IsArray: static bool] = object + width*: uint32 + height*: uint32 + minInterpolation*: VkFilter = VK_FILTER_LINEAR + magInterpolation*: VkFilter = VK_FILTER_LINEAR + wrapU*: VkSamplerAddressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT + wrapV*: VkSamplerAddressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT + data*: seq[T] + vk*: VkImage + imageview*: VkImageView + sampler*: VkSampler + isRenderTarget*: bool = false + samples*: VkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT + when IsArray: + nLayers*: uint32 + + Image*[T: PixelType] = ImageObject[T, false] + ImageArray*[T: PixelType] = ImageObject[T, true] + + # === fonts === + GlyphQuad*[MaxGlyphs: static int] = object + # vertex offsets to glyph center: [left, bottom, right, top] + pos*: array[MaxGlyphs, Vec4f] + 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] + + GlyphShader*[MaxGlyphs: static int] = object + position {.InstanceAttribute.}: Vec3f + color {.InstanceAttribute.}: Vec4f + scale {.InstanceAttribute.}: float32 + glyphIndex {.InstanceAttribute.}: uint16 + textRendering {.PushConstant.}: TextRendering + + fragmentUv {.Pass.}: Vec2f + fragmentColor {.PassFlat.}: Vec4f + outColor {.ShaderOutput.}: Vec4f + glyphData {.DescriptorSet: 3.}: GlyphDescriptorSet[MaxGlyphs] + vertexCode* = + """ +const int[6] indices = int[](0, 1, 2, 2, 3, 0); +const int[4] i_x = int[](0, 0, 2, 2); +const int[4] i_y = int[](1, 3, 3, 1); +const float epsilon = 0.0000001; +// const float epsilon = 0.1; + +void main() { + int vertexI = indices[gl_VertexIndex]; + vec3 vertexPos = vec3( + glyphquads.pos[glyphIndex][i_x[vertexI]] * scale / textRendering.aspectRatio, + glyphquads.pos[glyphIndex][i_y[vertexI]] * scale, + 0 + ); + // the epsilon-offset is necessary, as otherwise characters with the same Z might overlap, despite transparency + gl_Position = vec4(vertexPos + position - vec3(0, 0, clamp(0, 1, gl_InstanceIndex * epsilon)), 1.0); + vec2 uv = vec2(glyphquads.uv[glyphIndex][i_x[vertexI]], glyphquads.uv[glyphIndex][i_y[vertexI]]); + fragmentUv = uv; + fragmentColor = color; +} """ + fragmentCode* = + """void main() { + float a = texture(fontAtlas, fragmentUv).r; + outColor = vec4(fragmentColor.rgb, fragmentColor.a * a); +}""" + + FontObj*[MaxGlyphs: static int] = object + advance*: Table[Rune, float32] + kerning*: Table[(Rune, Rune), float32] + leftBearing*: Table[Rune, float32] + lineAdvance*: float32 + lineHeight*: float32 # like lineAdvance - lineGap + ascent*: float32 # from baseline to highest glyph + descent*: float32 # from baseline to lowest glyph + xHeight*: float32 # from baseline to height of lowercase x + descriptorSet*: DescriptorSetData[GlyphDescriptorSet[MaxGlyphs]] + descriptorGlyphIndex*: Table[Rune, uint16] + descriptorGlyphIndexRev*: Table[uint16, Rune] # only used for debugging atm + fallbackCharacter*: Rune + + Font*[MaxGlyphs: static int] = ref FontObj[MaxGlyphs] + + TextHandle* = object + index*: uint32 + generation*: uint32 + + TextAlignment* = enum + Left + Center + Right + + Text* = object + bufferOffset*: int + text*: seq[Rune] + position*: Vec3f = vec3() + alignment*: TextAlignment = TextAlignment.Left + anchor*: Vec2f = vec2() + scale*: float32 = 0 + color*: Vec4f = vec4(1, 1, 1, 1) + capacity*: int + + TextBuffer*[MaxGlyphs: static int] = object + cursor*: int + generation*: uint32 + font*: Font[MaxGlyphs] + baseScale*: float32 + position*: GPUArray[Vec3f, VertexBufferMapped] + color*: GPUArray[Vec4f, VertexBufferMapped] + scale*: GPUArray[float32, VertexBufferMapped] + glyphIndex*: GPUArray[uint16, VertexBufferMapped] + texts*: seq[Text] + + # === global engine object === + EngineObj = object + initialized*: bool + vulkan*: VulkanObject + mixer*: ptr Mixer + audiothread*: Thread[ptr Mixer] + + Engine* = ref EngineObj + +# prevent object copies + +proc `=copy`(dest: var VulkanObject, source: VulkanObject) {.error.} +proc `=copy`(dest: var RenderDataObject, source: RenderDataObject) {.error.} +proc `=copy`[T, S](dest: var GPUValue[T, S], source: GPUValue[T, S]) {.error.} +proc `=copy`[T, S](dest: var GPUArray[T, S], source: GPUArray[T, S]) {.error.} +proc `=copy`(dest: var MemoryBlock, source: MemoryBlock) {.error.} +proc `=copy`[T](dest: var Pipeline[T], source: Pipeline[T]) {.error.} +proc `=copy`[T](dest: var DescriptorSetData[T], source: DescriptorSetData[T]) {.error.} +proc `=copy`(dest: var Playback, source: Playback) {.error.} +proc `=copy`(dest: var Track, source: Track) {.error.} +proc `=copy`(dest: var Mixer, source: Mixer) {.error.} +proc `=copy`[S, T](dest: var ImageObject[S, T], source: ImageObject[S, T]) {.error.} +proc `=copy`(dest: var EngineObj, source: EngineObj) {.error.} +proc `=copy`[MaxGlyphs: static int]( + dest: var FontObj[MaxGlyphs], source: FontObj[MaxGlyphs] +) {.error.} + +proc `=copy`[MaxGlyphs: static int]( + dest: var TextBuffer[MaxGlyphs], source: TextBuffer[MaxGlyphs] +) {.error.}
--- a/semicongine/events.nim Wed Jan 01 19:36:55 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -type - EventType* = enum - Quit - ResizedWindow - MinimizedWindow - RestoredWindow - KeyPressed - KeyReleased - MousePressed - MouseReleased - MouseWheel - GotFocus - LostFocus - - Key* {.size: sizeof(cint), pure.} = enum - UNKNOWN - Escape - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 - F12 - NumberRowExtra1 - `1` - `2` - `3` - `4` - `5` - `6` - `7` - `8` - `9` - `0` - NumberRowExtra2 - NumberRowExtra3 # tilde, minus, plus - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - Tab - CapsLock - ShiftL - ShiftR - CtrlL - CtrlR - SuperL - SuperR - AltL - AltR - Space - Enter - Backspace - LetterRow1Extra1 - LetterRow1Extra2 # open bracket, close brackt, backslash - LetterRow2Extra1 - LetterRow2Extra2 - LetterRow2Extra3 # semicolon, quote - LetterRow3Extra1 - LetterRow3Extra2 - LetterRow3Extra3 # comma, period, slash - Up - Down - Left - Right - PageUp - PageDown - Home - End - Insert - Delete - PrintScreen - ScrollLock - Pause - - MouseButton* {.size: sizeof(cint), pure.} = enum - UNKNOWN - Mouse1 - Mouse2 - Mouse3 # Left, middle, right - - Event* = object - case eventType*: EventType - of KeyPressed, KeyReleased: - key*: Key - of MousePressed, MouseReleased: - button*: MouseButton - of MouseWheel: - amount*: float32 - of GotFocus: - discard - of LostFocus: - discard - else: - discard
--- a/semicongine/gltf.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/gltf.nim Thu Jan 09 01:00:58 2025 +0700 @@ -7,9 +7,7 @@ import ./core import ./rendering -import ./rendering/vulkan/api import ./image -import ./resources type GltfNode* = object
--- a/semicongine/image.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/image.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,12 +1,8 @@ import std/os import std/typetraits -import std/streams -import std/strutils import std/strformat import ./core -import ./resources -import ./rendering/vulkan/api {.emit: "#define STB_IMAGE_STATIC".} {.emit: "#define STB_IMAGE_IMPLEMENTATION".} @@ -22,35 +18,9 @@ desired_channels: cint, ): ptr uint8 {.importc, nodecl.} -type - Gray* = TVec1[uint8] - BGRA* = TVec4[uint8] - PixelType* = Gray | BGRA - - ImageObject*[T: PixelType, IsArray: static bool] = object - width*: uint32 - height*: uint32 - minInterpolation*: VkFilter = VK_FILTER_LINEAR - magInterpolation*: VkFilter = VK_FILTER_LINEAR - wrapU*: VkSamplerAddressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT - wrapV*: VkSamplerAddressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT - data*: seq[T] - vk*: VkImage - imageview*: VkImageView - sampler*: VkSampler - isRenderTarget*: bool = false - samples*: VkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT - when IsArray: - nLayers*: uint32 - - Image*[T: PixelType] = ImageObject[T, false] - ImageArray*[T: PixelType] = ImageObject[T, true] - template nLayers*(image: Image): untyped = 1'u32 -proc `=copy`[S, T](dest: var ImageObject[S, T], source: ImageObject[S, T]) {.error.} - func `$`*[S, IsArray](img: ImageObject[S, IsArray]): string = let pixelTypeName = S.name if IsArray == false:
--- a/semicongine/input.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/input.nim Thu Jan 09 01:00:58 2025 +0700 @@ -2,7 +2,6 @@ import std/tables import ./core -import ./events import ./rendering import ./storage @@ -34,16 +33,16 @@ input.mouseMove = vec2i(0, 0) input.windowWasResized = false - let newMousePos = getMousePosition(vulkan.window) + let newMousePos = getMousePosition(engine().vulkan.window) input.mouseMove = newMousePos - input.mousePosition if input.lockMouse and input.hasFocus: - input.mousePosition = vulkan.window.size div 2 - setMousePosition(vulkan.window, input.mousePosition) + input.mousePosition = engine().vulkan.window.size div 2 + setMousePosition(engine().vulkan.window, input.mousePosition) else: input.mousePosition = newMousePos var killed = false - for event in vulkan.window.pendingEvents(): + for event in engine().vulkan.window.pendingEvents(): case event.eventType of Quit: killed = true @@ -111,7 +110,8 @@ input.mousePosition proc mousePosition*(): Vec2f = - result = input.mousePosition.f32 / vulkan.window.size().f32 * 2.0'f32 - 1.0'f32 + result = + input.mousePosition.f32 / engine().vulkan.window.size().f32 * 2.0'f32 - 1.0'f32 result.y = result.y * -1 proc mouseMove*(): Vec2i =
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semicongine/platform/linux/types.nim Thu Jan 09 01:00:58 2025 +0700 @@ -0,0 +1,31 @@ +import ../../thirdparty/x11/xlib +import ../../thirdparty/x11/x as x11 + +type NativeWindow* = object + display*: ptr xlib.Display + window*: x11.Window + emptyCursor*: Cursor + +# alsa API +type + OpenMode* {.size: sizeof(culong).} = enum + SND_PCM_BLOCK = 0x00000000 # added by semicongine, for clarity + SND_PCM_NONBLOCK = 0x00000001 + + StreamMode* {.size: sizeof(cint).} = enum + SND_PCM_STREAM_PLAYBACK = 0 + + AccessMode* {.size: sizeof(cint).} = enum + SND_PCM_ACCESS_RW_INTERLEAVED = 3 + + PCMFormat* {.size: sizeof(cint).} = enum + SND_PCM_FORMAT_S16_LE = 2 + + snd_pcm_p* = ptr object + snd_pcm_hw_params_p* = ptr object + snd_pcm_uframes_t* = culong + snd_pcm_sframes_t* = clong + +type NativeSoundDevice* = object + handle*: snd_pcm_p + buffers*: seq[ptr SoundData]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semicongine/platform/windows/types.nim Thu Jan 09 01:00:58 2025 +0700 @@ -0,0 +1,10 @@ +import ../../thirdparty/winim/winim/inc/[windef, winuser, wincon, winbase] + +type NativeWindow* = object + hinstance*: HINSTANCE + hwnd*: HWND + g_wpPrev: WINDOWPLACEMENT + +type NativeSoundDevice* = object + handle*: HWAVEOUT + buffers*: seq[WAVEHDR]
--- a/semicongine/rendering.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,5 +1,4 @@ import std/logging -import std/enumerate import std/hashes import std/macros import std/os @@ -8,7 +7,7 @@ import std/strutils import std/typetraits -import ./rendering/vulkan/api +import ./core import ./image @@ -19,28 +18,6 @@ # - some utils code that is used in mutiple rendering files # - inclusion of all rendering files -# const definitions -const INFLIGHTFRAMES* = 2'u32 -const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment -const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 - # ca. 100mb per block, seems reasonable -const BUFFER_ALLOCATION_SIZE = 9_000_000'u64 - # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block -const MAX_DESCRIPTORSETS = 4 -const SURFACE_FORMAT* = VK_FORMAT_B8G8R8A8_SRGB -const DEPTH_FORMAT* = VK_FORMAT_D32_SFLOAT -const PUSH_CONSTANT_SIZE = 128 - -# custom pragmas to classify shader attributes -type DescriptorSetIndex = range[0 .. MAX_DESCRIPTORSETS - 1] -template VertexAttribute*() {.pragma.} -template InstanceAttribute*() {.pragma.} -template PushConstant*() {.pragma.} -template Pass*() {.pragma.} -template PassFlat*() {.pragma.} -template ShaderOutput*() {.pragma.} -template DescriptorSet*(index: DescriptorSetIndex) {.pragma.} - # there is a big, bad global vulkan object # believe me, this makes everything much, much easier @@ -49,140 +26,6 @@ when defined(linux): include ./rendering/platform/linux -type - # type aliases - SupportedGPUType = - float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | - TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | - TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | - TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | - TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | - TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | - TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[ - float32 - ] | TMat43[float64] | TMat4[float32] | TMat4[float64] - - VulkanGlobals* = object # populated through InitVulkan proc - instance*: VkInstance - device*: VkDevice - physicalDevice*: VkPhysicalDevice - surface: VkSurfaceKHR - window*: NativeWindow - graphicsQueueFamily*: uint32 - graphicsQueue*: VkQueue - debugMessenger: VkDebugUtilsMessengerEXT - # populated through the initSwapchain proc - swapchain*: Swapchain - # unclear as of yet - anisotropy*: float32 = 0 # needs to be enable during device creation - - RenderPass* = ref object - vk*: VkRenderPass - samples*: VkSampleCountFlagBits - depthBuffer*: bool - - Swapchain* = ref object - # parameters to initSwapchain, required for swapchain recreation - renderPass*: RenderPass - vSync*: bool - tripleBuffering*: bool - # populated through initSwapchain proc - vk: VkSwapchainKHR - width*: uint32 - height*: uint32 - framebuffers: seq[VkFramebuffer] - framebufferViews: seq[VkImageView] - currentFramebufferIndex: uint32 - commandBufferPool: VkCommandPool - # depth buffer stuff, if enabled - depthImage: VkImage - depthImageView*: VkImageView - depthMemory: VkDeviceMemory - # MSAA stuff, if enabled - msaaImage: VkImage - msaaImageView*: VkImageView - msaaMemory: VkDeviceMemory - # frame-in-flight handling - currentFiF: range[0 .. (INFLIGHTFRAMES - 1).int] - queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence] - imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] - renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] - commandBuffers: array[INFLIGHTFRAMES.int, VkCommandBuffer] - oldSwapchain: Swapchain - oldSwapchainCounter: int # swaps until old swapchain will be destroyed - - # shader related types - DescriptorSetData*[T: object] = object - data*: T - vk*: array[INFLIGHTFRAMES.int, VkDescriptorSet] - - Pipeline*[TShader] = object - vk: VkPipeline - vertexShaderModule: VkShaderModule - fragmentShaderModule: VkShaderModule - layout: VkPipelineLayout - descriptorSetLayouts*: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] - - # memory/buffer related types - BufferType* = enum - VertexBuffer - VertexBufferMapped - IndexBuffer - IndexBufferMapped - UniformBuffer - UniformBufferMapped - StorageBuffer - StorageBufferMapped - - MemoryBlock* = object - vk: VkDeviceMemory - size: uint64 - rawPointer: pointer # if not nil, this is mapped memory - offsetNextFree: uint64 - - Buffer* = object - vk: VkBuffer - size: uint64 - rawPointer: pointer # if not nil, buffer is using mapped memory - offsetNextFree: uint64 - memoryOffset: uint64 - memory: VkDeviceMemory - - GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object - # TODO: when using mapped buffer memory, directly write values to mapped location - # instead of using data as buffer - data*: seq[T] - buffer*: Buffer - offset*: uint64 - - GPUValue*[T: object, TBuffer: static BufferType] = object - data*: T - buffer*: Buffer - offset: uint64 - - GPUData* = GPUArray | GPUValue - - RenderDataObject = object - descriptorPool: VkDescriptorPool - memory: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]] - buffers: array[BufferType, seq[Buffer]] - images: seq[VkImage] - imageViews: seq[VkImageView] - samplers: seq[VkSampler] - - RenderData* = ref RenderDataObject - -var vulkan* = VulkanGlobals() -var fullscreen_internal: bool - -proc `=copy`(dest: var VulkanGlobals, source: VulkanGlobals) {.error.} -proc `=copy`(dest: var RenderDataObject, source: RenderDataObject) {.error.} -proc `=copy`[T, S](dest: var GPUValue[T, S], source: GPUValue[T, S]) {.error.} -proc `=copy`[T, S](dest: var GPUArray[T, S], source: GPUArray[T, S]) {.error.} -proc `=copy`(dest: var MemoryBlock, source: MemoryBlock) {.error.} -proc `=copy`[T](dest: var Pipeline[T], source: Pipeline[T]) {.error.} -proc `=copy`[T](dest: var DescriptorSetData[T], source: DescriptorSetData[T]) {.error.} - proc `[]`*[T, S](a: GPUArray[T, S], i: SomeInteger): T = a.data[i] @@ -200,59 +43,8 @@ ): BufferType {.compileTime.} = B -func getDescriptorType[T](): VkDescriptorType {.compileTIme.} = - when T is ImageObject: - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER - elif T is GPUValue: - when getBufferType(default(T)) in [UniformBuffer, UniformBufferMapped]: - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - elif getBufferType(default(T)) in [StorageBuffer, StorageBufferMapped]: - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER - else: - {.error: "Unsupported descriptor type: " & $T.} - elif T is array: - when elementType(default(T)) is ImageObject: - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER - elif elementType(default(T)) is GPUValue: - when getBufferType(default(elementType(default(T)))) in - [UniformBuffer, UniformBufferMapped]: - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - elif getBufferType(default(elementType(default(T)))) in - [StorageBuffer, StorageBufferMapped]: - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER - else: - {.error: "Unsupported descriptor type: " & $T.} - else: - {.error: "Unsupported descriptor type: " & $T.} - else: - {.error: "Unsupported descriptor type: " & $T.} - -func getDescriptorCount[T](): uint32 {.compileTIme.} = - when T is array: - len(T) - else: - 1 - -func getBindingNumber[T](field: static string): uint32 {.compileTime.} = - var c = 0'u32 - var found = false - for name, value in fieldPairs(default(T)): - when name == field: - result = c - found = true - else: - inc c - assert found, "Field '" & field & "' of descriptor '" & $T & "' not found" - -proc currentFiF*(): int = - assert vulkan.swapchain != nil, "Swapchain has not been initialized yet" - vulkan.swapchain.currentFiF - -include ./rendering/vulkan_wrappers -include ./rendering/renderpasses -include ./rendering/swapchain -include ./rendering/shaders -include ./rendering/renderer +import ./rendering/vulkan_wrappers +import ./rendering/swapchain proc debugCallback( messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, @@ -279,7 +71,7 @@ raise newException(Exception, errorMsg) return false -proc initVulkan*(appName: string = "semicongine app") = +proc initVulkan*(appName: string = "semicongine app"): VulkanObject = # instance creation # enagle all kind of debug stuff @@ -321,15 +113,15 @@ enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: instanceExtensionsC, ) - checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) - loadVulkan(vulkan.instance) + checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result.instance)) + loadVulkan(result.instance) # load extensions # for extension in requiredExtensions: - loadExtension(vulkan.instance, $extension) - vulkan.window = createWindow(appName) - vulkan.surface = createNativeSurface(vulkan.instance, vulkan.window) + loadExtension(result.instance, $extension) + result.window = createWindow(appName) + result.surface = createNativeSurface(result.instance, result.window) # logical device creation @@ -340,7 +132,7 @@ # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] var deviceExtensions = @["VK_KHR_swapchain"] for extension in deviceExtensions: - loadExtension(vulkan.instance, extension) + loadExtension(result.instance, extension) when not defined(release): var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT( @@ -351,19 +143,19 @@ pUserData: nil, ) checkVkResult vkCreateDebugUtilsMessengerEXT( - vulkan.instance, addr(debugMessengerCreateInfo), nil, addr(vulkan.debugMessenger) + result.instance, addr(debugMessengerCreateInfo), nil, addr(result.debugMessenger) ) # get physical device and graphics queue family - vulkan.physicalDevice = getBestPhysicalDevice(vulkan.instance) - vulkan.graphicsQueueFamily = - getQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT) + result.physicalDevice = getBestPhysicalDevice(result.instance) + result.graphicsQueueFamily = + getQueueFamily(result.physicalDevice, VK_QUEUE_GRAPHICS_BIT) let priority = cfloat(1) queueInfo = VkDeviceQueueCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - queueFamilyIndex: vulkan.graphicsQueueFamily, + queueFamilyIndex: result.graphicsQueueFamily, queueCount: 1, pQueuePriorities: addr(priority), ) @@ -384,49 +176,51 @@ pEnabledFeatures: addr(enabledFeatures), ) checkVkResult vkCreateDevice( - physicalDevice = vulkan.physicalDevice, + physicalDevice = result.physicalDevice, pCreateInfo = addr createDeviceInfo, pAllocator = nil, - pDevice = addr vulkan.device, + pDevice = addr result.device, ) - vulkan.graphicsQueue = - svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) + result.graphicsQueue = + svkGetDeviceQueue(result.device, result.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) proc clearSwapchain*() = - assert vulkan.swapchain != nil, "Swapchain has not been initialized yet" - destroySwapchain(vulkan.swapchain) - vulkan.swapchain = nil + assert engine().vulkan.swapchain != nil, "Swapchain has not been initialized yet" + destroySwapchain(engine().vulkan.swapchain) + engine().vulkan.swapchain = nil proc setupSwapchain*( renderPass: RenderPass, vSync: bool = false, tripleBuffering: bool = true ) = - assert vulkan.swapchain == nil, "Swapchain has already been initialized yet" - vulkan.swapchain = + assert engine().vulkan.swapchain == nil, "Swapchain has already been initialized yet" + engine().vulkan.swapchain = initSwapchain(renderPass, vSync = vSync, tripleBuffering = tripleBuffering) proc destroyVulkan*() = - if vulkan.swapchain != nil: + if engine().vulkan.swapchain != nil: clearSwapchain() - vkDestroyDevice(vulkan.device, nil) - vkDestroySurfaceKHR(vulkan.instance, vulkan.surface, nil) - if vulkan.debugMessenger.Valid: - vkDestroyDebugUtilsMessengerEXT(vulkan.instance, vulkan.debugMessenger, nil) - vkDestroyInstance(vulkan.instance, nil) + vkDestroyDevice(engine().vulkan.device, nil) + vkDestroySurfaceKHR(engine().vulkan.instance, engine().vulkan.surface, nil) + if engine().vulkan.debugMessenger.Valid: + vkDestroyDebugUtilsMessengerEXT( + engine().vulkan.instance, engine().vulkan.debugMessenger, nil + ) + vkDestroyInstance(engine().vulkan.instance, nil) proc showSystemCursor*(value: bool) = - vulkan.window.showSystemCursor(value) + engine().vulkan.window.showSystemCursor(value) proc fullscreen*(): bool = - fullscreen_internal + engine().vulkan.fullscreen_internal proc setFullscreen*(enable: bool) = - if enable != fullscreen_internal: - fullscreen_internal = enable - vulkan.window.setFullscreen(fullscreen_internal) + if enable != engine().vulkan.fullscreen_internal: + engine().vulkan.fullscreen_internal = enable + engine().vulkan.window.setFullscreen(engine().vulkan.fullscreen_internal) proc getAspectRatio*(): float32 = - assert vulkan.swapchain != nil, "Swapchain has not been initialized yet" - vulkan.swapchain.width.float32 / vulkan.swapchain.height.float32 + assert engine().vulkan.swapchain != nil, "Swapchain has not been initialized yet" + engine().vulkan.swapchain.width.float32 / engine().vulkan.swapchain.height.float32 proc maxFramebufferSampleCount*( maxSamples = VK_SAMPLE_COUNT_8_BIT
--- a/semicongine/rendering/platform/linux.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/platform/linux.nim Thu Jan 09 01:00:58 2025 +0700 @@ -6,9 +6,6 @@ import ../../thirdparty/x11/x as x11 import ../../thirdparty/x11/xkblib -import ../../core -import ../../events - const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_xlib_surface"] # got values (keycodes) from xev @@ -109,11 +106,6 @@ var deleteMessage*: Atom -type NativeWindow* = object - display*: ptr xlib.Display - window*: x11.Window - emptyCursor: Cursor - template checkXlibResult(call: untyped) = let value = call if value == 0: @@ -303,8 +295,8 @@ proc createNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - dpy: cast[ptr api.Display](window.display), - window: cast[api.Window](window.window), + dpy: cast[ptr core.Display](window.display), + window: cast[core.Window](window.window), ) checkVkResult vkCreateXlibSurfaceKHR( instance, addr(surfaceCreateInfo), nil, addr(result)
--- a/semicongine/rendering/platform/windows.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/platform/windows.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,13 +1,9 @@ import std/tables import std/options -import ../../core - import ../../thirdparty/winim/winim/inc/[windef, winuser, wincon, winbase] import ../../thirdparty/winim/winim/[winstr, utils] -import ../../events - const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_win32_surface"] const KeyTypeMap* = { @@ -98,11 +94,6 @@ VK_DELETE: Key.Delete, }.toTable -type NativeWindow* = object - hinstance*: HINSTANCE - hwnd*: HWND - g_wpPrev: WINDOWPLACEMENT - # sorry, have to use module-global variable to capture windows events var currentEvents: seq[Event]
--- a/semicongine/rendering/renderer.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/renderer.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,3 +1,11 @@ +import std/typetraits +import std/sequtils +import std/macros +import std/logging + +import ../core +import ./vulkan_wrappers + func pointerAddOffset[T: SomeInteger](p: pointer, offset: T): pointer = cast[pointer](cast[T](p) + offset) @@ -30,7 +38,7 @@ usage: usage.toBits, ) let formatCheck = vkGetPhysicalDeviceImageFormatProperties2( - vulkan.physicalDevice, addr formatInfo, addr formatProperties + engine().vulkan.physicalDevice, addr formatInfo, addr formatProperties ) if formatCheck == VK_SUCCESS: # found suitable format return format @@ -75,7 +83,9 @@ proc isMappable(memoryTypeIndex: uint32): bool = var physicalProperties: VkPhysicalDeviceMemoryProperties - vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) + vkGetPhysicalDeviceMemoryProperties( + engine().vulkan.physicalDevice, addr(physicalProperties) + ) let flags = toEnums(physicalProperties.memoryTypes[memoryTypeIndex].propertyFlags) return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags @@ -116,7 +126,7 @@ pSetLayouts: layouts.ToCPointer, ) checkVkResult vkAllocateDescriptorSets( - vulkan.device, addr(allocInfo), descriptorSet.vk.ToCPointer + engine().vulkan.device, addr(allocInfo), descriptorSet.vk.ToCPointer ) # allocate seq with high cap to prevent realocation while adding to set @@ -206,7 +216,7 @@ .} vkUpdateDescriptorSets( - device = vulkan.device, + device = engine().vulkan.device, descriptorWriteCount = descriptorSetWrites.len.uint32, pDescriptorWrites = descriptorSetWrites.ToCPointer, descriptorCopyCount = 0, @@ -219,7 +229,7 @@ ) if mType.isMappable(): checkVkResult vkMapMemory( - device = vulkan.device, + device = engine().vulkan.device, memory = result.vk, offset = 0'u64, size = result.size, @@ -234,7 +244,7 @@ offset: buffer.memoryOffset, size: buffer.size, ) - checkVkResult vkFlushMappedMemoryRanges(vulkan.device, 1, addr(flushRegion)) + checkVkResult vkFlushMappedMemoryRanges(engine().vulkan.device, 1, addr(flushRegion)) proc flushAllMemory*(renderData: RenderData) = var flushRegions = newSeq[VkMappedMemoryRange]() @@ -251,7 +261,7 @@ ) if flushRegions.len > 0: checkVkResult vkFlushMappedMemoryRanges( - vulkan.device, flushRegions.len.uint32, flushRegions.ToCPointer() + engine().vulkan.device, flushRegions.len.uint32, flushRegions.ToCPointer() ) proc allocateNewBuffer( @@ -292,7 +302,7 @@ renderData.memory[memoryType][selectedBlockI].offsetNextFree = alignedTo(selectedBlock.offsetNextFree, memoryRequirements.alignment) checkVkResult vkBindBufferMemory( - vulkan.device, result.vk, selectedBlock.vk, selectedBlock.offsetNextFree + engine().vulkan.device, result.vk, selectedBlock.vk, selectedBlock.offsetNextFree ) result.memory = selectedBlock.vk result.memoryOffset = selectedBlock.offsetNextFree @@ -404,28 +414,28 @@ maxSets: descriptorPoolLimit, ) checkVkResult vkCreateDescriptorPool( - vulkan.device, addr(poolInfo), nil, addr(result.descriptorPool) + engine().vulkan.device, addr(poolInfo), nil, addr(result.descriptorPool) ) proc destroyRenderData*(renderData: RenderData) = - vkDestroyDescriptorPool(vulkan.device, renderData.descriptorPool, nil) + vkDestroyDescriptorPool(engine().vulkan.device, renderData.descriptorPool, nil) for buffers in renderData.buffers: for buffer in buffers: - vkDestroyBuffer(vulkan.device, buffer.vk, nil) + vkDestroyBuffer(engine().vulkan.device, buffer.vk, nil) for imageView in renderData.imageViews: - vkDestroyImageView(vulkan.device, imageView, nil) + vkDestroyImageView(engine().vulkan.device, imageView, nil) for sampler in renderData.samplers: - vkDestroySampler(vulkan.device, sampler, nil) + vkDestroySampler(engine().vulkan.device, sampler, nil) for image in renderData.images: - vkDestroyImage(vulkan.device, image, nil) + vkDestroyImage(engine().vulkan.device, image, nil) for memoryBlocks in renderData.memory.litems: for memory in memoryBlocks: - vkFreeMemory(vulkan.device, memory.vk, nil) + vkFreeMemory(engine().vulkan.device, memory.vk, nil) proc transitionImageLayout( image: VkImage, oldLayout, newLayout: VkImageLayout, nLayers: uint32 @@ -491,8 +501,8 @@ addressModeU: addressModeU, addressModeV: addressModeV, addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT, - anisotropyEnable: vulkan.anisotropy > 0, - maxAnisotropy: vulkan.anisotropy, + anisotropyEnable: engine().vulkan.anisotropy > 0, + maxAnisotropy: engine().vulkan.anisotropy, borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK, unnormalizedCoordinates: VK_FALSE, compareEnable: VK_FALSE, @@ -502,7 +512,9 @@ minLod: 0, maxLod: 0, ) - checkVkResult vkCreateSampler(vulkan.device, addr(samplerInfo), nil, addr(result)) + checkVkResult vkCreateSampler( + engine().vulkan.device, addr(samplerInfo), nil, addr(result) + ) proc createVulkanImage(renderData: var RenderData, image: var ImageObject) = assert image.vk == VkImage(0), "Image has already been created" @@ -549,7 +561,7 @@ alignedTo(selectedBlock.offsetNextFree, memoryRequirements.alignment) checkVkResult vkBindImageMemory( - vulkan.device, + engine().vulkan.device, image.vk, selectedBlock.vk, renderData.memory[memoryType][selectedBlockI].offsetNextFree,
--- a/semicongine/rendering/renderpasses.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/renderpasses.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,7 +1,9 @@ +import ../core +import ./vulkan_wrappers + proc createDirectPresentationRenderPass*( depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT ): RenderPass = - assert vulkan.instance.Valid, "Vulkan not initialized" result = RenderPass(depthBuffer: depthBuffer, samples: samples) var attachments = @@ -98,8 +100,6 @@ proc createIndirectPresentationRenderPass*( depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT ): (RenderPass, RenderPass) = - assert vulkan.instance.Valid, "Vulkan not initialized" - result[0] = RenderPass(depthBuffer: depthBuffer, samples: samples) result[1] = RenderPass(depthBuffer: false, samples: VK_SAMPLE_COUNT_1_BIT) @@ -300,4 +300,4 @@ vkCmdEndRenderPass(commandbuffer) proc destroyRenderPass*(renderPass: RenderPass) = - vkDestroyRenderPass(vulkan.device, renderpass.vk, nil) + vkDestroyRenderPass(engine().vulkan.device, renderpass.vk, nil)
--- a/semicongine/rendering/shaders.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/shaders.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,3 +1,13 @@ +import std/macros +import std/hashes +import std/strformat +import std/strutils +import std/enumerate +import std/os + +import ../core +import ./vulkan_wrappers + func glslType[T: SupportedGPUType | ImageObject](value: T): string = when T is float32: "float" @@ -419,7 +429,7 @@ codeSize: csize_t(vertexBinary.len * sizeof(uint32)), pCode: vertexBinary.ToCPointer, ) - checkVkResult vulkan.device.vkCreateShaderModule( + checkVkResult engine().vulkan.device.vkCreateShaderModule( addr(createInfoVertex), nil, addr(result[0]) ) var createInfoFragment = VkShaderModuleCreateInfo( @@ -427,7 +437,7 @@ codeSize: csize_t(fragmentBinary.len * sizeof(uint32)), pCode: fragmentBinary.ToCPointer, ) - checkVkResult vulkan.device.vkCreateShaderModule( + checkVkResult engine().vulkan.device.vkCreateShaderModule( addr(createInfoFragment), nil, addr(result[1]) ) @@ -467,7 +477,7 @@ pBindings: layoutbindings.ToCPointer, ) checkVkResult vkCreateDescriptorSetLayout( - vulkan.device, + engine().vulkan.device, addr(layoutCreateInfo), nil, addr(result[value.getCustomPragmaVal(DescriptorSet)]), @@ -481,7 +491,7 @@ pBindings: nil, ) checkVkResult vkCreateDescriptorSetLayout( - vulkan.device, addr(layoutCreateInfo), nil, addr(result[i]) + engine().vulkan.device, addr(layoutCreateInfo), nil, addr(result[i]) ) proc createPipeline*[TShader]( @@ -513,7 +523,7 @@ pPushConstantRanges: addr(pushConstant), ) checkVkResult vkCreatePipelineLayout( - vulkan.device, addr(pipelineLayoutInfo), nil, addr(result.layout) + engine().vulkan.device, addr(pipelineLayoutInfo), nil, addr(result.layout) ) let stages = [ @@ -659,7 +669,12 @@ basePipelineIndex: -1, ) checkVkResult vkCreateGraphicsPipelines( - vulkan.device, VkPipelineCache(0), 1, addr(createInfo), nil, addr(result.vk) + engine().vulkan.device, + VkPipelineCache(0), + 1, + addr(createInfo), + nil, + addr(result.vk), ) func layout*(pipeline: Pipeline, level: int): VkDescriptorSetLayout = @@ -674,9 +689,9 @@ proc destroyPipeline*(pipeline: Pipeline) = for descriptorSetLayout in pipeline.descriptorSetLayouts: - vkDestroyDescriptorSetLayout(vulkan.device, descriptorSetLayout, nil) + vkDestroyDescriptorSetLayout(engine().vulkan.device, descriptorSetLayout, nil) - vkDestroyShaderModule(vulkan.device, pipeline.vertexShaderModule, nil) - vkDestroyShaderModule(vulkan.device, pipeline.fragmentShaderModule, nil) - vkDestroyPipelineLayout(vulkan.device, pipeline.layout, nil) - vkDestroyPipeline(vulkan.device, pipeline.vk, nil) + vkDestroyShaderModule(engine().vulkan.device, pipeline.vertexShaderModule, nil) + vkDestroyShaderModule(engine().vulkan.device, pipeline.fragmentShaderModule, nil) + vkDestroyPipelineLayout(engine().vulkan.device, pipeline.layout, nil) + vkDestroyPipeline(engine().vulkan.device, pipeline.vk, nil)
--- a/semicongine/rendering/swapchain.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/swapchain.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,14 +1,17 @@ -proc initSwapchain( +import std/options + +import ../core +import ./vulkan_wrappers + +proc initSwapchain*( renderPass: RenderPass, vSync: bool = false, tripleBuffering: bool = true, oldSwapchain: Swapchain = nil, ): Swapchain = - assert vulkan.instance.Valid, "Vulkan not initialized" - var capabilities: VkSurfaceCapabilitiesKHR checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - vulkan.physicalDevice, vulkan.surface, addr(capabilities) + engine().vulkan.physicalDevice, engine().vulkan.surface, addr(capabilities) ) let width = capabilities.currentExtent.width @@ -28,7 +31,7 @@ VK_PRESENT_MODE_MAILBOX_KHR in svkGetPhysicalDeviceSurfacePresentModesKHR() var swapchainCreateInfo = VkSwapchainCreateInfoKHR( sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - surface: vulkan.surface, + surface: engine().vulkan.surface, minImageCount: minFramebufferCount, imageFormat: SURFACE_FORMAT, imageColorSpace: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, @@ -62,7 +65,7 @@ ) if vkCreateSwapchainKHR( - vulkan.device, addr(swapchainCreateInfo), nil, addr(swapchain.vk) + engine().vulkan.device, addr(swapchainCreateInfo), nil, addr(swapchain.vk) ) != VK_SUCCESS: return nil @@ -83,7 +86,7 @@ requirements.size, bestMemory(mappable = false, filter = requirements.memoryTypes) ) checkVkResult vkBindImageMemory( - vulkan.device, swapchain.depthImage, swapchain.depthMemory, 0 + engine().vulkan.device, swapchain.depthImage, swapchain.depthMemory, 0 ) swapchain.depthImageView = svkCreate2DImageView( image = swapchain.depthImage, @@ -106,7 +109,7 @@ requirements.size, bestMemory(mappable = false, filter = requirements.memoryTypes) ) checkVkResult vkBindImageMemory( - vulkan.device, swapchain.msaaImage, swapchain.msaaMemory, 0 + engine().vulkan.device, swapchain.msaaImage, swapchain.msaaMemory, 0 ) swapchain.msaaImageView = svkCreate2DImageView(image = swapchain.msaaImage, format = SURFACE_FORMAT) @@ -114,11 +117,14 @@ # create framebuffers var actualNFramebuffers: uint32 checkVkResult vkGetSwapchainImagesKHR( - vulkan.device, swapchain.vk, addr(actualNFramebuffers), nil + engine().vulkan.device, swapchain.vk, addr(actualNFramebuffers), nil ) var framebuffers = newSeq[VkImage](actualNFramebuffers) checkVkResult vkGetSwapchainImagesKHR( - vulkan.device, swapchain.vk, addr(actualNFramebuffers), framebuffers.ToCPointer + engine().vulkan.device, + swapchain.vk, + addr(actualNFramebuffers), + framebuffers.ToCPointer, ) for framebuffer in framebuffers: @@ -157,10 +163,13 @@ var commandPoolCreateInfo = VkCommandPoolCreateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT], - queueFamilyIndex: vulkan.graphicsQueueFamily, + queueFamilyIndex: engine().vulkan.graphicsQueueFamily, ) checkVkResult vkCreateCommandPool( - vulkan.device, addr(commandPoolCreateInfo), nil, addr(swapchain.commandBufferPool) + engine().vulkan.device, + addr(commandPoolCreateInfo), + nil, + addr(swapchain.commandBufferPool), ) var allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -169,7 +178,7 @@ commandBufferCount: INFLIGHTFRAMES, ) checkVkResult vkAllocateCommandBuffers( - vulkan.device, addr(allocInfo), swapchain.commandBuffers.ToCPointer + engine().vulkan.device, addr(allocInfo), swapchain.commandBuffers.ToCPointer ) return swapchain @@ -179,40 +188,40 @@ destroySwapchain(swapchain.oldSwapchain) if swapchain.msaaImage.Valid: - vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil) - vkDestroyImage(vulkan.device, swapchain.msaaImage, nil) - vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil) + vkDestroyImageView(engine().vulkan.device, swapchain.msaaImageView, nil) + vkDestroyImage(engine().vulkan.device, swapchain.msaaImage, nil) + vkFreeMemory(engine().vulkan.device, swapchain.msaaMemory, nil) if swapchain.depthImage.Valid: - vkDestroyImageView(vulkan.device, swapchain.depthImageView, nil) - vkDestroyImage(vulkan.device, swapchain.depthImage, nil) - vkFreeMemory(vulkan.device, swapchain.depthMemory, nil) + vkDestroyImageView(engine().vulkan.device, swapchain.depthImageView, nil) + vkDestroyImage(engine().vulkan.device, swapchain.depthImage, nil) + vkFreeMemory(engine().vulkan.device, swapchain.depthMemory, nil) for fence in swapchain.queueFinishedFence: - vkDestroyFence(vulkan.device, fence, nil) + vkDestroyFence(engine().vulkan.device, fence, nil) for semaphore in swapchain.imageAvailableSemaphore: - vkDestroySemaphore(vulkan.device, semaphore, nil) + vkDestroySemaphore(engine().vulkan.device, semaphore, nil) for semaphore in swapchain.renderFinishedSemaphore: - vkDestroySemaphore(vulkan.device, semaphore, nil) + vkDestroySemaphore(engine().vulkan.device, semaphore, nil) for imageView in swapchain.framebufferViews: - vkDestroyImageView(vulkan.device, imageView, nil) + vkDestroyImageView(engine().vulkan.device, imageView, nil) for framebuffer in swapchain.framebuffers: - vkDestroyFramebuffer(vulkan.device, framebuffer, nil) + vkDestroyFramebuffer(engine().vulkan.device, framebuffer, nil) - vkDestroyCommandPool(vulkan.device, swapchain.commandBufferPool, nil) + vkDestroyCommandPool(engine().vulkan.device, swapchain.commandBufferPool, nil) - vkDestroySwapchainKHR(vulkan.device, swapchain.vk, nil) + vkDestroySwapchainKHR(engine().vulkan.device, swapchain.vk, nil) proc tryAcquireNextImage(swapchain: Swapchain): Option[VkFramebuffer] = if not swapchain.queueFinishedFence[swapchain.currentFiF].await(100_000_000): return none(VkFramebuffer) let nextImageResult = vkAcquireNextImageKHR( - vulkan.device, + engine().vulkan.device, swapchain.vk, high(uint64), swapchain.imageAvailableSemaphore[swapchain.currentFiF], @@ -240,7 +249,7 @@ pSignalSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentFiF]), ) checkVkResult vkQueueSubmit( - queue = vulkan.graphicsQueue, + queue = engine().vulkan.graphicsQueue, submitCount = 1, pSubmits = addr(submitInfo), fence = swapchain.queueFinishedFence[swapchain.currentFiF], @@ -255,7 +264,8 @@ pImageIndices: addr(swapchain.currentFramebufferIndex), pResults: nil, ) - let presentResult = vkQueuePresentKHR(vulkan.graphicsQueue, addr(presentInfo)) + let presentResult = + vkQueuePresentKHR(engine().vulkan.graphicsQueue, addr(presentInfo)) if swapchain.oldSwapchain != nil: dec swapchain.oldSwapchainCounter @@ -272,33 +282,32 @@ # for re-creation with same settings, e.g. window resized proc recreateSwapchain*() = let newSwapchain = initSwapchain( - renderPass = vulkan.swapchain.renderPass, - vSync = vulkan.swapchain.vSync, - tripleBuffering = vulkan.swapchain.tripleBuffering, - oldSwapchain = vulkan.swapchain, + renderPass = engine().vulkan.swapchain.renderPass, + vSync = engine().vulkan.swapchain.vSync, + tripleBuffering = engine().vulkan.swapchain.tripleBuffering, + oldSwapchain = engine().vulkan.swapchain, ) if newSwapchain != nil: - vulkan.swapchain = newSwapchain + engine().vulkan.swapchain = newSwapchain # for re-creation with different settings proc recreateSwapchain*(vSync: bool, tripleBuffering: bool) = let newSwapchain = initSwapchain( - renderPass = vulkan.swapchain.renderPass, + renderPass = engine().vulkan.swapchain.renderPass, vSync = vSync, tripleBuffering = tripleBuffering, - oldSwapchain = vulkan.swapchain, + oldSwapchain = engine().vulkan.swapchain, ) if newSwapchain != nil: - vulkan.swapchain = newSwapchain + engine().vulkan.swapchain = newSwapchain template withNextFrame*(framebufferName, commandBufferName, body: untyped): untyped = - assert vulkan.swapchain != nil, "Swapchain has not been initialized yet" - var maybeFramebuffer = tryAcquireNextImage(vulkan.swapchain) + var maybeFramebuffer = tryAcquireNextImage(engine().vulkan.swapchain) if maybeFramebuffer.isSome: block: let `framebufferName` {.inject.} = maybeFramebuffer.get let `commandBufferName` {.inject.} = - vulkan.swapchain.commandBuffers[vulkan.swapchain.currentFiF] + engine().vulkan.swapchain.commandBuffers[engine().vulkan.swapchain.currentFiF] let beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), @@ -311,6 +320,7 @@ body checkVkResult vkEndCommandBuffer(`commandBufferName`) - discard swap(swapchain = vulkan.swapchain, commandBuffer = `commandBufferName`) + discard + swap(swapchain = engine().vulkan.swapchain, commandBuffer = `commandBufferName`) else: recreateSwapchain()
--- a/semicongine/rendering/vulkan/api.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/vulkan/api.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,10 +1,3 @@ -import std/dynlib -import std/logging -import std/macros -import std/strutils -import std/typetraits -import std/tables - type VkHandle* = distinct uint VkNonDispatchableHandle* = distinct uint
--- a/semicongine/rendering/vulkan_wrappers.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/rendering/vulkan_wrappers.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,4 +1,9 @@ -proc getBestPhysicalDevice(instance: VkInstance): VkPhysicalDevice = +import std/strformat +import std/strutils + +import ../core + +proc getBestPhysicalDevice*(instance: VkInstance): VkPhysicalDevice = var nDevices: uint32 checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil) var devices = newSeq[VkPhysicalDevice](nDevices) @@ -29,11 +34,14 @@ proc svkGetPhysicalDeviceSurfaceSupportKHR*(queueFamily: uint32): bool = var presentation = VkBool32(false) checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR( - vulkan.physicalDevice, queueFamily, vulkan.surface, addr(presentation) + engine().vulkan.physicalDevice, + queueFamily, + engine().vulkan.surface, + addr(presentation), ) return bool(presentation) -proc getQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = +proc getQueueFamily*(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = var nQueuefamilies: uint32 vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) @@ -59,11 +67,14 @@ proc svkGetPhysicalDeviceSurfacePresentModesKHR*(): seq[VkPresentModeKHR] = var n_modes: uint32 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR( - vulkan.physicalDevice, vulkan.surface, addr(n_modes), nil + engine().vulkan.physicalDevice, engine().vulkan.surface, addr(n_modes), nil ) result = newSeq[VkPresentModeKHR](n_modes) checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR( - vulkan.physicalDevice, vulkan.surface, addr(n_modes), result.ToCPointer + engine().vulkan.physicalDevice, + engine().vulkan.surface, + addr(n_modes), + result.ToCPointer, ) proc hasValidationLayer*(): bool = @@ -78,7 +89,7 @@ return false proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties = - vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result)) + vkGetPhysicalDeviceProperties(engine().vulkan.physicalDevice, addr(result)) proc svkCreateBuffer*(size: uint64, usage: openArray[VkBufferUsageFlagBits]): VkBuffer = var createInfo = VkBufferCreateInfo( @@ -89,7 +100,7 @@ sharingMode: VK_SHARING_MODE_EXCLUSIVE, ) checkVkResult vkCreateBuffer( - device = vulkan.device, + device = engine().vulkan.device, pCreateInfo = addr(createInfo), pAllocator = nil, pBuffer = addr(result), @@ -102,7 +113,7 @@ memoryTypeIndex: typeIndex, ) checkVkResult vkAllocateMemory( - vulkan.device, addr(memoryAllocationInfo), nil, addr(result) + engine().vulkan.device, addr(memoryAllocationInfo), nil, addr(result) ) proc svkCreate2DImage*( @@ -114,7 +125,7 @@ ): VkImage = var imageProps: VkImageFormatProperties checkVkResult vkGetPhysicalDeviceImageFormatProperties( - vulkan.physicalDevice, + engine().vulkan.physicalDevice, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, @@ -136,14 +147,14 @@ sharingMode: VK_SHARING_MODE_EXCLUSIVE, samples: samples, ) - checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) + checkVkResult vkCreateImage(engine().vulkan.device, addr imageInfo, nil, addr(result)) proc svkCreate2DImageView*( image: VkImage, format: VkFormat, aspect = VK_IMAGE_ASPECT_COLOR_BIT, nLayers = 1'u32, - isArray = false + isArray = false, ): VkImageView = var createInfo = VkImageViewCreateInfo( sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -164,7 +175,9 @@ layerCount: nLayers, ), ) - checkVkResult vkCreateImageView(vulkan.device, addr(createInfo), nil, addr(result)) + checkVkResult vkCreateImageView( + engine().vulkan.device, addr(createInfo), nil, addr(result) + ) proc svkCreateFramebuffer*( renderpass: VkRenderPass, width, height: uint32, attachments: openArray[VkImageView] @@ -179,14 +192,14 @@ layers: 1, ) checkVkResult vkCreateFramebuffer( - vulkan.device, addr(framebufferInfo), nil, addr(result) + engine().vulkan.device, addr(framebufferInfo), nil, addr(result) ) proc svkGetBufferMemoryRequirements*( buffer: VkBuffer ): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = var reqs: VkMemoryRequirements - vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs)) + vkGetBufferMemoryRequirements(engine().vulkan.device, buffer, addr(reqs)) result.size = reqs.size result.alignment = reqs.alignment for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: @@ -197,7 +210,7 @@ image: VkImage ): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = var reqs: VkMemoryRequirements - vkGetImageMemoryRequirements(vulkan.device, image, addr(reqs)) + vkGetImageMemoryRequirements(engine().vulkan.device, image, addr(reqs)) result.size = reqs.size result.alignment = reqs.alignment for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: @@ -213,24 +226,29 @@ else: VkFenceCreateFlags(0), ) - checkVkResult vkCreateFence(vulkan.device, addr(fenceInfo), nil, addr(result)) + checkVkResult vkCreateFence( + engine().vulkan.device, addr(fenceInfo), nil, addr(result) + ) proc svkCreateSemaphore*(): VkSemaphore = var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) - checkVkResult vkCreateSemaphore(vulkan.device, addr(semaphoreInfo), nil, addr(result)) + checkVkResult vkCreateSemaphore( + engine().vulkan.device, addr(semaphoreInfo), nil, addr(result) + ) proc await*(fence: VkFence, timeout = high(uint64)): bool = - let waitResult = vkWaitForFences(vulkan.device, 1, addr(fence), false, timeout) + let waitResult = + vkWaitForFences(engine().vulkan.device, 1, addr(fence), false, timeout) if waitResult == VK_TIMEOUT: return false checkVkResult waitResult return true proc svkResetFences*(fence: VkFence) = - checkVkResult vkResetFences(vulkan.device, 1, addr(fence)) + checkVkResult vkResetFences(engine().vulkan.device, 1, addr(fence)) -proc svkCmdBindDescriptorSets( +proc svkCmdBindDescriptorSets*( commandBuffer: VkCommandBuffer, descriptorSets: openArray[VkDescriptorSet], layout: VkPipelineLayout, @@ -246,7 +264,7 @@ pDynamicOffsets = nil, ) -proc svkCmdBindDescriptorSet( +proc svkCmdBindDescriptorSet*( commandBuffer: VkCommandBuffer, descriptorSet: VkDescriptorSet, index: DescriptorSetIndex, @@ -263,7 +281,7 @@ pDynamicOffsets = nil, ) -proc svkCreateRenderPass( +proc svkCreateRenderPass*( attachments: openArray[VkAttachmentDescription], colorAttachments: openArray[VkAttachmentReference], depthAttachments: openArray[VkAttachmentReference], @@ -292,11 +310,15 @@ dependencyCount: uint32(dependencies.len), pDependencies: dependencies.ToCPointer, ) - checkVkResult vkCreateRenderPass(vulkan.device, addr(createInfo), nil, addr(result)) + checkVkResult vkCreateRenderPass( + engine().vulkan.device, addr(createInfo), nil, addr(result) + ) proc bestMemory*(mappable: bool, filter: seq[uint32] = @[]): uint32 = var physicalProperties: VkPhysicalDeviceMemoryProperties - vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) + vkGetPhysicalDeviceMemoryProperties( + engine().vulkan.physicalDevice, addr(physicalProperties) + ) var maxScore: float = -1 var maxIndex: uint32 = 0 @@ -329,10 +351,10 @@ createInfo = VkCommandPoolCreateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, flags: VkCommandPoolCreateFlags(0), - queueFamilyIndex: vulkan.graphicsQueueFamily, + queueFamilyIndex: engine().vulkan.graphicsQueueFamily, ) checkVkResult vkCreateCommandPool( - vulkan.device, addr createInfo, nil, addr(commandBufferPool) + engine().vulkan.device, addr createInfo, nil, addr(commandBufferPool) ) var `cmd` {.inject.}: VkCommandBuffer @@ -342,7 +364,9 @@ level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, commandBufferCount: 1, ) - checkVkResult vulkan.device.vkAllocateCommandBuffers(addr allocInfo, addr(`cmd`)) + checkVkResult engine().vulkan.device.vkAllocateCommandBuffers( + addr allocInfo, addr(`cmd`) + ) var beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), @@ -359,10 +383,12 @@ ) var fence = svkCreateFence() - checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence) + checkVkResult vkQueueSubmit( + engine().vulkan.graphicsQueue, 1, addr(submitInfo), fence + ) discard fence.await() - vkDestroyFence(vulkan.device, fence, nil) - vkDestroyCommandPool(vulkan.device, commandBufferPool, nil) + vkDestroyFence(engine().vulkan.device, fence, nil) + vkDestroyCommandPool(engine().vulkan.device, commandBufferPool, nil) template withStagingBuffer*[T: (VkBuffer, uint64) | (VkImage, uint32, uint32, uint32)]( target: T, bufferSize: uint64, dataPointer, body: untyped @@ -373,14 +399,16 @@ let memoryType = bestMemory(mappable = true, filter = memoryRequirements.memoryTypes) let stagingMemory = svkAllocateMemory(memoryRequirements.size, memoryType) checkVkResult vkMapMemory( - device = vulkan.device, + device = engine().vulkan.device, memory = stagingMemory, offset = 0'u64, size = VK_WHOLE_SIZE, flags = VkMemoryMapFlags(0), ppData = addr(`dataPointer`), ) - checkVkResult vkBindBufferMemory(vulkan.device, stagingBuffer, stagingMemory, 0) + checkVkResult vkBindBufferMemory( + engine().vulkan.device, stagingBuffer, stagingMemory, 0 + ) block: # usually: write data to dataPointer in body @@ -391,7 +419,7 @@ memory: stagingMemory, size: VK_WHOLE_SIZE, ) - checkVkResult vkFlushMappedMemoryRanges(vulkan.device, 1, addr(stagingRange)) + checkVkResult vkFlushMappedMemoryRanges(engine().vulkan.device, 1, addr(stagingRange)) withSingleUseCommandBuffer(commandBuffer): when T is (VkBuffer, uint64): @@ -443,5 +471,53 @@ pRegions = addr(region), ) - vkDestroyBuffer(vulkan.device, stagingBuffer, nil) - vkFreeMemory(vulkan.device, stagingMemory, nil) + vkDestroyBuffer(engine().vulkan.device, stagingBuffer, nil) + vkFreeMemory(engine().vulkan.device, stagingMemory, nil) + +func getDescriptorType*[T](): VkDescriptorType {.compileTIme.} = + when T is ImageObject: + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + elif T is GPUValue: + when getBufferType(default(T)) in [UniformBuffer, UniformBufferMapped]: + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + elif getBufferType(default(T)) in [StorageBuffer, StorageBufferMapped]: + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + else: + {.error: "Unsupported descriptor type: " & $T.} + elif T is array: + when elementType(default(T)) is ImageObject: + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + elif elementType(default(T)) is GPUValue: + when getBufferType(default(elementType(default(T)))) in + [UniformBuffer, UniformBufferMapped]: + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + elif getBufferType(default(elementType(default(T)))) in + [StorageBuffer, StorageBufferMapped]: + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + else: + {.error: "Unsupported descriptor type: " & $T.} + else: + {.error: "Unsupported descriptor type: " & $T.} + else: + {.error: "Unsupported descriptor type: " & $T.} + +func getDescriptorCount*[T](): uint32 {.compileTIme.} = + when T is array: + len(T) + else: + 1 + +func getBindingNumber*[T](field: static string): uint32 {.compileTime.} = + var c = 0'u32 + var found = false + for name, value in fieldPairs(default(T)): + when name == field: + result = c + found = true + else: + inc c + assert found, "Field '" & field & "' of descriptor '" & $T & "' not found" + +proc currentFiF*(): int = + assert engine().vulkan.swapchain != nil, "Swapchain has not been initialized yet" + engine().vulkan.swapchain.currentFiF
--- a/semicongine/resources.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/resources.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,7 +1,6 @@ import std/algorithm import std/dirs import std/json -import std/parsecfg import std/os import std/sequtils import std/sets
--- a/semicongine/text.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/text.nim Thu Jan 09 01:00:58 2025 +0700 @@ -11,116 +11,10 @@ import ./core import ./resources import ./rendering -import ./rendering/vulkan/api import ./image import ./contrib/algorithms/texture_packing - -type - 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] - - GlyphShader*[MaxGlyphs: static int] = object - position {.InstanceAttribute.}: Vec3f - color {.InstanceAttribute.}: Vec4f - scale {.InstanceAttribute.}: float32 - glyphIndex {.InstanceAttribute.}: uint16 - textRendering {.PushConstant.}: TextRendering - - fragmentUv {.Pass.}: Vec2f - fragmentColor {.PassFlat.}: Vec4f - outColor {.ShaderOutput.}: Vec4f - glyphData {.DescriptorSet: 3.}: GlyphDescriptorSet[MaxGlyphs] - vertexCode* = - """ -const int[6] indices = int[](0, 1, 2, 2, 3, 0); -const int[4] i_x = int[](0, 0, 2, 2); -const int[4] i_y = int[](1, 3, 3, 1); -const float epsilon = 0.0000001; -// const float epsilon = 0.1; - -void main() { - int vertexI = indices[gl_VertexIndex]; - vec3 vertexPos = vec3( - glyphquads.pos[glyphIndex][i_x[vertexI]] * scale / textRendering.aspectRatio, - glyphquads.pos[glyphIndex][i_y[vertexI]] * scale, - 0 - ); - // the epsilon-offset is necessary, as otherwise characters with the same Z might overlap, despite transparency - gl_Position = vec4(vertexPos + position - vec3(0, 0, clamp(0, 1, gl_InstanceIndex * epsilon)), 1.0); - vec2 uv = vec2(glyphquads.uv[glyphIndex][i_x[vertexI]], glyphquads.uv[glyphIndex][i_y[vertexI]]); - fragmentUv = uv; - fragmentColor = color; -} """ - fragmentCode* = - """void main() { - float a = texture(fontAtlas, fragmentUv).r; - outColor = vec4(fragmentColor.rgb, fragmentColor.a * a); -}""" - - FontObj*[MaxGlyphs: static int] = object - advance*: Table[Rune, float32] - kerning*: Table[(Rune, Rune), float32] - leftBearing*: Table[Rune, float32] - lineAdvance*: float32 - lineHeight*: float32 # like lineAdvance - lineGap - ascent*: float32 # from baseline to highest glyph - descent*: float32 # from baseline to lowest glyph - xHeight*: float32 # from baseline to height of lowercase x - descriptorSet*: DescriptorSetData[GlyphDescriptorSet[MaxGlyphs]] - descriptorGlyphIndex: Table[Rune, uint16] - descriptorGlyphIndexRev*: Table[uint16, Rune] # only used for debugging atm - fallbackCharacter: Rune - - Font*[MaxGlyphs: static int] = ref FontObj[MaxGlyphs] - - TextHandle* = object - index: uint32 - generation: uint32 - - 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 - generation: uint32 - 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.} - -proc `=copy`[MaxGlyphs: static int]( - dest: var TextBuffer[MaxGlyphs], source: TextBuffer[MaxGlyphs] -) {.error.} - -include ./text/font +import ./rendering/renderer +import ./text/font proc initTextBuffer*[MaxGlyphs: static int]( font: Font[MaxGlyphs],
--- a/semicongine/text/font.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/text/font.nim Thu Jan 09 01:00:58 2025 +0700 @@ -1,3 +1,16 @@ +import std/os +import std/strutils +import std/sequtils +import std/unicode +import std/streams +import std/logging +import std/tables + +import ../core +import ../resources +import ../rendering/renderer +import ../contrib/algorithms/texture_packing + {.emit: "#define STBTT_STATIC".} {.emit: "#define STB_TRUETYPE_IMPLEMENTATION".} {.
--- a/semicongine/thirdparty/parsetoml.nim Wed Jan 01 19:36:55 2025 +0700 +++ b/semicongine/thirdparty/parsetoml.nim Thu Jan 09 01:00:58 2025 +0700 @@ -795,7 +795,6 @@ raise newTomlError(state, "leading zero not allowed") else: raise newTomlError(state, "illegal character") - break proc parseFloat(state: var ParserState, intPart: int, forcedSign: Sign): TomlValueRef = var @@ -935,7 +934,6 @@ else: curSum, ) - break of '+', '-': forcedSign = if nextChar == '+': Pos else: Neg continue