Change from render to texture to render to renderbuffer
This commit is contained in:
parent
52d7676831
commit
9c32c0b98b
6 changed files with 83 additions and 31 deletions
|
@ -45,14 +45,10 @@ public:
|
|||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Presentation thread calls this after swap to release the frame and add it back to the queue
|
||||
*/
|
||||
virtual void ReleasePresentFrame(Frame* frame) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,23 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R
|
|||
|
||||
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() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
|
|
|
@ -12,6 +12,31 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
OGLTexture() = default;
|
||||
|
|
|
@ -89,6 +89,8 @@ OpenGLState::OpenGLState() {
|
|||
viewport.height = 0;
|
||||
|
||||
clip_distance = {};
|
||||
|
||||
renderbuffer = 0;
|
||||
}
|
||||
|
||||
void OpenGLState::Apply() const {
|
||||
|
@ -337,6 +339,10 @@ void OpenGLState::Apply() const {
|
|||
}
|
||||
}
|
||||
|
||||
if (renderbuffer != cur_state.renderbuffer) {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||
}
|
||||
|
||||
cur_state = *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,8 @@ public:
|
|||
|
||||
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
GLuint renderbuffer;
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
|
|
|
@ -38,7 +38,7 @@ struct Frame {
|
|||
u32 width{}; /// Width of the frame (to detect resize)
|
||||
u32 height{}; /// Height of the frame
|
||||
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 present{}; /// FBO created on the present thread
|
||||
GLsync render_fence{}; /// Fence created on the render thread
|
||||
|
@ -48,7 +48,10 @@ struct Frame {
|
|||
|
||||
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 {
|
||||
public:
|
||||
|
@ -58,6 +61,7 @@ public:
|
|||
std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{};
|
||||
std::deque<Frontend::Frame*> free_queue{};
|
||||
std::deque<Frontend::Frame*> present_queue{};
|
||||
Frontend::Frame* previous_frame = nullptr;
|
||||
|
||||
OGLTextureMailbox() {
|
||||
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 {
|
||||
frame->present.Release();
|
||||
|
@ -73,12 +80,12 @@ public:
|
|||
GLint previous_draw_fbo{};
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
frame->color.handle, 0);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
frame->color.handle);
|
||||
if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -89,14 +96,9 @@ public:
|
|||
// Recreate the color texture attachment
|
||||
frame->color.Release();
|
||||
frame->color.Create();
|
||||
state.texture_units[0].texture_2d = frame->color.handle;
|
||||
state.renderbuffer = frame->color.handle;
|
||||
state.Apply();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
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);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
|
||||
|
||||
// Recreate the FBO for the render target
|
||||
frame->render.Release();
|
||||
|
@ -104,8 +106,8 @@ public:
|
|||
state.draw.read_framebuffer = frame->render.handle;
|
||||
state.draw.draw_framebuffer = frame->render.handle;
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
frame->color.handle, 0);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
frame->color.handle);
|
||||
if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
|
||||
}
|
||||
|
@ -138,8 +140,15 @@ public:
|
|||
[&] { return !present_queue.empty(); });
|
||||
if (present_queue.empty()) {
|
||||
// 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
|
||||
Frontend::Frame* frame = present_queue.front();
|
||||
present_queue.pop_front();
|
||||
|
@ -149,14 +158,9 @@ public:
|
|||
free_cv.notify_one();
|
||||
}
|
||||
present_queue.clear();
|
||||
previous_frame = 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"(
|
||||
|
@ -838,12 +842,15 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
|||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
|
||||
if (!frame) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Try returned no frame to present");
|
||||
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
|
||||
return;
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Recreate the presentation FBO if the color attachment was changed
|
||||
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);
|
||||
}
|
||||
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 */
|
||||
frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
render_window.mailbox->ReleasePresentFrame(frame);
|
||||
}
|
||||
|
||||
void RendererOpenGL::PresentComplete() {
|
||||
|
|
Loading…
Reference in a new issue