diff --git a/examples/cornell_box b/examples/cornell_box index 5ccfb29..c498fdb 100644 Binary files a/examples/cornell_box and b/examples/cornell_box differ diff --git a/include/core/raytracer.h b/include/core/raytracer.h index fa8a3b2..4f60abf 100644 --- a/include/core/raytracer.h +++ b/include/core/raytracer.h @@ -71,7 +71,7 @@ public: /// @brief Set compute shader (called by renderer) /// @param shader Compute shader - void set_compute_shader(const std::shared_ptr& shader); + void set_compute_shader(const std::shared_ptr &shader); private: uint width_; @@ -90,6 +90,9 @@ private: Buffer bvh_triangle_buffer_; // 添加 bool bvh_built_; // 添加 + uint materials_hash_; + uint lights_hash_; + uint frame_count_; bool initialized_; diff --git a/include/resource/buffer.h b/include/resource/buffer.h index d0ea6da..4cb7521 100644 --- a/include/resource/buffer.h +++ b/include/resource/buffer.h @@ -25,6 +25,12 @@ class Buffer { public: /// @brief Constructor Buffer(); + + Buffer(const Buffer&) = delete; + Buffer& operator=(const Buffer&) = delete; + + Buffer(Buffer&& other) noexcept; + Buffer& operator=(Buffer&& other) noexcept; /// @brief Destructor ~Buffer(); diff --git a/include/resource/texture.h b/include/resource/texture.h index e195ef8..59cad16 100644 --- a/include/resource/texture.h +++ b/include/resource/texture.h @@ -46,6 +46,12 @@ class Texture { public: /// @brief Constructor Texture(); + + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + + Texture(Texture&& other) noexcept; + Texture& operator=(Texture&& other) noexcept; /// @brief Destructor ~Texture(); diff --git a/src/core/raytracer.cpp b/src/core/raytracer.cpp index e110708..8568a99 100644 --- a/src/core/raytracer.cpp +++ b/src/core/raytracer.cpp @@ -5,6 +5,18 @@ namespace are { +namespace { + uint fnv1a_hash_bytes(const void *data, size_t size) { + const uint8_t *bytes = static_cast(data); + uint h = 2166136261u; + for (size_t i = 0; i < size; ++i) { + h ^= bytes[i]; + h *= 16777619u; + } + return h; + } +} // namespace + RayTracer::RayTracer(uint width, uint height, const RayTracerConfig &config) : width_(width) , height_(height) @@ -15,6 +27,8 @@ RayTracer::RayTracer(uint width, uint height, const RayTracerConfig &config) , light_buffer_(INVALID_HANDLE) , bvh_(nullptr) , bvh_built_(false) + , materials_hash_(0u) + , lights_hash_(0u) , frame_count_(0) , initialized_(false) { } @@ -104,6 +118,7 @@ bool RayTracer::rebuild_bvh(const Scene &scene) { } bvh_built_ = true; + reset_accumulation(); Logger::info("BVH built and uploaded successfully"); return true; } @@ -227,7 +242,7 @@ void RayTracer::set_config(const RayTracerConfig &config) { } void RayTracer::upload_scene_data_(const Scene &scene) { - // Upload materials + // Upload materials (on change only) const auto &materials = scene.get_materials(); if (!materials.empty()) { struct MaterialData { @@ -244,7 +259,7 @@ void RayTracer::upload_scene_data_(const Scene &scene) { material_data.reserve(materials.size()); for (const auto &mat : materials) { - MaterialData data; + MaterialData data {}; data.albedo = mat->get_albedo(); data.metallic = mat->get_metallic(); data.emission = mat->get_emission(); @@ -254,14 +269,26 @@ void RayTracer::upload_scene_data_(const Scene &scene) { material_data.push_back(data); } - glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_); - glBufferData(GL_SHADER_STORAGE_BUFFER, - material_data.size() * sizeof(MaterialData), - material_data.data(), GL_DYNAMIC_DRAW); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_); + uint h = fnv1a_hash_bytes(material_data.data(), material_data.size() * sizeof(MaterialData)); + if (h != materials_hash_) { + materials_hash_ = h; + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, material_buffer_); + glBufferData(GL_SHADER_STORAGE_BUFFER, + material_data.size() * sizeof(MaterialData), + material_data.data(), GL_DYNAMIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_); + + reset_accumulation(); // materials changed => invalidate accumulation + } else { + // Still ensure bound (in case other code changed bindings) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, material_buffer_); + } + } else { + materials_hash_ = 0u; } - // Upload lights + // Upload lights (on change only) const auto &lights = scene.get_lights(); if (!lights.empty()) { struct LightData { @@ -279,7 +306,7 @@ void RayTracer::upload_scene_data_(const Scene &scene) { light_data.reserve(lights.size()); for (const auto &light : lights) { - LightData data; + LightData data {}; data.position = light->get_position(); data.type = static_cast(light->get_type()); data.direction = light->get_direction(); @@ -290,11 +317,22 @@ void RayTracer::upload_scene_data_(const Scene &scene) { light_data.push_back(data); } - glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_); - glBufferData(GL_SHADER_STORAGE_BUFFER, - light_data.size() * sizeof(LightData), - light_data.data(), GL_DYNAMIC_DRAW); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_); + uint h = fnv1a_hash_bytes(light_data.data(), light_data.size() * sizeof(LightData)); + if (h != lights_hash_) { + lights_hash_ = h; + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, light_buffer_); + glBufferData(GL_SHADER_STORAGE_BUFFER, + light_data.size() * sizeof(LightData), + light_data.data(), GL_DYNAMIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_); + + reset_accumulation(); // lights changed => invalidate accumulation + } else { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, light_buffer_); + } + } else { + lights_hash_ = 0u; } } diff --git a/src/resource/buffer.cpp b/src/resource/buffer.cpp index 3172908..67d4ad4 100644 --- a/src/resource/buffer.cpp +++ b/src/resource/buffer.cpp @@ -32,8 +32,31 @@ Buffer::Buffer() , usage_(BufferUsage::STATIC_DRAW) { } +Buffer::Buffer(Buffer&& other) noexcept + : handle_(other.handle_) + , type_(other.type_) + , size_(other.size_) + , usage_(other.usage_) { + other.handle_ = INVALID_HANDLE; + other.size_ = 0; +} + +Buffer& Buffer::operator=(Buffer&& other) noexcept { + if (this == &other) return *this; + + release(); + handle_ = other.handle_; + type_ = other.type_; + size_ = other.size_; + usage_ = other.usage_; + + other.handle_ = INVALID_HANDLE; + other.size_ = 0; + return *this; +} + Buffer::~Buffer() { - // Don't auto-release, let user control lifetime + release(); } bool Buffer::create(BufferType type, size_t size, const void* data, BufferUsage usage) { diff --git a/src/resource/texture.cpp b/src/resource/texture.cpp index 521e955..30cfe67 100644 --- a/src/resource/texture.cpp +++ b/src/resource/texture.cpp @@ -104,8 +104,37 @@ Texture::Texture() , has_mipmaps_(false) { } +Texture::Texture(Texture&& other) noexcept + : handle_(other.handle_) + , width_(other.width_) + , height_(other.height_) + , format_(other.format_) + , has_mipmaps_(other.has_mipmaps_) { + other.handle_ = INVALID_HANDLE; + other.width_ = 0; + other.height_ = 0; + other.has_mipmaps_ = false; +} + +Texture& Texture::operator=(Texture&& other) noexcept { + if (this == &other) return *this; + + release(); + handle_ = other.handle_; + width_ = other.width_; + height_ = other.height_; + format_ = other.format_; + has_mipmaps_ = other.has_mipmaps_; + + other.handle_ = INVALID_HANDLE; + other.width_ = 0; + other.height_ = 0; + other.has_mipmaps_ = false; + return *this; +} + Texture::~Texture() { - // Don't auto-release, let user control lifetime + release(); } bool Texture::load_from_file(const std::string& path, bool generate_mipmaps) {