diff options
| author | hachem <im@hachem.wtf> | 2025-08-13 18:20:54 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-13 18:20:54 +0200 |
| commit | 2d8f706efe6e0a02b3238c2f22e7cdce620e9807 (patch) | |
| tree | 06734503f8422c1509da09b5dd6a3ce50bad875c /src | |
| parent | 3b02022fd79ad0e130b8c49e7707d221bfc97bb5 (diff) | |
feat: compute shaders and compute textures
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/application.cpp | 122 | ||||
| -rw-r--r-- | src/core/application.h | 7 | ||||
| -rw-r--r-- | src/core/event.h | 42 | ||||
| -rw-r--r-- | src/platform/opengl/opengl_shader.cpp | 30 | ||||
| -rw-r--r-- | src/platform/opengl/opengl_shader.h | 6 | ||||
| -rw-r--r-- | src/platform/opengl/opengl_texture.cpp | 7 | ||||
| -rw-r--r-- | src/platform/opengl/opengl_texture.h | 4 | ||||
| -rw-r--r-- | src/rendering/shader.cpp | 11 | ||||
| -rw-r--r-- | src/rendering/shader.h | 11 | ||||
| -rw-r--r-- | src/rendering/texture.h | 4 |
10 files changed, 208 insertions, 36 deletions
diff --git a/src/core/application.cpp b/src/core/application.cpp index 0acd232..435b6aa 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -223,6 +223,30 @@ namespace Donut m_texture->set_data(pixel_data, 256 * 256 * 4); delete[] pixel_data; + + m_compute_shader = std::shared_ptr<Shader>(Shader::create("assets/texture_processor.glsl")); + + if (!m_compute_shader) + { + std::cout << "Failed to create compute shader! Falling back to CPU texture generation." << std::endl; + m_use_compute_shader = false; + } + else + std::cout << "Compute shader created successfully!" << std::endl; + + m_processed_texture = Texture2D::create(256, 256); + m_compute_brightness = 0.0f; + m_compute_contrast = 1.0f; + m_compute_saturation = 1.0f; + m_use_compute_shader = true; + + std::cout << "=== Compute Shader Test ===" << std::endl; + std::cout << "C - Toggle compute shader on/off" << std::endl; + std::cout << "Arrow Keys - Adjust brightness (Up/Down) and contrast (Left/Right)" << std::endl; + std::cout << "Q/E - Adjust saturation" << std::endl; + std::cout << "WASD - Move camera" << std::endl; + std::cout << "Mouse - Look around" << std::endl; + std::cout << "==========================" << std::endl; } void Application::on_shutdown() @@ -252,6 +276,42 @@ namespace Donut m_camera->move_down(m_delta_time); } + static bool c_key_pressed = false; + if (m_keys[GLFW_KEY_C] && !c_key_pressed) + { + m_use_compute_shader = !m_use_compute_shader; + std::cout << "Compute shader: " << (m_use_compute_shader ? "ON" : "OFF") << std::endl; + c_key_pressed = true; + } + if (!m_keys[GLFW_KEY_C]) + c_key_pressed = false; + if (m_keys[GLFW_KEY_UP]) + m_compute_brightness += m_delta_time * 0.5f; + if (m_keys[GLFW_KEY_DOWN]) + m_compute_brightness -= m_delta_time * 0.5f; + if (m_keys[GLFW_KEY_LEFT]) + m_compute_contrast -= m_delta_time * 0.5f; + if (m_keys[GLFW_KEY_RIGHT]) + m_compute_contrast += m_delta_time * 0.5f; + if (m_keys[GLFW_KEY_Q]) + m_compute_saturation -= m_delta_time * 0.5f; + if (m_keys[GLFW_KEY_E]) + m_compute_saturation += m_delta_time * 0.5f; + + m_compute_brightness = glm::clamp(m_compute_brightness, -1.0f, 1.0f); + m_compute_contrast = glm::clamp(m_compute_contrast, 0.1f, 3.0f); + m_compute_saturation = glm::clamp(m_compute_saturation, 0.0f, 2.0f); + + static float status_timer = 0.0f; + status_timer += m_delta_time; + if (status_timer > 2.0f) + { + std::cout << "Compute Shader Status - Brightness: " << m_compute_brightness + << ", Contrast: " << m_compute_contrast + << ", Saturation: " << m_compute_saturation << std::endl; + status_timer = 0.0f; + } + update_texture(); } @@ -260,7 +320,12 @@ namespace Donut Renderer::set_clear_color({ 0.0f, 0.0f, 0.0f, 1.0f }); Renderer::clear(); - if (m_texture) + if (m_use_compute_shader && m_processed_texture) + { + m_processed_texture->bind(0); + m_shader->set_int("u_Texture", 0); + } + else if (m_texture) { m_texture->bind(0); m_shader->set_int("u_Texture", 0); @@ -299,28 +364,45 @@ namespace Donut m_texture_time += m_delta_time; - uint32_t* pixel_data = new uint32_t[256 * 256]; - for (int y = 0; y < 256; y++) + if (m_use_compute_shader && m_compute_shader && m_processed_texture) { - for (int x = 0; x < 256; x++) + m_compute_shader->bind(); + m_texture->bind(0); + m_compute_shader->set_int("u_InputTexture", 0); + m_processed_texture->bind_as_image(1, false); + m_compute_shader->set_float2("u_TextureSize", glm::vec2(256.0f, 256.0f)); + m_compute_shader->set_float("u_Time", m_texture_time); + m_compute_shader->set_float("u_Brightness", m_compute_brightness); + m_compute_shader->set_float("u_Contrast", m_compute_contrast); + m_compute_shader->set_float("u_Saturation", m_compute_saturation); + m_compute_shader->dispatch(16, 16, 1); + m_compute_shader->memory_barrier(IMAGE_ACCESS_BARRIER_BIT); + } + else + { + uint32_t* pixel_data = new uint32_t[256 * 256]; + for (int y = 0; y < 256; y++) { - float time = m_texture_time * 2.0f; - float u = (float)x / 256.0f; - float v = (float)y / 256.0f; - - float wave = sin(u * 10.0f + time) * cos(v * 10.0f + time * 0.5f); - wave = (wave + 1.0f) * 0.5f; - - uint8_t r = (uint8_t)(wave * 255); - uint8_t g = (uint8_t)((1.0f - wave) * 255); - uint8_t b = (uint8_t)((u + v) * 0.5f * 255); - uint8_t a = 255; - - pixel_data[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + for (int x = 0; x < 256; x++) + { + float time = m_texture_time * 2.0f; + float u = (float)x / 256.0f; + float v = (float)y / 256.0f; + + float wave = sin(u * 10.0f + time) * cos(v * 10.0f + time * 0.5f); + wave = (wave + 1.0f) * 0.5f; + + uint8_t r = (uint8_t)(wave * 255); + uint8_t g = (uint8_t)((1.0f - wave) * 255); + uint8_t b = (uint8_t)((u + v) * 0.5f * 255); + uint8_t a = 255; + + pixel_data[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + } } + + m_texture->set_data(pixel_data, 256 * 256 * 4); + delete[] pixel_data; } - - m_texture->set_data(pixel_data, 256 * 256 * 4); - delete[] pixel_data; } } diff --git a/src/core/application.h b/src/core/application.h index 2d8c55a..713ecba 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -47,6 +47,13 @@ namespace Donut std::shared_ptr<Shader> m_shader; std::shared_ptr<Texture2D> m_texture; + std::shared_ptr<Shader> m_compute_shader; + std::shared_ptr<Texture2D> m_processed_texture; + float m_compute_brightness; + float m_compute_contrast; + float m_compute_saturation; + bool m_use_compute_shader; + std::unique_ptr<Camera> m_camera; bool m_keys[1024] = { false }; diff --git a/src/core/event.h b/src/core/event.h index 6639f28..e4d1be1 100644 --- a/src/core/event.h +++ b/src/core/event.h @@ -81,7 +81,8 @@ namespace Donut virtual int get_category_flags() const override { return EventCategoryKeyboard | \ EventCategoryInput; } - class WindowResizeEvent : public Event + class WindowResizeEvent + : public Event { public: WindowResizeEvent(unsigned int width, unsigned int height) @@ -101,28 +102,32 @@ namespace Donut unsigned int m_width, m_height; }; - class WindowCloseEvent : public Event + class WindowCloseEvent + : public Event { public: WindowCloseEvent() = default; APPLICATION_EVENT_CLASS_TYPE(WindowClose) }; - class WindowFocusEvent : public Event + class WindowFocusEvent + : public Event { public: WindowFocusEvent() = default; APPLICATION_EVENT_CLASS_TYPE(WindowFocus) }; - class WindowLostFocusEvent : public Event + class WindowLostFocusEvent + : public Event { public: WindowLostFocusEvent() = default; APPLICATION_EVENT_CLASS_TYPE(WindowLostFocus) }; - class WindowMovedEvent : public Event + class WindowMovedEvent + : public Event { public: WindowMovedEvent(int x, int y) @@ -142,7 +147,8 @@ namespace Donut int m_x, m_y; }; - class KeyEvent : public Event + class KeyEvent + : public Event { public: int get_key_code() const { return m_key_code; } @@ -154,7 +160,8 @@ namespace Donut int m_key_code; }; - class KeyPressedEvent : public KeyEvent + class KeyPressedEvent + : public KeyEvent { public: KeyPressedEvent(int keycode, bool is_repeat = false) @@ -174,7 +181,8 @@ namespace Donut bool m_is_repeat; }; - class KeyReleasedEvent : public KeyEvent + class KeyReleasedEvent + : public KeyEvent { public: KeyReleasedEvent(int keycode) : KeyEvent(keycode) { } @@ -187,7 +195,8 @@ namespace Donut KEYBOARD_EVENT_CLASS_TYPE(KeyReleased) }; - class KeyTypedEvent : public KeyEvent + class KeyTypedEvent + : public KeyEvent { public: KeyTypedEvent(int keycode) : KeyEvent(keycode) { } @@ -200,7 +209,8 @@ namespace Donut KEYBOARD_EVENT_CLASS_TYPE(KeyTyped) }; - class MouseMovedEvent : public Event + class MouseMovedEvent + : public Event { public: MouseMovedEvent(float x, float y) @@ -220,7 +230,8 @@ namespace Donut float m_mouse_x, m_mouse_y; }; - class MouseScrolledEvent : public Event + class MouseScrolledEvent + : public Event { public: MouseScrolledEvent(float x_offset, float y_offset) @@ -241,7 +252,8 @@ namespace Donut float m_x_offset, m_y_offset; }; - class MouseButtonEvent : public Event + class MouseButtonEvent + : public Event { public: int get_mouse_button() const { return m_button; } @@ -254,7 +266,8 @@ namespace Donut int m_button; }; - class MouseButtonPressedEvent : public MouseButtonEvent + class MouseButtonPressedEvent + : public MouseButtonEvent { public: MouseButtonPressedEvent(int button) : MouseButtonEvent(button) { } @@ -267,7 +280,8 @@ namespace Donut MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonPressed) }; - class MouseButtonReleasedEvent : public MouseButtonEvent + class MouseButtonReleasedEvent + : public MouseButtonEvent { public: MouseButtonReleasedEvent(int button) : MouseButtonEvent(button) { } diff --git a/src/platform/opengl/opengl_shader.cpp b/src/platform/opengl/opengl_shader.cpp index 39e582a..10da3de 100644 --- a/src/platform/opengl/opengl_shader.cpp +++ b/src/platform/opengl/opengl_shader.cpp @@ -12,6 +12,8 @@ namespace Donut return GL_VERTEX_SHADER; if (type == "fragment" || type == "pixel") return GL_FRAGMENT_SHADER; + if (type == "compute") + return GL_COMPUTE_SHADER; return 0; } @@ -37,6 +39,14 @@ namespace Donut compile(sources); } + OpenGLShader::OpenGLShader(const std::string& name, const std::string& compute_src) + : m_name(name) + { + std::unordered_map<uint32_t, std::string> sources; + sources[GL_COMPUTE_SHADER] = compute_src; + compile(sources); + } + OpenGLShader::~OpenGLShader() { glDeleteProgram(m_renderer_id); @@ -160,6 +170,11 @@ namespace Donut upload_uniform_float(name, value); } + void OpenGLShader::set_float2(const std::string& name, const glm::vec2& value) + { + upload_uniform_float2(name, value); + } + void OpenGLShader::set_float3(const std::string& name, const glm::vec3& value) { upload_uniform_float3(name, value); @@ -222,4 +237,19 @@ namespace Donut int location = glGetUniformLocation(m_renderer_id, name.c_str()); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); } + + void OpenGLShader::dispatch(uint32_t x, uint32_t y, uint32_t z) + { + glDispatchCompute(x, y, z); + } + + void OpenGLShader::dispatch_indirect(uint32_t offset) + { + glDispatchComputeIndirect(offset); + } + + void OpenGLShader::memory_barrier(uint32_t barriers) + { + glMemoryBarrier(barriers); + } }; diff --git a/src/platform/opengl/opengl_shader.h b/src/platform/opengl/opengl_shader.h index e87c839..1a79461 100644 --- a/src/platform/opengl/opengl_shader.h +++ b/src/platform/opengl/opengl_shader.h @@ -12,6 +12,7 @@ namespace Donut public: OpenGLShader(const std::string& filepath); OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src); + OpenGLShader(const std::string& name, const std::string& compute_src); virtual ~OpenGLShader(); virtual void bind() const override; @@ -20,10 +21,15 @@ namespace Donut virtual void set_int(const std::string& name, int value) override; virtual void set_int_array(const std::string& name, int* values, uint32_t count) override; virtual void set_float(const std::string& name, float value) override; + virtual void set_float2(const std::string& name, const glm::vec2& value) override; virtual void set_float3(const std::string& name, const glm::vec3& value) override; virtual void set_float4(const std::string& name, const glm::vec4& value) override; virtual void set_mat4(const std::string& name, const glm::mat4& value) override; + virtual void dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) override; + virtual void dispatch_indirect(uint32_t offset = 0) override; + virtual void memory_barrier(uint32_t barriers) override; + virtual const std::string& get_name() const override { return m_name; } void upload_uniform_int(const std::string& name, int value); diff --git a/src/platform/opengl/opengl_texture.cpp b/src/platform/opengl/opengl_texture.cpp index 87e9345..100909f 100644 --- a/src/platform/opengl/opengl_texture.cpp +++ b/src/platform/opengl/opengl_texture.cpp @@ -55,6 +55,7 @@ namespace Donut std::cout << "Data must be entire texture!" << std::endl; return; } + glTextureSubImage2D(m_renderer_id, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, data); } @@ -62,4 +63,10 @@ namespace Donut { glBindTextureUnit(slot, m_renderer_id); } + + void OpenGLTexture2D::bind_as_image(uint32_t slot, bool read_only) const + { + GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY; + glBindImageTexture(slot, m_renderer_id, 0, GL_FALSE, 0, access, m_internal_format); + } }; diff --git a/src/platform/opengl/opengl_texture.h b/src/platform/opengl/opengl_texture.h index 0782e8b..ddd4f39 100644 --- a/src/platform/opengl/opengl_texture.h +++ b/src/platform/opengl/opengl_texture.h @@ -5,7 +5,8 @@ namespace Donut { - class OpenGLTexture2D : public Texture2D + class OpenGLTexture2D + : public Texture2D { public: OpenGLTexture2D(uint32_t width, uint32_t height); @@ -18,6 +19,7 @@ namespace Donut virtual void set_data(void* data, uint32_t size) override; virtual void bind(uint32_t slot = 0) const override; + virtual void bind_as_image(uint32_t slot = 0, bool read_only = false) const override; virtual bool operator==(const Texture& other) const override { diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp index 10f6c49..d3fef07 100644 --- a/src/rendering/shader.cpp +++ b/src/rendering/shader.cpp @@ -27,6 +27,17 @@ namespace Donut } } + Shader* Shader::create_compute(const std::string& name, const std::string& compute_src) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return new OpenGLShader(name, compute_src); + default: + return nullptr; + } + } + void ShaderLibrary::add(const std::shared_ptr<Shader>& shader) { auto& name = shader->get_name(); diff --git a/src/rendering/shader.h b/src/rendering/shader.h index d9128f6..fe869ba 100644 --- a/src/rendering/shader.h +++ b/src/rendering/shader.h @@ -5,6 +5,11 @@ #include <memory> #include <glm/glm.hpp> +#define SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define UNIFORM_BARRIER_BIT 0x00000004 +#define TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define IMAGE_ACCESS_BARRIER_BIT 0x00000020 + namespace Donut { class Shader @@ -18,14 +23,20 @@ namespace Donut virtual void set_int(const std::string& name, int value) = 0; virtual void set_int_array(const std::string& name, int* values, uint32_t count) = 0; virtual void set_float(const std::string& name, float value) = 0; + virtual void set_float2(const std::string& name, const glm::vec2& value) = 0; virtual void set_float3(const std::string& name, const glm::vec3& value) = 0; virtual void set_float4(const std::string& name, const glm::vec4& value) = 0; virtual void set_mat4(const std::string& name, const glm::mat4& value) = 0; + virtual void dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) = 0; + virtual void dispatch_indirect(uint32_t offset = 0) = 0; + virtual void memory_barrier(uint32_t barriers) = 0; + virtual const std::string& get_name() const = 0; static Shader* create(const std::string& filepath); static Shader* create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src); + static Shader* create_compute(const std::string& name, const std::string& compute_src); }; class ShaderLibrary diff --git a/src/rendering/texture.h b/src/rendering/texture.h index e3c53f8..68866b5 100644 --- a/src/rendering/texture.h +++ b/src/rendering/texture.h @@ -17,11 +17,13 @@ namespace Donut virtual void set_data(void* data, uint32_t size) = 0; virtual void bind(uint32_t slot = 0) const = 0; + virtual void bind_as_image(uint32_t slot = 0, bool read_only = false) const = 0; virtual bool operator==(const Texture& other) const = 0; }; - class Texture2D : public Texture + class Texture2D + : public Texture { public: static std::shared_ptr<Texture2D> create(uint32_t width, uint32_t height); |
