diff --git a/.ignore b/.ignore index 60758b0..8ce5f45 100644 --- a/.ignore +++ b/.ignore @@ -1,3 +1,4 @@ .git build external +assets diff --git a/dub.json b/dub.json index 3f9d297..f97fe16 100644 --- a/dub.json +++ b/dub.json @@ -7,7 +7,7 @@ "targetType": "executable", "targetName": "Gears", "targetPath": "build", - "sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a"], + "sourceFiles-linux": ["build/libvma.a", "build/libstb_image.a", "build/libm3d.a"], "sourceFiles-windows": [], "importPaths": ["src/gears", "src/shared", "src/generated", "external/xxhash"], "sourcePaths": ["src/gears", "src/shared", "src/generated", "external/xxhash"], diff --git a/src/gears/renderer.d b/src/gears/renderer.d index d56829a..7c6f149 100644 --- a/src/gears/renderer.d +++ b/src/gears/renderer.d @@ -12,7 +12,6 @@ import p = platform; alias Shader = VkShaderModule; alias Pipeline = PipelineHandle; alias Attribute = VkVertexInputAttributeDescription; -alias Buffer = VkBuffer; enum InputRate : int { @@ -78,6 +77,8 @@ struct Renderer Pipeline triangle_pipeline; Pipeline compute_pipeline; Pipeline ui_pipeline; + + Model yoder; } struct GlobalUniforms @@ -90,6 +91,20 @@ struct ShaderUniforms f32 placeholder; } +struct Material +{ + u32 albedo_texture; + u32 ambient_texture; + u32 specular_texture; + b32 albedo_has_texture; + b32 ambient_has_texture; + b32 specular_has_texture; + Vec4 ambient; + Vec4 diffuse; + Vec4 specular; + f32 shininess = 0.0; +} + struct UIVertex { Vec2 p0; @@ -100,8 +115,8 @@ struct UIVertex struct Vertex { - Vec3 pos; - Vec3 n; + Vec4 pos; + Vec4 n; Vec2 uv; u32 col; } @@ -122,10 +137,7 @@ struct Model string name; Buffer[] materials; - u32[] m_indices; - ImageView[] textures; - u32[] t_indices; } Renderer @@ -165,6 +177,8 @@ Init(p.Window* window) rd.ui_pipeline = BuildGfxPipeline(&rd, &ui_info); rd.compute_pipeline = BuildCompPipeline(&rd, "shaders/gradient.comp"); + rd.yoder = LoadModel(&rd, "models/yoda"); + return rd; } @@ -215,64 +229,216 @@ LoadModel(Renderer* rd, string name) u8[] data = LoadAssetData(&rd.temp_arena, name); m3d_t* m3d = m3d_load(data.ptr, null, null, null); + u32[] tex_lookup = AllocArray!(u32)(&rd.temp_arena, m3d.numtexture); + model.textures = AllocArray!(ImageView)(&rd.arena, m3d.numtexture); + foreach(i; 0 .. m3d.numtexture) + { + u32 w = m3d.texture[i].w; u32 h = m3d.texture[i].h; u32 ch = m3d.texture[i].f; + + model.textures[i] = CreateImageView(&rd.vk, w, h, ch); + u8[] texture_data = m3d.texture[i].d[0 .. w * h * ch]; + assert(Transfer(&rd.vk, &model.textures[i], texture_data, w, h), "LoadModel failure: Texture transfer error"); + + tex_lookup[i] = Pop(&rd.vk, DT.SampledImage); + } + + u32[] mat_lookup = AllocArray!(u32)(&rd.temp_arena, m3d.nummaterial); + model.materials = AllocArray!(Buffer)(&rd.arena, m3d.nummaterial); + foreach(i; 0 .. m3d.nummaterial) + { + Material mat; + + foreach(j; 0 .. m3d.material[i].numprop) + { + switch (m3d.material[i].prop[j].type) + { + case m3dp_Ka: ConvertColor(&mat.ambient, m3d.material[i].prop[j].value.color); break; + case m3dp_Ks: ConvertColor(&mat.specular, m3d.material[i].prop[j].value.color); break; + case m3dp_Ns: mat.shininess = m3d.material[i].prop[j].value.fnum; break; + case m3dp_map_Kd: + { + mat.albedo_texture = tex_lookup[m3d.material[i].prop[j].value.textureid]; + mat.albedo_has_texture = true; + } break; + case m3dp_map_Ka: + { + mat.ambient_texture = tex_lookup[m3d.material[i].prop[j].value.textureid]; + mat.ambient_has_texture = true; + } break; + case m3dp_map_Ks: + { + mat.specular_texture = tex_lookup[m3d.material[i].prop[j].value.textureid]; + mat.specular_has_texture = true; + } break; + default: break; + } + } + + model.materials[i] = CreateBuffer(&rd.vk, BT.Uniform, Material.sizeof, false); + assert(Transfer(&rd.vk, &model.materials[i], &mat), "LoadModel failure: Transfer error when transferring material"); + + mat_lookup[i] = Pop(&rd.vk, DT.Material); + } + + u32 mesh_count = 0; + u64 last = u64.max; + foreach(i; 0 .. m3d.numface) + { + if (m3d.face[i].materialid != last) + { + last = m3d.face[i].materialid; + mesh_count += 1; + } + } + + model.parts = AllocArray!(MeshPart)(&rd.arena, mesh_count); + last = u64.max; + u32 index = 0; + foreach(i; 0 .. m3d.numface) + { + if (last == u64.max) + { + model.parts[index].mat = mat_lookup[m3d.face[i].materialid]; + model.parts[index].offset = 0; + last = m3d.face[i].materialid; + } + else if (m3d.face[i].materialid != last) + { + u32 vertex_index = i * 3; + + model.parts[index].length = vertex_index - model.parts[index].offset; + index += 1; + + model.parts[index].mat = mat_lookup[m3d.face[i].materialid]; + model.parts[index].offset = vertex_index; + last = m3d.face[i].materialid; + } + else if (i == m3d.numface-1) + { + u32 vertex_index = i * 3; + model.parts[index].length = vertex_index+3 - model.parts[index].offset; + } + } + + m3dv_t* vertex; + + u32[] indices = AllocArray!(u32)(&rd.temp_arena, m3d.numface * 3); + Vertex[] vertices = AllocArray!(Vertex)(&rd.temp_arena, m3d.numface * 3); + + foreach(i; 0 .. m3d.numface) + { + u32 vi = (i * 3); + + CopyVertex(&vertices[vi+0].pos, &m3d.vertex[m3d.face[i].vertex[0]]); + CopyVertex(&vertices[vi+1].pos, &m3d.vertex[m3d.face[i].vertex[1]]); + CopyVertex(&vertices[vi+2].pos, &m3d.vertex[m3d.face[i].vertex[2]]); + + CopyVertex(&vertices[vi+0].n, &m3d.vertex[m3d.face[i].vertex[0]]); + CopyVertex(&vertices[vi+1].n, &m3d.vertex[m3d.face[i].vertex[1]]); + CopyVertex(&vertices[vi+2].n, &m3d.vertex[m3d.face[i].vertex[2]]); + + vertices[vi+0].col = m3d.vertex[m3d.face[i].vertex[0]].color; + vertices[vi+1].col = m3d.vertex[m3d.face[i].vertex[1]].color; + vertices[vi+2].col = m3d.vertex[m3d.face[i].vertex[2]].color; + + if (m3d.numtmap) + { + vertices[vi+0].uv.x = m3d.tmap[m3d.face[i].texcoord[0]].u; + vertices[vi+0].uv.y = m3d.tmap[m3d.face[i].texcoord[0]].v; + + vertices[vi+1].uv.x = m3d.tmap[m3d.face[i].texcoord[1]].u; + vertices[vi+1].uv.y = m3d.tmap[m3d.face[i].texcoord[1]].v; + + vertices[vi+2].uv.x = m3d.tmap[m3d.face[i].texcoord[2]].u; + vertices[vi+2].uv.y = m3d.tmap[m3d.face[i].texcoord[2]].v; + } + + indices[vi+0] = vi+0; + indices[vi+1] = vi+1; + indices[vi+2] = vi+2; + } + + model.vertex_buffer = CreateBuffer(&rd.vk, BT.Vertex, vertices.length * Vertex.sizeof, false); + model.index_buffer = CreateBuffer(&rd.vk, BT.Index, indices.length * u32.sizeof, false); + + assert(Transfer(&rd.vk, &model.vertex_buffer, vertices), "LoadModel failure: Unable to transfer vertex buffer"); + assert(Transfer(&rd.vk, &model.index_buffer, indices), "LoadModel failure: Unable to transfer index buffer"); + + Reset(&rd.temp_arena); + return model; } -bool +pragma(inline): void +CopyVertex(Vec4* dst, m3dv_t* src) +{ + asm + { + mov R8, src; + mov R9, dst; + movups XMM0, src.x.offsetof[R8]; + movups dst.x.offsetof[R9], XMM0; + } + + assert(dst.x == src.x && dst.y == src.y && dst.z == src.z && dst.w == src.w, "CopyVertex failure"); +} + +pragma(inline): bool BeginFrame(Renderer* rd) { return BeginFrame(&rd.vk); } -bool +pragma(inline): bool FinishFrame(Renderer* rd) { return FinishFrame(&rd.vk); } -void +pragma(inline): void Dispatch(Renderer* rd) { Dispatch(&rd.vk); } -void +pragma(inline): void PrepCompute(Renderer* rd) { PrepCompute(&rd.vk); } -void +pragma(inline): void SetUniform(Renderer* rd, GlobalUniforms* uniforms) { SetUniform(&rd.vk, uniforms); } -void +pragma(inline): void BeginRender(Renderer* rd) { BeginRender(&rd.vk); } -void +pragma(inline): void BindUIBuffers(Renderer* rd) { BindUIBuffers(&rd.vk); } -void +pragma(inline): void DrawIndexed(Renderer* rd, u32 index_count, u32 instance_count) { DrawIndexed(&rd.vk, index_count, instance_count); } -void +pragma(inline): void Draw(Renderer* rd, u32 index_count, u32 instance_count) { Draw(&rd.vk, index_count, instance_count); } -void Bind(Renderer* rd, Pipeline* pipeline) +pragma(inline): void +Bind(Renderer* rd, Pipeline* pipeline) { Bind(&rd.vk, pipeline); } @@ -298,13 +464,13 @@ DrawRect(Renderer* rd, f32 p0_x, f32 p0_y, f32 p1_x, f32 p1_y, Vec4 col) rd.ui_count += 1; } -Pipeline +pragma(inline): Pipeline BuildGfxPipeline(Renderer* rd, GfxPipelineInfo* info) { return CreateGraphicsPipeline(&rd.vk, info); } -Pipeline +pragma(inline): Pipeline BuildCompPipeline(Renderer* rd, string compute) { return CreateComputePipeline(&rd.vk, compute); diff --git a/src/gears/vulkan.d b/src/gears/vulkan.d index 42c6b71..bfe6880 100644 --- a/src/gears/vulkan.d +++ b/src/gears/vulkan.d @@ -3,9 +3,9 @@ import vulkan_logging; import aliases; import std.stdio; import std.algorithm.comparison; -import core.stdc.string : strcmp; +import core.stdc.string : strcmp, memcpy; import std.format : sformat; -import u = util : HashTable, Result, Logf, Log, MB; +import u = util : HashTable, Result, Logf, Log, MB, Delete; import alloc; import p = platform; import ap = assets; @@ -643,10 +643,12 @@ CreateImageView(Vulkan* vk, u32 w, u32 h, u32 ch) } ImageView view = { - format: VK_IMAGE_LAYOUT_UNDEFINED, + base: { + layout: VK_IMAGE_LAYOUT_UNDEFINED, + }, }; - VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, *view.alloc, null); + VkResult result = vmaCreateImage(vk.vma, &image_info, &alloc_info, &view.image, &view.alloc, null); // TODO: handle errors and realloc assert(VkCheck("CreateImageView failure: vmaCreateImage error", result), "CreateImageView failure"); @@ -670,9 +672,66 @@ CreateImageView(Vulkan* vk, u32 w, u32 h, u32 ch) } u32 -Push(Vulkan* vk, ImageView* view) +Pop(Vulkan* vk, DescType type) { + DescBindings* bindings = vk.desc_bindings.ptr + type; + assert(bindings.count > 0, "Pop failure: no free bindings remaining"); + bindings.count -= 1; + + return bindings.free[bindings.count]; +} + +void +Push(Vulkan* vk, u32 index, DescType type) +{ + DescBindings* bindings = vk.desc_bindings.ptr + type; + + bindings.free[bindings.count] = index; + bindings.count += 1; +} + +u32 +Pop(Vulkan* vk, string name, DescType type) +{ + DescBindings* bindings = vk.desc_bindings.ptr + type; + + u32 index; + auto result = bindings.lookup_table[name]; + if (!result.ok) + { + // TODO: handle unbinding assets (maybe) + assert(bindings.count > 0, "Pop failure: no free bindings remaining"); + bindings.count -= 1; + index = bindings.free[bindings.count]; + } + else + { + index = result.value; + } + + return index; +} + +void +Push(Vulkan* vk, string name, DescType type) +{ + DescBindings* bindings = vk.desc_bindings.ptr + type; + auto result = Delete(&bindings.lookup_table, name); + if (result.ok) + { + assert(bindings.count < bindings.free.length, "Push failure: attempt to push a binding into a full stack"); + + bindings.free[bindings.count] = result.value; + bindings.count += 1; + } +} + +bool +Transfer(T)(Vulkan* vk, Buffer* buf, T[] data) +{ + u8[] u8_data = (cast(u8*)(data.ptr))[0 .. T.sizeof * data.length]; + return Transfer(vk, buf, u8_data); } bool @@ -708,6 +767,33 @@ Transfer(Vulkan* vk, Buffer* buf, u8[] data) return success; } +bool +Transfer(T)(Vulkan* vk, Buffer* buf, T* ptr) +{ + assert(T.sizeof < vk.transfer_buf.data.length, "Transfer failure: structure size is too large"); + + memcpy(vk.transfer_buf.data.ptr, ptr, T.sizeof); + + auto fn = delegate() + { + VkBufferCopy copy = { + srcOffset: 0, + dstOffset: 0, + size: T.sizeof, + }; + + vkCmdCopyBuffer(vk.imm_cmd, vk.transfer_buf.buffer, buf.buffer, 1, ©); + }; + + return ImmSubmit(vk, fn); +} + +pragma(inline): bool +Transfer(Vulkan* vk, ImageView* view, u8[] data, u32 w, u32 h) +{ + return Transfer(vk, &view.base, data, w, h); +} + bool Transfer(Vulkan* vk, Image* image, u8[] data, u32 w, u32 h) { diff --git a/src/shared/util.d b/src/shared/util.d index 45cd275..a518157 100644 --- a/src/shared/util.d +++ b/src/shared/util.d @@ -42,6 +42,29 @@ GB(u64 v) return MB(v) * 1024; }; +pragma(inline): void +ConvertColor(Vec4 *dst, u32 src) +{ + if (src == 0) + { + dst.v[] = 0.0; + dst.a = 1.0; + } + else + { + Convert(dst, src); + } +} + +pragma(inline): void +Convert(Vec4* dst, u32 src) +{ + dst.r = cast(f32)((src >> 0) & 0xFF) / 255.0; + dst.g = cast(f32)((src >> 8) & 0xFF) / 255.0; + dst.b = cast(f32)((src >> 16) & 0xFF) / 255.0; + dst.a = cast(f32)((src >> 24) & 0xFF) / 255.0; +} + bool BitEq(u64 l, u64 r) { @@ -103,6 +126,30 @@ Pop(T)(SLList!(T)*list, Node!(T)* nil) return node; } +pragma(inline): void +Remove(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* prev, Node!(T)* nil) +{ + node.next = nil; + + if (list.first == list.last) + { + list.first = list.last = nil; + } + else if (list.first == node) + { + list.first = node.next; + } + else if (list.last == node) + { + list.last = prev; + prev.next = nil; + } + else + { + prev.next = node.next; + } +} + pragma(inline): void PushFront(T)(SLList!(T)*list, Node!(T)* node, Node!(T)* nil) { @@ -282,10 +329,7 @@ Delete(K, V)(HashTable!(K, V)* ht, K key) { if (node.value.key == key) { - if (prev != ht.nil) - { - prev.next = node.next; - } + Remove(list, node, prev, ht.nil); result.ok = true; result.value = node.value.value;