Cogs.Core
SwapChainGL20.cpp
1#include "SwapChainGL20.h"
2
3#include "CommonGL20.h"
4#include "FormatsGL20.h"
5#include "GraphicsDeviceGL20.h"
6
7#include "Foundation/Platform/WindowData.h"
8
9namespace {
10
11 Cogs::Logging::Log logger = Cogs::Logging::getLogger("SwapChainGL20");
12}
13
14Cogs::SwapChainGL20::SwapChainGL20(GraphicsDeviceGL20* device, bool isPrimary)
15 : graphicsDevice(device),
16 isPrimary(isPrimary) {
17}
18
19Cogs::SwapChainGL20::~SwapChainGL20() {
20}
21
25bool Cogs::SwapChainGL20::initialize(WindowData* winData, bool debugLogging)
26{
27 windowData = winData;
28
29 GraphicsDeviceSettings settings = graphicsDevice->getSettings();
30 if (!isPrimary) {
31 // Secondary swapchains doen't need MSAA back buffer since we use a texture for drawing.
32 // Present blit does resolve.
33 settings.numSamples = 1;
34 }
35
36#if defined(__APPLE__)
37 (void)debugLogging;
38
39 if (isPrimary) {
40 glContext = graphicsDevice->getShareContext();
41 }
42 else if (windowData && windowData->defaultContext) {
43 glContext = GLContext(windowData->defaultContext);
44 }
45 else {
46 LOG_ERROR(logger, "No share or window context .");
47 return false;
48 }
49#else
50 if (!glContext.create(nullptr, winData, &graphicsDevice->getShareContext(), &settings, GLContext::Platform::GL, debugLogging)) {
51 LOG_ERROR(logger, "Can't retrieve window's device context.");
52 return false;
53 }
54#endif
55 glContext.makeCurrent();
56 if (!isPrimary) {
57 // If we are a secondary framebuffer, we create an FBO in our context that we will bind to the
58 // texture in the primary framebuffer that holds our image. Framebuffer objects aren't shared.
59 glGenFramebuffers(1, &framebuffer);
60
61 // And back to the primary context
62 graphicsDevice->getDefaultSwapChain()->glContext.makeCurrent();
63 }
64
65 return true;
66}
67
68void Cogs::SwapChainGL20::destroy()
69{
70 glContext.destroy();
71}
72
73
78 if (isPrimary) {
79 glContext.makeCurrent();
80 glBindFramebuffer(GL_FRAMEBUFFER, 0);
81 glDrawBuffer(GL_BACK);
82 }
83 else {
84 if (needsResize) {
85 recreateOffscreenBuffers();
86 needsResize = false;
87 }
88 ReleaseWait(); // This can be optimized by moving it to where the render target is first used.
89 }
90}
91
95void Cogs::SwapChainGL20::endFrame(uint32_t syncInterval, uint32_t presentFlags) {
96
97 if (!isPrimary) {
98 // Switch to secondary context and blit contents from primary context
99 GLsync acquire = 0;
100 if (framebuffer){
101 acquire = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
102 }
103 glContext.makeCurrent();
104 if (framebuffer) {
105 glWaitSync(acquire, 0, GL_TIMEOUT_IGNORED);
106 glDeleteSync(acquire);
107 glDisable(GL_SCISSOR_TEST);
108 glViewport(0, 0, width, height);
109 glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
110 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
111 glDrawBuffer(GL_BACK);
112 glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
113 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
114 ReleaseFence();
115 }
116 }
117
118 // Swap this context regardless of it being primary or secondary
119 glContext.swapBuffers(syncInterval, presentFlags);
120
121 if (!isPrimary) {
122 // If secondary, swap back to primary context
123 graphicsDevice->getDefaultSwapChain()->glContext.makeCurrent();
124 }
125}
126
127void Cogs::SwapChainGL20::ReleaseFence()
128{
129 if(release) glDeleteSync(release);
130 release = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
131}
132
133void Cogs::SwapChainGL20::ReleaseWait()
134{
135 if(release){
136 glWaitSync(release, 0, GL_TIMEOUT_IGNORED);
137 glDeleteSync(release);
138 release = 0;
139 }
140}
141
146void Cogs::SwapChainGL20::setSize(int newWidth, int newHeight, bool forceIt) {
147 if ((width != newWidth) || (height != newHeight) || forceIt) {
148 width = newWidth;
149 height = newHeight;
150 needsResize = true;
151 }
152}
153
155}
156
158 return graphicsDevice->getSettings().numSamples;
159}
160
168 assert(!isPrimary);
169
170 // Create texture and rendertarget in primary context
171 const GraphicsDeviceSettings& settings = graphicsDevice->getSettings();
172 RenderTargetsGL20* renderTargets = graphicsDevice->getRenderTargets();
173 TexturesGL20* textures = graphicsDevice->getTextures();
175
176 desc.width = width;
177 desc.height = height;
178 desc.format = settings.colorFormat;
179 desc.samples = settings.numSamples;
180 desc.target = Cogs::ResourceDimensions::Texture2DMS;
182
183 if (Cogs::HandleIsValid(depthStencil)) {
184 renderTargets->releaseDepthStencilTarget(depthStencil);
185 depthStencil = DepthStencilHandle();
186 }
187 if (Cogs::HandleIsValid(renderTarget)) {
188 renderTargets->releaseRenderTarget(renderTarget);
189 renderTarget = RenderTargetHandle();
190 }
191 if (Cogs::HandleIsValid(textureHandle)) {
192 textures->releaseTexture(textureHandle);
193 textureHandle = TextureHandle();
194 }
195
196 textureHandle = textures->loadTexture(desc, nullptr);
197 if (!Cogs::HandleIsValid(this->textureHandle)) {
198 LOG_ERROR(logger, "Unable to create offscreen texture.");
199 return false;
200 }
201
202 RenderTargetViewDescription rt_desc = {};
203 rt_desc.texture = textureHandle;
204 rt_desc.numLayers = 1;
205 renderTarget = renderTargets->createRenderTarget(&rt_desc, 1);
206 if (!Cogs::HandleIsValid(renderTarget)) {
207 LOG_ERROR(logger, "Error creating render target.");
208 return false;
209 }
210
211 depthStencil = renderTargets->createDepthStencilTarget(renderTarget);
212 if (!Cogs::HandleIsValid(depthStencil)) {
213 LOG_ERROR(logger, "Error creating depth stencil.");
214 return false;
215 }
216
217 // Switch to secondary context to set up FBO that we will use for rendering
218 GLsync acquire = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
219 glContext.makeCurrent();
220 glWaitSync(acquire, 0, GL_TIMEOUT_IGNORED);
221 glDeleteSync(acquire);
222
223 const TextureGL20& texture = graphicsDevice->getTextures()->textures[textureHandle];
224 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
225 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.type, texture.textureId, 0);
226 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
227 switch (status) {
228 case GL_FRAMEBUFFER_COMPLETE:
229 break;
230 case GL_FRAMEBUFFER_UNDEFINED: [[fallthrough]];
231 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: [[fallthrough]];
232 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: [[fallthrough]];
233 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: [[fallthrough]];
234 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: [[fallthrough]];
235 case GL_FRAMEBUFFER_UNSUPPORTED: [[fallthrough]];
236 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: [[fallthrough]];
237 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: [[fallthrough]];
238 default:
239 LOG_ERROR(logger, "Failed to create swapchain FBO, glCheckFramebufferStatus()=0x%x", status);
240 glDeleteFramebuffers(1, &framebuffer);
241 framebuffer = 0;
242 break;
243 }
244
245 // We are secondary, so swap back to primary context
246 ReleaseFence();
247 graphicsDevice->getDefaultSwapChain()->glContext.makeCurrent();
248 return true;
249}
Log implementation class.
Definition: LogManager.h:139
bool recreateOffscreenBuffers()
Recreates the textures and render targets used for offscreen rendering.
virtual void endFrame(uint32_t syncInterval=0, uint32_t presentFlags=PresentFlags::None) override
Finalises rendering and swaps the front and back buffers of this swap chain instance.
virtual void setSize(int newWidth, int newHeight, bool forceIt=false) override
Changes the size of this swap chain's viewport.
virtual void setFullscreen(bool enabled) override
Switch this swap chain to fullscreen or windowed depending on the parameter.
virtual void beginFrame() override
Prepares this swap chain for rendering.
virtual int getSamples() const override
Returns the number of samples this swap chain has.
bool initialize(WindowData *windowData, bool debugLogging)
Initialises this swap chain and attaches it to the window provided.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Settings for graphics device initialization.
int numSamples
Number of samples to use for back buffer MSAA.
TextureFormat colorFormat
Back buffer format.
Describes a single render target view and which resources to use from the underlying texture.
uint16_t numLayers
Number of available layers to render to.
TextureHandle texture
Texture handle.
void releaseRenderTarget(RenderTargetHandle renderTargetHandle) override
Release the render target with the given renderTargetHandle.
void releaseDepthStencilTarget(DepthStencilHandle depthStencilHandle) override
Release the depth target with the given depthStencilHandle.
RenderTargetHandle createRenderTarget(const RenderTargetViewDescription *renderTargetViews, const size_t numViews) override
Create a render target using the given view descriptions.
DepthStencilHandle createDepthStencilTarget(const RenderTargetHandle handle) override
Creates a depth/stencil target to back the render target with the given handle.
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120
TextureHandle loadTexture(const TextureDescription &desc, const TextureData *data) override
Load a texture from the given description.
void releaseTexture(TextureHandle textureHandle) override
Release the texture with the given textureHandle.