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