Change from render to texture to render to renderbuffer

This commit is contained in:
James Rowe 2019-09-20 18:27:42 -06:00
parent 52d7676831
commit 9c32c0b98b
6 changed files with 83 additions and 31 deletions

View file

@ -45,14 +45,10 @@ public:
/** /**
* Presentation thread calls this to get the latest frame available to present. If there is no * Presentation thread calls this to get the latest frame available to present. If there is no
* frame available after timeout, returns nullptr * frame available after timeout, returns the previous frame. If there is no previous frame it
* returns nullptr
*/ */
virtual Frontend::Frame* TryGetPresentFrame(int timeout_ms) = 0; virtual Frontend::Frame* TryGetPresentFrame(int timeout_ms) = 0;
/**
* Presentation thread calls this after swap to release the frame and add it back to the queue
*/
virtual void ReleasePresentFrame(Frame* frame) = 0;
}; };
/** /**

View file

@ -15,6 +15,23 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R
namespace OpenGL { namespace OpenGL {
void OGLRenderbuffer::Create() {
if (handle != 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
glGenRenderbuffers(1, &handle);
}
void OGLRenderbuffer::Release() {
if (handle == 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteRenderbuffers(1, &handle);
handle = 0;
}
void OGLTexture::Create() { void OGLTexture::Create() {
if (handle != 0) if (handle != 0)
return; return;

View file

@ -12,6 +12,31 @@
namespace OpenGL { namespace OpenGL {
class OGLRenderbuffer : private NonCopyable {
public:
OGLRenderbuffer() = default;
OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
~OGLRenderbuffer() {
Release();
}
OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept {
Release();
handle = std::exchange(o.handle, 0);
return *this;
}
/// Creates a new internal OpenGL resource and stores the handle
void Create();
/// Deletes the internal OpenGL resource
void Release();
GLuint handle = 0;
};
class OGLTexture : private NonCopyable { class OGLTexture : private NonCopyable {
public: public:
OGLTexture() = default; OGLTexture() = default;

View file

@ -89,6 +89,8 @@ OpenGLState::OpenGLState() {
viewport.height = 0; viewport.height = 0;
clip_distance = {}; clip_distance = {};
renderbuffer = 0;
} }
void OpenGLState::Apply() const { void OpenGLState::Apply() const {
@ -337,6 +339,10 @@ void OpenGLState::Apply() const {
} }
} }
if (renderbuffer != cur_state.renderbuffer) {
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
}
cur_state = *this; cur_state = *this;
} }

View file

@ -144,6 +144,8 @@ public:
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
GLuint renderbuffer;
OpenGLState(); OpenGLState();
/// Get the currently active OpenGL state /// Get the currently active OpenGL state

View file

@ -38,7 +38,7 @@ struct Frame {
u32 width{}; /// Width of the frame (to detect resize) u32 width{}; /// Width of the frame (to detect resize)
u32 height{}; /// Height of the frame u32 height{}; /// Height of the frame
bool color_reloaded = false; /// Texture attachment was recreated (ie: resized) bool color_reloaded = false; /// Texture attachment was recreated (ie: resized)
OpenGL::OGLTexture color{}; /// Texture shared between the render/present FBO OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
GLsync render_fence{}; /// Fence created on the render thread GLsync render_fence{}; /// Fence created on the render thread
@ -48,7 +48,10 @@ struct Frame {
namespace OpenGL { namespace OpenGL {
constexpr std::size_t SWAP_CHAIN_SIZE = 5; // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
// number but 8 seems to be a good trade off for now
constexpr std::size_t SWAP_CHAIN_SIZE = 8;
class OGLTextureMailbox : public Frontend::TextureMailbox { class OGLTextureMailbox : public Frontend::TextureMailbox {
public: public:
@ -58,6 +61,7 @@ public:
std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{}; std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{};
std::deque<Frontend::Frame*> free_queue{}; std::deque<Frontend::Frame*> free_queue{};
std::deque<Frontend::Frame*> present_queue{}; std::deque<Frontend::Frame*> present_queue{};
Frontend::Frame* previous_frame = nullptr;
OGLTextureMailbox() { OGLTextureMailbox() {
for (auto& frame : swap_chain) { for (auto& frame : swap_chain) {
@ -65,7 +69,10 @@ public:
} }
} }
~OGLTextureMailbox() override = default; ~OGLTextureMailbox() override {
// lock the mutex and clear out the present and free_queues and notify any people who are
// blocked to prevent deadlock on shutdown
}
void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override { void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override {
frame->present.Release(); frame->present.Release();
@ -73,12 +80,12 @@ public:
GLint previous_draw_fbo{}; GLint previous_draw_fbo{};
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
frame->color.handle, 0); frame->color.handle);
if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
} }
glBindFramebuffer(GL_FRAMEBUFFER, previous_draw_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
frame->color_reloaded = false; frame->color_reloaded = false;
} }
@ -89,14 +96,9 @@ public:
// Recreate the color texture attachment // Recreate the color texture attachment
frame->color.Release(); frame->color.Release();
frame->color.Create(); frame->color.Create();
state.texture_units[0].texture_2d = frame->color.handle; state.renderbuffer = frame->color.handle;
state.Apply(); state.Apply();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// Recreate the FBO for the render target // Recreate the FBO for the render target
frame->render.Release(); frame->render.Release();
@ -104,8 +106,8 @@ public:
state.draw.read_framebuffer = frame->render.handle; state.draw.read_framebuffer = frame->render.handle;
state.draw.draw_framebuffer = frame->render.handle; state.draw.draw_framebuffer = frame->render.handle;
state.Apply(); state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
frame->color.handle, 0); frame->color.handle);
if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
} }
@ -138,8 +140,15 @@ public:
[&] { return !present_queue.empty(); }); [&] { return !present_queue.empty(); });
if (present_queue.empty()) { if (present_queue.empty()) {
// timed out waiting for a frame to draw so return nullptr // timed out waiting for a frame to draw so return nullptr
return nullptr; return previous_frame;
} }
// free the previous frame and add it back to the free queue
if (previous_frame) {
free_queue.push_back(previous_frame);
free_cv.notify_one();
}
// the newest entries are pushed to the front of the queue // the newest entries are pushed to the front of the queue
Frontend::Frame* frame = present_queue.front(); Frontend::Frame* frame = present_queue.front();
present_queue.pop_front(); present_queue.pop_front();
@ -149,14 +158,9 @@ public:
free_cv.notify_one(); free_cv.notify_one();
} }
present_queue.clear(); present_queue.clear();
previous_frame = frame;
return frame; return frame;
} }
void ReleasePresentFrame(Frontend::Frame* frame) override {
std::unique_lock<std::mutex> lock(swap_chain_lock);
free_queue.push_back(frame);
free_cv.notify_one();
}
}; };
static const char vertex_shader[] = R"( static const char vertex_shader[] = R"(
@ -838,12 +842,15 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
const auto& layout = render_window.GetFramebufferLayout(); const auto& layout = render_window.GetFramebufferLayout();
auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms); auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
if (!frame) { if (!frame) {
LOG_CRITICAL(Render_OpenGL, "Try returned no frame to present"); LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
return; return;
} }
glClear(GL_COLOR_BUFFER_BIT);
// Recreate the presentation FBO if the color attachment was changed // Recreate the presentation FBO if the color attachment was changed
if (frame->color_reloaded) { if (frame->color_reloaded) {
LOG_CRITICAL(Render_OpenGL, "Reloading present frame"); LOG_DEBUG(Render_OpenGL, "Reloading present frame");
render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height); render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
} }
glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
@ -860,7 +867,6 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
/* insert fence for the main thread to block on */ /* insert fence for the main thread to block on */
frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush(); glFlush();
render_window.mailbox->ReleasePresentFrame(frame);
} }
void RendererOpenGL::PresentComplete() { void RendererOpenGL::PresentComplete() {