diff --git a/.gitmodules b/.gitmodules index 74c0b8f..e222a0b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "thirdparty/miniz"] path = subprojects/miniz url = https://github.com/richgel999/miniz +[submodule "subprojects/libspng"] + path = subprojects/libspng + url = https://github.com/randy408/libspng diff --git a/include/Orange/Core/Hash.h b/include/Orange/Core/Hash.h index 1ff1a3d..4ef704c 100644 --- a/include/Orange/Core/Hash.h +++ b/include/Orange/Core/Hash.h @@ -13,4 +13,15 @@ namespace orange { return HashString(s, count); } + + inline size_t& hashCombine(size_t& seed) { (void)seed; return seed; } + + template + inline size_t& hashCombine(size_t& seed, const T& v, Rest... rest) + { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hashCombine(seed, rest...); + return seed; + } } diff --git a/include/Orange/Graphics/Mesh.h b/include/Orange/Graphics/Mesh.h index 6a88cec..805bb46 100644 --- a/include/Orange/Graphics/Mesh.h +++ b/include/Orange/Graphics/Mesh.h @@ -6,6 +6,18 @@ namespace orange { +//#define USE_32BIT_INDICES + +#ifdef USE_32BIT_INDICES + using IndexType = uint32_t; + static constexpr VkIndexType VulkanIndexType = VK_INDEX_TYPE_UINT32; + static constexpr size_t MaxMeshIndex = UINT32_MAX; +#else + using IndexType = uint16_t; + static constexpr VkIndexType VulkanIndexType = VK_INDEX_TYPE_UINT16; + static constexpr size_t MaxMeshIndex = UINT16_MAX; +#endif + struct MeshVertexData { MeshVertexData(MeshVertexType type) @@ -64,9 +76,9 @@ namespace orange MeshData(MeshVertexType type) : vertexData{ type } {} - MeshVertexData vertexData; - Vector indices; - AABB bounds; + MeshVertexData vertexData; + Vector indices; + AABB bounds; }; Result ParseOBJ(StringView buffer); diff --git a/include/Orange/Render/RenderContext.h b/include/Orange/Render/RenderContext.h index 558ce3c..27afd47 100644 --- a/include/Orange/Render/RenderContext.h +++ b/include/Orange/Render/RenderContext.h @@ -14,6 +14,7 @@ namespace orange VkBuffer buffer; VkDeviceSize offset; VkDeviceSize size; + void* ptr; }; struct GPUMemoryBuffer @@ -33,7 +34,8 @@ namespace orange Result AllocSlice(VkDeviceSize size, VkDeviceSize alignment = 1u) { uint32_t offset = Align(m_offset, alignment); - BufferSlice slice{ m_buffer.buffer, offset, size }; + BufferSlice slice{ m_buffer.buffer, offset, size, + m_buffer.ptr ? reinterpret_cast(m_buffer.ptr) + offset : nullptr }; if (offset + size > m_buffer.size) return Result::Error(); @@ -80,7 +82,8 @@ namespace orange VulkanResult BeginCommandBuffer(VkCommandBuffer buffer); VulkanResult EndCommandBuffer(VkCommandBuffer buffer); VulkanResult CreateBuffer(VkDeviceSize size); - VulkanResult CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format); + VulkanResult CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage); + VulkanResult CreateImageView(VkImage image, VkFormat format, VkImageAspectFlagBits aspect); VulkanResult CreateShader(Span code); diff --git a/include/Orange/Render/Window.h b/include/Orange/Render/Window.h index 63cdffb..2d222eb 100644 --- a/include/Orange/Render/Window.h +++ b/include/Orange/Render/Window.h @@ -25,6 +25,8 @@ namespace orange bool Update(Input::InputHandler& handler); void EnableRelativeMouse(bool relative); + + void SetTitle(const char* title); protected: friend Result; Window(SDL_Window* window); diff --git a/meson.build b/meson.build index 86e7f3b..2c9acc2 100644 --- a/meson.build +++ b/meson.build @@ -21,4 +21,15 @@ orange_include = include_directories(['include', 'subprojects']) sdl2_dep = dependency('SDL2') vulkan_dep = dependency('vulkan') # get rid of me! +miniz_proj = subproject('miniz', default_options: [ + 'default_library=static', +]) +miniz_dep = miniz_proj.get_variable('miniz_dep') + +spng_proj = subproject('libspng', default_options: [ + 'default_library=static', + 'use_miniz=True', +]) +spng_dep = spng_proj.get_variable('spng_dep') + subdir('src') diff --git a/src/Apps/Tools/CubeTest.cpp b/src/Apps/Tools/CubeTest.cpp index b717047..39107c0 100644 --- a/src/Apps/Tools/CubeTest.cpp +++ b/src/Apps/Tools/CubeTest.cpp @@ -18,9 +18,12 @@ #include #include #include +#include #include #include +#include + using namespace orange; struct MiniPipelineInfo @@ -103,7 +106,7 @@ static VulkanResult CreateGraphicsPipeline(VkDevice device, const Mi VkPipelineRasterizationStateCreateInfo rasterizationInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .cullMode = VK_CULL_MODE_NONE, //VK_CULL_MODE_BACK_BIT, + .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .lineWidth = 1.0f, }; @@ -191,6 +194,7 @@ int main(int argc, char** argv) if (!r_swapchain) return 1; + //auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/gman.obj"); auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/chair.obj"); //auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/cube.obj"); if (!r_objData) @@ -198,11 +202,16 @@ int main(int argc, char** argv) auto r_mesh = ParseOBJ(*r_objData); + auto r_buffer = r_renderContext->CreateBuffer(256 * 1024 * 1024); + if (!r_buffer) + return 1; + auto pooler = MemoryPool{ *r_buffer }; + auto r_vsMesh = r_renderContext->CreateShader(vs_Mesh); if (!r_vsMesh) return 1; auto r_vsFullscreen = r_renderContext->CreateShader(vs_Fullscreen); if (!r_vsFullscreen) return 1; - auto r_fsMesh = r_renderContext->CreateShader(fs_DebugVertColor); + auto r_fsMesh = r_renderContext->CreateShader(fs_Mesh); if (!r_fsMesh) return 1; auto r_fsRed = r_renderContext->CreateShader(fs_Red); if (!r_fsRed) return 1; @@ -354,10 +363,58 @@ int main(int argc, char** argv) .vertexAttributes = Span(nullptr, 0), }); - auto r_buffer = r_renderContext->CreateBuffer(256 * 1024 * 1024); - if (!r_buffer) + struct ImageUpload + { + BufferSlice from; + VkImage to; + VkExtent3D extent; + }; + Vector imageUploads; + + auto r_pngData = fs::OpenFileIntoBuffer("/home/joshua/chair_color.png"); + if (!r_pngData) return 1; + spng_ctx *ctx = spng_ctx_new(0); + spng_set_png_buffer(ctx, r_pngData->data, r_pngData->size); + size_t pngOutSize = 0zu; + spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &pngOutSize); + auto pngLinearSlice = *pooler.AllocSlice(pngOutSize, 16); + spng_decode_image(ctx, pngLinearSlice.ptr, pngOutSize, SPNG_FMT_RGBA8, 0); + struct spng_ihdr ihdr; + spng_get_ihdr(ctx, &ihdr); + spng_ctx_free(ctx); + + auto r_texture = r_renderContext->CreateImage(pooler, ihdr.width, ihdr.height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT); + auto r_textureView = r_renderContext->CreateImageView(*r_texture, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); + + VkDescriptorImageInfo descriptorImageInfo = + { + .imageView = *r_textureView, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + + VkWriteDescriptorSet writeImageDescriptorSet[] = + { + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSet, + .dstBinding = 2, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo = &descriptorImageInfo, + }, + }; + vkUpdateDescriptorSets(r_renderContext->Device(), Size(writeImageDescriptorSet), writeImageDescriptorSet, 0, nullptr); + + imageUploads.PushBack(ImageUpload + { + .from = pngLinearSlice, + .to = *r_texture, + .extent = VkExtent3D{ ihdr.width, ihdr.height, 1u }, + }); + struct UniformData { mat4 projection; @@ -377,25 +434,12 @@ int main(int argc, char** argv) .view = camera.view(), }; - auto pooler = MemoryPool{ *r_buffer }; auto meshData = r_mesh->vertexData.View(); auto vertexSlice = *pooler.AllocSlice(meshData.size, 16); - auto indexSlice = *pooler.AllocSlice(r_mesh->indices.Size() * sizeof(uint16_t), 16); + auto indexSlice = *pooler.AllocSlice(r_mesh->indices.Size() * sizeof(IndexType), 16); auto uniformSlice = *pooler.AllocSlice(sizeof(UniformData), 16); - auto r_depthImage = r_renderContext->CreateImage(pooler, 1280, 720, depthFormat); - - VkImageViewCreateInfo depthImageViewInfo = - { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = *r_depthImage, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = depthFormat, - .subresourceRange = FirstDepthMipSubresourceRange, - }; - - VkImageView depthImageView = VK_NULL_HANDLE; - if (vkCreateImageView(r_renderContext->Device(), &depthImageViewInfo, nullptr, &depthImageView) != VK_SUCCESS) - return 1; + auto r_depthImage = r_renderContext->CreateImage(pooler, 1280, 720, depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + auto depthImageView = *r_renderContext->CreateImageView(*r_depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); VkDescriptorBufferInfo bufferInfo = { @@ -430,9 +474,14 @@ int main(int argc, char** argv) while (r_window->Update(handler)) { const auto t2 = Time::now(); - const float dt = Seconds(t2 - t1).count(); + auto delta = t2 - t1; + const float dt = Seconds(delta).count(); t1 = t2; + //char title[64]; + //stbsp_snprintf(title, sizeof(title), "CubeTest - %gfps", 1.0f / dt); + //r_window->SetTitle(title); + { auto mouseDelta = handler.getMouseMotionDelta(); auto scrollDelta = handler.getMouseScrollDelta(); @@ -477,6 +526,24 @@ int main(int argc, char** argv) VkCommandBuffer cmdBuf = r_swapchain->CommandBuffer(); r_renderContext->BeginCommandBuffer(cmdBuf); { + for (auto& upload : imageUploads) + { + VkBufferImageCopy region = + { + .bufferOffset = upload.from.offset, + .imageSubresource = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0u, + .baseArrayLayer = 0u, + .layerCount = 1u, + }, + .imageExtent = upload.extent, + }; + vkCmdCopyBufferToImage(cmdBuf, upload.from.buffer, upload.to, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1, ®ion); + } + imageUploads.Clear(); + VkViewport viewport = { .x = 0.0f, @@ -498,7 +565,7 @@ int main(int argc, char** argv) vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdBindVertexBuffers2(cmdBuf, 0, 1, &vertexSlice.buffer, &vertexSlice.offset, &vertexSlice.size, nullptr); - vkCmdBindIndexBuffer(cmdBuf, r_buffer->buffer, indexSlice.offset, VK_INDEX_TYPE_UINT16); + vkCmdBindIndexBuffer(cmdBuf, r_buffer->buffer, indexSlice.offset, VulkanIndexType); const VkImageMemoryBarrier undefinedToColorBarrier = { diff --git a/src/Apps/Tools/meson.build b/src/Apps/Tools/meson.build index 0a2cadc..d9b62c2 100644 --- a/src/Apps/Tools/meson.build +++ b/src/Apps/Tools/meson.build @@ -1,4 +1,4 @@ executable('CubeTest', 'CubeTest.cpp', orange_src, orange_shaders, - dependencies : [ sdl2_dep, vulkan_dep ], + dependencies : [ sdl2_dep, vulkan_dep, spng_dep, miniz_dep ], include_directories : [ orange_include ] ) diff --git a/src/Orange/Graphics/Mesh.cpp b/src/Orange/Graphics/Mesh.cpp index 50fb0f8..b325815 100644 --- a/src/Orange/Graphics/Mesh.cpp +++ b/src/Orange/Graphics/Mesh.cpp @@ -3,9 +3,22 @@ #include #include +#include + +#include namespace orange { + struct StaticVertexHash + { + size_t operator() (const StaticVertex& key) const + { + size_t hash = 0; + return hashCombine(hash, key.pos.x, key.pos.y, key.pos.z, key.uv.x, key.uv.y, key.normal.x, key.normal.y, key.normal.z); + } + }; + + Result ParseOBJ(StringView buffer) { MeshData data{ MeshVertexType::Static }; @@ -14,6 +27,8 @@ namespace orange Vector uvs; Vector normals; + std::unordered_map indexTracker; + const char* obj = buffer.data; const char* end = buffer.data + buffer.size; while (obj != end) @@ -32,9 +47,9 @@ namespace orange } if (element == "v") - positions.EmplaceBack(vtx[0], vtx[1], vtx[2]); + positions.EmplaceBack(vtx[0], -vtx[2], vtx[1]); // TODO remove hack and make this customizable else if (element == "vt") - uvs.EmplaceBack(vtx[0], vtx[1]); + uvs.EmplaceBack(vtx[0], 1.0f - vtx[1]); // Fix the space from gl -> vk/dx else if (element == "vn") normals.EmplaceBack(vtx[0], vtx[1], vtx[2]); } @@ -69,7 +84,9 @@ namespace orange } } - for (int i = 0; i < 3; i++) + // Do backwards so we can have front faces + // be CCW. + for (int i = 2; i >= 0; i--) { StaticVertex vertex = { @@ -80,15 +97,19 @@ namespace orange auto& vertices = data.vertexData.GetStaticVertices(); - size_t vertexIdx = vertices.FindIdx(vertex); - if (vertexIdx == vertices.InvalidIdx) + auto iter = indexTracker.find(vertex); + size_t vertexIdx; + if (iter == indexTracker.end()) { data.bounds.Extend(vertex.pos); vertexIdx = vertices.PushBack(vertex); + indexTracker.emplace(vertex, vertexIdx); } + else + vertexIdx = iter->second; - Assert(vertexIdx < UINT16_MAX); - data.indices.PushBack(uint16_t(vertexIdx)); + Assert(vertexIdx < MaxMeshIndex); + data.indices.PushBack(IndexType(vertexIdx)); } } else if (!element.Empty()) diff --git a/src/Orange/Render/RenderContext_Util.cpp b/src/Orange/Render/RenderContext_Util.cpp index d61645b..366b265 100644 --- a/src/Orange/Render/RenderContext_Util.cpp +++ b/src/Orange/Render/RenderContext_Util.cpp @@ -113,7 +113,7 @@ namespace orange return VulkanResult::Success(memory, buffer, size, data); } - VulkanResult RenderContext::CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format) + VulkanResult RenderContext::CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage) { VkImageCreateInfo imageInfo = { @@ -125,7 +125,7 @@ namespace orange .arrayLayers = 1u, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .usage = usage, }; VkImage image = VK_NULL_HANDLE; @@ -142,4 +142,30 @@ namespace orange return VulkanResult::Success(image); } + + VulkanResult RenderContext::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlagBits aspect) + { + VkImageViewCreateInfo imageViewInfo = + { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange = + { + .aspectMask = aspect, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + VkImageView imageView = VK_NULL_HANDLE; + VkResult res = VK_SUCCESS; + if ((res = vkCreateImageView(m_device, &imageViewInfo, nullptr, &imageView)) != VK_SUCCESS) + return VulkanResult::Error(res); + + return VulkanResult::Success(imageView); + } } diff --git a/src/Orange/Render/Shaders/fs_Mesh.frag b/src/Orange/Render/Shaders/fs_Mesh.frag new file mode 100644 index 0000000..a40caa7 --- /dev/null +++ b/src/Orange/Render/Shaders/fs_Mesh.frag @@ -0,0 +1,15 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : enable + +layout(set = 0, binding = 1) uniform sampler s_sampler; +layout(set = 0, binding = 2) uniform texture2D s_images[]; + +layout(location = 0) in flat int id; +layout(location = 1) in vec2 uv; + +layout(location = 0) out vec4 outColor; + +void main() +{ + outColor = texture(sampler2D(s_images[nonuniformEXT(id)], s_sampler), uv); +} \ No newline at end of file diff --git a/src/Orange/Render/Shaders/vs_Mesh.vert b/src/Orange/Render/Shaders/vs_Mesh.vert index 0ec7e48..3ee1239 100644 --- a/src/Orange/Render/Shaders/vs_Mesh.vert +++ b/src/Orange/Render/Shaders/vs_Mesh.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_ARB_shader_draw_parameters : enable layout(binding = 0) uniform UniformData { mat4 projection; @@ -9,18 +10,12 @@ layout(location = 0) in vec3 i_pos; layout(location = 1) in vec2 i_uv; layout(location = 2) in vec3 i_normal; -layout(location = 0) out vec4 o_color; - -vec3 k_colors[3] = vec3[] -( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); +layout(location = 0) out flat int o_id; +layout(location = 1) out vec2 o_uv; void main() { gl_Position = u_data.projection * u_data.view * vec4(i_pos, 1.0); - //o_color = vec4(k_colors[gl_VertexIndex % 3], 1.0); - o_color = vec4(i_uv, 0.0f, 1.0); + o_id = gl_DrawIDARB; + o_uv = i_uv; } \ No newline at end of file diff --git a/src/Orange/Render/Window.cpp b/src/Orange/Render/Window.cpp index 66ffd69..cb804a4 100644 --- a/src/Orange/Render/Window.cpp +++ b/src/Orange/Render/Window.cpp @@ -117,4 +117,9 @@ namespace orange SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE); SDL_ShowCursor(m_hasFocus && relative ? SDL_FALSE : SDL_TRUE); } + + void Window::SetTitle(const char* title) + { + SDL_SetWindowTitle(m_window, title); + } } diff --git a/src/Orange/meson.build b/src/Orange/meson.build index 0af84dd..2c8a92c 100644 --- a/src/Orange/meson.build +++ b/src/Orange/meson.build @@ -1,5 +1,6 @@ orange_shaders = glsl_generator.process([ 'Render/Shaders/fs_DebugVertColor.frag', + 'Render/Shaders/fs_Mesh.frag', 'Render/Shaders/fs_Red.frag', 'Render/Shaders/fs_SkyGradient.frag', 'Render/Shaders/vs_Fullscreen.vert', diff --git a/subprojects/libspng b/subprojects/libspng new file mode 160000 index 0000000..c4126ad --- /dev/null +++ b/subprojects/libspng @@ -0,0 +1 @@ +Subproject commit c4126ad8ce0513ec16aa2e4180eb6545548710a0