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