mirror of
https://github.com/YACReader/yacreader
synced 2026-04-12 15:49:53 -04:00
Ensure resources dynamically
Content and settings can vary at any moment and we need to be prepared for always have a consistent context
This commit is contained in:
@ -95,17 +95,18 @@ void YACReaderFlow3D::stopAnimationTimer()
|
|||||||
|
|
||||||
void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
||||||
{
|
{
|
||||||
if (m_rhi != rhi()) {
|
auto newRhi = rhi();
|
||||||
|
if (m_rhi != newRhi) {
|
||||||
scene.reset();
|
scene.reset();
|
||||||
m_rhi = rhi();
|
m_rhi = newRhi;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_rhi)
|
if (m_rhi == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Helper to get or create resource update batch
|
// Helper to get or create resource update batch
|
||||||
auto getResourceBatch = [this]() {
|
auto getResourceBatch = [this]() {
|
||||||
if (!scene.resourceUpdates)
|
if (scene.resourceUpdates == nullptr)
|
||||||
scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
|
scene.resourceUpdates = m_rhi->nextResourceUpdateBatch();
|
||||||
return scene.resourceUpdates;
|
return scene.resourceUpdates;
|
||||||
};
|
};
|
||||||
@ -191,8 +192,93 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
scene.instanceBuffer->create();
|
scene.instanceBuffer->create();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup graphics pipeline
|
// Determine how many items we'll have (either already populated or pending lazy population)
|
||||||
if (!scene.pipeline) {
|
const int itemCount = (lazyPopulateObjects != -1) ? lazyPopulateObjects : numObjects;
|
||||||
|
|
||||||
|
// Create uniform buffer sized for the actual content
|
||||||
|
// Each item needs up to 3 draw slots: cover + reflection + mark
|
||||||
|
// If no items yet, we'll create the buffer later in ensureUniformBufferCapacity()
|
||||||
|
if (!scene.uniformBuffer && itemCount > 0) {
|
||||||
|
const int requiredSlots = itemCount * 3;
|
||||||
|
const int totalSize = requiredSlots * scene.alignedUniformSize;
|
||||||
|
scene.uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalSize));
|
||||||
|
if (scene.uniformBuffer->create()) {
|
||||||
|
scene.uniformBufferCapacity = requiredSlots;
|
||||||
|
} else {
|
||||||
|
qWarning() << "YACReaderFlow3D: Failed to create uniform buffer for" << itemCount << "items";
|
||||||
|
scene.uniformBuffer.reset();
|
||||||
|
scene.uniformBufferCapacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create shader bindings for pipeline (requires uniform buffer)
|
||||||
|
if (!scene.shaderBindings && scene.uniformBuffer) {
|
||||||
|
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
||||||
|
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get(), sizeof(UniformData)),
|
||||||
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
||||||
|
scene.shaderBindings->create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipeline if we have all prerequisites
|
||||||
|
ensurePipeline();
|
||||||
|
|
||||||
|
// Submit any pending resource updates
|
||||||
|
if (scene.resourceUpdates) {
|
||||||
|
cb->resourceUpdate(scene.resourceUpdates);
|
||||||
|
scene.resourceUpdates = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call populate only once per data loaded.
|
||||||
|
if (!hasBeenInitialized && lazyPopulateObjects != -1) {
|
||||||
|
populate(lazyPopulateObjects);
|
||||||
|
lazyPopulateObjects = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasBeenInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void YACReaderFlow3D::ensureUniformBufferCapacity(int requiredSlots)
|
||||||
|
{
|
||||||
|
if (!m_rhi || scene.alignedUniformSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if we need to resize
|
||||||
|
if (scene.uniformBufferCapacity >= requiredSlots && scene.uniformBuffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Reset uniform buffer
|
||||||
|
scene.uniformBuffer.reset();
|
||||||
|
|
||||||
|
// Create new larger buffer
|
||||||
|
// Each draw needs its own uniform slot (cover + reflection + optional mark = 3 per object)
|
||||||
|
const int totalSize = requiredSlots * scene.alignedUniformSize;
|
||||||
|
scene.uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalSize));
|
||||||
|
if (scene.uniformBuffer->create()) {
|
||||||
|
scene.uniformBufferCapacity = requiredSlots;
|
||||||
|
|
||||||
|
// Invalidate shader bindings cache since the uniform buffer changed
|
||||||
|
qDeleteAll(scene.shaderBindingsCache);
|
||||||
|
scene.shaderBindingsCache.clear();
|
||||||
|
|
||||||
|
// Recreate default shader bindings for pipeline (using dynamic offset)
|
||||||
|
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
||||||
|
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get(), sizeof(UniformData)),
|
||||||
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
||||||
|
scene.shaderBindings->create();
|
||||||
|
|
||||||
|
// If pipeline doesn't exist yet (content added after initial empty initialization),
|
||||||
|
// we need to create it now. This is handled by ensurePipeline().
|
||||||
|
} else {
|
||||||
|
qWarning() << "YACReaderFlow3D: Failed to create uniform buffer of size" << totalSize;
|
||||||
|
scene.uniformBufferCapacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void YACReaderFlow3D::ensurePipeline()
|
||||||
|
{
|
||||||
|
if (scene.pipeline || !m_rhi || !scene.uniformBuffer || !scene.shaderBindings)
|
||||||
|
return;
|
||||||
|
|
||||||
// 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"));
|
||||||
@ -202,13 +288,6 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create default shader resource bindings for pipeline creation
|
|
||||||
// We'll create texture-specific ones on-demand in drawCover
|
|
||||||
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
|
||||||
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get()),
|
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
|
||||||
scene.shaderBindings->create();
|
|
||||||
|
|
||||||
// Create pipeline
|
// Create pipeline
|
||||||
scene.pipeline.reset(m_rhi->newGraphicsPipeline());
|
scene.pipeline.reset(m_rhi->newGraphicsPipeline());
|
||||||
|
|
||||||
@ -234,7 +313,7 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
if (requestedSamples > 1 && m_rhi) {
|
if (requestedSamples > 1 && m_rhi) {
|
||||||
QVector<int> supported = m_rhi->supportedSampleCounts();
|
QVector<int> supported = m_rhi->supportedSampleCounts();
|
||||||
int maxSupported = 1;
|
int maxSupported = 1;
|
||||||
for (int s : supported) {
|
for (int s : std::as_const(supported)) {
|
||||||
if (s > maxSupported)
|
if (s > maxSupported)
|
||||||
maxSupported = s;
|
maxSupported = s;
|
||||||
}
|
}
|
||||||
@ -279,55 +358,6 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit any pending resource updates
|
|
||||||
if (scene.resourceUpdates) {
|
|
||||||
cb->resourceUpdate(scene.resourceUpdates);
|
|
||||||
scene.resourceUpdates = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call populate only once per data loaded.
|
|
||||||
if (!hasBeenInitialized && lazyPopulateObjects != -1) {
|
|
||||||
populate(lazyPopulateObjects);
|
|
||||||
lazyPopulateObjects = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasBeenInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void YACReaderFlow3D::ensureUniformBufferCapacity(int requiredSlots)
|
|
||||||
{
|
|
||||||
if (!m_rhi || scene.alignedUniformSize == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if we need to resize
|
|
||||||
if (scene.uniformBufferCapacity >= requiredSlots && scene.uniformBuffer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Reset uniform buffer
|
|
||||||
scene.uniformBuffer.reset();
|
|
||||||
|
|
||||||
// Create new larger buffer
|
|
||||||
// Each draw needs its own uniform slot (cover + reflection + optional mark = 3 per object)
|
|
||||||
const int totalSize = requiredSlots * scene.alignedUniformSize;
|
|
||||||
scene.uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalSize));
|
|
||||||
if (scene.uniformBuffer->create()) {
|
|
||||||
scene.uniformBufferCapacity = requiredSlots;
|
|
||||||
|
|
||||||
// Invalidate shader bindings cache since the uniform buffer changed
|
|
||||||
qDeleteAll(scene.shaderBindingsCache);
|
|
||||||
scene.shaderBindingsCache.clear();
|
|
||||||
|
|
||||||
// Recreate default shader bindings for pipeline
|
|
||||||
scene.shaderBindings.reset(m_rhi->newShaderResourceBindings());
|
|
||||||
scene.shaderBindings->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get()),
|
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, scene.defaultTexture.get(), scene.sampler.get()) });
|
|
||||||
scene.shaderBindings->create();
|
|
||||||
} else {
|
|
||||||
qWarning() << "YACReaderFlow3D: Failed to create uniform buffer of size" << totalSize;
|
|
||||||
scene.uniformBufferCapacity = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
||||||
{
|
{
|
||||||
if (!m_rhi || numObjects == 0)
|
if (!m_rhi || numObjects == 0)
|
||||||
@ -437,8 +467,16 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure pipeline exists (may not exist if content was added after empty initialization)
|
||||||
|
ensurePipeline();
|
||||||
|
|
||||||
|
if (!scene.pipeline) {
|
||||||
|
qWarning() << "YACReaderFlow3D: No pipeline available for rendering";
|
||||||
|
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);
|
auto requiredInstanceSize = static_cast<quint32>(draws.size() * 22 * sizeof(float));
|
||||||
if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) {
|
if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) {
|
||||||
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize));
|
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize));
|
||||||
if (!scene.instanceBuffer->create()) {
|
if (!scene.instanceBuffer->create()) {
|
||||||
@ -453,7 +491,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// Process pending texture uploads
|
// Process pending texture uploads
|
||||||
if (!pendingTextureUploads.isEmpty()) {
|
if (!pendingTextureUploads.isEmpty()) {
|
||||||
for (const auto &upload : pendingTextureUploads) {
|
for (const auto &upload : std::as_const(pendingTextureUploads)) {
|
||||||
if (upload.index >= 0 && upload.index < images.size() && images[upload.index].texture) {
|
if (upload.index >= 0 && upload.index < images.size() && images[upload.index].texture) {
|
||||||
batch->uploadTexture(images[upload.index].texture, upload.image);
|
batch->uploadTexture(images[upload.index].texture, upload.image);
|
||||||
}
|
}
|
||||||
@ -476,7 +514,6 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
// === 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 (scene.pipeline) {
|
|
||||||
cb->setGraphicsPipeline(scene.pipeline.get());
|
cb->setGraphicsPipeline(scene.pipeline.get());
|
||||||
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
||||||
|
|
||||||
@ -485,7 +522,6 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
const DrawInfo &draw = draws[i];
|
const DrawInfo &draw = draws[i];
|
||||||
executeDrawWithOffset(cb, draw.texture, draw.instanceData, i);
|
executeDrawWithOffset(cb, draw.texture, draw.instanceData, i);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cb->endPass();
|
cb->endPass();
|
||||||
}
|
}
|
||||||
@ -579,7 +615,7 @@ void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *
|
|||||||
QRhiShaderResourceBindings *srb = scene.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, scene.uniformBuffer.get()),
|
srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, scene.uniformBuffer.get(), sizeof(UniformData)),
|
||||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, scene.sampler.get()) });
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, scene.sampler.get()) });
|
||||||
srb->create();
|
srb->create();
|
||||||
scene.shaderBindingsCache.insert(texture, srb);
|
scene.shaderBindingsCache.insert(texture, srb);
|
||||||
@ -887,6 +923,7 @@ void YACReaderFlow3D::populate(int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
loaded = QVector<bool>(n, false);
|
loaded = QVector<bool>(n, false);
|
||||||
|
marks = QVector<YACReaderComicReadStatus>(n, Unread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void YACReaderFlow3D::reset()
|
void YACReaderFlow3D::reset()
|
||||||
@ -895,6 +932,7 @@ void YACReaderFlow3D::reset()
|
|||||||
|
|
||||||
currentSelected = 0;
|
currentSelected = 0;
|
||||||
loaded.clear();
|
loaded.clear();
|
||||||
|
marks.clear();
|
||||||
|
|
||||||
// Clean up image textures and remove their entries from shader bindings cache
|
// Clean up image textures and remove their entries from shader bindings cache
|
||||||
for (int i = 0; i < numObjects; i++) {
|
for (int i = 0; i < numObjects; i++) {
|
||||||
|
|||||||
@ -228,6 +228,7 @@ protected:
|
|||||||
void updateUniformBuffer(QRhiCommandBuffer *cb, const UniformData &data);
|
void updateUniformBuffer(QRhiCommandBuffer *cb, const UniformData &data);
|
||||||
void prepareMarkInstanceData(const YACReader3DImageRHI &image, QVector<float> &data);
|
void prepareMarkInstanceData(const YACReader3DImageRHI &image, QVector<float> &data);
|
||||||
void ensureUniformBufferCapacity(int requiredSlots);
|
void ensureUniformBufferCapacity(int requiredSlots);
|
||||||
|
void ensurePipeline();
|
||||||
void prepareDrawData(const YACReader3DImageRHI &image, bool isReflection, bool isMark,
|
void prepareDrawData(const YACReader3DImageRHI &image, bool isReflection, bool isMark,
|
||||||
const QMatrix4x4 &viewProjectionMatrix, float *outInstanceData,
|
const QMatrix4x4 &viewProjectionMatrix, float *outInstanceData,
|
||||||
UniformData &outUniformData);
|
UniformData &outUniformData);
|
||||||
|
|||||||
Reference in New Issue
Block a user