mirror of
https://github.com/YACReader/yacreader
synced 2026-04-12 15:49:53 -04:00
Put rhi resources into a scene struct
This commit is contained in:
@ -2,17 +2,6 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
// Structure for per-instance data
|
|
||||||
struct InstanceData {
|
|
||||||
QMatrix4x4 modelMatrix;
|
|
||||||
float leftUpShading;
|
|
||||||
float leftDownShading;
|
|
||||||
float rightUpShading;
|
|
||||||
float rightDownShading;
|
|
||||||
float opacity;
|
|
||||||
float padding[3]; // Align to 16 bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
/*** Preset Configurations ***/
|
/*** Preset Configurations ***/
|
||||||
// Note: The preset configurations are already defined in yacreader_flow_gl.cpp
|
// Note: The preset configurations are already defined in yacreader_flow_gl.cpp
|
||||||
// We just reference them here as extern to avoid duplicate symbols
|
// We just reference them here as extern to avoid duplicate symbols
|
||||||
@ -87,58 +76,57 @@ void YACReaderFlow3D::stopAnimationTimer()
|
|||||||
void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
||||||
{
|
{
|
||||||
if (m_rhi != rhi()) {
|
if (m_rhi != rhi()) {
|
||||||
releaseResources();
|
scene.reset();
|
||||||
m_rhi = rhi();
|
m_rhi = rhi();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_rhi)
|
if (!m_rhi)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Helper to get or create resource update batch
|
||||||
|
auto getResourceBatch = [this]() {
|
||||||
|
if (!scene.resourceUpdates)
|
||||||
|
scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
|
||||||
|
return scene.resourceUpdates;
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize default texture from image
|
// Initialize default texture from image
|
||||||
if (!defaultTexture) {
|
if (!scene.defaultTexture) {
|
||||||
QImage defaultImage(":/images/defaultCover.png");
|
QImage defaultImage(":/images/defaultCover.png");
|
||||||
|
|
||||||
defaultTexture = m_rhi->newTexture(QRhiTexture::BGRA8, defaultImage.size(), 1, QRhiTexture::MipMapped);
|
scene.defaultTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, defaultImage.size(), 1, QRhiTexture::MipMapped));
|
||||||
defaultTexture->create();
|
scene.defaultTexture->create();
|
||||||
QRhiResourceUpdateBatch *batch = m_rhi->nextResourceUpdateBatch();
|
getResourceBatch()->uploadTexture(scene.defaultTexture.get(), defaultImage);
|
||||||
batch->uploadTexture(defaultTexture, defaultImage);
|
|
||||||
cb->resourceUpdate(batch);
|
|
||||||
qDebug() << "YACReaderFlow3D: Created defaultTexture" << defaultImage.size();
|
qDebug() << "YACReaderFlow3D: Created defaultTexture" << defaultImage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef YACREADER_LIBRARY
|
#ifdef YACREADER_LIBRARY
|
||||||
// Initialize mark textures
|
// Initialize mark textures
|
||||||
if (!markTexture) {
|
if (!scene.markTexture) {
|
||||||
QImage markImage(":/images/readRibbon.png");
|
QImage markImage(":/images/readRibbon.png");
|
||||||
if (!markImage.isNull()) {
|
if (!markImage.isNull()) {
|
||||||
markTexture = m_rhi->newTexture(QRhiTexture::BGRA8, markImage.size(), 1, QRhiTexture::MipMapped);
|
scene.markTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, markImage.size(), 1, QRhiTexture::MipMapped));
|
||||||
markTexture->create();
|
scene.markTexture->create();
|
||||||
|
getResourceBatch()->uploadTexture(scene.markTexture.get(), markImage);
|
||||||
QRhiResourceUpdateBatch *batch = m_rhi->nextResourceUpdateBatch();
|
|
||||||
batch->uploadTexture(markTexture, markImage);
|
|
||||||
cb->resourceUpdate(batch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!readingTexture) {
|
if (!scene.readingTexture) {
|
||||||
QImage readingImage(":/images/readingRibbon.png");
|
QImage readingImage(":/images/readingRibbon.png");
|
||||||
if (!readingImage.isNull()) {
|
if (!readingImage.isNull()) {
|
||||||
readingTexture = m_rhi->newTexture(QRhiTexture::BGRA8, readingImage.size(), 1, QRhiTexture::MipMapped);
|
scene.readingTexture.reset(m_rhi->newTexture(QRhiTexture::BGRA8, readingImage.size(), 1, QRhiTexture::MipMapped));
|
||||||
readingTexture->create();
|
scene.readingTexture->create();
|
||||||
|
getResourceBatch()->uploadTexture(scene.readingTexture.get(), readingImage);
|
||||||
QRhiResourceUpdateBatch *batch = m_rhi->nextResourceUpdateBatch();
|
|
||||||
batch->uploadTexture(readingTexture, readingImage);
|
|
||||||
cb->resourceUpdate(batch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Create vertex buffer (quad geometry)
|
// Create vertex buffer (quad geometry)
|
||||||
if (!vertexBuffer) {
|
if (!scene.vertexBuffer) {
|
||||||
// Use a triangle list (two triangles = 6 vertices) because some RHI backends
|
// Use a triangle list (two triangles = 6 vertices) because some RHI backends
|
||||||
// don't support TriangleFan. Each vertex: x,y,z,u,v (5 floats).
|
// don't support TriangleFan. Each vertex: x,y,z,u,v (5 floats).
|
||||||
vertexBuffer = m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, 6 * 5 * sizeof(float));
|
scene.vertexBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, 6 * 5 * sizeof(float)));
|
||||||
vertexBuffer->create();
|
scene.vertexBuffer->create();
|
||||||
|
|
||||||
// Two triangles forming a quad (triangle list):
|
// Two triangles forming a quad (triangle list):
|
||||||
// Tri 1: bottom-left, bottom-right, top-right
|
// Tri 1: bottom-left, bottom-right, top-right
|
||||||
@ -155,38 +143,36 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f // Top-left
|
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f // Top-left
|
||||||
};
|
};
|
||||||
|
|
||||||
QRhiResourceUpdateBatch *batch = m_rhi->nextResourceUpdateBatch();
|
getResourceBatch()->uploadStaticBuffer(scene.vertexBuffer.get(), vertices);
|
||||||
batch->uploadStaticBuffer(vertexBuffer, vertices);
|
|
||||||
cb->resourceUpdate(batch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize alignment for uniform buffers
|
// Initialize alignment for uniform buffers
|
||||||
if (alignedUniformSize == 0) {
|
if (scene.alignedUniformSize == 0) {
|
||||||
alignedUniformSize = m_rhi->ubufAligned(sizeof(UniformData));
|
scene.alignedUniformSize = m_rhi->ubufAligned(sizeof(UniformData));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create sampler
|
// Create sampler
|
||||||
if (!sampler) {
|
if (!scene.sampler) {
|
||||||
// Use no mipmap sampling to avoid LOD changes with camera Z
|
// Use no mipmap sampling to avoid LOD changes with camera Z
|
||||||
sampler = m_rhi->newSampler(
|
scene.sampler.reset(m_rhi->newSampler(
|
||||||
QRhiSampler::Linear,
|
QRhiSampler::Linear,
|
||||||
QRhiSampler::Linear,
|
QRhiSampler::Linear,
|
||||||
QRhiSampler::None,
|
QRhiSampler::None,
|
||||||
QRhiSampler::ClampToEdge,
|
QRhiSampler::ClampToEdge,
|
||||||
QRhiSampler::ClampToEdge);
|
QRhiSampler::ClampToEdge));
|
||||||
sampler->create();
|
scene.sampler->create();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create instance buffer for per-draw instance data
|
// Create instance buffer for per-draw instance data
|
||||||
if (!instanceBuffer) {
|
if (!scene.instanceBuffer) {
|
||||||
// Allocate buffer for per-instance data (model matrix + shading + opacity + flipFlag)
|
// Allocate buffer for per-instance data (model matrix + shading + opacity + flipFlag)
|
||||||
// mat4 (16 floats) + vec4 (4 floats) + float (1 float) + float (1 float) = 22 floats
|
// mat4 (16 floats) + vec4 (4 floats) + float (1 float) + float (1 float) = 22 floats
|
||||||
instanceBuffer = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, 22 * sizeof(float));
|
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, 22 * sizeof(float)));
|
||||||
instanceBuffer->create();
|
scene.instanceBuffer->create();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup graphics pipeline
|
// Setup graphics pipeline
|
||||||
if (!pipeline) {
|
if (!scene.pipeline) {
|
||||||
// Load shaders
|
// Load shaders
|
||||||
QShader vertShader = getShader(QLatin1String(":/shaders/flow.vert.qsb"));
|
QShader vertShader = getShader(QLatin1String(":/shaders/flow.vert.qsb"));
|
||||||
QShader fragShader = getShader(QLatin1String(":/shaders/flow.frag.qsb"));
|
QShader fragShader = getShader(QLatin1String(":/shaders/flow.frag.qsb"));
|
||||||
@ -198,33 +184,31 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// Create default shader resource bindings for pipeline creation
|
// Create default shader resource bindings for pipeline creation
|
||||||
// We'll create texture-specific ones on-demand in drawCover
|
// We'll create texture-specific ones on-demand in drawCover
|
||||||
shaderBindings = m_rhi->newShaderResourceBindings();
|
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
||||||
shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, uniformBuffer),
|
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get()),
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, defaultTexture, sampler) });
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
||||||
shaderBindings->create();
|
scene.shaderBindings->create();
|
||||||
|
|
||||||
// Create pipeline
|
// Create pipeline
|
||||||
pipeline = m_rhi->newGraphicsPipeline();
|
scene.pipeline.reset(m_rhi->newGraphicsPipeline());
|
||||||
|
|
||||||
// Disable alpha blending temporarily to test if blending causes darkening
|
// Setup alpha blending
|
||||||
QRhiGraphicsPipeline::TargetBlend blend;
|
QRhiGraphicsPipeline::TargetBlend blend;
|
||||||
blend.enable = true;
|
blend.enable = true;
|
||||||
blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
|
blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
|
||||||
blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
||||||
blend.srcAlpha = QRhiGraphicsPipeline::One;
|
blend.srcAlpha = QRhiGraphicsPipeline::One;
|
||||||
blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
blend.dstAlpha = QRhiGraphicsPipeline::OneMinusSrcAlpha;
|
||||||
pipeline->setTargetBlends({ blend });
|
scene.pipeline->setTargetBlends({ blend });
|
||||||
|
|
||||||
// Enable depth test (restore depth writes for normal rendering)
|
// Enable depth test
|
||||||
pipeline->setDepthTest(true);
|
scene.pipeline->setDepthTest(true);
|
||||||
pipeline->setDepthWrite(true);
|
scene.pipeline->setDepthWrite(true);
|
||||||
pipeline->setDepthOp(QRhiGraphicsPipeline::Less);
|
scene.pipeline->setDepthOp(QRhiGraphicsPipeline::Less);
|
||||||
|
|
||||||
// Diagnostic: disable culling to avoid missing-triangle artifacts
|
scene.pipeline->setCullMode(QRhiGraphicsPipeline::Back);
|
||||||
pipeline->setCullMode(QRhiGraphicsPipeline::Back);
|
|
||||||
|
|
||||||
// Determine the MSAA sample count to use. Query the RHI for supported counts
|
// Determine the MSAA sample count to use
|
||||||
// and clamp to at most 4 samples for safety.
|
|
||||||
int requestedSamples = sampleCount();
|
int requestedSamples = sampleCount();
|
||||||
int samplesToUse = 1;
|
int samplesToUse = 1;
|
||||||
if (requestedSamples > 1 && m_rhi) {
|
if (requestedSamples > 1 && m_rhi) {
|
||||||
@ -237,20 +221,17 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
samplesToUse = qMin(requestedSamples, qMin(4, maxSupported));
|
samplesToUse = qMin(requestedSamples, qMin(4, maxSupported));
|
||||||
}
|
}
|
||||||
if (samplesToUse > 1)
|
if (samplesToUse > 1)
|
||||||
pipeline->setSampleCount(samplesToUse);
|
scene.pipeline->setSampleCount(samplesToUse);
|
||||||
|
|
||||||
// Use triangle fan topology to match OpenGL draw mode (this makes the app to crash)
|
|
||||||
// pipeline->setTopology(QRhiGraphicsPipeline::TriangleFan);
|
|
||||||
|
|
||||||
// Set shaders
|
// Set shaders
|
||||||
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vertShader },
|
scene.pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vertShader },
|
||||||
{ QRhiShaderStage::Fragment, fragShader } });
|
{ QRhiShaderStage::Fragment, fragShader } });
|
||||||
|
|
||||||
// Setup vertex input layout
|
// Setup vertex input layout
|
||||||
QRhiVertexInputLayout inputLayout;
|
QRhiVertexInputLayout inputLayout;
|
||||||
inputLayout.setBindings({
|
inputLayout.setBindings({
|
||||||
{ 5 * sizeof(float) }, // Per-vertex data (position + texCoord)
|
{ 5 * sizeof(float) }, // Per-vertex data (position + texCoord)
|
||||||
{ 22 * sizeof(float), QRhiVertexInputBinding::PerInstance } // Per-instance data (+ flip flag)
|
{ 22 * sizeof(float), QRhiVertexInputBinding::PerInstance } // Per-instance data
|
||||||
});
|
});
|
||||||
inputLayout.setAttributes({
|
inputLayout.setAttributes({
|
||||||
// Per-vertex attributes
|
// Per-vertex attributes
|
||||||
@ -266,19 +247,24 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
{ 1, 7, QRhiVertexInputAttribute::Float, 20 * sizeof(float) }, // opacity
|
{ 1, 7, QRhiVertexInputAttribute::Float, 20 * sizeof(float) }, // opacity
|
||||||
{ 1, 8, QRhiVertexInputAttribute::Float, 21 * sizeof(float) } // flipFlag (1.0 = reflection)
|
{ 1, 8, QRhiVertexInputAttribute::Float, 21 * sizeof(float) } // flipFlag (1.0 = reflection)
|
||||||
});
|
});
|
||||||
pipeline->setVertexInputLayout(inputLayout);
|
scene.pipeline->setVertexInputLayout(inputLayout);
|
||||||
|
|
||||||
// Set shader resource bindings and render pass descriptor
|
// Set shader resource bindings and render pass descriptor
|
||||||
pipeline->setShaderResourceBindings(shaderBindings);
|
scene.pipeline->setShaderResourceBindings(scene.shaderBindings.get());
|
||||||
pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
|
scene.pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor());
|
||||||
|
|
||||||
if (!pipeline->create()) {
|
if (!scene.pipeline->create()) {
|
||||||
qWarning() << "YACReaderFlow3D: Failed to create graphics pipeline!";
|
qWarning() << "YACReaderFlow3D: Failed to create graphics pipeline!";
|
||||||
delete pipeline;
|
scene.pipeline.reset();
|
||||||
pipeline = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Submit any pending resource updates
|
||||||
|
if (scene.resourceUpdates) {
|
||||||
|
cb->resourceUpdate(scene.resourceUpdates);
|
||||||
|
scene.resourceUpdates = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Call populate only once per data loaded.
|
// Call populate only once per data loaded.
|
||||||
if (!hasBeenInitialized && lazyPopulateObjects != -1) {
|
if (!hasBeenInitialized && lazyPopulateObjects != -1) {
|
||||||
populate(lazyPopulateObjects);
|
populate(lazyPopulateObjects);
|
||||||
@ -290,43 +276,35 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
void YACReaderFlow3D::ensureUniformBufferCapacity(int requiredSlots)
|
void YACReaderFlow3D::ensureUniformBufferCapacity(int requiredSlots)
|
||||||
{
|
{
|
||||||
if (!m_rhi || alignedUniformSize == 0)
|
if (!m_rhi || scene.alignedUniformSize == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if we need to resize
|
// Check if we need to resize
|
||||||
if (uniformBufferCapacity >= requiredSlots && uniformBuffer)
|
if (scene.uniformBufferCapacity >= requiredSlots && scene.uniformBuffer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Delete old buffer if it exists
|
// Reset uniform buffer
|
||||||
if (uniformBuffer) {
|
scene.uniformBuffer.reset();
|
||||||
delete uniformBuffer;
|
|
||||||
uniformBuffer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new larger buffer
|
// Create new larger buffer
|
||||||
// Each draw needs its own uniform slot (cover + reflection + optional mark = 3 per object)
|
// Each draw needs its own uniform slot (cover + reflection + optional mark = 3 per object)
|
||||||
const int totalSize = requiredSlots * alignedUniformSize;
|
const int totalSize = requiredSlots * scene.alignedUniformSize;
|
||||||
uniformBuffer = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalSize);
|
scene.uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalSize));
|
||||||
if (uniformBuffer->create()) {
|
if (scene.uniformBuffer->create()) {
|
||||||
uniformBufferCapacity = requiredSlots;
|
scene.uniformBufferCapacity = requiredSlots;
|
||||||
|
|
||||||
// Invalidate shader bindings cache since the uniform buffer changed
|
// Invalidate shader bindings cache since the uniform buffer changed
|
||||||
for (auto *srb : shaderBindingsCache) {
|
qDeleteAll(scene.shaderBindingsCache);
|
||||||
delete srb;
|
scene.shaderBindingsCache.clear();
|
||||||
}
|
|
||||||
shaderBindingsCache.clear();
|
|
||||||
|
|
||||||
// Recreate default shader bindings for pipeline
|
// Recreate default shader bindings for pipeline
|
||||||
if (shaderBindings) {
|
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
||||||
delete shaderBindings;
|
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get()),
|
||||||
}
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
||||||
shaderBindings = m_rhi->newShaderResourceBindings();
|
scene.shaderBindings->create();
|
||||||
shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, uniformBuffer),
|
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, defaultTexture, sampler) });
|
|
||||||
shaderBindings->create();
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "YACReaderFlow3D: Failed to create uniform buffer of size" << totalSize;
|
qWarning() << "YACReaderFlow3D: Failed to create uniform buffer of size" << totalSize;
|
||||||
uniformBufferCapacity = 0;
|
scene.uniformBufferCapacity = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +396,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// Add mark draw immediately after its cover
|
// Add mark draw immediately after its cover
|
||||||
if (showMarks && loaded[idx] && marks[idx] != Unread) {
|
if (showMarks && loaded[idx] && marks[idx] != Unread) {
|
||||||
QRhiTexture *markTex = (marks[idx] == Read) ? markTexture : readingTexture;
|
QRhiTexture *markTex = (marks[idx] == Read) ? scene.markTexture.get() : scene.readingTexture.get();
|
||||||
if (markTex) {
|
if (markTex) {
|
||||||
DrawInfo markDraw;
|
DrawInfo markDraw;
|
||||||
markDraw.imageIndex = idx;
|
markDraw.imageIndex = idx;
|
||||||
@ -434,19 +412,16 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
// Ensure uniform buffer is large enough
|
// Ensure uniform buffer is large enough
|
||||||
ensureUniformBufferCapacity(draws.size());
|
ensureUniformBufferCapacity(draws.size());
|
||||||
|
|
||||||
if (!uniformBuffer) {
|
if (!scene.uniformBuffer) {
|
||||||
qWarning() << "YACReaderFlow3D: No uniform buffer available for rendering";
|
qWarning() << "YACReaderFlow3D: No uniform buffer available for rendering";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure instance buffer is large enough for all draws
|
// Ensure instance buffer is large enough for all draws
|
||||||
const int requiredInstanceSize = draws.size() * 22 * sizeof(float);
|
const int requiredInstanceSize = draws.size() * 22 * sizeof(float);
|
||||||
if (!instanceBuffer || instanceBuffer->size() < requiredInstanceSize) {
|
if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) {
|
||||||
if (instanceBuffer) {
|
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize));
|
||||||
delete instanceBuffer;
|
if (!scene.instanceBuffer->create()) {
|
||||||
}
|
|
||||||
instanceBuffer = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize);
|
|
||||||
if (!instanceBuffer->create()) {
|
|
||||||
qWarning() << "YACReaderFlow3D: Failed to create instance buffer of size" << requiredInstanceSize;
|
qWarning() << "YACReaderFlow3D: Failed to create instance buffer of size" << requiredInstanceSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -468,21 +443,21 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// Update uniform buffer with all draw data
|
// Update uniform buffer with all draw data
|
||||||
for (int i = 0; i < draws.size(); ++i) {
|
for (int i = 0; i < draws.size(); ++i) {
|
||||||
int offset = i * alignedUniformSize;
|
int offset = i * scene.alignedUniformSize;
|
||||||
batch->updateDynamicBuffer(uniformBuffer, offset, sizeof(UniformData), &draws[i].uniformData);
|
batch->updateDynamicBuffer(scene.uniformBuffer.get(), offset, sizeof(UniformData), &draws[i].uniformData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update instance buffer with all instance data
|
// Update instance buffer with all instance data
|
||||||
for (int i = 0; i < draws.size(); ++i) {
|
for (int i = 0; i < draws.size(); ++i) {
|
||||||
int offset = i * 22 * sizeof(float);
|
int offset = i * 22 * sizeof(float);
|
||||||
batch->updateDynamicBuffer(instanceBuffer, offset, 22 * sizeof(float), draws[i].instanceData);
|
batch->updateDynamicBuffer(scene.instanceBuffer.get(), offset, 22 * sizeof(float), draws[i].instanceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === PHASE 2: RENDER (DURING PASS) ===
|
// === PHASE 2: RENDER (DURING PASS) ===
|
||||||
cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, batch);
|
cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, batch);
|
||||||
|
|
||||||
if (pipeline) {
|
if (scene.pipeline) {
|
||||||
cb->setGraphicsPipeline(pipeline);
|
cb->setGraphicsPipeline(scene.pipeline.get());
|
||||||
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
||||||
|
|
||||||
// Execute all draws
|
// Execute all draws
|
||||||
@ -575,43 +550,31 @@ void YACReaderFlow3D::prepareDrawData(const YACReader3DImageRHI &image, bool isR
|
|||||||
void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *texture,
|
void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *texture,
|
||||||
const float *instanceData, int uniformSlot)
|
const float *instanceData, int uniformSlot)
|
||||||
{
|
{
|
||||||
if (!texture || !instanceBuffer || !vertexBuffer)
|
if (!texture || !scene.instanceBuffer || !scene.vertexBuffer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// NOTE: We cannot update the instance buffer here during the render pass!
|
Q_UNUSED(instanceData)
|
||||||
// Instead, we'll need to either:
|
|
||||||
// 1. Use the instance data from uniforms (move it to uniform buffer)
|
|
||||||
// 2. Or pre-upload all instance data before the pass
|
|
||||||
//
|
|
||||||
// For now, let's use approach #1: embed instance data in uniforms via a large instance buffer
|
|
||||||
// that we populate before the pass, similar to uniforms
|
|
||||||
//
|
|
||||||
// Actually, the simplest solution: update the instance buffer ONCE per draw using dynamic updates
|
|
||||||
// But we need to do this cleverly - we can't call resourceUpdate during pass.
|
|
||||||
//
|
|
||||||
// The solution: Create an instance buffer large enough for ALL draws, update it before pass,
|
|
||||||
// and use offsets during drawing.
|
|
||||||
|
|
||||||
// Get or create shader resource bindings for this texture with dynamic offset support
|
// Get or create shader resource bindings for this texture with dynamic offset support
|
||||||
QRhiShaderResourceBindings *srb = shaderBindingsCache.value(texture, nullptr);
|
QRhiShaderResourceBindings *srb = scene.shaderBindingsCache.value(texture, nullptr);
|
||||||
if (!srb) {
|
if (!srb) {
|
||||||
srb = m_rhi->newShaderResourceBindings();
|
srb = m_rhi->newShaderResourceBindings();
|
||||||
srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, uniformBuffer),
|
srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get()),
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler) });
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, scene.sampler.get()) });
|
||||||
srb->create();
|
srb->create();
|
||||||
shaderBindingsCache.insert(texture, srb);
|
scene.shaderBindingsCache.insert(texture, srb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set shader resources with dynamic offset for uniform buffer
|
// Set shader resources with dynamic offset for uniform buffer
|
||||||
QRhiCommandBuffer::DynamicOffset dynOfs[] = {
|
QRhiCommandBuffer::DynamicOffset dynOfs[] = {
|
||||||
{ 0, quint32(uniformSlot * alignedUniformSize) }
|
{ 0, quint32(uniformSlot * scene.alignedUniformSize) }
|
||||||
};
|
};
|
||||||
cb->setShaderResources(srb, 1, dynOfs);
|
cb->setShaderResources(srb, 1, dynOfs);
|
||||||
|
|
||||||
// Bind vertex buffers with offset into instance buffer
|
// Bind vertex buffers with offset into instance buffer
|
||||||
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
|
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
|
||||||
{ vertexBuffer, 0 },
|
{ scene.vertexBuffer.get(), 0 },
|
||||||
{ instanceBuffer, quint32(uniformSlot * 22 * sizeof(float)) } // Use slot index for instance data offset
|
{ scene.instanceBuffer.get(), quint32(uniformSlot * 22 * sizeof(float)) }
|
||||||
};
|
};
|
||||||
cb->setVertexInput(0, 2, vbufBindings);
|
cb->setVertexInput(0, 2, vbufBindings);
|
||||||
|
|
||||||
@ -625,39 +588,7 @@ void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *
|
|||||||
|
|
||||||
void YACReaderFlow3D::releaseResources()
|
void YACReaderFlow3D::releaseResources()
|
||||||
{
|
{
|
||||||
delete vertexBuffer;
|
scene.reset();
|
||||||
vertexBuffer = nullptr;
|
|
||||||
|
|
||||||
delete instanceBuffer;
|
|
||||||
instanceBuffer = nullptr;
|
|
||||||
|
|
||||||
delete uniformBuffer;
|
|
||||||
uniformBuffer = nullptr;
|
|
||||||
|
|
||||||
delete sampler;
|
|
||||||
sampler = nullptr;
|
|
||||||
|
|
||||||
delete pipeline;
|
|
||||||
pipeline = nullptr;
|
|
||||||
|
|
||||||
delete shaderBindings;
|
|
||||||
shaderBindings = nullptr;
|
|
||||||
|
|
||||||
// Clean up shader bindings cache
|
|
||||||
for (auto *srb : shaderBindingsCache) {
|
|
||||||
delete srb;
|
|
||||||
}
|
|
||||||
shaderBindingsCache.clear();
|
|
||||||
|
|
||||||
delete defaultTexture;
|
|
||||||
defaultTexture = nullptr;
|
|
||||||
|
|
||||||
delete markTexture;
|
|
||||||
markTexture = nullptr;
|
|
||||||
|
|
||||||
delete readingTexture;
|
|
||||||
readingTexture = nullptr;
|
|
||||||
|
|
||||||
m_rhi = nullptr;
|
m_rhi = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,7 +800,7 @@ void YACReaderFlow3D::remove(int item)
|
|||||||
}
|
}
|
||||||
images.removeAt(item);
|
images.removeAt(item);
|
||||||
|
|
||||||
if (texture != defaultTexture)
|
if (texture != scene.defaultTexture.get())
|
||||||
delete texture;
|
delete texture;
|
||||||
|
|
||||||
numObjects--;
|
numObjects--;
|
||||||
@ -890,7 +821,7 @@ void YACReaderFlow3D::add(int item)
|
|||||||
images[i].index++;
|
images[i].index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(s.toLocal8Bit().data(), defaultTexture, x, y, item);
|
insert(s.toLocal8Bit().data(), scene.defaultTexture.get(), x, y, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
YACReader3DImageRHI YACReaderFlow3D::getCurrentSelected()
|
YACReader3DImageRHI YACReaderFlow3D::getCurrentSelected()
|
||||||
@ -925,7 +856,7 @@ void YACReaderFlow3D::populate(int n)
|
|||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
QString s = "cover";
|
QString s = "cover";
|
||||||
insert(s.toLocal8Bit().data(), defaultTexture, x, y);
|
insert(s.toLocal8Bit().data(), scene.defaultTexture.get(), x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded = QVector<bool>(n, false);
|
loaded = QVector<bool>(n, false);
|
||||||
@ -939,7 +870,7 @@ void YACReaderFlow3D::reset()
|
|||||||
loaded.clear();
|
loaded.clear();
|
||||||
|
|
||||||
for (int i = 0; i < numObjects; i++) {
|
for (int i = 0; i < numObjects; i++) {
|
||||||
if (images[i].texture != defaultTexture)
|
if (images[i].texture != scene.defaultTexture.get())
|
||||||
delete images[i].texture;
|
delete images[i].texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1469,7 +1400,7 @@ YACReaderPageFlow3D::~YACReaderPageFlow3D()
|
|||||||
|
|
||||||
// Clean up textures
|
// Clean up textures
|
||||||
for (auto &image : images) {
|
for (auto &image : images) {
|
||||||
if (image.texture != defaultTexture) {
|
if (image.texture != scene.defaultTexture.get()) {
|
||||||
delete image.texture;
|
delete image.texture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,12 +89,10 @@ class YACReaderFlow3D : public QRhiWidget, public ScrollManagement
|
|||||||
protected:
|
protected:
|
||||||
int timerId;
|
int timerId;
|
||||||
|
|
||||||
/*** System variables ***/
|
|
||||||
YACReader3DImageRHI dummy;
|
YACReader3DImageRHI dummy;
|
||||||
int viewRotateActive;
|
int viewRotateActive;
|
||||||
float stepBackup;
|
float stepBackup;
|
||||||
|
|
||||||
/*functions*/
|
|
||||||
void calcPos(YACReader3DImageRHI &image, int pos);
|
void calcPos(YACReader3DImageRHI &image, int pos);
|
||||||
void calcVector(YACReader3DVector &vector, int pos);
|
void calcVector(YACReader3DVector &vector, int pos);
|
||||||
bool animate(YACReader3DVector ¤tVector, YACReader3DVector &toVector);
|
bool animate(YACReader3DVector ¤tVector, YACReader3DVector &toVector);
|
||||||
@ -103,34 +101,7 @@ protected:
|
|||||||
int updateCount;
|
int updateCount;
|
||||||
int fontSize;
|
int fontSize;
|
||||||
|
|
||||||
// RHI resources
|
// Uniform buffer data structure (must match shader layout)
|
||||||
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 {
|
struct UniformData {
|
||||||
QMatrix4x4 viewProjectionMatrix;
|
QMatrix4x4 viewProjectionMatrix;
|
||||||
QVector3D backgroundColor;
|
QVector3D backgroundColor;
|
||||||
@ -143,7 +114,65 @@ protected:
|
|||||||
float _pad2;
|
float _pad2;
|
||||||
};
|
};
|
||||||
|
|
||||||
void timerEvent(QTimerEvent *);
|
// Pending texture uploads (for async image loading)
|
||||||
|
struct PendingTextureUpload {
|
||||||
|
int index;
|
||||||
|
QImage image;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scene struct encapsulating all RHI resources
|
||||||
|
struct Scene {
|
||||||
|
// Textures
|
||||||
|
std::unique_ptr<QRhiTexture> defaultTexture;
|
||||||
|
std::unique_ptr<QRhiTexture> markTexture;
|
||||||
|
std::unique_ptr<QRhiTexture> readingTexture;
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
std::unique_ptr<QRhiBuffer> vertexBuffer;
|
||||||
|
std::unique_ptr<QRhiBuffer> instanceBuffer;
|
||||||
|
std::unique_ptr<QRhiBuffer> uniformBuffer;
|
||||||
|
|
||||||
|
// Pipeline and bindings
|
||||||
|
std::unique_ptr<QRhiSampler> sampler;
|
||||||
|
std::unique_ptr<QRhiGraphicsPipeline> pipeline;
|
||||||
|
std::unique_ptr<QRhiShaderResourceBindings> shaderBindings;
|
||||||
|
|
||||||
|
// Cache of shader resource bindings per texture (raw pointers, owned by this struct)
|
||||||
|
QMap<QRhiTexture *, QRhiShaderResourceBindings *> shaderBindingsCache;
|
||||||
|
|
||||||
|
// Uniform buffer sizing
|
||||||
|
int alignedUniformSize = 0;
|
||||||
|
int uniformBufferCapacity = 0;
|
||||||
|
|
||||||
|
// Pending resource updates (accumulated between frames)
|
||||||
|
QRhiResourceUpdateBatch *resourceUpdates = nullptr;
|
||||||
|
|
||||||
|
// Reset all resources (cleans up cache manually)
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
qDeleteAll(shaderBindingsCache);
|
||||||
|
shaderBindingsCache.clear();
|
||||||
|
defaultTexture.reset();
|
||||||
|
markTexture.reset();
|
||||||
|
readingTexture.reset();
|
||||||
|
vertexBuffer.reset();
|
||||||
|
instanceBuffer.reset();
|
||||||
|
uniformBuffer.reset();
|
||||||
|
sampler.reset();
|
||||||
|
pipeline.reset();
|
||||||
|
shaderBindings.reset();
|
||||||
|
alignedUniformSize = 0;
|
||||||
|
uniformBufferCapacity = 0;
|
||||||
|
resourceUpdates = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Scene scene;
|
||||||
|
QVector<PendingTextureUpload> pendingTextureUploads;
|
||||||
|
|
||||||
|
void timerEvent(QTimerEvent *) override;
|
||||||
|
|
||||||
int numObjects;
|
int numObjects;
|
||||||
int lazyPopulateObjects;
|
int lazyPopulateObjects;
|
||||||
@ -205,12 +234,6 @@ protected:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
QRhi *m_rhi = nullptr;
|
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:
|
public:
|
||||||
YACReaderFlow3D(QWidget *parent = nullptr, struct Preset p = pressetYACReaderFlowDownConfig);
|
YACReaderFlow3D(QWidget *parent = nullptr, struct Preset p = pressetYACReaderFlowDownConfig);
|
||||||
|
|||||||
Reference in New Issue
Block a user