Add an initial rhi implementation that mimics the opengl implementation

This commit is contained in:
luisangelsm
2026-01-17 22:46:27 +01:00
parent 91b8a31727
commit 3381754c12
25 changed files with 2739 additions and 21 deletions

View File

@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(qsb:*)",
"Bash(ls:*)"
]
}
}

View File

@ -27,10 +27,20 @@ INCLUDEPATH += ../common \
!CONFIG(no_opengl) {
INCLUDEPATH += ../common/gl
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
INCLUDEPATH += ../common/rhi
DEFINES += YACREADER_USE_RHI
}
}
win32 {
LIBS += -loleaut32 -lole32 -lshell32 -lopengl32 -luser32
LIBS += -loleaut32 -lole32 -lshell32 -luser32
# When using RHI (Qt 6.7+), don't link OpenGL directly - QRhiWidget handles graphics APIs
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
message("RHI mode: not linking opengl32 (using QRhiWidget)")
} else {
LIBS += -lopengl32
}
msvc {
QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL
@ -51,6 +61,10 @@ QT += network widgets core multimedia svg
greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets core5compat
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
QT += gui-private
}
#CONFIG += release
CONFIG -= flat
@ -93,6 +107,10 @@ HEADERS += ../common/comic.h \
!CONFIG(no_opengl) {
HEADERS += ../common/gl/yacreader_flow_gl.h \
goto_flow_gl.h
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
HEADERS += ../common/rhi/yacreader_flow_rhi.h
}
}
SOURCES += ../common/comic.cpp \
@ -132,6 +150,11 @@ SOURCES += ../common/comic.cpp \
!CONFIG(no_opengl) {
SOURCES += ../common/gl/yacreader_flow_gl.cpp \
goto_flow_gl.cpp
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
SOURCES += ../common/rhi/yacreader_flow_rhi.cpp
RESOURCES += ../common/rhi/shaders/shaders.qrc
}
}
include(../custom_widgets/custom_widgets_yacreader.pri)

View File

