diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 710a40a..99ebe7f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(ngf) add_subdirectory(normals) add_subdirectory(palette) add_subdirectory(particles) -add_subdirectory(raytracing) \ No newline at end of file +add_subdirectory(raytracing) +add_subdirectory(pathtracing) \ No newline at end of file diff --git a/examples/experimental/ire.cpp b/examples/experimental/ire.cpp index 6d54527..358e16c 100644 --- a/examples/experimental/ire.cpp +++ b/examples/experimental/ire.cpp @@ -77,14 +77,28 @@ auto ftn = procedure ("main") << []() // arr[1] = vec4(br2.data[14], 1); }; +auto B = procedure("B") << [](vec3 v) -> vec3 +{ + return sin(v.y); +}; + +auto A = procedure("A") << [](vec3 v) -> vec3 +{ + return v * B(v); +}; + // TODO: l-value propagation // TODO: shadertoy example // TODO: optimization using graphviz for visualization... int main() { - ftn.dump(); - ftn.graphviz("graph.dot"); - dump_lines("EXPERIMENTAL IRE", link(ftn).generate_glsl()); - link(ftn).generate_spirv(vk::ShaderStageFlagBits::eCompute); + dump_lines("A", link(A).generate_glsl()); + dump_lines("B", link(B).generate_glsl()); + dump_lines("A", link(A).generate_glsl()); + + // ftn.dump(); + // ftn.graphviz("graph.dot"); + // dump_lines("EXPERIMENTAL IRE", link(ftn).generate_glsl()); + // link(ftn).generate_spirv(vk::ShaderStageFlagBits::eCompute); } diff --git a/examples/pathtracing/CMakeLists.txt b/examples/pathtracing/CMakeLists.txt new file mode 100644 index 0000000..19feea5 --- /dev/null +++ b/examples/pathtracing/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(pathtracing main.cpp shaders.cpp) + +target_link_libraries(pathtracing javelin common) \ No newline at end of file diff --git a/examples/pathtracing/main.cpp b/examples/pathtracing/main.cpp new file mode 100644 index 0000000..5938996 --- /dev/null +++ b/examples/pathtracing/main.cpp @@ -0,0 +1,438 @@ +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include "common/acceleration_structure.hpp" +#include "common/aperature.hpp" +#include "common/application.hpp" +#include "common/default_framebuffer_set.hpp" +#include "common/imgui.hpp" +#include "common/imported_asset.hpp" +#include "common/sbt.hpp" +#include "common/vulkan_resources.hpp" +#include "common/vulkan_triangle_mesh.hpp" + +#include "shaders.hpp" + +struct Application : CameraApplication { + littlevk::Pipeline rtx_pipeline; + littlevk::Pipeline blit_pipeline; + + vk::RenderPass render_pass; + DefaultFramebufferSet framebuffers; + + vk::DescriptorSet rtx_descriptor; + vk::DescriptorSet blit_descriptor; + + littlevk::Image target; + + ShaderBindingTable sbt; + VulkanAccelerationStructure tlas; + + glm::vec3 min; + glm::vec3 max; + bool automatic; + + static inline const std::vector bindings { + vk::DescriptorSetLayoutBinding() + .setBinding(0) + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eAccelerationStructureKHR) + .setStageFlags(vk::ShaderStageFlagBits::eRaygenKHR), + vk::DescriptorSetLayoutBinding() + .setBinding(1) + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eStorageImage) + .setStageFlags(vk::ShaderStageFlagBits::eRaygenKHR), + vk::DescriptorSetLayoutBinding() + .setBinding(2) + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eStorageBuffer) + .setStageFlags(vk::ShaderStageFlagBits::eClosestHitKHR), + }; + + Application() : CameraApplication("Pathtracing", { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, + VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, + VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, + VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, + }, features_include, features_activate) + { + render_pass = littlevk::RenderPassAssembler(resources.device, resources.dal) + .add_attachment(littlevk::default_color_attachment(resources.swapchain.format)) + .add_attachment(littlevk::default_depth_attachment()) + .add_subpass(vk::PipelineBindPoint::eGraphics) + .color_attachment(0, vk::ImageLayout::eColorAttachmentOptimal) + .depth_attachment(1, vk::ImageLayout::eDepthStencilAttachmentOptimal) + .done(); + + framebuffers.resize(resources, render_pass); + + configure_imgui(resources, render_pass); + + compile_rtx_pipeline(); + compile_blit_pipeline(); + } + + static void features_include(VulkanFeatureChain &features) { + features.add (); + features.add (); + features.add (); + features.add (); + } + + static void features_activate(VulkanFeatureChain &features) { + MODULE(features_activate); + + for (auto &ptr : features) { + switch (ptr->sType) { + feature_case(vk::PhysicalDeviceRayTracingPipelineFeaturesKHR) + .setRayTracingPipeline(true); + break; + feature_case(vk::PhysicalDeviceAccelerationStructureFeaturesKHR) + .setAccelerationStructure(true); + break; + feature_case(vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR) + .setBufferDeviceAddress(true) + .setBufferDeviceAddressCaptureReplay(false) + .setBufferDeviceAddressMultiDevice(false); + break; + feature_case(vk::PhysicalDeviceScalarBlockLayoutFeaturesEXT) + .setScalarBlockLayout(true); + break; + default: + break; + } + } + } + + void compile_rtx_pipeline() { + shader_debug(); + + auto rgen_spv = link(ray_generation).generate_spirv(vk::ShaderStageFlagBits::eRaygenKHR); + auto rgen_info = vk::ShaderModuleCreateInfo().setCode(rgen_spv); + auto rgen_module = resources.device.createShaderModule(rgen_info); + + auto rchit_spv = link(primary_closest_hit).generate_spirv(vk::ShaderStageFlagBits::eClosestHitKHR); + auto rchit_info = vk::ShaderModuleCreateInfo().setCode(rchit_spv); + auto rchit_module = resources.device.createShaderModule(rchit_info); + + auto rmiss_spv = link(primary_miss).generate_spirv(vk::ShaderStageFlagBits::eMissKHR); + auto rmiss_info = vk::ShaderModuleCreateInfo().setCode(rmiss_spv); + auto rmiss_module = resources.device.createShaderModule(rmiss_info); + + auto smiss_spv = link(shadow_miss).generate_spirv(vk::ShaderStageFlagBits::eMissKHR); + auto smiss_info = vk::ShaderModuleCreateInfo().setCode(smiss_spv); + auto smiss_module = resources.device.createShaderModule(smiss_info); + + auto constants = vk::PushConstantRange() + .setOffset(0) + .setSize(sizeof(solid_t )) + .setStageFlags(vk::ShaderStageFlagBits::eRaygenKHR); + + std::tie(rtx_pipeline, sbt) = raytracing_pipeline(resources, + rgen_module, + { rmiss_module, smiss_module }, + { rchit_module }, + bindings, + { constants }); + + rtx_descriptor = littlevk::bind(resources.device, resources.descriptor_pool) + .allocate_descriptor_sets(*rtx_pipeline.dsl) + .front(); + } + + void compile_blit_pipeline() { + std::string quad_shader = link(quad).generate_glsl(); + std::string blit_shader = link(blit).generate_glsl(); + + dump_lines("QUAD", quad_shader); + dump_lines("BLIT", blit_shader); + + auto blit_bundle = littlevk::ShaderStageBundle(resources.device, resources.dal) + .source(quad_shader, vk::ShaderStageFlagBits::eVertex) + .source(blit_shader, vk::ShaderStageFlagBits::eFragment); + + blit_pipeline = littlevk::PipelineAssembler + (resources.device, resources.window, resources.dal) + .with_shader_bundle(blit_bundle) + .with_render_pass(render_pass, 0) + .with_dsl_binding(0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment); + + blit_descriptor = littlevk::bind(resources.device, resources.descriptor_pool) + .allocate_descriptor_sets(*blit_pipeline.dsl) + .front(); + } + + void configure(argparse::ArgumentParser &program) override { + program.add_argument("mesh") + .help("input mesh"); + } + + void preload(const argparse::ArgumentParser &program) override { + // Load the asset and scene + std::filesystem::path path = program.get("mesh"); + + auto asset = ImportedAsset::from(path).value(); + + std::vector vk_meshes; + + struct Ref { + uint64_t vertices; + uint64_t triangles; + }; + + std::vector references; + + min = glm::vec3(1e10); + max = -min; + + for (auto &g : asset.geometries) { + g.deduplicate_vertices().recompute_normals(); + + auto tm = TriangleMesh::from(g).value(); + + auto vkm = VulkanTriangleMesh::from(resources.allocator(), tm, + VertexFlags::ePosition + | VertexFlags::eNormal, + vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR).value(); + + vk_meshes.emplace_back(vkm); + + auto taddr = resources.device.getBufferAddress(vkm.triangles.buffer); + auto vaddr = resources.device.getBufferAddress(vkm.vertices.buffer); + references.emplace_back(vaddr, taddr); + + auto [bmin, bmax] = tm.scale(); + min = glm::min(min, bmin); + max = glm::max(max, bmax); + } + + std::vector blases; + std::vector transforms; + std::vector instances; + + littlevk::submit_now(resources.device, resources.command_pool, resources.graphics_queue, + [&](const vk::CommandBuffer &cmd) { + for (size_t i = 0; i < vk_meshes.size(); i++) { + auto model = Transform().matrix(); + auto blas = VulkanAccelerationStructure::blas(resources, cmd, vk_meshes[i], 2 * sizeof(glm::vec3)); + blases.emplace_back(blas); + transforms.emplace_back(vk_transform(model)); + instances.emplace_back(i, 0xff); + } + }); + + littlevk::submit_now(resources.device, resources.command_pool, resources.graphics_queue, + [&](const vk::CommandBuffer &cmd) { + tlas = VulkanAccelerationStructure::tlas(resources, cmd, blases, transforms, instances); + }); + + littlevk::ImageCreateInfo image_info { + 1000, + 1000, + vk::Format::eR32G32B32A32Sfloat, + vk::ImageUsageFlagBits::eStorage + | vk::ImageUsageFlagBits::eSampled, + vk::ImageAspectFlagBits::eColor, + vk::ImageType::e2D, + vk::ImageViewType::e2D, + false + }; + + target = resources.allocator().image(image_info); + + littlevk::Buffer vk_refs = resources.allocator() + .buffer(references, vk::BufferUsageFlagBits::eStorageBuffer); + + littlevk::submit_now(resources.device, resources.command_pool, resources.graphics_queue, + [&](const vk::CommandBuffer &cmd) { + target.transition(cmd, vk::ImageLayout::eGeneral); + }); + + auto tlas_write = vk::WriteDescriptorSetAccelerationStructureKHR() + .setAccelerationStructureCount(1) + .setAccelerationStructures(tlas.handle); + + auto target_sampler = littlevk::SamplerAssembler(resources.device, resources.dal); + + std::vector writes; + + writes.emplace_back(vk::WriteDescriptorSet() + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eAccelerationStructureKHR) + .setDstBinding(0) + .setDstSet(rtx_descriptor) + .setPNext(&tlas_write)); + + auto target_descriptor1 = vk::DescriptorImageInfo() + .setImageView(target.view) + .setImageLayout(vk::ImageLayout::eGeneral) + .setSampler(target_sampler); + + writes.emplace_back(vk::WriteDescriptorSet() + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eStorageImage) + .setDstBinding(1) + .setDstSet(rtx_descriptor) + .setImageInfo(target_descriptor1)); + + auto target_descriptor2 = vk::DescriptorImageInfo() + .setImageView(target.view) + .setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setSampler(target_sampler); + + writes.emplace_back(vk::WriteDescriptorSet() + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eCombinedImageSampler) + .setDstBinding(0) + .setDstSet(blit_descriptor) + .setImageInfo(target_descriptor2)); + + auto refs_descriptor = vk::DescriptorBufferInfo() + .setBuffer(vk_refs.buffer) + .setOffset(0) + .setRange(vk_refs.device_size()); + + writes.emplace_back(vk::WriteDescriptorSet() + .setDescriptorCount(1) + .setDescriptorType(vk::DescriptorType::eStorageBuffer) + .setDstBinding(2) + .setDstSet(rtx_descriptor) + .setBufferInfo(refs_descriptor)); + + resources.device.updateDescriptorSets(writes, { }); + + automatic = (program["--auto"] == true); + } + + void render(const vk::CommandBuffer &cmd, uint32_t index) override { + if (automatic) { + float time = 2.5 * glfwGetTime(); + + auto &xform = camera.transform; + + float r = 0.75 * glm::length(max - min); + float a = time + glm::pi () / 2.0f; + + xform.translate = glm::vec3 { + r * glm::cos(time), + -0.5 * (max + min).y, + r * glm::sin(time), + }; + + xform.rotation = glm::angleAxis(-a, glm::vec3(0, 1, 0)); + } else { + camera.controller.handle_movement(resources.window); + } + + auto subresource_range = vk::ImageSubresourceRange() + .setAspectMask(vk::ImageAspectFlagBits::eColor) + .setBaseArrayLayer(0) + .setBaseMipLevel(0) + .setLayerCount(1) + .setLevelCount(1); + + solid_t frame; + + auto &extent = resources.window.extent; + camera.aperature.aspect = float(extent.width) / float(extent.height); + + auto rayframe = camera.aperature.rayframe(camera.transform); + + frame.get <0> () = rayframe.origin; + frame.get <1> () = rayframe.lower_left; + frame.get <2> () = rayframe.horizontal; + frame.get <3> () = rayframe.vertical; + frame.get <5> () = glfwGetTime(); + + { + float time = glfwGetTime(); + + glm::vec3 direction = glm::vec3 { + 0.1 * glm::cos(time), + -1, + 0.1 * glm::sin(time), + }; + + frame.get <4> () = glm::normalize(direction); + } + + { + auto memory_barrier = vk::ImageMemoryBarrier() + .setImage(target.image) + .setSubresourceRange(subresource_range) + .setOldLayout(vk::ImageLayout::eUndefined) + .setNewLayout(vk::ImageLayout::eGeneral) + .setSrcAccessMask(vk::AccessFlagBits::eNone) + .setDstAccessMask(vk::AccessFlagBits::eShaderWrite); + + cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eRayTracingShaderKHR, + vk::DependencyFlags(), + { }, { }, memory_barrier); + } + + cmd.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, rtx_pipeline.handle); + + cmd.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, rtx_pipeline.layout, 0, rtx_descriptor, { }); + cmd.pushConstants > (rtx_pipeline.layout, vk::ShaderStageFlagBits::eRaygenKHR, 0, frame); + + cmd.traceRaysKHR(sbt.ray_generation, + sbt.misses, + sbt.closest_hits, + sbt.callables, + target.extent.width, target.extent.height, 1); + + { + auto memory_barrier = vk::ImageMemoryBarrier() + .setImage(target.image) + .setSubresourceRange(subresource_range) + .setOldLayout(vk::ImageLayout::eGeneral) + .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setSrcAccessMask(vk::AccessFlagBits::eShaderWrite) + .setDstAccessMask(vk::AccessFlagBits::eShaderRead); + + cmd.pipelineBarrier(vk::PipelineStageFlagBits::eRayTracingShaderKHR, + vk::PipelineStageFlagBits::eFragmentShader, + vk::DependencyFlags(), + { }, { }, memory_barrier); + } + + // Configure the rendering extent + littlevk::viewport_and_scissor(cmd, littlevk::RenderArea(resources.window.extent)); + + littlevk::RenderPassBeginInfo(2) + .with_render_pass(render_pass) + .with_framebuffer(framebuffers[index]) + .with_extent(resources.window.extent) + .clear_color(0, std::array { 1, 1, 1, 1 }) + .clear_depth(1, 1) + .begin(cmd); + + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, blit_pipeline.handle); + cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, blit_pipeline.layout, 0, blit_descriptor, { }); + cmd.draw(6, 1, 0, 0); + + imgui(cmd); + + cmd.endRenderPass(); + } + + void imgui(const vk::CommandBuffer &cmd) { + ImGuiRenderContext context(cmd); + + ImGui::Begin("Ray Tracing: Options"); + { + // TODO: rendering modes... + } + ImGui::End(); + } + + void resize() override { + framebuffers.resize(resources, render_pass); + } +}; + +APPLICATION_MAIN() \ No newline at end of file diff --git a/examples/pathtracing/shaders.cpp b/examples/pathtracing/shaders.cpp new file mode 100644 index 0000000..6deff03 --- /dev/null +++ b/examples/pathtracing/shaders.cpp @@ -0,0 +1,226 @@ +#include "common/util.hpp" + +#include "shaders.hpp" + +//////////////////////////// +// Target display shaders // +//////////////////////////// + +Procedure quad = procedure ("main") << []() +{ + array locations = std::array { + vec4(-1, -1, 0, 0), + vec4(1, -1, 1, 0), + vec4(-1, 1, 0, 1), + vec4(-1, 1, 0, 1), + vec4(1, -1, 1, 0), + vec4(1, 1, 1, 1), + }; + + layout_out uv(0); + + vec4 v = locations[gl_VertexIndex % 6]; + gl_Position = vec4(v.xy(), 1 - 0.0001f, 1); + uv = v.zw(); +}; + +Procedure blit = procedure ("main") << []() +{ + layout_in uv(0); + + layout_out fragment(0); + + sampler2D sampler(0); + + fragment = vec4(ire::texture(sampler, uv).xyz(), 1); +}; + +////////////////////////// +// Path tracing shaders // +////////////////////////// + +struct Hit { + vec3 color; + vec3 position; + vec3 normal; + boolean missed; + + auto layout() { + return layout_from("Hit", + verbatim_field(color), + verbatim_field(position), + verbatim_field(normal), + verbatim_field(missed)); + } +}; + +Procedure B = procedure("pcg3d") << [](uvec3 v) -> uvec3 +{ + v = v * 1664525u + 1013904223u; + v.x += v.y * v.z; + v.y += v.z * v.x; + v.z += v.x * v.y; + v ^= v >> 16u; + v.x += v.y * v.z; + v.y += v.z * v.x; + v.z += v.x * v.y; + return v; +}; + +Procedure A = procedure("random3") << [](inout seed) -> vec3 +{ + seed = uintBitsToFloat((B(floatBitsToUint(seed)) & 0x007FFFFFu) | 0x3F800000u) - 1.0; + return seed; +}; + +Procedure ray_generation = procedure ("main") << []() +{ + ray_payload hit(0); + ray_payload shadow(1); + + accelerationStructureEXT tlas(0); + + writeonly image(1); + + push_constant constants; + + vec3 seed = vec3(f32(gl_LaunchIDEXT.x), f32(gl_LaunchIDEXT.y), constants.time); + + vec2 center = vec2(gl_LaunchIDEXT.xy()) + vec2(0.5); + vec2 uv = center / vec2(imageSize(image)); + vec3 ray = constants.at(uv); + + traceRayEXT(tlas, + gl_RayFlagsOpaqueEXT, + 0xFF, + 0, 0, 0, + constants.origin, 1e-3, + ray, 1e10, + 0); + + vec4 color = vec4(hit.color, 1.0); + + // Shadow testing + $if(!hit.missed); + { + shadow = true; + + vec3 offset = hit.position + 1e-2 * hit.normal; + + traceRayEXT(tlas, + gl_RayFlagsOpaqueEXT + | gl_RayFlagsTerminateOnFirstHitEXT + | gl_RayFlagsSkipClosestHitShaderEXT, + 0xFF, + 0, 0, 1, + offset, 1e-3, + constants.light, 1e10, + 1); + + hit.color = 0.5 + 0.5 * hit.normal; + hit.color *= 0.5 + 0.5 * (1.0f - f32(shadow)); + } + $end(); + + // TODO: more semantic traceRaysEXT... + // traceRayEXT(tlas, flags, mask, nullptr, ..., shadow_miss, ray..., shadow) + + imageStore(image, ivec2(gl_LaunchIDEXT.xy()), color); +}; + +struct Vertex { + vec3 position; + vec3 normal; + + auto layout() { + return layout_from("Vertex", + verbatim_field(position), + verbatim_field(normal)); + } +}; + +Procedure primary_closest_hit = procedure ("main") << []() +{ + using Triangles = scalar >>; + using Vertices = scalar >>; + + ray_payload_in hit(0); + + buffer > references(2); + + hit_attribute bary; + + u32 iid = gl_InstanceCustomIndexEXT; + u32 pid = gl_PrimitiveID; + + Reference ref = references[iid]; + + Triangles triangles = Triangles(ref.triangles); + Vertices vertices = Vertices(ref.vertices); + + ivec3 tri = triangles[pid]; + + Vertex v0 = vertices[tri.x]; + Vertex v1 = vertices[tri.y]; + Vertex v2 = vertices[tri.z]; + + vec3 b = vec3(1.0f - bary.x - bary.y, bary.x, bary.y); + + vec3 p = b.x * v0.position + b.y * v1.position + b.z * v2.position; + p = (gl_ObjectToWorldEXT * vec4(p, 1)).xyz(); + + vec3 n = b.x * v0.normal + b.y * v1.normal + b.z * v2.normal; + n = normalize(n); + + // $if(dot(n, gl_WorldRayDirectionEXT) > 0); + // n = -n; + // $end(); + + hit.position = p; + hit.normal = n; + hit.color = b; + hit.missed = false; +}; + +Procedure primary_miss = procedure ("main") << []() +{ + ray_payload_in hit(0); + + hit.position = vec3(0); + hit.color = vec3(1); + hit.missed = true; +}; + +Procedure shadow_miss = procedure ("main") << []() +{ + ray_payload_in shadow(1); + + shadow = false; +}; + +// Debugging +void shader_debug() +{ + std::string local = std::filesystem::path(__FILE__).parent_path(); + + ray_generation.graphviz(local + "/ray_generation.dot"); + primary_closest_hit.graphviz(local + "/primary_closest_hit.dot"); + primary_miss.graphviz(local + "/primary_miss.dot"); + shadow_miss.graphviz(local + "/shadow_miss.dot"); + B.graphviz(local + "/pcg3d.dot"); + A.graphviz(local + "/random3.dot"); + + std::string rgen_shader = link(ray_generation).generate_glsl(); + std::string rchit_shader = link(primary_closest_hit).generate_glsl(); + std::string rmiss_shader = link(primary_miss).generate_glsl(); + std::string smiss_shader = link(shadow_miss).generate_glsl(); + std::string pcg3d_shader = link(B).generate_glsl(); + std::string random3_shader = link(A).generate_glsl(); + + dump_lines("RAY GENERATION", rgen_shader); + dump_lines("PRIMARY CLOSEST HIT", rchit_shader); + dump_lines("PRIMARY MISS", rmiss_shader); + dump_lines("SHADOW MISS", smiss_shader); + dump_lines("PCG3D", pcg3d_shader); + dump_lines("RANDOM3", random3_shader); +} \ No newline at end of file diff --git a/examples/pathtracing/shaders.hpp b/examples/pathtracing/shaders.hpp new file mode 100644 index 0000000..b35d2d3 --- /dev/null +++ b/examples/pathtracing/shaders.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include + +using namespace jvl; +using namespace jvl::ire; + +struct Constants { + vec3 origin; + vec3 lower_left; + vec3 horizontal; + vec3 vertical; + vec3 light; + f32 time; + + vec3 at(vec2 uv) { + return normalize(lower_left + uv.x * horizontal + uv.y * vertical - origin); + } + + auto layout() { + return layout_from("Constants", + verbatim_field(origin), + verbatim_field(lower_left), + verbatim_field(horizontal), + verbatim_field(vertical), + verbatim_field(light), + verbatim_field(time)); + } +}; + +struct Reference { + u64 vertices; + u64 triangles; + + auto layout() { + return layout_from("Reference", + verbatim_field(vertices), + verbatim_field(triangles)); + } +}; + +// Actual shaders +extern Procedure quad; +extern Procedure blit; + +extern Procedure ray_generation; +extern Procedure primary_closest_hit; +extern Procedure primary_miss; +extern Procedure shadow_miss; + +extern void shader_debug(); \ No newline at end of file diff --git a/include/thunder/linkage_unit.hpp b/include/thunder/linkage_unit.hpp index c70eefe..8e376a3 100644 --- a/include/thunder/linkage_unit.hpp +++ b/include/thunder/linkage_unit.hpp @@ -103,6 +103,7 @@ struct LinkageUnit { std::set process_function(const Function &); + void add(uint32_t, const NamedBuffer &); void add(const TrackedBuffer &); generator_list configure_generators() const; diff --git a/include/thunder/tracked_buffer.hpp b/include/thunder/tracked_buffer.hpp index 7aa32ee..b5929d6 100644 --- a/include/thunder/tracked_buffer.hpp +++ b/include/thunder/tracked_buffer.hpp @@ -6,35 +6,36 @@ namespace jvl::thunder { -struct TrackedBuffer : Buffer { +// Buffer with name +struct NamedBuffer : Buffer { + std::string name; +}; + +// Buffer with name and unique index +struct TrackedBuffer : NamedBuffer { // Global list of callables - static auto &tracked() { - static std::map map; - return map; - } + struct cache_entry_t { + int32_t count; + NamedBuffer buffer; + const NamedBuffer *link; + }; - static TrackedBuffer *search_tracked(size_t cid) { - auto &t = tracked(); - if (t.contains(cid)) - return t[cid]; + using cache_t = std::map ; - return nullptr; - } + static cache_t &cache(); + static const NamedBuffer &cache_load(int32_t); + static void cache_increment(int32_t); + static void cache_decrement(int32_t); + static void cache_unlink(int32_t); + static void cache_insert(const TrackedBuffer *); // Unique id - size_t cid; - - // An optional name (defaults to "callable") - std::string name; - - // For callables we can track back used and synthesized - // insructions from working backwards at the returns + int32_t cid; TrackedBuffer(); TrackedBuffer(const TrackedBuffer &); TrackedBuffer &operator=(const TrackedBuffer &); - - // TODO: destructor, which offloads it from the global list + ~TrackedBuffer(); void dump() const; }; diff --git a/source/thunder/atom.cpp b/source/thunder/atom.cpp index e5227b8..15cb879 100644 --- a/source/thunder/atom.cpp +++ b/source/thunder/atom.cpp @@ -265,10 +265,10 @@ std::string Call::to_string() const { std::string result; - TrackedBuffer *cbl = TrackedBuffer::search_tracked(cid); + auto &buffer = TrackedBuffer::cache_load(cid); result += header("CALL", fmt::color::dark_magenta); - result += fmt::format("\n :: callable: ${}", cbl->name); + result += fmt::format("\n :: callable: ${}", buffer.name); if (args != -1) result += fmt::format("\n :: args: %{}", args); diff --git a/source/thunder/c_like_generator.cpp b/source/thunder/c_like_generator.cpp index 6d88a4d..efec847 100644 --- a/source/thunder/c_like_generator.cpp +++ b/source/thunder/c_like_generator.cpp @@ -348,12 +348,12 @@ std::string c_like_generator_t::inlined(Index index) const { auto &call = atom.as (); - TrackedBuffer *cbl = TrackedBuffer::search_tracked(call.cid); + auto &buffer = TrackedBuffer::cache_load(call.cid); std::string args; if (call.args != -1) args = arguments_to_string(arguments(call.args)); - return fmt::format("{}{}", cbl->name, args); + return fmt::format("{}{}", buffer.name, args); } variant_case(Atom, Load): @@ -573,12 +573,12 @@ void c_like_generator_t::generate(const Construct &construct, Index index) template <> void c_like_generator_t::generate(const Call &call, Index index) { - TrackedBuffer *cbl = TrackedBuffer::search_tracked(call.cid); + auto &buffer = TrackedBuffer::cache_load(call.cid); std::string args = "()"; if (call.args != -1) args = arguments_to_string(arguments(call.args)); - define(index, cbl->name + args); + define(index, buffer.name + args); } template <> diff --git a/source/thunder/graphviz.cpp b/source/thunder/graphviz.cpp index c65da14..861bb12 100644 --- a/source/thunder/graphviz.cpp +++ b/source/thunder/graphviz.cpp @@ -140,8 +140,8 @@ std::string instruction_record(const Atom &atom) variant_case(Atom, Call): { auto &call = atom.as (); - TrackedBuffer *cbl = TrackedBuffer::search_tracked(call.cid); - return cbl->name + " | "; + auto &buffer= TrackedBuffer::cache_load(call.cid); + return buffer.name + " | "; } variant_case(Atom, Load): diff --git a/source/thunder/linkage/core.cpp b/source/thunder/linkage/core.cpp index 7f22cb3..30ae8bb 100644 --- a/source/thunder/linkage/core.cpp +++ b/source/thunder/linkage/core.cpp @@ -219,6 +219,11 @@ void LinkageUnit::process_function_qualifier(Function &function, size_t fidx, In break; // Miscellaneous + case qualifier_in: + case qualifier_out: + case qualifier_inout: + break; + case arrays: break; @@ -412,22 +417,27 @@ std::set LinkageUnit::process_function(const Function &ftn) return referenced; } -void LinkageUnit::add(const TrackedBuffer &callable) +void LinkageUnit::add(uint32_t cid, const NamedBuffer &callable) { - if (loaded.contains(callable.cid)) + if (loaded.contains(cid)) return; Function converted { callable, callable.name, - callable.cid + cid }; auto referenced = process_function(converted); - loaded.insert(callable.cid); + loaded.insert(cid); for (Index i : referenced) - add(*TrackedBuffer::search_tracked(i)); + add(i, TrackedBuffer::cache_load(i)); +} + +void LinkageUnit::add(const TrackedBuffer &callable) +{ + return add(callable.cid, callable); } } // namespace jvl::thunder diff --git a/source/thunder/linkage/glsl.cpp b/source/thunder/linkage/glsl.cpp index b31e419..0735162 100644 --- a/source/thunder/linkage/glsl.cpp +++ b/source/thunder/linkage/glsl.cpp @@ -420,6 +420,7 @@ auto topological_sort(const std::map > &dependencies) included.insert(i); sorted.push_front(i); + JVL_ASSERT(dependencies.contains(i), "dependencies missing function index {}", i); for (auto j : dependencies.at(i)) proc.push_front(j); } diff --git a/source/thunder/tracked_buffer.cpp b/source/thunder/tracked_buffer.cpp index a25c131..dca3cbb 100644 --- a/source/thunder/tracked_buffer.cpp +++ b/source/thunder/tracked_buffer.cpp @@ -2,13 +2,83 @@ namespace jvl::thunder { -TrackedBuffer::TrackedBuffer() : Buffer() +MODULE(tracked-buffer); + +// Global cache management +TrackedBuffer::cache_t &TrackedBuffer::cache() +{ + static cache_t cache; + + // JVL_INFO("cached buffers:"); + // for (auto &[k, entry] : cache) { + // fmt::println("\t{} -> ({}, {}, {})", + // k, entry.count, + // entry.buffer.name, + // (void *) entry.link); + // } + + return cache; +} + +const NamedBuffer &TrackedBuffer::cache_load(int32_t cid) +{ + auto &c = cache(); + if (c.contains(cid)) { + auto &entry = c[cid]; + if (entry.link) + return *entry.link; + else + return c[cid].buffer; + } + + JVL_ABORT("no tracked buffer cache entry @{}", cid); +} + +void TrackedBuffer::cache_increment(int32_t cid) +{ + auto &c = cache(); + c[cid].count++; +} + +void TrackedBuffer::cache_decrement(int32_t cid) +{ + auto &c = cache(); + + JVL_ASSERT(c.contains(cid), "no tracked buffer cache entry @{}", cid); + if (--c[cid].count <= 0) { + auto p = c[cid]; + JVL_INFO("offloading cache entry {} (@{})", p.buffer.name, cid); + c.erase(cid); + } +} + +void TrackedBuffer::cache_unlink(int32_t cid) +{ + auto &c = cache(); + auto &entry = c[cid]; + + // Transfer linked buffer, then "destroy" it + if (entry.link) { + entry.buffer = *entry.link; + entry.link = nullptr; + } +} + +void TrackedBuffer::cache_insert(const TrackedBuffer *tb) +{ + auto &c = cache(); + c[tb->cid] = cache_entry_t(1, *tb, static_cast (tb)); +} + +// Track buffer methods +TrackedBuffer::TrackedBuffer() { static size_t id = 0; + cid = id++; name = fmt::format("callable{}", cid); - tracked()[cid] = this; + cache_insert(this); } TrackedBuffer::TrackedBuffer(const TrackedBuffer &other) @@ -16,23 +86,27 @@ TrackedBuffer::TrackedBuffer(const TrackedBuffer &other) *this = other; } -// TODO: destructor will erase the entry if the active * == this - -// TODO: disable copies; overwrite ID during moves... TrackedBuffer &TrackedBuffer::operator=(const TrackedBuffer &other) { if (this != &other) { - Buffer::operator=(other); - cid = other.cid; - name = other.name; + NamedBuffer::operator=(other); // Replace with the most recent tracked version - tracked()[cid] = this; + cid = other.cid; + cache_increment(cid); } return *this; } +TrackedBuffer::~TrackedBuffer() +{ + // Unlink; by now the buffer should have its contents + cache_unlink(cid); + + cache_decrement(cid); +} + void TrackedBuffer::dump() const { fmt::println("------------------------------"); diff --git a/testing/ggx.cpp b/testing/ggx.cpp index c507bf6..98a4da0 100644 --- a/testing/ggx.cpp +++ b/testing/ggx.cpp @@ -32,7 +32,7 @@ struct Material { }; // Random number generation -auto pcg3d = procedure("pcg3d") << [](uvec3 v) +auto B = procedure("pcg3d") << [](uvec3 v) { v = v * 1664525u + 1013904223u; v.x += v.y * v.z; @@ -45,10 +45,10 @@ auto pcg3d = procedure("pcg3d") << [](uvec3 v) return v; }; -auto random3 = procedure("random3") << [](/* inout */ vec3 seed) +auto A = procedure("random3") << [](/* inout */ vec3 seed) { seed = uintBitsToFloat( - (pcg3d(floatBitsToUint(seed)) & 0x007FFFFFu) + (B(floatBitsToUint(seed)) & 0x007FFFFFu) | 0x3F800000u ) - 1.0; @@ -164,7 +164,7 @@ auto ggx_sample = procedure ("ggx_sample") << [](Material mat, vec3 n, vec t = max(avg_Ks/(avg_Kd + avg_Ks), 0.25f); $end(); - vec3 eta = fract(random3(seed)); + vec3 eta = fract(A(seed)); $if(eta.x < t); {