Change from render to texture to render to renderbuffer
This commit is contained in:
parent
52d7676831
commit
9c32c0b98b
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Reference in New Issue