The acrylic effect blits its backdrop from the default framebuffer using the logical viewport size as the source rectangle. On a HiDPI display the framebuffer is larger than the logical size, so the blur sampled only the lower-left corner of the screen and the glass showed the wrong region stretched across each panel. - The capture blit source now uses the real framebuffer pixel size (logical size times ImGui's DisplayFramebufferScale, truncated the same way the GL backend does), while the capture and blur targets stay at logical size. The framebuffer size is tracked separately from the viewport size so quality or buffer-lifetime resets cannot lose it, and moving the window between a Retina and a non-Retina display redoes the capture. DX11 is unaffected; its scale is one. - The debug screenshot sweep captures at framebuffer size too, so HiDPI captures are no longer cropped. Tests: the framebuffer-extent helper matches the GL backend's truncation and falls back to the logical size when the scale is unknown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
193 lines
4.9 KiB
C++
193 lines
4.9 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#pragma once
|
|
|
|
// GL type definitions needed by class declarations.
|
|
// If GLAD was already included, these types are already defined.
|
|
#ifndef GLAD_GL_H_
|
|
typedef unsigned int GLuint;
|
|
typedef int GLint;
|
|
typedef unsigned int GLenum;
|
|
typedef int GLsizei;
|
|
#endif
|
|
#include <cstdint>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
namespace effects {
|
|
|
|
/**
|
|
* @brief OpenGL Framebuffer Object manager for off-screen rendering
|
|
*
|
|
* Used for capturing screen content to use as blur source for acrylic effects.
|
|
*/
|
|
class Framebuffer {
|
|
public:
|
|
Framebuffer();
|
|
~Framebuffer();
|
|
|
|
// Non-copyable
|
|
Framebuffer(const Framebuffer&) = delete;
|
|
Framebuffer& operator=(const Framebuffer&) = delete;
|
|
|
|
// Move constructors
|
|
Framebuffer(Framebuffer&& other) noexcept;
|
|
Framebuffer& operator=(Framebuffer&& other) noexcept;
|
|
|
|
/**
|
|
* @brief Initialize framebuffer with given dimensions
|
|
* @param width Width in pixels
|
|
* @param height Height in pixels
|
|
* @param useFloat16 Use RGBA16F format for higher precision (reduces banding)
|
|
* @return true if successful
|
|
*/
|
|
bool init(int width, int height, bool useFloat16 = false);
|
|
|
|
/**
|
|
* @brief Resize framebuffer (recreates internal textures)
|
|
* @param width New width
|
|
* @param height New height
|
|
* @return true if successful
|
|
*/
|
|
bool resize(int width, int height);
|
|
|
|
/**
|
|
* @brief Release all OpenGL resources
|
|
*/
|
|
void destroy();
|
|
|
|
/**
|
|
* @brief Bind this framebuffer for rendering
|
|
*/
|
|
void bind();
|
|
|
|
/**
|
|
* @brief Unbind (return to default framebuffer)
|
|
*/
|
|
void unbind();
|
|
|
|
/**
|
|
* @brief Clear the framebuffer
|
|
* @param r Red component (0-1)
|
|
* @param g Green component (0-1)
|
|
* @param b Blue component (0-1)
|
|
* @param a Alpha component (0-1)
|
|
*/
|
|
void clear(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f);
|
|
|
|
/**
|
|
* @brief Get the color texture attached to this framebuffer
|
|
* @return OpenGL texture ID
|
|
*/
|
|
GLuint getColorTexture() const { return colorTexture_; }
|
|
|
|
/**
|
|
* @brief Get the underlying framebuffer object ID
|
|
* @return OpenGL FBO ID
|
|
*/
|
|
GLuint getFbo() const { return fbo_; }
|
|
|
|
/**
|
|
* @brief Get framebuffer dimensions
|
|
*/
|
|
int getWidth() const { return width_; }
|
|
int getHeight() const { return height_; }
|
|
|
|
/**
|
|
* @brief Check if framebuffer is valid and complete
|
|
*/
|
|
bool isValid() const { return fbo_ != 0 && isComplete_; }
|
|
|
|
/**
|
|
* @brief Copy a region from the current framebuffer to this one
|
|
* @param srcX Source X position
|
|
* @param srcY Source Y position
|
|
* @param srcWidth Source width
|
|
* @param srcHeight Source height
|
|
* @param dstX Destination X position
|
|
* @param dstY Destination Y position
|
|
*/
|
|
void blitFrom(GLuint srcFbo, int srcX, int srcY, int srcWidth, int srcHeight,
|
|
int dstX = 0, int dstY = 0);
|
|
|
|
/**
|
|
* @brief Copy entire default framebuffer to this one
|
|
* @param viewportWidth Current viewport width
|
|
* @param viewportHeight Current viewport height
|
|
*/
|
|
void captureScreen(int srcWidth, int srcHeight);
|
|
|
|
private:
|
|
void cleanup();
|
|
bool checkComplete();
|
|
|
|
GLuint fbo_ = 0;
|
|
GLuint colorTexture_ = 0;
|
|
|
|
int width_ = 0;
|
|
int height_ = 0;
|
|
bool useFloat16_ = false; // RGBA16F for blur precision
|
|
bool isComplete_ = false;
|
|
};
|
|
|
|
/**
|
|
* @brief Manages multiple framebuffers for ping-pong blur operations
|
|
*/
|
|
class FramebufferPingPong {
|
|
public:
|
|
FramebufferPingPong() = default;
|
|
~FramebufferPingPong() = default;
|
|
|
|
/**
|
|
* @brief Initialize both framebuffers
|
|
*/
|
|
bool init(int width, int height, bool useFloat16 = false);
|
|
|
|
/**
|
|
* @brief Resize both framebuffers
|
|
*/
|
|
bool resize(int width, int height);
|
|
|
|
/**
|
|
* @brief Destroy both framebuffers
|
|
*/
|
|
void destroy();
|
|
|
|
/**
|
|
* @brief Swap which buffer is source/destination
|
|
*/
|
|
void swap();
|
|
|
|
/**
|
|
* @brief Get current source framebuffer
|
|
*/
|
|
Framebuffer& getSource() { return buffers_[currentSource_]; }
|
|
|
|
/**
|
|
* @brief Get current destination framebuffer
|
|
*/
|
|
Framebuffer& getDest() { return buffers_[1 - currentSource_]; }
|
|
|
|
/**
|
|
* @brief Get source texture for reading
|
|
*/
|
|
GLuint getSourceTexture() const { return buffers_[currentSource_].getColorTexture(); }
|
|
|
|
/**
|
|
* @brief Get destination texture
|
|
*/
|
|
GLuint getDestTexture() const { return buffers_[1 - currentSource_].getColorTexture(); }
|
|
|
|
bool isValid() const { return buffers_[0].isValid() && buffers_[1].isValid(); }
|
|
|
|
private:
|
|
Framebuffer buffers_[2];
|
|
int currentSource_ = 0;
|
|
};
|
|
|
|
} // namespace effects
|
|
} // namespace ui
|
|
} // namespace dragonx
|