1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#include "GeometryBuffer.h"
#include "RenderDataBuffer.hpp"
#include "Rendering/GlobalRendering.h"
#include "System/Config/ConfigHandler.h"
#include <algorithm>
#include <cstring> // memset
void GL::GeometryBuffer::Init(bool ctor) {
// if dead, this must be a non-ctor reload
assert(!dead || !ctor);
memset(&bufferTextureIDs[0], 0, sizeof(bufferTextureIDs));
memset(&bufferAttachments[0], 0, sizeof(bufferAttachments));
// NOTE:
// initial buffer size must be 0 s.t. prevSize != currSize when !init
// (Lua can toggle drawDeferred and might be the first to cause a call
// to Create)
prevBufferSize = GetWantedSize(false);
currBufferSize = GetWantedSize(true);
dead = false;
bound = false;
msaa = configHandler->GetBool("AllowMultiSampledFrameBuffers");
}
void GL::GeometryBuffer::Kill(bool dtor) {
if (dead) {
// if already dead, this must be final cleanup
assert(dtor);
return;
}
if (buffer.IsValid())
DetachTextures(false);
dead = true;
}
void GL::GeometryBuffer::Clear() const {
assert(bound);
glAttribStatePtr->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glAttribStatePtr->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void GL::GeometryBuffer::SetDepthRange(float nearDepth, float farDepth) const {
#if 0
if (globalRendering->supportClipSpaceControl) {
// TODO: need to inform shaders about this, modify PM instead
glAttribStatePtr->DepthRange(nearDepth, farDepth);
glAttribStatePtr->ClearDepth(farDepth);
glAttribStatePtr->DepthFunc((nearDepth <= farDepth)? GL_LEQUAL: GL_GREATER);
}
#else
glAttribStatePtr->ClearDepth(std::max(nearDepth, farDepth));
glAttribStatePtr->DepthFunc(GL_LEQUAL);
#endif
}
void GL::GeometryBuffer::DetachTextures(const bool init) {
// nothing to detach yet during init
if (init)
return;
buffer.Bind();
// detach only actually attached textures, ATI drivers might crash
for (unsigned int i = 0; i < (ATTACHMENT_COUNT - 1); ++i) {
buffer.Detach(GL_COLOR_ATTACHMENT0 + i);
}
buffer.Detach(GL_DEPTH_ATTACHMENT);
buffer.Unbind();
glDeleteTextures(ATTACHMENT_COUNT, &bufferTextureIDs[0]);
// return to incomplete state
memset(&bufferTextureIDs[0], 0, sizeof(bufferTextureIDs));
memset(&bufferAttachments[0], 0, sizeof(bufferAttachments));
}
void GL::GeometryBuffer::DrawDebug(const unsigned int texID, const float2 texMins, const float2 texMaxs) const {
GL::RenderDataBuffer2DT* buffer = GL::GetRenderBuffer2DT();
Shader::IProgramObject* shader = buffer->GetShader();
// FIXME: needs resolve or different shader if msaa
glActiveTexture(GL_TEXTURE0);
glBindTexture(GetTextureTarget(), texID);
shader->Enable();
shader->SetUniformMatrix4x4<float>("u_movi_mat", false, CMatrix44f::Identity());
shader->SetUniformMatrix4x4<float>("u_proj_mat", false, CMatrix44f::Identity());
buffer->SafeAppend({texMins.x, texMins.y, texMins.x, texMins.y}); // tl
buffer->SafeAppend({texMaxs.x, texMins.y, texMaxs.x, texMins.y}); // tr
buffer->SafeAppend({texMaxs.x, texMaxs.y, texMaxs.x, texMaxs.y}); // br
buffer->SafeAppend({texMaxs.x, texMaxs.y, texMaxs.x, texMaxs.y}); // br
buffer->SafeAppend({texMins.x, texMaxs.y, texMins.x, texMaxs.y}); // bl
buffer->SafeAppend({texMins.x, texMins.y, texMins.x, texMins.y}); // tl
buffer->Submit(GL_TRIANGLES);
shader->Disable();
glBindTexture(GetTextureTarget(), 0);
}
bool GL::GeometryBuffer::Create(const int2 size) {
const unsigned int texTarget = GetTextureTarget();
for (unsigned int n = 0; n < ATTACHMENT_COUNT; n++) {
glGenTextures(1, &bufferTextureIDs[n]);
glBindTexture(texTarget, bufferTextureIDs[n]);
glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (n == ATTACHMENT_ZVALTEX) {
if (texTarget == GL_TEXTURE_2D)
glTexImage2D(texTarget, 0, GL_DEPTH_COMPONENT32F, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
else
glTexImage2DMultisample(texTarget, globalRendering->msaaLevel, GL_DEPTH_COMPONENT32F, size.x, size.y, GL_TRUE);
bufferAttachments[n] = GL_DEPTH_ATTACHMENT;
} else {
if (texTarget == GL_TEXTURE_2D)
glTexImage2D(texTarget, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
else
glTexImage2DMultisample(texTarget, globalRendering->msaaLevel, GL_RGBA8, size.x, size.y, GL_TRUE);
bufferAttachments[n] = GL_COLOR_ATTACHMENT0 + n;
}
}
// sic; Mesa complains about an incomplete FBO if calling Bind before TexImage (?)
buffer.Bind();
buffer.AttachTextures(bufferTextureIDs, bufferAttachments, texTarget, ATTACHMENT_COUNT);
glBindTexture(texTarget, 0);
// define the attachments we are going to draw into
// note: the depth-texture attachment does not count
// here and will be GL_NONE implicitly!
glDrawBuffers(ATTACHMENT_COUNT - 1, &bufferAttachments[0]);
// FBO must have been valid from point of construction
// if we reached CreateGeometryBuffer, but CheckStatus
// can still invalidate it
assert(buffer.IsValid());
const bool ret = buffer.CheckStatus(name);
buffer.Unbind();
return ret;
}
bool GL::GeometryBuffer::Update(const bool init) {
currBufferSize = GetWantedSize(true);
// FBO must be valid from point of construction
if (!buffer.IsValid())
return false;
// buffer isn't bound by calling context, can not call
// GetStatus to check for GL_FRAMEBUFFER_COMPLETE here
//
if (HasAttachments()) {
// technically a buffer can not be complete yet during
// initialization, however the GL spec says that FBO's
// with only empty attachments are complete by default
// assert(!init);
// FBO was already initialized (during init or from Lua)
// so it will have attachments -> check if they need to
// be regenerated, eg. if a window resize event happened
if (prevBufferSize == currBufferSize)
return true;
DetachTextures(init);
}
return (Create(prevBufferSize = currBufferSize));
}
int2 GL::GeometryBuffer::GetWantedSize(bool allowed) const {
return {globalRendering->viewSizeX * allowed, globalRendering->viewSizeY * allowed};
}
|