1 //
2 // Copyright 2020 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/garch/glApi.h"
25
26 #include "pxr/pxr.h"
27 #include "pxr/imaging/hgi/blitCmdsOps.h"
28 #include "pxr/imaging/hgiVulkan/hgi.h"
29 #include "pxr/imaging/hgiInterop/vulkan.h"
30 #include "pxr/base/vt/value.h"
31
32 PXR_NAMESPACE_OPEN_SCOPE
33
34 static const char* _vertexFullscreen =
35 "#version 120\n"
36 "attribute vec4 position;\n"
37 "attribute vec2 uvIn;\n"
38 "varying vec2 uv;\n"
39 "void main(void)\n"
40 "{\n"
41 " gl_Position = position;\n"
42 " uv = uvIn;\n"
43 "}\n";
44
45 static const char* _fragmentNoDepthFullscreen =
46 "#version 120\n"
47 "varying vec2 uv;\n"
48 "uniform sampler2D colorIn;\n"
49 "void main(void)\n"
50 "{\n"
51 " gl_FragColor = texture2D(colorIn, uv);\n"
52 "}\n";
53
54 static const char* _fragmentDepthFullscreen =
55 "#version 120\n"
56 "varying vec2 uv;\n"
57 "uniform sampler2D colorIn;\n"
58 "uniform sampler2D depthIn;\n"
59 "void main(void)\n"
60 "{\n"
61 " float depth = texture2D(depthIn, uv).r;\n"
62 " gl_FragColor = texture2D(colorIn, uv);\n"
63 " gl_FragDepth = depth;\n"
64 "}\n";
65
66 static uint32_t
_CompileShader(const char * src,GLenum stage)67 _CompileShader(const char* src, GLenum stage)
68 {
69 const uint32_t shaderId = glCreateShader(stage);
70 glShaderSource(shaderId, 1, &src, nullptr);
71 glCompileShader(shaderId);
72 GLint status;
73 glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
74 TF_VERIFY(status == GL_TRUE);
75 return shaderId;
76 }
77
78 static uint32_t
_LinkProgram(uint32_t vs,uint32_t fs)79 _LinkProgram(uint32_t vs, uint32_t fs)
80 {
81 const uint32_t programId = glCreateProgram();
82 glAttachShader(programId, vs);
83 glAttachShader(programId, fs);
84 glLinkProgram(programId);
85 GLint status;
86 glGetProgramiv(programId, GL_LINK_STATUS, &status);
87 TF_VERIFY(status == GL_TRUE);
88 return programId;
89 }
90
91 static uint32_t
_CreateVertexBuffer()92 _CreateVertexBuffer()
93 {
94 static const float vertices[] = {
95 /* position uv */
96 -1, 3, -1, 1, 0, 2,
97 -1, -1, -1, 1, 0, 0,
98 3, -1, -1, 1, 2, 0 };
99 uint32_t vertexBuffer = 0;
100 glGenBuffers(1, &vertexBuffer);
101 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
102 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
103 glBindBuffer(GL_ARRAY_BUFFER, 0);
104 return vertexBuffer;
105 }
106
107 static void
_ConvertVulkanTextureToOpenGL(HgiVulkan * hgiVulkan,HgiTextureHandle const & src,uint32_t * glDest)108 _ConvertVulkanTextureToOpenGL(
109 HgiVulkan* hgiVulkan,
110 HgiTextureHandle const &src,
111 uint32_t* glDest)
112 {
113 // XXX we want to use EXT_external_objects and GL_EXT_semaphore to share
114 // memory between openGL and Vulkan.
115 // See examples: Nvidia gl_vk_simple_interop and Khronos: open_gl_interop
116 // But for now we do a CPU readback of the GPU texels and upload to GPU.
117
118 HgiTextureDesc const& texDesc = src->GetDescriptor();
119 const size_t byteSize = src->GetByteSizeOfResource();
120
121 std::vector<uint8_t> texels(byteSize, 0);
122 HgiTextureGpuToCpuOp readBackOp;
123 readBackOp.cpuDestinationBuffer = texels.data();
124 readBackOp.destinationBufferByteSize = byteSize;
125 readBackOp.destinationByteOffset = 0;
126 readBackOp.gpuSourceTexture = src;
127 readBackOp.mipLevel = 0;
128 readBackOp.sourceTexelOffset = GfVec3i(0);
129
130 HgiBlitCmdsUniquePtr blitCmds = hgiVulkan->CreateBlitCmds();
131 blitCmds->CopyTextureGpuToCpu(readBackOp);
132 hgiVulkan->SubmitCmds(blitCmds.get(), HgiSubmitWaitTypeWaitUntilCompleted);
133
134 if (*glDest == 0) {
135 glGenTextures(1, glDest);
136 glBindTexture(GL_TEXTURE_2D, *glDest);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
139 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
141 } else {
142 glBindTexture(GL_TEXTURE_2D, *glDest);
143 }
144
145 const int32_t width = texDesc.dimensions[0];
146 const int32_t height = texDesc.dimensions[1];
147
148 if (texDesc.format == HgiFormatFloat32Vec4) {
149 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA,
150 GL_FLOAT, texels.data());
151 } else if (texDesc.format == HgiFormatFloat16Vec4) {
152 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA,
153 GL_HALF_FLOAT, texels.data());
154 } else if (texDesc.format == HgiFormatUNorm8Vec4) {
155 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,
156 GL_UNSIGNED_BYTE, texels.data());
157 } else if (texDesc.format == HgiFormatFloat32) {
158 glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED,
159 GL_FLOAT, texels.data());
160 } else {
161 TF_WARN("Unsupported texture format for Vulkan-GL interop");
162 }
163
164 glBindTexture(GL_TEXTURE_2D, 0);
165 }
166
HgiInteropVulkan(Hgi * hgiVulkan)167 HgiInteropVulkan::HgiInteropVulkan(Hgi* hgiVulkan)
168 : _hgiVulkan(static_cast<HgiVulkan*>(hgiVulkan))
169 , _vs(0)
170 , _fsNoDepth(0)
171 , _fsDepth(0)
172 , _prgNoDepth(0)
173 , _prgDepth(0)
174 , _vertexBuffer(0)
175 , _glColorTex(0)
176 , _glDepthTex(0)
177 {
178 _vs = _CompileShader(_vertexFullscreen, GL_VERTEX_SHADER);
179 _fsNoDepth = _CompileShader(_fragmentNoDepthFullscreen, GL_FRAGMENT_SHADER);
180 _fsDepth = _CompileShader(_fragmentDepthFullscreen, GL_FRAGMENT_SHADER);
181 _prgNoDepth = _LinkProgram(_vs, _fsNoDepth);
182 _prgDepth = _LinkProgram(_vs, _fsDepth);
183 _vertexBuffer = _CreateVertexBuffer();
184 TF_VERIFY(glGetError() == GL_NO_ERROR);
185 }
186
~HgiInteropVulkan()187 HgiInteropVulkan::~HgiInteropVulkan()
188 {
189 glDeleteShader(_vs);
190 glDeleteShader(_fsNoDepth);
191 glDeleteShader(_fsDepth);
192 glDeleteProgram(_prgNoDepth);
193 glDeleteProgram(_prgDepth);
194 glDeleteBuffers(1, &_vertexBuffer);
195 if (_glColorTex) {
196 glDeleteTextures(1, &_glColorTex);
197 }
198 if (_glDepthTex) {
199 glDeleteTextures(1, &_glDepthTex);
200 }
201 TF_VERIFY(glGetError() == GL_NO_ERROR);
202 }
203
204 void
CompositeToInterop(HgiTextureHandle const & color,HgiTextureHandle const & depth,VtValue const & framebuffer,GfVec4i const & compRegion)205 HgiInteropVulkan::CompositeToInterop(
206 HgiTextureHandle const &color,
207 HgiTextureHandle const &depth,
208 VtValue const &framebuffer,
209 GfVec4i const &compRegion)
210 {
211 if (!ARCH_UNLIKELY(color)) {
212 TF_WARN("No valid color texture provided");
213 return;
214 }
215
216 // Verify there were no gl errors coming in.
217 TF_VERIFY(glGetError() == GL_NO_ERROR);
218
219 GLint restoreDrawFramebuffer = 0;
220 bool doRestoreDrawFramebuffer = false;
221
222 if (!framebuffer.IsEmpty()) {
223 if (framebuffer.IsHolding<uint32_t>()) {
224 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING,
225 &restoreDrawFramebuffer);
226 doRestoreDrawFramebuffer = true;
227 glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
228 framebuffer.UncheckedGet<uint32_t>());
229 } else {
230 TF_CODING_ERROR(
231 "dstFramebuffer must hold uint32_t when targeting OpenGL");
232 }
233 }
234
235 // Convert textures from Vulkan to GL
236 _ConvertVulkanTextureToOpenGL(_hgiVulkan, color, &_glColorTex);
237 _ConvertVulkanTextureToOpenGL(_hgiVulkan, depth, &_glDepthTex);
238
239 if (!ARCH_UNLIKELY(_glColorTex)) {
240 TF_CODING_ERROR("A valid color texture handle is required.\n");
241 return;
242 }
243
244 #if defined(GL_KHR_debug)
245 if (GARCH_GLAPI_HAS(KHR_debug)) {
246 glPushDebugGroup(GL_DEBUG_SOURCE_THIRD_PARTY, 0, -1, "Interop");
247 }
248 #endif
249
250 GLint restoreActiveTexture = 0;
251 glGetIntegerv(GL_ACTIVE_TEXTURE, &restoreActiveTexture);
252
253 // Setup shader program
254 const uint32_t prg = color && depth ? _prgDepth : _prgNoDepth;
255 glUseProgram(prg);
256
257 {
258 glActiveTexture(GL_TEXTURE0);
259 glBindTexture(GL_TEXTURE_2D, _glColorTex);
260 const GLint loc = glGetUniformLocation(prg, "colorIn");
261 glUniform1i(loc, 0);
262 }
263
264 // Depth is optional
265 if (_glDepthTex) {
266 glActiveTexture(GL_TEXTURE1);
267 glBindTexture(GL_TEXTURE_2D, _glDepthTex);
268 const GLint loc = glGetUniformLocation(prg, "depthIn");
269 glUniform1i(loc, 1);
270 }
271
272 // Get the current array buffer binding state
273 GLint restoreArrayBuffer = 0;
274 glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &restoreArrayBuffer);
275
276 // Vertex attributes
277 const GLint locPosition = glGetAttribLocation(prg, "position");
278 glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
279 glVertexAttribPointer(locPosition, 4, GL_FLOAT, GL_FALSE,
280 sizeof(float)*6, 0);
281 glEnableVertexAttribArray(locPosition);
282
283 const GLint locUv = glGetAttribLocation(prg, "uvIn");
284 glVertexAttribPointer(locUv, 2, GL_FLOAT, GL_FALSE,
285 sizeof(float)*6, reinterpret_cast<void*>(sizeof(float)*4));
286 glEnableVertexAttribArray(locUv);
287
288 // Since we want to composite over the application's framebuffer contents,
289 // we need to honor depth testing if we have a valid depth texture.
290 const GLboolean restoreDepthEnabled = glIsEnabled(GL_DEPTH_TEST);
291 GLboolean restoreDepthMask;
292 glGetBooleanv(GL_DEPTH_WRITEMASK, &restoreDepthMask);
293 GLint restoreDepthFunc;
294 glGetIntegerv(GL_DEPTH_FUNC, &restoreDepthFunc);
295 if (_glDepthTex) {
296 glEnable(GL_DEPTH_TEST);
297 glDepthMask(GL_TRUE);
298 // Note: Use LEQUAL and not LESS to ensure that fragments with only
299 // translucent contribution (that don't update depth) are composited.
300 glDepthFunc(GL_LEQUAL);
301 } else {
302 glDisable(GL_DEPTH_TEST);
303 glDepthMask(GL_FALSE);
304 }
305
306 // Enable blending to composite correctly over framebuffer contents.
307 // Use pre-multiplied alpha scaling factors.
308 GLboolean blendEnabled;
309 glGetBooleanv(GL_BLEND, &blendEnabled);
310 glEnable(GL_BLEND);
311 GLint restoreColorSrcFnOp, restoreAlphaSrcFnOp;
312 GLint restoreColorDstFnOp, restoreAlphaDstFnOp;
313 glGetIntegerv(GL_BLEND_SRC_RGB, &restoreColorSrcFnOp);
314 glGetIntegerv(GL_BLEND_SRC_ALPHA, &restoreAlphaSrcFnOp);
315 glGetIntegerv(GL_BLEND_DST_RGB, &restoreColorDstFnOp);
316 glGetIntegerv(GL_BLEND_DST_ALPHA, &restoreAlphaDstFnOp);
317 glBlendFuncSeparate(/*srcColor*/GL_ONE,
318 /*dstColor*/GL_ONE_MINUS_SRC_ALPHA,
319 /*srcAlpha*/GL_ONE,
320 /*dstAlpha*/GL_ONE);
321 GLint restoreColorOp, restoreAlphaOp;
322 glGetIntegerv(GL_BLEND_EQUATION_RGB, &restoreColorOp);
323 glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &restoreAlphaOp);
324 glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
325
326 // Disable alpha to coverage (we want to composite the pixels as-is)
327 GLboolean restoreAlphaToCoverage;
328 glGetBooleanv(GL_SAMPLE_ALPHA_TO_COVERAGE, &restoreAlphaToCoverage);
329 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
330
331 int32_t restoreVp[4];
332 glGetIntegerv(GL_VIEWPORT, restoreVp);
333 glViewport(compRegion[0], compRegion[1], compRegion[2], compRegion[3]);
334
335 // Draw fullscreen triangle
336 glDrawArrays(GL_TRIANGLES, 0, 3);
337
338 // Restore state and verify gl errors
339 glDisableVertexAttribArray(locPosition);
340 glDisableVertexAttribArray(locUv);
341 glBindBuffer(GL_ARRAY_BUFFER, restoreArrayBuffer);
342
343 if (!blendEnabled) {
344 glDisable(GL_BLEND);
345 }
346 glBlendFuncSeparate(restoreColorSrcFnOp, restoreColorDstFnOp,
347 restoreAlphaSrcFnOp, restoreAlphaDstFnOp);
348 glBlendEquationSeparate(restoreColorOp, restoreAlphaOp);
349
350 if (!restoreDepthEnabled) {
351 glDisable(GL_DEPTH_TEST);
352 } else {
353 glEnable(GL_DEPTH_TEST);
354 }
355 glDepthMask(restoreDepthMask);
356 glDepthFunc(restoreDepthFunc);
357
358 if (restoreAlphaToCoverage) {
359 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
360 }
361 glViewport(restoreVp[0], restoreVp[1], restoreVp[2], restoreVp[3]);
362
363 glUseProgram(0);
364
365 glActiveTexture(GL_TEXTURE1);
366 glBindTexture(GL_TEXTURE_2D, 0);
367 glActiveTexture(GL_TEXTURE0);
368 glBindTexture(GL_TEXTURE_2D, 0);
369
370 #if defined(GL_KHR_debug)
371 if (GARCH_GLAPI_HAS(KHR_debug)) {
372 glPopDebugGroup();
373 }
374 #endif
375
376 glActiveTexture(restoreActiveTexture);
377
378 if (doRestoreDrawFramebuffer) {
379 glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
380 restoreDrawFramebuffer);
381 }
382
383 TF_VERIFY(glGetError() == GL_NO_ERROR);
384 }
385
386 PXR_NAMESPACE_CLOSE_SCOPE
387