gl_vk_simple_interop
/
main.cpp
418 строк · 13.2 Кб
1/*
2* Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*
16* SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION
17* SPDX-License-Identifier: Apache-2.0
18*/
19
20
21//--------------------------------------------------------------------------------------------------
22// Very simple Vulkan-OpenGL example:
23// - The vertex buffer is allocated with Vulkan, but used by OpenGL to render
24// - The animation is updating the buffer allocated by Vulkan, and the changes are
25// reflected in the OGL render.
26//
27
28#ifdef WIN32
29#include <accctrl.h>
30#include <aclapi.h>
31#endif
32
33#include <array>
34#include <chrono>
35#include <iostream>
36#include <vulkan/vulkan_core.h>
37
38#define IMGUI_DEFINE_MATH_OPERATORS
39
40#include "backends/imgui_impl_glfw.h"
41#include "imgui/imgui_helper.h"
42#include "imgui/backends/imgui_impl_gl.h"
43
44#include "compute.hpp"
45#include "nvgl/contextwindow_gl.hpp"
46#include "nvgl/extensions_gl.hpp"
47#include "nvpsystem.hpp"
48#include "nvvkhl/appbase_vkpp.hpp"
49#include "nvvk/commands_vk.hpp"
50#include "nvvk/context_vk.hpp"
51
52VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
53
54int const SAMPLE_SIZE_WIDTH = 1200;
55int const SAMPLE_SIZE_HEIGHT = 900;
56
57// Default search path for shaders
58std::vector<std::string> defaultSearchPaths{
59"./",
60"../",
61std::string(PROJECT_NAME),
62std::string("SPV_" PROJECT_NAME),
63NVPSystem::exePath() + PROJECT_RELDIRECTORY,
64NVPSystem::exePath() + std::string(PROJECT_RELDIRECTORY),
65};
66
67// An array of 3 vectors which represents 3 vertices
68struct Vertex
69{
70glm::vec3 pos;
71glm::vec2 uv;
72};
73
74// The triangle
75static std::vector<Vertex> g_vertexDataVK = {{{-1.0f, -1.0f, 0.0f}, {0, 0}},
76{{1.0f, -0.0f, 0.0f}, {1, 0}},
77{{0.0f, 1.0f, 0.0f}, {0.5, 1}}};
78
79
80//--------------------------------------------------------------------------------------------------
81//
82//
83class InteropExample : public nvvkhl::AppBase
84{
85public:
86void prepare(uint32_t queueIdxCompute)
87{
88m_alloc.init(m_device, m_physicalDevice);
89
90createShaders(); // Create the GLSL shaders
91createBufferVK(); // Create the vertex buffer
92
93// Initialize the Vulkan compute shader
94m_compute.setup(m_device, m_physicalDevice, m_graphicsQueueIndex, queueIdxCompute, m_alloc);
95m_compute.update({1024, 1024}); // Initial size
96}
97
98void destroy() override
99{
100m_device.waitIdle();
101m_bufferVk.destroy(m_alloc);
102m_compute.destroy();
103
104ImGui_ImplGlfw_Shutdown();
105ImGui::ShutdownGL();
106AppBase::destroy();
107}
108
109//--------------------------------------------------------------------------------------------------
110// Create the vertex buffer with Vulkan
111//
112void createBufferVK()
113{
114m_bufferVk.bufVk = m_alloc.createBuffer(g_vertexDataVK.size() * sizeof(Vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
115VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
116
117createBufferGL(m_alloc, m_bufferVk);
118
119// Same as usual
120int pos_loc = 0;
121int uv_loc = 1;
122glCreateVertexArrays(1, &m_vertexArray);
123glEnableVertexArrayAttrib(m_vertexArray, pos_loc);
124glEnableVertexArrayAttrib(m_vertexArray, uv_loc);
125
126glVertexArrayAttribFormat(m_vertexArray, pos_loc, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex, pos));
127glVertexArrayAttribBinding(m_vertexArray, pos_loc, 0);
128glVertexArrayAttribFormat(m_vertexArray, uv_loc, 2, GL_FLOAT, GL_FALSE, offsetof(Vertex, uv));
129glVertexArrayAttribBinding(m_vertexArray, uv_loc, 0);
130
131glVertexArrayVertexBuffer(m_vertexArray, 0, m_bufferVk.oglId, 0, sizeof(Vertex));
132}
133
134//--------------------------------------------------------------------------------------------------
135//
136//
137void onWindowRefresh()
138{
139// Compute FPS
140static float fps = 0.f;
141{
142static float frameNumber{0};
143static auto tStart = std::chrono::high_resolution_clock::now();
144auto tEnd = std::chrono::high_resolution_clock::now();
145auto tDiff = std::chrono::duration<float>(tEnd - tStart).count();
146frameNumber++;
147if(tDiff > 1.f)
148{
149tStart = tEnd;
150fps = frameNumber / tDiff;
151LOGI("FPS: %f\n", fps);
152frameNumber = 0;
153}
154}
155
156// Input GUI
157ImGui::NewFrame();
158ImGui::SetNextWindowSize(ImGuiH::dpiScaled(350, 0), ImGuiCond_FirstUseEver);
159if(ImGui::Begin("gl_vk_simple_interop"))
160{
161ImGui::Text("FPS: %.3f", fps);
162
163int textureWidth = int(m_compute.m_textureTarget.imgSize.width);
164int textureHeight = int(m_compute.m_textureTarget.imgSize.height);
165// The slider max of 16384 here is somewhat arbitrary; Ctrl-click to set
166// it to a larger value. It's set to 16K so that casually sliding the
167// sliders won't run out of memory on most GPUs.
168// Use glGetIntegerv(GL_MAX_TEXTURE_SIZE) to get the maximum texture size.
169ImGui::SliderInt("Texture Width", &textureWidth, 1, 16384, "%d", ImGuiSliderFlags_Logarithmic);
170ImGui::SliderInt("Texture Height", &textureHeight, 1, 16384, "%d", ImGuiSliderFlags_Logarithmic);
171const VkExtent2D newSize = {uint32_t(textureWidth), uint32_t(textureHeight)};
172// Did the size change?
173if(0 != memcmp(&newSize, &m_compute.m_textureTarget.imgSize, sizeof(VkExtent2D)))
174{
175// Recreate the interop texture:
176m_compute.update(newSize);
177}
178}
179ImGui::End();
180
181glViewport(0, 0, m_size.width, m_size.height);
182
183// Signal Vulkan it can use the texture
184GLenum dstLayout = GL_LAYOUT_SHADER_READ_ONLY_EXT;
185glSignalSemaphoreEXT(m_compute.m_semaphores.glReady, 0, nullptr, 1, &m_compute.m_textureTarget.oglId, &dstLayout);
186
187// Invoke Vulkan
188m_compute.buildCommandBuffers();
189m_compute.submit();
190
191// Wait (on the GPU side) for the Vulkan semaphore to be signaled (finished compute)
192GLenum srcLayout = GL_LAYOUT_COLOR_ATTACHMENT_EXT;
193glWaitSemaphoreEXT(m_compute.m_semaphores.glComplete, 0, nullptr, 1, &m_compute.m_textureTarget.oglId, &srcLayout);
194
195// Issue OpenGL commands to draw a triangle using this texture
196glBindVertexArray(m_vertexArray);
197glBindTextureUnit(0, m_compute.m_textureTarget.oglId);
198glUseProgram(m_programID);
199glDrawArrays(GL_TRIANGLES, 0, 3);
200glBindTextureUnit(0, 0);
201
202// Draw GUI
203ImGui::Render();
204ImGui::RenderDrawDataGL(ImGui::GetDrawData());
205ImGui::EndFrame();
206}
207
208//--------------------------------------------------------------------------------------------------
209//
210//
211void animate()
212{
213static auto startTime = std::chrono::high_resolution_clock::now();
214auto currentTime = std::chrono::high_resolution_clock::now();
215float t = std::chrono::duration<float>(currentTime - startTime).count() * 0.5f;
216// Modify the buffer and upload it in the Vulkan allocated buffer
217g_vertexDataVK[0].pos.x = sinf(t);
218g_vertexDataVK[1].pos.y = cosf(t);
219g_vertexDataVK[2].pos.x = -sinf(t);
220
221void* mapped = m_alloc.map(m_bufferVk.bufVk);
222// This works because the buffer was created with
223// VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
224memcpy(mapped, g_vertexDataVK.data(), g_vertexDataVK.size() * sizeof(Vertex));
225m_alloc.unmap(m_bufferVk.bufVk);
226}
227
228//--------------------------------------------------------------------------------------------------
229// Creating the OpenGL shaders
230//
231GLuint createShaders()
232{
233// OpenGL - Create shaders
234char buf[512];
235int len = 0;
236
237// OpenGL 4.2 Core
238GLuint vs = glCreateShader(GL_VERTEX_SHADER);
239GLchar const* vss = {R"(
240#version 450
241layout(location = 0) in vec3 inVertex;
242layout(location = 1) in vec2 inUV;
243layout (location = 0) out vec2 outUV;
244
245void main()
246{
247outUV = inUV;
248gl_Position = vec4(inVertex, 1.0f);
249}
250)"};
251
252glShaderSource(vs, 1, &vss, nullptr);
253glCompileShader(vs);
254glGetShaderInfoLog(vs, 512, (GLsizei*)&len, buf);
255
256GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
257GLchar const* fss = {R"(
258#version 450
259layout (location = 0) in vec2 inUV;
260layout(location = 0) out vec4 fragColor;
261
262uniform sampler2D myTextureSampler;
263
264void main()
265{
266vec3 color = texture( myTextureSampler, inUV ).rgb;
267fragColor = vec4(color,1);
268}
269
270)"};
271
272glShaderSource(fs, 1, &fss, nullptr);
273glCompileShader(fs);
274glGetShaderInfoLog(fs, 512, (GLsizei*)&len, buf);
275
276GLuint mSH2D = glCreateProgram();
277glAttachShader(mSH2D, vs);
278glAttachShader(mSH2D, fs);
279glLinkProgram(mSH2D);
280
281m_programID = mSH2D;
282return mSH2D;
283}
284
285//--------------------------------------------------------------------------------------------------
286// Initialization of the GUI
287// - Need to be call after the device creation
288//
289void initUI(int width, int height)
290{
291m_size.width = width;
292m_size.height = height;
293
294// UI
295ImGuiH::Init(width, height, this, ImGuiH::FONT_PROPORTIONAL_SCALED);
296ImGui::InitGL();
297}
298
299//- Override the default resize
300void onFramebufferSize(int w, int h) override
301{
302m_size.width = w;
303m_size.height = h;
304ImGui::GetIO().DisplaySize = ImVec2(float(w), float(h));
305}
306
307virtual void onMouseMotion(int x, int y) override { ImGuiH::mouse_pos(x, y); }
308virtual void onMouseButton(int button, int action, int mods) override { ImGuiH::mouse_button(button, action); }
309virtual void onMouseWheel(int delta) override { ImGuiH::mouse_wheel(delta); }
310virtual void onKeyboard(int key, int /*scancode*/, int action, int mods) override
311{
312ImGuiH::key_button(key, action, mods);
313}
314virtual void onKeyboardChar(unsigned char key) override { ImGuiH::key_char(key); }
315
316private:
317nvvk::BufferVkGL m_bufferVk;
318nvvk::ExportResourceAllocatorDedicated m_alloc;
319
320GLuint m_vertexArray = 0; // VAO
321GLuint m_programID = 0; // Shader program
322
323ComputeImageVk m_compute; // Compute in Vulkan
324};
325
326//--------------------------------------------------------------------------------------------------
327//
328//
329int main(int argc, char** argv)
330{
331// setup some basic things for the sample, logging file for example
332NVPSystem system(PROJECT_NAME);
333
334nvprintSetBreakpoints(true); // DEBUG
335glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
336glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
337// Create window with graphics context
338GLFWwindow* window = glfwCreateWindow(SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT, PROJECT_NAME, NULL, NULL);
339if(window == nullptr)
340return 1;
341glfwMakeContextCurrent(window);
342glfwSwapInterval(1); // Enable vsync
343
344nvvk::ContextCreateInfo deviceInfo;
345deviceInfo.addInstanceExtension(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
346deviceInfo.addInstanceExtension(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
347deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
348deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
349#ifdef WIN32
350deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
351deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
352#else
353deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
354deviceInfo.addDeviceExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
355#endif
356
357// Creating the Vulkan instance and device
358nvvk::Context vkctx;
359if(!vkctx.init(deviceInfo))
360{
361LOGE("Could not initialize the Vulkan instance and device! See the above messages for more info.\n");
362return EXIT_FAILURE;
363}
364
365
366InteropExample example;
367nvgl::ContextWindow contextWindowGL;
368
369// Loading all OpenGL symbols
370load_GL(nvgl::ContextWindow::sysGetProcAddress);
371if(!has_GL_EXT_semaphore)
372{
373LOGE("GL_EXT_semaphore Not Available !\n");
374return EXIT_FAILURE;
375}
376
377example.setup(vkctx.m_instance, vkctx.m_device, vkctx.m_physicalDevice, vkctx.m_queueGCT.familyIndex);
378
379// Printing which GPU we are using for Vulkan
380LOGI("using %s\n", example.getPhysicalDevice().getProperties().deviceName.data());
381
382// Initialize the window, UI ..
383example.initUI(SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT);
384
385// Prepare the example
386example.prepare(vkctx.m_queueGCT.familyIndex);
387
388
389// GLFW Callback
390example.setupGlfwCallbacks(window);
391ImGui_ImplGlfw_InitForOpenGL(window, false);
392
393// Main loop
394while(!glfwWindowShouldClose(window))
395{
396glfwPollEvents();
397int w, h;
398glfwGetWindowSize(window, &w, &h);
399if(w == 0 || h == 0)
400continue;
401
402glClearColor(0.5f, 0.0f, 0.0f, 0.0f);
403glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
404
405example.animate();
406example.onWindowRefresh();
407
408glfwSwapBuffers(window);
409}
410
411example.destroy();
412vkctx.deinit();
413
414glfwDestroyWindow(window);
415glfwTerminate();
416
417return EXIT_SUCCESS;
418}
419