203 lines
5.7 KiB
C++
203 lines
5.7 KiB
C++
#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;
|
|
}
|