1 /*
2 * Stellarium
3 * Copyright (C) 2016 Georg Zotti, Florian Schaukowitsch
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "SpoutSender.hpp"
21 #include "../../external/SpoutLibrary.h"
22
23 Q_LOGGING_CATEGORY(spout,"stel.spout")
24
SpoutSender(const QString & senderName)25 SpoutSender::SpoutSender(const QString &senderName)
26 : valid(false), width(800), height(600), localTexture(0), textureDirty(true)
27 {
28 initializeOpenGLFunctions();
29
30 // Acquire spout object through C API
31 spoutLib = GetSpout();
32 //we have to copy the name, because the array should be at least 256 bytes
33 qstrncpy(name, qPrintable(senderName), 256);
34
35 qCDebug(spout) << "Sender name is:" << name;
36 int numAdapters = spoutLib->GetNumAdapters();
37 qCDebug(spout) << "Found" << numAdapters << "GPUs";
38 for (int i=0; i<numAdapters; i++){
39 char gpuName[256]; // 256 chars min required by Spout specs!
40 spoutLib->GetAdapterName(i, gpuName, 256);
41 qCDebug(spout) << " GPU" << i << ": " << gpuName;
42 }
43 qCDebug(spout) << "Currently used: GPU" << spoutLib->GetAdapter();
44
45 //ignore the sizes here, we have to call resize later
46 valid = spoutLib->CreateSender(name,width,height);
47 if(!valid)
48 {
49 qCCritical(spout) << "Could not create spout sender";
50 qCCritical(spout) << "You may need a better GPU for this function, see Spout docs.";
51 qCCritical(spout) << "On a notebook with NVidia Optimus, force running Stellarium on the NVidia GPU.";
52 return;
53 }
54
55 //create the local GL texture
56 glGenTextures(1, &localTexture);
57 //setup some texture parameters
58 //probably not needed because we are not sampling, but who knows
59 glBindTexture(GL_TEXTURE_2D, localTexture);
60 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
61 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
62 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
63 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
64 glBindTexture(GL_TEXTURE_2D, 0);
65
66 qCDebug(spout) << "Sender has been created in" << (spoutLib->GetMemoryShareMode() ? "Memory Share Mode" : "OpenGL/DirectX interop mode");
67 qCDebug(spout) << "DX9 mode:" << spoutLib->GetDX9();
68 }
69
~SpoutSender()70 SpoutSender::~SpoutSender()
71 {
72 //probably ok to release even if invalid?
73 spoutLib->ReleaseSender();
74 // Release spout object, can't call any methods afterwards
75 spoutLib->Release();
76 spoutLib = Q_NULLPTR;
77
78 glDeleteTextures(1, &localTexture);
79 localTexture = 0;
80 qCDebug(spout)<<"Sender"<<name<<"released";
81 }
82
captureAndSendFrame(GLuint fbo)83 void SpoutSender::captureAndSendFrame(GLuint fbo)
84 {
85 if(!valid)
86 return;
87
88 if(fbo)
89 {
90 //if there is a FBO, try to use its texture directly to avoid a copy
91 //the Qt GL widget classes hide their FBO internally,
92 //so this seems like the only way to find out the texture ID
93 //I don't know the performance implications of these queries yet
94 GLint objType;
95 glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,&objType);
96 if(objType == GL_TEXTURE) //we can only do this directly if it is a texture, not an RB
97 {
98 GLint texId;
99 glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,&texId);
100 if(texId)
101 {
102 //use the texture directly
103 spoutLib->SendTexture(static_cast<GLuint>(texId), GL_TEXTURE_2D, width, height, true, fbo);
104 return;
105 }
106 }
107 }
108
109 //if no FBO is bound, or the above method failed, we have
110 //to copy from the framebuffer to a local texture first
111 glBindTexture(GL_TEXTURE_2D, localTexture);
112 if(textureDirty)
113 {
114 //recreate the texture by using glCopyTexImage2D
115 //would GL_BGRA be better here?
116 glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,0,0,static_cast<GLsizei>(width),static_cast<GLsizei>(height),0);
117
118 //manual calling of UpdateSender seems buggy, it creates a new sender name
119 //but we don't need to use it
120 //SendTexture will update the sender automaticallly if the size is wrong, without these bugs
121 //only possible drawback is the sent frame seems to be discarded, only next call will draw properly
122 //spoutLib->UpdateSender(name,width,height);
123 textureDirty = false;
124 }
125 else
126 {
127 //use glCopyTexSubImage2D, should give faster perf (in theory)
128 glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,static_cast<GLsizei>(width),static_cast<GLsizei>(height));
129 }
130 //unbind tex
131 glBindTexture(GL_TEXTURE_2D, 0);
132
133 //send to spout
134 spoutLib->SendTexture(localTexture, GL_TEXTURE_2D, width, height, true, fbo);
135 }
136
resize(uint width,uint height)137 void SpoutSender::resize(uint width, uint height)
138 {
139 //qCDebug(spout)<<"Resized output:"<<width<<height;
140 this->width = width;
141 this->height = height;
142
143 //mark the texture for reinit, we don't do it here because we may not have GL access
144 textureDirty = true;
145 }
146
147