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