@ -15,17 +15,17 @@ GoToFlowGL::GoToFlowGL(QWidget *parent, FlowType flowType)
: GoToFlowWidget(parent)
{
Q_UNUSED(flowType)
flow = new YACReaderPageFlowGL(this);
flow = new YACReaderPageFlowImpl(this);
flow->setShowMarks(false);
imageSize = Configuration::getConfiguration().getGotoSlideSize();
flow->setSlideSize(imageSize);
connect(flow, &YACReaderFlowGL::centerIndexChanged, this, &GoToFlowWidget::setPageNumber);
connect(flow, &YACReaderFlowGL::selected, this, &GoToFlowGL::goToPage);
connect(flow, &YACReaderPageFlowImpl::centerIndexChanged, this, &GoToFlowWidget::setPageNumber);
connect(flow, &YACReaderPageFlowImpl::selected, this, &GoToFlowGL::goToPage);
connect(toolBar, &GoToFlowToolBar::goToPage, this, &GoToFlowGL::goToPage);
connect(toolBar, &GoToFlowToolBar::setCenter, flow, &YACReaderFlowGL::setCenterIndex);
connect(toolBar, &GoToFlowToolBar::setCenter, flow, &YACReaderPageFlowImpl::setCenterIndex);
mainLayout->addWidget(flow);
toolBar->raise();

View File

@ -3,7 +3,15 @@
#include "yacreader_global.h"
#include "goto_flow_widget.h"
// Conditional include based on Qt version and RHI availability
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
#include "yacreader_flow_rhi.h"
using YACReaderPageFlowImpl = YACReaderPageFlow3D;
#else
#include "yacreader_flow_gl.h"
using YACReaderPageFlowImpl = YACReaderPageFlowGL;
#endif
class QLineEdit;
class QIntValidator;
@ -28,7 +36,7 @@ public:
void setFlowRightToLeft(bool b) override;
private:
YACReaderPageFlowGL *flow;
YACReaderPageFlowImpl *flow;
void keyPressEvent(QKeyEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
// Comic * comic;

View File

@ -86,6 +86,13 @@ Viewer::Viewer(QWidget *parent)
// CONFIG GOTO_FLOW--------------------------------------------------------
#ifndef NO_OPENGL
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
// When using RHI, don't check OpenGL - assume hardware acceleration is available
bool openGLAvailable = true;
if (!settings->contains(USE_OPEN_GL))
settings->setValue(USE_OPEN_GL, 2);
#else
OpenGLChecker openGLChecker;
bool openGLAvailable = openGLChecker.hasCompatibleOpenGLVersion();
@ -93,6 +100,7 @@ Viewer::Viewer(QWidget *parent)
settings->setValue(USE_OPEN_GL, 2);
else if (!openGLAvailable)
settings->setValue(USE_OPEN_GL, 0);
#endif
if ((settings->value(USE_OPEN_GL).toBool() == true))
goToFlow = new GoToFlowGL(this, Configuration::getConfiguration().getFlowType());

View File

@ -20,8 +20,18 @@ include (../dependencies/pdf_backend.pri)
INCLUDEPATH += ../common/gl
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
INCLUDEPATH += ../common/rhi
DEFINES += YACREADER_USE_RHI
}
win32 {
LIBS += -loleaut32 -lole32 -lshell32 -lopengl32 -luser32
LIBS += -loleaut32 -lole32 -lshell32 -luser32
# When using RHI (Qt 6.7+), don't link OpenGL directly - QRhiWidget handles graphics APIs
message("RHI mode: not linking opengl32 (using QRhiWidget)")
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
} else {
LIBS += -lopengl32
}
msvc {
QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL
@ -51,6 +61,10 @@ QT += sql network widgets svg quickcontrols2
greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets core5compat
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
QT += gui-private
}
# Input
HEADERS += comic_flow.h \
../common/concurrent_queue.h \
@ -140,6 +154,10 @@ HEADERS += comic_flow.h \
!CONFIG(no_opengl) {
HEADERS += ../common/gl/yacreader_flow_gl.h
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
HEADERS += ../common/rhi/yacreader_flow_rhi.h
}
}
SOURCES += comic_flow.cpp \
@ -228,6 +246,11 @@ SOURCES += comic_flow.cpp \
!CONFIG(no_opengl) {
SOURCES += ../common/gl/yacreader_flow_gl.cpp
greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) {
SOURCES += ../common/rhi/yacreader_flow_rhi.cpp
RESOURCES += ../common/rhi/shaders/shaders.qrc
}
}
macx {

View File

@ -158,10 +158,15 @@ void ComicFlowWidgetSW::resortCovers(QList<int> newOrder)
ComicFlowWidgetGL::ComicFlowWidgetGL(QWidget *parent)
: ComicFlowWidget(parent)
{
flow = new YACReaderComicFlowGL(parent);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
qDebug() << "ComicFlowWidgetGL: Creating YACReaderComicFlow3D (RHI implementation)";
#else
qDebug() << "ComicFlowWidgetGL: Creating YACReaderComicFlowGL (OpenGL implementation)";
#endif
flow = new YACReaderComicFlowImpl(this);
connect(flow, &YACReaderFlowGL::centerIndexChanged, this, &ComicFlowWidget::centerIndexChanged);
connect(flow, &YACReaderFlowGL::selected, this, &ComicFlowWidget::selected);
connect(flow, &YACReaderComicFlowImpl::centerIndexChanged, this, &ComicFlowWidget::centerIndexChanged);
connect(flow, &YACReaderComicFlowImpl::selected, this, &ComicFlowWidget::selected);
auto l = new QVBoxLayout;
l->addWidget(flow);

View File

@ -6,7 +6,14 @@
#include "pictureflow.h"
#include "comic_flow.h"
#ifndef NO_OPENGL
// Conditional include based on Qt version and RHI availability
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
#include "yacreader_flow_rhi.h"
using YACReaderComicFlowImpl = YACReaderComicFlow3D;
#else
#include "yacreader_flow_gl.h"
using YACReaderComicFlowImpl = YACReaderComicFlowGL;
#endif
#endif
class ComicFlowWidget : public QWidget
{
@ -83,7 +90,7 @@ class ComicFlowWidgetGL : public ComicFlowWidget
{
Q_OBJECT
private:
YACReaderComicFlowGL *flow;
YACReaderComicFlowImpl *flow;
public:
ComicFlowWidgetGL(QWidget *parent = nullptr);

View File

@ -17,10 +17,10 @@ ComicsView::ComicsView(QWidget *parent)
view = new QQuickWidget();
// QQuickWidget requires rendering into OpenGL framebuffer objects
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
view->quickWindow()->setGraphicsApi(QSGRendererInterface::OpenGL);
#endif
// In Qt 6, QQuickWidget supports Qt RHI and can use any graphics backend
// (Vulkan, Metal, Direct3D, OpenGL, or software rendering).
// The backend can be configured via QQuickWindow::setGraphicsApi() or QSG_RHI_BACKEND env var.
// Note: All widgets in the same top-level window must use the same graphics API.
view->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect(

View File

@ -26,10 +26,10 @@ FolderContentView::FolderContentView(QAction *toogleRecentVisibilityAction, QWid
view = new QQuickWidget();
// QQuickWidget requires rendering into OpenGL framebuffer objects
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
view->quickWindow()->setGraphicsApi(QSGRendererInterface::OpenGL);
#endif
// In Qt 6, QQuickWidget supports Qt RHI and can use any graphics backend
// (Vulkan, Metal, Direct3D, OpenGL, or software rendering).
// The backend can be configured via QQuickWindow::setGraphicsApi() or QSG_RHI_BACKEND env var.
// Note: All widgets in the same top-level window must use the same graphics API.
view->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect(

View File

@ -204,6 +204,12 @@ void LibraryWindow::setupOpenglSetting()
// FLOW-----------------------------------------------------------------------
//---------------------------------------------------------------------------
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
// When using RHI, assume hardware acceleration is available
bool openGLAvailable = true;
if (!settings->contains(USE_OPEN_GL))
settings->setValue(USE_OPEN_GL, 2);
#else
OpenGLChecker openGLChecker;
bool openGLAvailable = openGLChecker.hasCompatibleOpenGLVersion();
@ -212,6 +218,7 @@ void LibraryWindow::setupOpenglSetting()
else if (!openGLAvailable)
settings->setValue(USE_OPEN_GL, 0);
#endif
#endif
}
void LibraryWindow::setupUI()

View File

@ -83,8 +83,12 @@ void logSystemAndConfig()
else
QLOG_INFO() << "OpenGL : disabled";
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
QLOG_INFO() << "Using RHI (Qt Rendering Hardware Interface) - graphics backend will be auto-selected";
#else
OpenGLChecker checker;
QLOG_INFO() << "OpenGL version : " << checker.textVersionDescription();
#endif
auto libraries = DBHelper::getLibraries().getLibraries();
QLOG_INFO() << "Libraries: ";
@ -200,6 +204,8 @@ int main(int argc, char **argv)
#endif
parser.process(app);
// When using RHI (Qt 6.7+), don't allow OpenGL attribute overrides
#if !defined(YACREADER_USE_RHI) || QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
#ifdef Q_OS_WIN
if (parser.isSet("opengl")) {
QTextStream qout(stdout);
@ -216,6 +222,7 @@ int main(int argc, char **argv)
parser.showHelp();
}
}
#endif
#endif
if (parser.isSet("loglevel")) {

View File

@ -1,7 +1,5 @@
#include "opengl_checker.h"
#include "QsLog.h"
OpenGLChecker::OpenGLChecker()
: compatibleOpenGLVersion(true)
{

287
common/rhi/README.md Normal file
View File

@ -0,0 +1,287 @@
# YACReader Flow RHI Implementation
This directory contains the QRhiWidget-based implementation of the YACReader 3D cover flow, providing cross-platform 3D rendering support for Qt 6.7+.
## Overview
The RHI (Rendering Hardware Interface) implementation is a modern replacement for the OpenGL-based flow (`yacreader_flow_gl`) that:
- ✅ Supports **multiple graphics APIs**: Vulkan, Metal, Direct3D 11/12, OpenGL
- ✅ Provides **native performance** on modern platforms (Metal on macOS, D3D on Windows)
- ✅ Maintains **100% API compatibility** with the OpenGL version
- ✅ Works seamlessly with **Qt 6.7+** while Qt5 continues using OpenGL
- ✅ Enables **future-proof** rendering infrastructure
## Architecture
### Class Hierarchy
```
QRhiWidget (Qt base class)
└── YACReaderFlow3D (Base implementation)
├── YACReaderComicFlow3D (File path-based loading for library)
└── YACReaderPageFlow3D (Byte array-based loading for viewer)
```
### Files
- **yacreader_flow_rhi.h** - Header with class definitions
- **yacreader_flow_rhi.cpp** - Implementation
- **shaders/** - GLSL 450 shaders and compiled .qsb files
- **README.md** - This file
## Key Features
### Graphics Pipeline
The implementation uses:
- **Instanced rendering** for efficient batch drawing
- **Dual-pass rendering** (reflections + covers)
- **MSAA** (4x by default) for anti-aliasing
- **Depth testing** and back-face culling
- **Alpha blending** for transparency
### Resource Management
QRhi resources managed:
- `QRhiBuffer` for vertices, instance data, and uniforms
- `QRhiTexture` for cover images, marks, and default texture
- `QRhiSampler` for texture filtering
- `QRhiGraphicsPipeline` for render state
- `QRhiShaderResourceBindings` for uniform/texture bindings
### Shader System
Shaders are written in **GLSL 4.50** and compiled to `.qsb` format supporting:
- OpenGL ES 2.0, 3.0
- OpenGL 2.1, 3.0+
- HLSL (Direct3D 11/12)
- Metal Shading Language (macOS/iOS)
## Integration
### Qt5 vs Qt6 Selection
The build system automatically selects the appropriate implementation:
**Qt 5.x**: Uses `YACReaderFlowGL` (OpenGL-based)
**Qt 6.0-6.6**: Uses `YACReaderFlowGL` (OpenGL-based)
**Qt 6.7+**: Uses `YACReaderFlow3D` (RHI-based) if `YACREADER_USE_RHI` is defined
### Type Aliases
Applications use type aliases for seamless switching:
```cpp
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI)
using YACReaderPageFlowImpl = YACReaderPageFlow3D;
using YACReaderComicFlowImpl = YACReaderComicFlow3D;
#else
using YACReaderPageFlowImpl = YACReaderPageFlowGL;
using YACReaderComicFlowImpl = YACReaderComicFlowGL;
#endif
```
### Example Usage (YACReader Viewer)
```cpp
// goto_flow_gl.cpp
flow = new YACReaderPageFlowImpl(this);
flow->setShowMarks(false);
flow->populate(numPages);
connect(flow, &YACReaderPageFlowImpl::selected, this, &GoToFlowGL::goToPage);
```
### Example Usage (YACReaderLibrary)
```cpp
// comic_flow_widget.cpp
flow = new YACReaderComicFlowImpl(parent);
flow->setImagePaths(pathsList);
connect(flow, &YACReaderComicFlowImpl::centerIndexChanged,
this, &ComicFlowWidget::centerIndexChanged);
```
## API Compatibility
All public methods from `YACReaderFlowGL` are preserved:
### Navigation
- `showPrevious()`, `showNext()`
- `setCurrentIndex(int)`, `setCenterIndex(unsigned int)`
- `showSlide(int)`, `centerIndex()`
### Configuration
- `setPreset(const Preset &)`
- `setPerformance(Performance)`
- `setFlowRightToLeft(bool)`
- `setZoom(int)`, `setRotation(int)`
- `setCF_RX/RY/RZ(int)`, `setCF_Y/Z(int)`
- `setX_Distance(int)`, `setCenter_Distance(int)`, etc.
### Appearance
- `setBackgroundColor(const QColor &)`
- `setTextColor(const QColor &)`
- `setShadingColor(const QColor &)`
- `setShowMarks(bool)`, `setMarks(QVector<...>)`
### Content Management
- `populate(int)`, `clear()`, `reset()`, `reload()`
- `insert()`, `remove(int)`, `add(int)`, `replace()`
- Subclass-specific: `setImagePaths()`, `resortCovers()`, etc.
## Building
### Prerequisites
1. **Qt 6.7 or later**
2. **qsb tool** (Qt Shader Baker) in PATH
3. **C++17 compiler**
### Compile Shaders
Before building YACReader, compile the shaders:
```bash
cd common/rhi/shaders
# Windows
compile_shaders.bat
# Unix/macOS
chmod +x compile_shaders.sh
./compile_shaders.sh
```
This generates `flow.vert.qsb` and `flow.frag.qsb` which are embedded via `shaders.qrc`.
### Build YACReader
The `.pro` files automatically include RHI sources for Qt 6.7+:
```bash
qmake YACReader.pro
make
```
For Qt 5 builds, the OpenGL version is used automatically.
## Graphics API Selection
QRhiWidget auto-selects the best API per platform:
- **macOS/iOS**: Metal (native)
- **Windows**: Direct3D 11 (default) or Direct3D 12
- **Linux**: Vulkan or OpenGL
- **Android**: OpenGL ES 3.0 or Vulkan
You can force a specific API via environment variables (for testing):
```bash
# Force OpenGL
export QSG_RHI_BACKEND=opengl
# Force Vulkan
export QSG_RHI_BACKEND=vulkan
# Force Direct3D 11 (Windows)
set QSG_RHI_BACKEND=d3d11
```
## Performance
Performance tiers match the OpenGL version:
- **Low**: 8 covers loaded, 128px textures (page flow) / 200px (comic flow)
- **Medium**: 10 covers, 196px / 256px textures
- **High**: 12 covers, 256px / 320px textures
- **Ultra High**: 14-16 covers, full resolution textures
Texture loading happens asynchronously via worker threads (`ImageLoader3D`, `ImageLoaderByteArray3D`).
## Debugging
### Enable Validation Layers
For debugging graphics issues, enable validation:
```cpp
flow->setDebugLayerEnabled(true); // Call before widget is shown
```
This activates:
- **Vulkan**: VK_LAYER_KHRONOS_validation
- **Direct3D**: D3D11 debug layer
- **Metal**: Metal API validation
### Check Active Graphics API
```cpp
QRhi *rhi = flow->rhi();
qDebug() << "Backend:" << rhi->backend();
qDebug() << "Driver:" << rhi->driverInfo();
```
### Common Issues
**Problem**: Shaders fail to load
**Solution**: Ensure `.qsb` files are compiled and included in resources
**Problem**: Black screen on Qt 6.7
**Solution**: Check if `YACREADER_USE_RHI` is defined in build
**Problem**: Crashes on resize/reparent
**Solution**: `releaseResources()` properly cleans up all QRhi objects
## Migration from OpenGL
The RHI version is a **drop-in replacement** requiring no application code changes beyond the build system.
### What's Different (Internal)
| OpenGL API | QRhi Equivalent |
|------------|-----------------|
| `glDrawArraysInstanced()` | `cb->drawIndexed(instanceCount)` |
| `glUniform*()` | Update `QRhiBuffer` with uniform data |
| `glBindTexture()` | `QRhiShaderResourceBindings` |
| `glBlendFunc()` | `QRhiGraphicsPipeline::setTargetBlends()` |
| `glEnable(GL_DEPTH_TEST)` | `pipeline->setDepthTest(true)` |
### What's the Same
- All public API methods and signatures
- Animation system and timing
- Preset configurations
- Event handling (mouse, keyboard, wheel)
- Worker thread texture loading
- Performance tiers
## Known Limitations
1. **Qt Version**: Requires Qt 6.7+ (released April 2024)
2. **QRhi Stability**: QRhi APIs may change in minor Qt releases
3. **Mixed Renderers**: Only one graphics API per window
4. **Shader Compilation**: Must recompile shaders when modifying source
## Future Improvements
Potential enhancements:
- [ ] GPU-side frustum culling
- [ ] Compute shader for texture generation
- [ ] HDR/wide color gamut support
- [ ] Ray-traced reflections (via RHI compute)
- [ ] Dynamic LOD based on distance
## References
- [QRhiWidget Class Documentation](https://doc.qt.io/qt-6/qrhiwidget.html)
- [QRhi Overview](https://doc.qt.io/qt-6/qrhi.html)
- [Qt RHI Examples](https://doc.qt.io/qt-6/qtwidgets-rhi-cuberhiwidget-example.html)
- [Qt Shader Tools (qsb)](https://doc.qt.io/qt-6/qtshadertools-index.html)
## License
Same as YACReader project license.
## Author
Generated as part of the YACReader OpenGL modernization initiative.

View File

@ -0,0 +1,49 @@
# YACReader Flow RHI Shaders
This directory contains the GLSL 4.50 shaders for the QRhiWidget-based flow implementation.
## Files
- `flow.vert` - Vertex shader (GLSL 450)
- `flow.frag` - Fragment shader (GLSL 450)
- `flow.vert.qsb` - Compiled vertex shader (multi-platform)
- `flow.frag.qsb` - Compiled fragment shader (multi-platform)
- `compile_shaders.bat` - Windows compilation script
- `compile_shaders.sh` - Unix/macOS compilation script
- `shaders.qrc` - Qt resource file
## Compiling Shaders
The shaders must be compiled to `.qsb` format using Qt's `qsb` tool before building YACReader.
### Prerequisites
Ensure `qsb` is in your PATH. It's typically located in:
- Windows: `C:\Qt\6.x.x\msvc2019_64\bin\qsb.exe`
- macOS: `/opt/Qt/6.x.x/macos/bin/qsb`
- Linux: `/opt/Qt/6.x.x/gcc_64/bin/qsb`
### Compilation
**Windows:**
```cmd
cd common/rhi/shaders
compile_shaders.bat
```
**Unix/macOS:**
```bash
cd common/rhi/shaders
chmod +x compile_shaders.sh
./compile_shaders.sh
```
The compiled `.qsb` files contain shader variants for:
- OpenGL ES 2.0, 3.0
- OpenGL 2.1, 3.0+
- HLSL (Direct3D 11/12)
- Metal Shading Language (macOS/iOS)
## Note
The `.qsb` files are included in the repository for convenience. Recompile only if you modify the shader source.

View File

@ -0,0 +1,19 @@
@echo off
REM Compile shaders to .qsb format for Qt RHI
REM Requires qsb tool from Qt installation
echo Compiling flow vertex shader...
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o flow.vert.qsb flow.vert
if %ERRORLEVEL% NEQ 0 (
echo Error compiling vertex shader
exit /b 1
)
echo Compiling flow fragment shader...
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o flow.frag.qsb flow.frag
if %ERRORLEVEL% NEQ 0 (
echo Error compiling fragment shader
exit /b 1
)
echo Shader compilation complete!

View File

@ -0,0 +1,19 @@
#!/bin/bash
# Compile shaders to .qsb format for Qt RHI
# Requires qsb tool from Qt installation
echo "Compiling flow vertex shader..."
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o flow.vert.qsb flow.vert
if [ $? -ne 0 ]; then
echo "Error compiling vertex shader"
exit 1
fi
echo "Compiling flow fragment shader..."
qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -o flow.frag.qsb flow.frag
if [ $? -ne 0 ]; then
echo "Error compiling fragment shader"
exit 1
fi
echo "Shader compilation complete!"

View File

@ -0,0 +1,44 @@
#version 450
// Inputs from vertex shader
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec4 vColor;
layout(location = 2) in float vIsReflection;
// Output
layout(location = 0) out vec4 fragColor;
// Uniform buffer
layout(std140, binding = 0) uniform UniformBuffer
{
mat4 viewProjectionMatrix;
vec3 backgroundColor;
float _pad0;
vec3 shadingColor;
float _pad1;
float reflectionUp;
float reflectionDown;
int isReflection;
float _pad2;
};
// Texture and sampler
layout(binding = 1) uniform sampler2D coverTexture;
void main()
{
vec4 texColor = texture(coverTexture, vTexCoord);
// Apply shading: multiply texture by vColor.r to darken
float shadingAmount = vColor.r;
// For reflections, apply gradient fade (darker at bottom, fading to black)
if (vIsReflection > 0.5) {
// vTexCoord.y goes from 1 (top of reflection, near cover) to 0 (bottom, far from cover)
// We want it brightest near the cover and fading away
float gradientFade = mix(0.0, 0.33, vTexCoord.y);
shadingAmount *= gradientFade;
}
fragColor = vec4(texColor.rgb * shadingAmount, texColor.a);
}

Binary file not shown.

View File

@ -0,0 +1,59 @@
#version 450
// Per-vertex attributes
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
// Per-instance attributes (mat4 split into 4 vec4s for better D3D11 compatibility)
layout(location = 2) in vec4 instanceModel_row0;
layout(location = 3) in vec4 instanceModel_row1;
layout(location = 4) in vec4 instanceModel_row2;
layout(location = 5) in vec4 instanceModel_row3;
layout(location = 6) in vec4 instanceShading1;
layout(location = 7) in float instanceOpacity;
layout(location = 8) in float instanceFlip;
// Outputs to fragment shader
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out vec4 vColor;
layout(location = 2) out float vIsReflection;
// Uniform buffer
layout(std140, binding = 0) uniform UniformBuffer
{
mat4 viewProjectionMatrix;
vec3 backgroundColor;
float _pad0;
vec3 shadingColor;
float _pad1;
float reflectionUp;
float reflectionDown;
int isReflection;
float _pad2;
};
void main()
{
// Reconstruct instance model matrix from 4 vec4 rows
mat4 instanceModel = mat4(instanceModel_row0, instanceModel_row1, instanceModel_row2, instanceModel_row3);
gl_Position = viewProjectionMatrix * instanceModel * vec4(position, 1.0);
vTexCoord = texCoord;
// Flip texture vertically per-instance when requested (reflection)
if (instanceFlip != 0.0) {
vTexCoord.y = 1.0 - texCoord.y;
}
float leftUpShading = instanceShading1.x;
float leftDownShading = instanceShading1.y;
float rightUpShading = instanceShading1.z;
float rightDownShading = instanceShading1.w;
float leftShading = mix(leftDownShading, leftUpShading, (position.y + 0.5));
float rightShading = mix(rightDownShading, rightUpShading, (position.y + 0.5));
float shading = mix(leftShading, rightShading, (position.x + 0.5));
vColor = vec4(shading * instanceOpacity);
vIsReflection = instanceFlip;
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/shaders">
<file>flow.vert.qsb</file>
<file>flow.frag.qsb</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,394 @@
// Qt RHI-based Coverflow for YACReader
// Compatible with Qt 6.7+ using QRhiWidget
#ifndef __YACREADER_FLOW_RHI_H
#define __YACREADER_FLOW_RHI_H
#include <QtGlobal>
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
#include <QRhiWidget>
#include <QtWidgets>
#include <QtGui>
#include <rhi/qrhi.h>
#include "pictureflow.h"
#include "scroll_management.h"
// Reuse enums and structs from OpenGL version
enum Performance {
low = 0,
medium,
high,
ultraHigh
};
// Cover Vector
struct YACReader3DVector {
float x;
float y;
float z;
float rot;
};
// the image/texture info struct
struct YACReader3DImageRHI {
QRhiTexture *texture;
float width;
float height;
int index;
YACReader3DVector current;
YACReader3DVector animEnd;
};
struct Preset {
/*** Animation Settings ***/
float animationStep;
float animationSpeedUp;
float animationStepMax;
float animationFadeOutDist;
float preRotation;
float viewRotateLightStrenght;
float viewRotateAdd;
float viewRotateSub;
float viewAngle;
/*** Position Configuration ***/
float cfX;
float cfY;
float cfZ;
float cfRX;
float cfRY;
float cfRZ;
float rotation;
float xDistance;
float centerDistance;
float zDistance;
float yDistance;
float zoom;
};
extern struct Preset defaultYACReaderFlowConfig;
extern struct Preset presetYACReaderFlowClassicConfig;
extern struct Preset presetYACReaderFlowStripeConfig;
extern struct Preset presetYACReaderFlowOverlappedStripeConfig;
extern struct Preset pressetYACReaderFlowUpConfig;
extern struct Preset pressetYACReaderFlowDownConfig;
class ImageLoader3D;
class ImageLoaderByteArray3D;
class YACReaderFlow3D : public QRhiWidget, public ScrollManagement
{
Q_OBJECT
protected:
int timerId;
/*** System variables ***/
YACReader3DImageRHI dummy;
int viewRotateActive;
float stepBackup;
/*functions*/
void calcPos(YACReader3DImageRHI &image, int pos);
void calcVector(YACReader3DVector &vector, int pos);
bool animate(YACReader3DVector &currentVector, YACReader3DVector &toVector);
void prepareInstanceData(const YACReader3DImageRHI &image, bool isReflection, QVector<float> &data);
int updateCount;
int fontSize;
// RHI resources
QRhiTexture *defaultTexture = nullptr;
QRhiTexture *markTexture = nullptr;
QRhiTexture *readingTexture = nullptr;
QRhiBuffer *vertexBuffer = nullptr;
QRhiBuffer *instanceBuffer = nullptr;
QRhiBuffer *uniformBuffer = nullptr;
int alignedUniformSize = 0; // Cached aligned uniform buffer size
int uniformBufferCapacity = 0; // Number of uniform slots allocated
QRhiSampler *sampler = nullptr;
QRhiGraphicsPipeline *pipeline = nullptr;
QRhiShaderResourceBindings *shaderBindings = nullptr;
// Cache of shader resource bindings per texture (to avoid recreating every frame)
QMap<QRhiTexture *, QRhiShaderResourceBindings *> shaderBindingsCache;
// Pending texture uploads (for async image loading)
struct PendingTextureUpload {
int index;
QImage image;
float x;
float y;
};
QVector<PendingTextureUpload> pendingTextureUploads;
// Uniform buffer data structure
struct UniformData {
QMatrix4x4 viewProjectionMatrix;
QVector3D backgroundColor;
float _pad0;
QVector3D shadingColor;
float _pad1;
float reflectionUp;
float reflectionDown;
int isReflection;
float _pad2;
};
void timerEvent(QTimerEvent *);
int numObjects;
int lazyPopulateObjects;
bool showMarks;
QVector<bool> loaded;
QVector<YACReaderComicReadStatus> marks;
QVector<YACReader3DImageRHI> images;
bool hasBeenInitialized;
Performance performance;
/*** Animation Settings ***/
Preset config;
int currentSelected;
YACReader3DVector centerPos;
/*** Style ***/
float shadingTop;
float shadingBottom;
float reflectionUp;
float reflectionBottom;
/*** Theme Colors ***/
QColor backgroundColor;
QColor textColor;
QColor shadingColor;
/*** System info ***/
float viewRotate;
static int updateInterval;
bool flowRightToLeft;
void startAnimationTimer();
void stopAnimationTimer();
// QRhiWidget overrides
void initialize(QRhiCommandBuffer *cb) override;
void render(QRhiCommandBuffer *cb) override;
void releaseResources() override;
void showEvent(QShowEvent *event) override;
// Helper methods
QRhiTexture *createTextureFromImage(QRhiCommandBuffer *cb, const QImage &image);
void updateUniformBuffer(QRhiCommandBuffer *cb, const UniformData &data);
void prepareMarkInstanceData(const YACReader3DImageRHI &image, QVector<float> &data);
void ensureUniformBufferCapacity(int requiredSlots);
void prepareDrawData(const YACReader3DImageRHI &image, bool isReflection, bool isMark,
const QMatrix4x4 &viewProjectionMatrix, float *outInstanceData,
UniformData &outUniformData);
void executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *texture,
const float *instanceData, int uniformSlot);
protected:
QRhi *m_rhi = nullptr;
std::unique_ptr<QRhiBuffer> m_vbuf;
std::unique_ptr<QRhiBuffer> m_ubuf;
std::unique_ptr<QRhiShaderResourceBindings> m_srb;
std::unique_ptr<QRhiGraphicsPipeline> m_pipeline;
QMatrix4x4 m_viewProjection;
float m_rotation = 0.0f;
public:
YACReaderFlow3D(QWidget *parent = nullptr, struct Preset p = pressetYACReaderFlowDownConfig);
virtual ~YACReaderFlow3D();
QSize minimumSizeHint() const override;
void showPrevious();
void showNext();
void setCurrentIndex(int pos);
void cleanupAnimation();
void draw();
void updatePositions();
void insert(char *name, QRhiTexture *texture, float x, float y, int item = -1);
virtual void remove(int item);
void add(int item);
void replace(char *name, QRhiTexture *texture, float x, float y, int item);
void populate(int n);
YACReader3DImageRHI getCurrentSelected();
public slots:
void setCF_RX(int value);
void setCF_RY(int value);
void setCF_RZ(int value);
void setZoom(int zoom);
void setRotation(int angle);
void setX_Distance(int distance);
void setCenter_Distance(int distance);
void setZ_Distance(int distance);
void setCF_Y(int value);
void setCF_Z(int value);
void setY_Distance(int value);
void setFadeOutDist(int value);
void setLightStrenght(int value);
void setMaxAngle(int value);
void setPreset(const Preset &p);
void setPerformance(Performance performance);
void useVSync(bool b); // Compatibility method (no-op for RHI)
void setFlowRightToLeft(bool b);
// Theme color setters
void setBackgroundColor(const QColor &color);
void setTextColor(const QColor &color);
void setShadingColor(const QColor &color);
virtual void updateImageData() = 0;
void reset();
void reload();
void setShowMarks(bool value);
void setMarks(QVector<YACReader::YACReaderComicReadStatus> marks);
void setMarkImage(QImage &image);
void markSlide(int index, YACReader::YACReaderComicReadStatus status);
void unmarkSlide(int index);
void setSlideSize(QSize size);
void clear();
void setCenterIndex(unsigned int index);
void showSlide(int index);
int centerIndex();
void updateMarks();
void render(); // Compatibility method (triggers update())
void resizeGL(int width, int height); // Compatibility method (no-op for RHI)
QVector3D getPlaneIntersection(int x, int y, YACReader3DImageRHI plane);
void mouseDoubleClickEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
friend class ImageLoader3D;
friend class ImageLoaderByteArray3D;
signals:
void centerIndexChanged(int);
void selected(unsigned int);
};
class YACReaderComicFlow3D : public YACReaderFlow3D
{
public:
YACReaderComicFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig);
void setImagePaths(QStringList paths);
void updateImageData() override;
void remove(int item) override;
void add(const QString &path, int index);
void resortCovers(QList<int> newOrder);
friend class ImageLoader3D;
private:
ImageLoader3D *worker;
protected:
QList<QString> paths;
};
class YACReaderPageFlow3D : public YACReaderFlow3D
{
public:
YACReaderPageFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig);
~YACReaderPageFlow3D();
void updateImageData() override;
void populate(int n);
QVector<bool> imagesReady;
QVector<QByteArray> rawImages;
QVector<bool> imagesSetted;
friend class ImageLoaderByteArray3D;
private:
ImageLoaderByteArray3D *worker;
};
class ImageLoader3D : public QThread
{
public:
ImageLoader3D(YACReaderFlow3D *flow);
~ImageLoader3D();
bool busy() const;
void generate(int index, const QString &fileName);
void reset()
{
idx = -1;
fileName = "";
}
int index() const { return idx; }
void lock();
void unlock();
QImage result();
YACReaderFlow3D *flow;
QImage loadImage(const QString &fileName);
protected:
void run() override;
private:
QMutex mutex;
QWaitCondition condition;
bool restart;
bool working;
int idx;
QString fileName;
QSize size;
QImage img;
};
class ImageLoaderByteArray3D : public QThread
{
public:
ImageLoaderByteArray3D(YACReaderFlow3D *flow);
~ImageLoaderByteArray3D();
bool busy() const;
void generate(int index, const QByteArray &raw);
void reset()
{
idx = -1;
rawData.clear();
}
int index() const { return idx; }
QImage result();
YACReaderFlow3D *flow;
QImage loadImage(const QByteArray &rawData);
protected:
void run() override;
private:
QMutex mutex;
QWaitCondition condition;
bool restart;
bool working;
int idx;
QByteArray rawData;
QSize size;
QImage img;
};
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
#endif // __YACREADER_FLOW_RHI_H

View File

@ -41,6 +41,9 @@ void QsDebugOutput::output( const QString& message )
{
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), message.utf16(), message.size(), NULL, NULL);
WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), L"\n", 1, NULL, NULL);
fprintf(stdout, "%s\n", qPrintable(message));
fflush(stdout);
}
#endif