diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 5e417ba..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "(lldb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/bin/RayTracer", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}/bin/", - "environment": [], - "externalConsole": false, - "MIMode": "lldb" - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 88ffd3c..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "cmake.buildDirectory": "${workspaceFolder}/bin/", - "cmake.generator": "" -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c1ed07..341f1ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/external/vcpkg/scripts/buil project(RayTracer) +find_package(Vulkan REQUIRED) find_package(glew CONFIG REQUIRED) find_package(assimp CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED) @@ -20,14 +21,12 @@ find_package(imgui CONFIG REQUIRED) add_executable(RayTracer "") target_include_directories(RayTracer PUBLIC src/) +target_link_libraries(RayTracer PUBLIC Vulkan::Vulkan) +target_link_libraries(RayTracer PUBLIC Vulkan::Headers) target_link_libraries(RayTracer PUBLIC assimp::assimp) target_link_libraries(RayTracer PUBLIC glfw) target_link_libraries(RayTracer PUBLIC imgui::imgui) -if(APPLE) target_link_libraries(RayTracer PUBLIC GLEW::GLEW) -else() -target_link_libraries(RayTracer PUBLIC GLEW::glew) -endif() target_link_libraries(RayTracer PUBLIC glm::glm) target_link_libraries(RayTracer PUBLIC KTX::ktx) diff --git a/external/vcpkg b/external/vcpkg index 01be99f..791ae5e 160000 --- a/external/vcpkg +++ b/external/vcpkg @@ -1 +1 @@ -Subproject commit 01be99fec01f777c4113ceea192a45115c69cdb7 +Subproject commit 791ae5e74bfca0908f067e75b7b3d10dc10acfd6 diff --git a/cube.fbx b/res/models/cube.fbx similarity index 100% rename from cube.fbx rename to res/models/cube.fbx diff --git a/res/shaders/RayGen.slang b/res/shaders/RayGen.slang new file mode 100644 index 0000000..e69de29 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3fba944..6c9dcf9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,8 +3,10 @@ target_sources(RayTracer main.cpp Minimal.h ThreadPool.h - ThreadPool.cpp) + ThreadPool.cpp +) +add_subdirectory(gpu/) add_subdirectory(scene/) add_subdirectory(window/) add_subdirectory(util/) diff --git a/src/Minimal.h b/src/Minimal.h index 3e0ac92..65b3c69 100644 --- a/src/Minimal.h +++ b/src/Minimal.h @@ -1,9 +1,22 @@ #pragma once +#include #include -#define DEFINE_REF(x) \ - typedef ::std::unique_ptr P##x; \ +#define DEFINE_REF(x) typedef ::std::unique_ptr P##x; #define DECLARE_REF(x) \ - class x; \ - typedef ::std::unique_ptr P##x; \ + class x; \ + typedef ::std::unique_ptr P##x; + +struct Task +{ + struct promise_type + { + Task get_return_object() { return {std::coroutine_handle::from_promise(*this)}; } + std::suspend_always initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + std::coroutine_handle handle; +}; diff --git a/src/ThreadPool.cpp b/src/ThreadPool.cpp index b8e9266..b61170b 100644 --- a/src/ThreadPool.cpp +++ b/src/ThreadPool.cpp @@ -3,64 +3,66 @@ ThreadPool::ThreadPool(uint32_t numThreads) { - for (uint32_t i = 0; i < numThreads; ++i) - { - workers.push_back(std::thread(&ThreadPool::work, this)); - } + for (uint32_t i = 0; i < numThreads; ++i) + { + workers.push_back(std::thread(&ThreadPool::work, this)); + } } ThreadPool::~ThreadPool() { - running.store(false); - { - std::unique_lock l(queueLock); - queueCV.notify_all(); - } - for (auto& worker : workers) - { - worker.join(); - } + running.store(false); + { + std::unique_lock l(queueLock); + queueCV.notify_all(); + } + for (auto& worker : workers) + { + worker.join(); + } } void ThreadPool::runBatch(Batch&& batch) { - { - std::unique_lock l(queueLock); - taskQueue.push_back(batch); - queueCV.notify_one(); - } - while (true) - { - std::unique_lock l(queueLock); - if (taskQueue.empty()) - return; - completedCV.wait(l); - } + { + std::unique_lock l(queueLock); + numRemaining = batch.jobs.size(); + taskQueue.push_back(batch); + queueCV.notify_all(); + } + while (true) + { + std::unique_lock l(queueLock); + if (taskQueue.empty()) + return; + completedCV.wait(l); + } } void ThreadPool::work() { - while (running) + while (running) + { + Task job; { - std::function job; - { - std::unique_lock l(queueLock); - if (taskQueue.empty() || taskQueue.front().jobs.empty()) - { - queueCV.wait(l); - continue; - } - job = taskQueue.front().jobs.front(); - taskQueue.front().jobs.pop_front(); - } - job(); - { - std::unique_lock l(queueLock); - if (taskQueue.front().jobs.empty()) - { - taskQueue.pop_front(); - completedCV.notify_one(); - } - } + std::unique_lock l(queueLock); + if (taskQueue.empty() || taskQueue.front().jobs.empty()) + { + queueCV.wait(l); + continue; + } + job = taskQueue.front().jobs.front(); + taskQueue.front().jobs.pop_front(); } + job.handle(); + { + std::unique_lock l(queueLock); + numRemaining--; + if (numRemaining == 0) + { + taskQueue.pop_front(); + completedCV.notify_one(); + } + } + } } diff --git a/src/ThreadPool.h b/src/ThreadPool.h index dba5b67..b5832b3 100644 --- a/src/ThreadPool.h +++ b/src/ThreadPool.h @@ -3,10 +3,11 @@ #include #include #include +#include "Minimal.h" struct Batch { - std::list> jobs; + std::list jobs; }; class ThreadPool @@ -21,6 +22,7 @@ private: std::mutex queueLock; std::condition_variable queueCV; std::condition_variable completedCV; + uint32_t numRemaining; std::list taskQueue; std::vector workers; }; \ No newline at end of file diff --git a/src/gpu/CMakeLists.txt b/src/gpu/CMakeLists.txt new file mode 100644 index 0000000..d65cbf6 --- /dev/null +++ b/src/gpu/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(RayTracer + PRIVATE + Renderer.h + Renderer.cpp) \ No newline at end of file diff --git a/src/gpu/Renderer.cpp b/src/gpu/Renderer.cpp new file mode 100644 index 0000000..dcea9e4 --- /dev/null +++ b/src/gpu/Renderer.cpp @@ -0,0 +1,72 @@ +#include "Renderer.h" + +Renderer::Renderer() + : instance(nullptr), physicalDevice(nullptr), device(nullptr), queue(nullptr), cmdPool(nullptr), cmdBuffers(nullptr), + descriptorLayout(nullptr), descriptorSet(nullptr), descriptorPool(nullptr), pipelineLayout(nullptr), rayGen(nullptr), + closestHit(nullptr), miss(nullptr), pipeline(nullptr) + +{ + + + vk::RayTracingPipelineCreateInfoKHR pipelineCreateInfo(0, ); +} + +Renderer::~Renderer() {} + +void Renderer::createDevice() { + vk::ApplicationInfo appInfo("RayTracer", 1, "RayTracer", 1, VK_API_VERSION_1_3); + vk::InstanceCreateInfo instanceCreateInfo({}, &appInfo); + instance = Instance(context, instanceCreateInfo); + auto physicalDevices = PhysicalDevices(instance); + for (auto& dev : physicalDevices) + { + for (auto ext : dev.enumerateDeviceExtensionProperties()) + { + if (std::strcmp(ext.extensionName, vk::KHRRayTracingPipelineExtensionName)) + { + physicalDevice = dev; + break; + } + } + } + uint32_t computeQueueFamily = 0; + auto queueProps = physicalDevice.getQueueFamilyProperties(); + for (uint32_t i = 0; i < queueProps.size(); ++i) + { + if (queueProps[i].queueFlags & vk::QueueFlagBits::eCompute) + { + computeQueueFamily = i; + break; + } + } + float queuePriority = 0.0f; + vk::DeviceQueueCreateInfo deviceQueueCreateInfo({}, computeQueueFamily, 1, &queuePriority); + vk::DeviceCreateInfo deviceCreateInfo({}, deviceQueueCreateInfo); + device = Device(physicalDevice, deviceCreateInfo); +} + +void Renderer::createCommands() +{ + vk::CommandPoolCreateInfo commandPoolCreateInfo({}, computeQueueFamily); + cmdPool = CommandPool(device, commandPoolCreateInfo); + + // allocate a CommandBuffer from the CommandPool + vk::CommandBufferAllocateInfo commandBufferAllocateInfo(cmdPool, vk::CommandBufferLevel::ePrimary, 10); + cmdBuffers = vk::raii::CommandBuffers(device, commandBufferAllocateInfo); +} + +void Renderer::createDescriptors() { + vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex); + vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo({}, descriptorSetLayoutBinding); + descriptorLayout = DescriptorSetLayout(device, descriptorSetLayoutCreateInfo); + + // create a PipelineLayout using that DescriptorSetLayout + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, *descriptorLayout); + pipelineLayout = PipelineLayout(device, pipelineLayoutCreateInfo); +} + +void Renderer::createShaders() { + +} + +void Renderer::render(Camera cam, RenderParameter param) {} \ No newline at end of file diff --git a/src/gpu/Renderer.h b/src/gpu/Renderer.h new file mode 100644 index 0000000..290696b --- /dev/null +++ b/src/gpu/Renderer.h @@ -0,0 +1,42 @@ +#pragma once +#include "scene/Scene.h" +#include +#include + +using namespace vk::raii; + +struct Renderer : public Scene +{ +public: + Renderer(); + virtual ~Renderer(); + +private: + void createDevice(); + void createCommands(); + void createDescriptors(); + void createShaders(); + + Context context; + Instance instance; + PhysicalDevice physicalDevice; + Device device; + Queue queue; + + uint32_t computeQueueFamily; + CommandPool cmdPool; + CommandBuffers cmdBuffers; + + DescriptorSetLayout descriptorLayout; + DescriptorSet descriptorSet; + DescriptorPool descriptorPool; + PipelineLayout pipelineLayout; + + ShaderModule rayGen; + ShaderModule closestHit; + ShaderModule miss; + + Pipeline pipeline; + + virtual void render(Camera cam, RenderParameter param); +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f772a6d..42c9758 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,15 +8,15 @@ int main() { Scene scene; - Window window(800, 600); - scene.render( + Window window(1920, 1080); + scene.startRender( Camera{ .position = glm::vec3(10, 0, 0), .direction = glm::vec3(-1, 0, 0), }, RenderParameter{ - .width = 800, - .height = 600, + .width = 1920, + .height = 1080, .numSamples = 10000, }); while (true) diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp index 2cc4c60..cd8e0e6 100644 --- a/src/scene/Scene.cpp +++ b/src/scene/Scene.cpp @@ -1,69 +1,64 @@ #include "Scene.h" #include "util/ModelLoader.h" -#include #include +#include Scene::Scene() { - bvh.addModels(ModelLoader::loadModel("../cube.fbx"), - glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 4.0f, 0.0f))); + bvh.addModels(ModelLoader::loadModel("../res/models/cube.fbx"), + glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f))); bvh.generate(); } Scene::~Scene() {} static bool firstTime = true; +void Scene::startRender(Camera cam, RenderParameter params) +{ + if (!firstTime) + { + pendingCancel = true; + worker.join(); + firstTime = false; + } + pendingCancel = false; + image.clear(); + accumulator.clear(); + image.resize(params.width * params.height); + accumulator.resize(params.width * params.height); + worker = std::thread(&Scene::render, this, cam, params); +} + void Scene::render(Camera cam, RenderParameter params) { - if (!firstTime) + for (int samp = 0; samp < params.numSamples; ++samp) + { + if (pendingCancel) + return; + Batch batch; + for (int w = 0; w < params.width; ++w) { - pendingCancel = true; - worker.join(); - firstTime = false; - } - pendingCancel = false; - image.clear(); - accumulator.clear(); - image.resize(params.width * params.height); - accumulator.resize(params.width * params.height); - worker = std::thread( - [&, cam, params]() - { - for (int samp = 0; samp < params.numSamples; ++samp) + batch.jobs.push_back( + [&](int w) -> Task + { + for (int h = 0; h < params.height; ++h) { - if (pendingCancel) - return; - Batch batch; - for (int w = 0; w < params.width; ++w) - { - batch.jobs.push_back( - [&, w]() - { - for (int h = 0; h < params.height; ++h) - { - Ray r = Ray{ - .origin = glm::vec3(0.0f), - .direction = glm::normalize(glm::vec3((float)std::rand() / RAND_MAX, (float)std::rand() / RAND_MAX, (float)std::rand() / RAND_MAX)) - }; + Ray r = Ray{.origin = glm::vec3(0.0f), + .direction = glm::normalize( + glm::vec3((float)std::rand() / RAND_MAX, (float)std::rand() / RAND_MAX, (float)std::rand() / RAND_MAX))}; + bvh.traceRay(r); - auto intersectionInfo = bvh.traceRay(r); - - if(intersectionInfo.has_value()) - { - accumulator[w + h * params.width] += intersectionInfo->albedo; - //glm::vec3(w / float(params.width * params.numSamples), h / float(params.height * params.numSamples), 0); - } - } - }); - } - auto start = std::chrono::high_resolution_clock::now(); - threadPool.runBatch(std::move(batch)); - auto end = std::chrono::high_resolution_clock::now(); - std::cout << std::chrono::duration_cast(end - start).count() << std::endl; - std::memcpy(image.data(), accumulator.data(), accumulator.size() * sizeof(glm::vec3)); + accumulator[w + h * params.width] += + glm::vec3(w / float(params.width * params.numSamples), h / float(params.height * params.numSamples), 0); } - }); + co_return; + }(w)); + } + auto start = std::chrono::high_resolution_clock::now(); + threadPool.runBatch(std::move(batch)); + auto end = std::chrono::high_resolution_clock::now(); + std::cout << std::chrono::duration_cast(end - start).count() << std::endl; + std::memcpy(image.data(), accumulator.data(), accumulator.size() * sizeof(glm::vec3)); + } } diff --git a/src/scene/Scene.h b/src/scene/Scene.h index 8f860e9..f4f3e53 100644 --- a/src/scene/Scene.h +++ b/src/scene/Scene.h @@ -15,10 +15,11 @@ class Scene { public: Scene(); - ~Scene(); - void render(Camera cam, RenderParameter params); + virtual ~Scene(); + void startRender(Camera cam, RenderParameter params); constexpr const std::vector& getImage() const { return image; } private: + virtual void render(Camera cam, RenderParameter params); std::atomic_bool pendingCancel = false; ThreadPool threadPool; std::thread worker; diff --git a/vcpkg.json b/vcpkg.json index 7e3fffa..b1444c1 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,12 +4,12 @@ "name": "imgui", "features": [ "glfw-binding", "opengl3-binding" ] }, + "vulkan", "assimp", "ktx", "glfw3", "glew", "glm", - "stb", "fmt" ] } \ No newline at end of file