data storage
This commit is contained in:
@@ -59,6 +59,7 @@ void OpenGLRenderPass::Render(
|
||||
}
|
||||
|
||||
mRenderer.TemporalHistory().PushSourceFramebuffer(mRenderer.DecodeFramebuffer(), inputFrameWidth, inputFrameHeight);
|
||||
mRenderer.FeedbackBuffers().FinalizeFrame();
|
||||
}
|
||||
|
||||
void OpenGLRenderPass::RenderDecodePass(unsigned inputFrameWidth, unsigned inputFrameHeight, unsigned captureTextureWidth, VideoIOPixelFormat inputPixelFormat)
|
||||
@@ -195,6 +196,7 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
||||
pass.passProgram = &passProgram;
|
||||
pass.layerState = &state;
|
||||
pass.capturePreLayerHistory = passIndex == 0 && state.temporalHistorySource == TemporalHistorySource::PreLayerInput;
|
||||
pass.captureFeedbackWrite = state.feedback.enabled && passProgram.passId == state.feedback.writePassId;
|
||||
passes.push_back(pass);
|
||||
|
||||
// A later pass can reference either the explicit output name or the
|
||||
@@ -236,6 +238,8 @@ void OpenGLRenderPass::RenderLayerPass(
|
||||
|
||||
if (pass.capturePreLayerHistory)
|
||||
mRenderer.TemporalHistory().PushPreLayerFramebuffer(pass.layerId, pass.sourceFramebuffer, inputFrameWidth, inputFrameHeight);
|
||||
if (pass.captureFeedbackWrite)
|
||||
mRenderer.FeedbackBuffers().CaptureFeedbackFramebuffer(pass.layerId, pass.destinationFramebuffer, inputFrameWidth, inputFrameHeight);
|
||||
}
|
||||
|
||||
void OpenGLRenderPass::RenderShaderProgram(
|
||||
@@ -261,14 +265,19 @@ void OpenGLRenderPass::RenderShaderProgram(
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
const std::vector<GLuint> sourceHistoryTextures = mRenderer.TemporalHistory().ResolveSourceHistoryTextures(sourceTexture, state.isTemporal ? historyCap : 0);
|
||||
const std::vector<GLuint> temporalHistoryTextures = mRenderer.TemporalHistory().ResolveTemporalHistoryTextures(state, sourceTexture, state.isTemporal ? historyCap : 0);
|
||||
const GLuint feedbackTexture = mRenderer.FeedbackBuffers().ResolveReadTexture(state);
|
||||
const ShaderTextureBindings::RuntimeTextureBindingPlan texturePlan =
|
||||
mTextureBindings.BuildLayerRuntimeBindingPlan(passProgram, sourceTexture, sourceHistoryTextures, temporalHistoryTextures);
|
||||
mTextureBindings.BuildLayerRuntimeBindingPlan(passProgram, sourceTexture, state, feedbackTexture, sourceHistoryTextures, temporalHistoryTextures);
|
||||
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
|
||||
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
||||
glUseProgram(passProgram.program);
|
||||
// The UBO is shared by every pass in a layer; texture routing is what
|
||||
// changes from pass to pass.
|
||||
updateGlobalParams(state, mRenderer.TemporalHistory().SourceAvailableCount(), mRenderer.TemporalHistory().AvailableCountForLayer(state.layerId));
|
||||
updateGlobalParams(
|
||||
state,
|
||||
mRenderer.TemporalHistory().SourceAvailableCount(),
|
||||
mRenderer.TemporalHistory().AvailableCountForLayer(state.layerId),
|
||||
mRenderer.FeedbackBuffers().FeedbackAvailable(state));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glUseProgram(0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
using LayerProgram = OpenGLRenderer::LayerProgram;
|
||||
using PassProgram = OpenGLRenderer::LayerProgram::PassProgram;
|
||||
using TextBindingUpdater = std::function<bool(const RuntimeRenderState&, LayerProgram::TextBinding&, std::string&)>;
|
||||
using GlobalParamsUpdater = std::function<bool(const RuntimeRenderState&, unsigned, unsigned)>;
|
||||
using GlobalParamsUpdater = std::function<bool(const RuntimeRenderState&, unsigned, unsigned, bool)>;
|
||||
|
||||
explicit OpenGLRenderPass(OpenGLRenderer& renderer);
|
||||
|
||||
|
||||
@@ -36,4 +36,5 @@ struct RenderPassDescriptor
|
||||
OpenGLRenderer::LayerProgram::PassProgram* passProgram = nullptr;
|
||||
const RuntimeRenderState* layerState = nullptr;
|
||||
bool capturePreLayerHistory = false;
|
||||
bool captureFeedbackWrite = false;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "ShaderFeedbackBuffers.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace
|
||||
{
|
||||
void ConfigureFeedbackTexture(unsigned frameWidth, unsigned frameHeight)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, frameWidth, frameHeight, 0, GL_RGBA, GL_FLOAT, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderFeedbackBuffers::EnsureResources(const std::vector<RuntimeRenderState>& layerStates, unsigned frameWidth, unsigned frameHeight, std::string& error)
|
||||
{
|
||||
if (!EnsureZeroTexture())
|
||||
{
|
||||
error = "Failed to initialize shader feedback fallback texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<std::string> requiredLayerIds;
|
||||
for (const RuntimeRenderState& state : layerStates)
|
||||
{
|
||||
if (!state.feedback.enabled)
|
||||
continue;
|
||||
|
||||
requiredLayerIds.insert(state.layerId);
|
||||
auto surfaceIt = mSurfacesByLayerId.find(state.layerId);
|
||||
if (surfaceIt == mSurfacesByLayerId.end() ||
|
||||
surfaceIt->second.width != frameWidth ||
|
||||
surfaceIt->second.height != frameHeight)
|
||||
{
|
||||
Surface replacement;
|
||||
if (!CreateSurface(replacement, frameWidth, frameHeight, error))
|
||||
return false;
|
||||
mSurfacesByLayerId[state.layerId] = std::move(replacement);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = mSurfacesByLayerId.begin(); it != mSurfacesByLayerId.end();)
|
||||
{
|
||||
if (requiredLayerIds.find(it->first) == requiredLayerIds.end())
|
||||
{
|
||||
DestroySurface(it->second);
|
||||
it = mSurfacesByLayerId.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::DestroyResources()
|
||||
{
|
||||
for (auto& entry : mSurfacesByLayerId)
|
||||
DestroySurface(entry.second);
|
||||
mSurfacesByLayerId.clear();
|
||||
|
||||
if (mZeroTexture != 0)
|
||||
{
|
||||
glDeleteTextures(1, &mZeroTexture);
|
||||
mZeroTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::ResetState()
|
||||
{
|
||||
for (auto& entry : mSurfacesByLayerId)
|
||||
ClearSurfaceState(entry.second);
|
||||
}
|
||||
|
||||
GLuint ShaderFeedbackBuffers::ResolveReadTexture(const RuntimeRenderState& state) const
|
||||
{
|
||||
if (!state.feedback.enabled)
|
||||
return mZeroTexture;
|
||||
|
||||
auto surfaceIt = mSurfacesByLayerId.find(state.layerId);
|
||||
if (surfaceIt == mSurfacesByLayerId.end() || !surfaceIt->second.hasData)
|
||||
return mZeroTexture;
|
||||
|
||||
return surfaceIt->second.slots[surfaceIt->second.readIndex].texture != 0
|
||||
? surfaceIt->second.slots[surfaceIt->second.readIndex].texture
|
||||
: mZeroTexture;
|
||||
}
|
||||
|
||||
bool ShaderFeedbackBuffers::FeedbackAvailable(const RuntimeRenderState& state) const
|
||||
{
|
||||
if (!state.feedback.enabled)
|
||||
return false;
|
||||
|
||||
auto surfaceIt = mSurfacesByLayerId.find(state.layerId);
|
||||
return surfaceIt != mSurfacesByLayerId.end() && surfaceIt->second.hasData;
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::CaptureFeedbackFramebuffer(const std::string& layerId, GLuint sourceFramebuffer, unsigned frameWidth, unsigned frameHeight)
|
||||
{
|
||||
auto surfaceIt = mSurfacesByLayerId.find(layerId);
|
||||
if (surfaceIt == mSurfacesByLayerId.end())
|
||||
return;
|
||||
|
||||
Surface& surface = surfaceIt->second;
|
||||
const unsigned writeIndex = 1u - surface.readIndex;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, surface.slots[writeIndex].framebuffer);
|
||||
glBlitFramebuffer(0, 0, frameWidth, frameHeight, 0, 0, frameWidth, frameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
surface.pendingWrite = true;
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::FinalizeFrame()
|
||||
{
|
||||
for (auto& entry : mSurfacesByLayerId)
|
||||
{
|
||||
Surface& surface = entry.second;
|
||||
if (!surface.pendingWrite)
|
||||
continue;
|
||||
|
||||
surface.readIndex = 1u - surface.readIndex;
|
||||
surface.hasData = true;
|
||||
surface.pendingWrite = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderFeedbackBuffers::EnsureZeroTexture()
|
||||
{
|
||||
if (mZeroTexture != 0)
|
||||
return true;
|
||||
|
||||
glGenTextures(1, &mZeroTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mZeroTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
const float zeroPixel[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 1, 1, 0, GL_RGBA, GL_FLOAT, zeroPixel);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return mZeroTexture != 0;
|
||||
}
|
||||
|
||||
bool ShaderFeedbackBuffers::CreateSurface(Surface& surface, unsigned frameWidth, unsigned frameHeight, std::string& error)
|
||||
{
|
||||
DestroySurface(surface);
|
||||
|
||||
surface.width = frameWidth;
|
||||
surface.height = frameHeight;
|
||||
for (Slot& slot : surface.slots)
|
||||
{
|
||||
glGenTextures(1, &slot.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, slot.texture);
|
||||
ConfigureFeedbackTexture(frameWidth, frameHeight);
|
||||
|
||||
glGenFramebuffers(1, &slot.framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, slot.framebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, slot.texture, 0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
error = "Failed to initialize a shader feedback framebuffer.";
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
DestroySurface(surface);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
ClearSurfaceState(surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::DestroySurface(Surface& surface)
|
||||
{
|
||||
for (Slot& slot : surface.slots)
|
||||
{
|
||||
if (slot.framebuffer != 0)
|
||||
glDeleteFramebuffers(1, &slot.framebuffer);
|
||||
if (slot.texture != 0)
|
||||
glDeleteTextures(1, &slot.texture);
|
||||
slot.framebuffer = 0;
|
||||
slot.texture = 0;
|
||||
}
|
||||
|
||||
surface.width = 0;
|
||||
surface.height = 0;
|
||||
surface.readIndex = 0;
|
||||
surface.hasData = false;
|
||||
surface.pendingWrite = false;
|
||||
}
|
||||
|
||||
void ShaderFeedbackBuffers::ClearSurfaceState(Surface& surface)
|
||||
{
|
||||
surface.readIndex = 0;
|
||||
surface.hasData = false;
|
||||
surface.pendingWrite = false;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLExtensions.h"
|
||||
#include "ShaderTypes.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ShaderFeedbackBuffers
|
||||
{
|
||||
public:
|
||||
struct Slot
|
||||
{
|
||||
GLuint texture = 0;
|
||||
GLuint framebuffer = 0;
|
||||
};
|
||||
|
||||
struct Surface
|
||||
{
|
||||
Slot slots[2];
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
unsigned readIndex = 0;
|
||||
bool hasData = false;
|
||||
bool pendingWrite = false;
|
||||
};
|
||||
|
||||
bool EnsureResources(const std::vector<RuntimeRenderState>& layerStates, unsigned frameWidth, unsigned frameHeight, std::string& error);
|
||||
void DestroyResources();
|
||||
void ResetState();
|
||||
GLuint ResolveReadTexture(const RuntimeRenderState& state) const;
|
||||
bool FeedbackAvailable(const RuntimeRenderState& state) const;
|
||||
void CaptureFeedbackFramebuffer(const std::string& layerId, GLuint sourceFramebuffer, unsigned frameWidth, unsigned frameHeight);
|
||||
void FinalizeFrame();
|
||||
|
||||
private:
|
||||
bool EnsureZeroTexture();
|
||||
bool CreateSurface(Surface& surface, unsigned frameWidth, unsigned frameHeight, std::string& error);
|
||||
void DestroySurface(Surface& surface);
|
||||
void ClearSurfaceState(Surface& surface);
|
||||
|
||||
private:
|
||||
std::map<std::string, Surface> mSurfacesByLayerId;
|
||||
GLuint mZeroTexture = 0;
|
||||
};
|
||||
@@ -19,7 +19,8 @@ bool TemporalHistoryBuffers::ValidateTextureUnitBudget(const std::vector<Runtime
|
||||
++textTextureCount;
|
||||
}
|
||||
const unsigned totalShaderTextures = static_cast<unsigned>(state.textureAssets.size()) + textTextureCount;
|
||||
const unsigned layerRequiredUnits = kSourceHistoryTextureUnitBase + (state.isTemporal ? historyCap + historyCap : 0u) + totalShaderTextures;
|
||||
const unsigned feedbackTextureCount = state.feedback.enabled ? 1u : 0u;
|
||||
const unsigned layerRequiredUnits = kSourceHistoryTextureUnitBase + (state.isTemporal ? historyCap + historyCap : 0u) + feedbackTextureCount + totalShaderTextures;
|
||||
if (layerRequiredUnits > requiredUnits)
|
||||
requiredUnits = layerRequiredUnits;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user