├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── settings.json ├── CMakeLists.txt ├── README.md ├── Source ├── ExMesh.cpp ├── ExRenderBuffer.cpp ├── ExRenderDelegate.cpp ├── ExRenderPass.cpp ├── ExRendererPlugin.cpp ├── Include │ └── ExampleDelegate │ │ ├── ExMesh.h │ │ ├── ExRenderBuffer.h │ │ ├── ExRenderDelegate.h │ │ ├── ExRenderPass.h │ │ ├── ExRendererPlugin.h │ │ ├── PxrUsage.h │ │ └── StbUsage.h ├── PxrUsage.cpp ├── StbUsage.cpp └── plugInfo.json ├── StandaloneTest ├── CMakeLists.txt └── StandaloneTest.cpp ├── vcpkg-configuration.json └── vcpkg.json /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | .vs 4 | .idea -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "External/VulkanWrappers"] 2 | path = External/VulkanWrappers 3 | url = git@github.com:parsaiej/VulkanWrappers.git 4 | [submodule "External/stb"] 5 | path = External/stb 6 | url = git@github.com:nothings/stb.git 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | // Test the standalone executable. 9 | { 10 | "type": "lldb", 11 | "request": "launch", 12 | "name": "Debug", 13 | "program": "${workspaceFolder}/build/StandaloneTest/StandaloneTest", 14 | "cwd": "${workspaceFolder}/build/StandaloneTest/", 15 | "env": { 16 | "PXR_PLUGINPATH_NAME": "C:/Development/custom_usd_plugins" 17 | } 18 | }, 19 | 20 | // Test USDView via Python. 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "USDView", 25 | "program": "python3.9", 26 | "cwd": "${workspaceFolder}/../OpenUSD_Build/bin/", 27 | 28 | "args": ["usdview", "${workspaceFolder}/../../Downloads/Kitchen_set/Kitchen_set.usd"], 29 | 30 | "env": 31 | { 32 | // Need to manually supply the environment... 33 | "PXR_PLUGINPATH_NAME": "/Users/johnparsaie/Development/custom_usd_plugins", 34 | "PYTHONPATH": "$PYTHONPATH:/Users/johnparsaie/Development/OpenUSD_Build/lib/python" 35 | } 36 | }, 37 | 38 | // Test Houdini 39 | { 40 | "type": "lldb", 41 | "request": "launch", 42 | "name": "Houdini", 43 | "program": "/Applications/Houdini/Current/Houdini FX 20.0.506.app", 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "vector": "cpp", 4 | "deque": "cpp", 5 | "list": "cpp", 6 | "format": "cpp", 7 | "xstring": "cpp" 8 | } 9 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(VCPKG_TARGET_TRIPLET "x64-windows-static") 4 | set(CMAKE_TOOLCHAIN_FILE "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file") 5 | 6 | set(PROJECT_NAME ExampleHydraRenderDelegate) 7 | project(${PROJECT_NAME}) 8 | 9 | # Project Config 10 | # -------------------------------------------------- 11 | 12 | set (CMAKE_CXX_STANDARD 17) 13 | set (CMAKE_CXX_STANDARD_REQUIRED True) 14 | 15 | # Plugin install path 16 | set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/Development/custom_usd_plugins") 17 | 18 | if (APPLE) 19 | # In order for targets to properly load Vulkan libs etc. 20 | list(APPEND CMAKE_INSTALL_RPATH "/usr/local/lib") 21 | else() 22 | add_compile_definitions(NOMINMAX) 23 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 24 | endif() 25 | 26 | # Set RPATH for all targets in the project 27 | list(APPEND CMAKE_INSTALL_RPATH "@loader_path") 28 | 29 | # User Options 30 | # -------------------------------------------------- 31 | 32 | # This will link directly against the custom USD build that ships with the Houdini HDK. 33 | # This is required in order for the render delegate to be correctly loaded by houdini, 34 | # otherwise there will be issues due to loading a plugin that was made with a different version. 35 | option(BUILD_FOR_HOUDINI "Build for Houdini" OFF) 36 | 37 | find_package(Vulkan REQUIRED) 38 | find_package(OpenGL REQUIRED) 39 | find_package(GLEW REQUIRED) 40 | 41 | if (NOT BUILD_FOR_HOUDINI) 42 | # Path to a OpenUSD build / installation. 43 | list( APPEND CMAKE_PREFIX_PATH "~/Development/OpenUSD_Build_Debug/" ) 44 | 45 | # Configure a OpenUSD installation. 46 | find_package(pxr REQUIRED) 47 | else() 48 | # Path to a Houdini HDK installation. 49 | list( APPEND CMAKE_PREFIX_PATH "/Applications/Houdini/Houdini20.0.506/Frameworks/Houdini.framework/Versions/20.0/Resources/toolkit/cmake/" ) 50 | 51 | # Note: We also will need Python in order to link with Houdini HDK correctly. 52 | # Note: For Houdini 20.x you will need Python 3.10 (i.e. brew install python@3.10). 53 | find_package(Python REQUIRED COMPONENTS Development) 54 | 55 | # Info: https://www.sidefx.com/docs/hdk/_h_d_k__intro__compiling.html 56 | find_package(Houdini REQUIRED) 57 | endif() 58 | 59 | # Configure misc. packages 60 | # -------------------------------------------------- 61 | 62 | # Enable a define in the wrappers to enable Vulkan validation layers. 63 | add_definitions(-DENABLE_VALIDATION_LAYERS) 64 | 65 | add_subdirectory(External/VulkanWrappers) 66 | 67 | # Find Packages 68 | # -------------------------------------------------- 69 | 70 | # Library 71 | # -------------------------------------------------- 72 | 73 | add_library(${PROJECT_NAME} SHARED 74 | "Source/PxrUsage.cpp" 75 | "Source/StbUsage.cpp" 76 | "Source/ExMesh.cpp" 77 | "Source/ExRendererPlugin.cpp" 78 | "Source/ExRenderDelegate.cpp" 79 | "Source/ExRenderPass.cpp" 80 | "Source/ExRenderBuffer.cpp" 81 | ) 82 | 83 | # Include 84 | # -------------------------------------------------- 85 | 86 | target_include_directories (${PROJECT_NAME} PRIVATE ${Vulkan_INCLUDE_DIRS}) 87 | target_include_directories (${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/External/VulkanWrappers/Include) 88 | target_include_directories (${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/Source/Include) 89 | target_include_directories (${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/External/) 90 | 91 | if (NOT BUILD_FOR_HOUDINI) 92 | # Include OpenUSD headers if we aren't building with Houdini HDK framework. 93 | target_include_directories (${PROJECT_NAME} PRIVATE ${PXR_INCLUDE_DIRS}) 94 | endif() 95 | 96 | # Link 97 | # -------------------------------------------------- 98 | 99 | target_link_libraries (${PROJECT_NAME} ${Vulkan_LIBRARIES}) 100 | target_link_libraries (${PROJECT_NAME} OpenGL::GL) 101 | target_link_libraries (${PROJECT_NAME} GLEW::GLEW) 102 | target_link_libraries (${PROJECT_NAME} VulkanWrappers) 103 | 104 | if (NOT BUILD_FOR_HOUDINI) 105 | # Link against OpenUSD directly. 106 | target_link_libraries (${PROJECT_NAME} ${PXR_LIBRARIES}) 107 | else() 108 | # Warning: On macOS some recent versions of Houdini 20.x will not correctly link due to a Houdini.framework 109 | # that is missing the necessary symbolic links. 110 | 111 | # Link against HDK (contains SideFX custom USD build) + Library. 112 | target_link_libraries (${PROJECT_NAME} ${Python_LIBRARIES}) 113 | target_link_libraries (${PROJECT_NAME} Houdini) 114 | endif() 115 | 116 | # Install Config 117 | # -------------------------------------------------- 118 | 119 | # Plugin install path 120 | set(CMAKE_INSTALL_PREFIX "C:/Development/custom_usd_plugins") 121 | 122 | # Move the library + plugin info to a location that USD checks plugins for. 123 | # Note: This location should be pointed to by the $PXR_PLUGINPATH_NAME env variable. 124 | install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION .) 125 | install(TARGETS VulkanWrappers LIBRARY DESTINATION .) 126 | if (APPLE) 127 | install(TARGETS MetalUtility LIBRARY DESTINATION .) 128 | endif() 129 | install(FILES ${CMAKE_SOURCE_DIR}/Source/plugInfo.json DESTINATION ExampleHydraRenderDelegate/resources/) 130 | 131 | # Standalone Executable Test 132 | # -------------------------------------------------- 133 | 134 | if (NOT BUILD_FOR_HOUDINI) 135 | add_subdirectory(StandaloneTest/) 136 | endif() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dead-simple Hydra Render Delegate that can be used in a Houdini Solaris viewport, OpenUSD's USD View, a provided sample executable with manual swapchain handling, and any other DCC that implements Hydra. 2 | 3 | ![image](https://github.com/parsaiej/ExampleHydraRenderDelegate/assets/11955370/18cb68c4-fdc0-4f66-ba41-6426dac26e11) 4 | -------------------------------------------------------------------------------- /Source/ExMesh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | ExMesh::ExMesh(SdfPath const& id) 6 | : HdMesh(id) 7 | { 8 | } 9 | 10 | HdDirtyBits ExMesh::GetInitialDirtyBitsMask() const 11 | { 12 | return HdChangeTracker::Clean | HdChangeTracker::DirtyTransform; 13 | } 14 | 15 | HdDirtyBits ExMesh::_PropagateDirtyBits(HdDirtyBits bits) const 16 | { 17 | return bits; 18 | } 19 | 20 | void ExMesh::_InitRepr(TfToken const &reprToken, HdDirtyBits *dirtyBits) 21 | { 22 | 23 | } 24 | 25 | void ExMesh::Sync(HdSceneDelegate *sceneDelegate, 26 | HdRenderParam *renderParam, 27 | HdDirtyBits *dirtyBits, 28 | TfToken const &reprToken) 29 | { 30 | std::cout << "* (multithreaded) Sync Tiny Mesh id=" << GetId() << std::endl; 31 | } 32 | -------------------------------------------------------------------------------- /Source/ExRenderBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /Source/ExRenderDelegate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | const TfTokenVector ExRenderDelegate::SUPPORTED_RPRIM_TYPES = 11 | { 12 | HdPrimTypeTokens->mesh, 13 | }; 14 | 15 | const TfTokenVector ExRenderDelegate::SUPPORTED_SPRIM_TYPES = 16 | { 17 | HdPrimTypeTokens->camera, 18 | }; 19 | 20 | const TfTokenVector ExRenderDelegate::SUPPORTED_BPRIM_TYPES = 21 | { 22 | }; 23 | 24 | ExRenderDelegate::ExRenderDelegate() : HdRenderDelegate() 25 | { 26 | _Initialize(); 27 | } 28 | 29 | ExRenderDelegate::ExRenderDelegate(HdRenderSettingsMap const& settingsMap) : HdRenderDelegate(settingsMap) 30 | { 31 | _Initialize(); 32 | } 33 | 34 | void ExRenderDelegate::_Initialize() 35 | { 36 | std::cout << "Creating Custom RenderDelegate" << std::endl; 37 | _resourceRegistry = std::make_shared(); 38 | } 39 | 40 | ExRenderDelegate::~ExRenderDelegate() 41 | { 42 | _resourceRegistry.reset(); 43 | std::cout << "Destroying Custom RenderDelegate" << std::endl; 44 | } 45 | 46 | // In case 47 | struct NoOpDeleter { void operator()(VulkanWrappers::Device* ptr) const {} }; 48 | 49 | void ExRenderDelegate::SetDrivers(HdDriverVector const& drivers) 50 | { 51 | for (const auto& driver : drivers) 52 | { 53 | if (driver->name == TfToken("CustomVulkanDevice") && driver->driver.IsHolding()) 54 | { 55 | m_GraphicsDevice = driver->driver.UncheckedGet(); 56 | return; 57 | } 58 | } 59 | 60 | // If no driver is passed, then create it here (no window). 61 | m_DefaultGraphicsDevice = std::make_unique(); 62 | 63 | // And set the main device resource to the default one. 64 | m_GraphicsDevice = m_DefaultGraphicsDevice.get(); 65 | } 66 | 67 | TfTokenVector const& ExRenderDelegate::GetSupportedRprimTypes() const 68 | { 69 | return SUPPORTED_RPRIM_TYPES; 70 | } 71 | 72 | TfTokenVector const& ExRenderDelegate::GetSupportedSprimTypes() const 73 | { 74 | return SUPPORTED_SPRIM_TYPES; 75 | } 76 | 77 | TfTokenVector const& ExRenderDelegate::GetSupportedBprimTypes() const 78 | { 79 | return SUPPORTED_BPRIM_TYPES; 80 | } 81 | 82 | HdResourceRegistrySharedPtr ExRenderDelegate::GetResourceRegistry() const 83 | { 84 | return _resourceRegistry; 85 | } 86 | 87 | void ExRenderDelegate::CommitResources(HdChangeTracker *tracker) 88 | { 89 | } 90 | 91 | HdRenderPassSharedPtr ExRenderDelegate::CreateRenderPass(HdRenderIndex *index, HdRprimCollection const& collection) 92 | { 93 | std::cout << "Create Custom RenderPass with Collection=" << collection.GetName() << std::endl; 94 | return HdRenderPassSharedPtr(new ExRenderPass(index, collection, this)); 95 | } 96 | 97 | HdRprim* ExRenderDelegate::CreateRprim(TfToken const& typeId, SdfPath const& rprimId) 98 | { 99 | std::cout << "Create Custom Rprim type=" << typeId.GetText() 100 | << " id=" << rprimId 101 | << std::endl; 102 | 103 | if (typeId == HdPrimTypeTokens->mesh) { 104 | return new ExMesh(rprimId); 105 | } else { 106 | TF_CODING_ERROR("Unknown Rprim type=%s id=%s", 107 | typeId.GetText(), 108 | rprimId.GetText()); 109 | } 110 | return nullptr; 111 | } 112 | 113 | void ExRenderDelegate::DestroyRprim(HdRprim *rPrim) 114 | { 115 | std::cout << "Destroy Custom Rprim id=" << rPrim->GetId() << std::endl; 116 | delete rPrim; 117 | } 118 | 119 | HdSprim* ExRenderDelegate::CreateSprim(TfToken const& typeId, SdfPath const& sprimId) 120 | { 121 | TF_CODING_ERROR("Unknown Sprim type=%s id=%s", typeId.GetText(), sprimId.GetText()); 122 | return nullptr; 123 | } 124 | 125 | HdSprim* ExRenderDelegate::CreateFallbackSprim(TfToken const& typeId) 126 | { 127 | TF_CODING_ERROR("Creating unknown fallback sprim type=%s", typeId.GetText()); 128 | return nullptr; 129 | } 130 | 131 | void ExRenderDelegate::DestroySprim(HdSprim *sPrim) 132 | { 133 | TF_CODING_ERROR("Destroy Sprim not supported"); 134 | } 135 | 136 | HdBprim* ExRenderDelegate::CreateBprim(TfToken const& typeId, SdfPath const& bprimId) 137 | { 138 | TF_CODING_ERROR("Unknown Bprim type=%s id=%s", typeId.GetText(), bprimId.GetText()); 139 | return nullptr; 140 | } 141 | 142 | HdBprim* ExRenderDelegate::CreateFallbackBprim(TfToken const& typeId) 143 | { 144 | TF_CODING_ERROR("Creating unknown fallback bprim type=%s", typeId.GetText()); 145 | return nullptr; 146 | } 147 | 148 | void ExRenderDelegate::DestroyBprim(HdBprim *bPrim) 149 | { 150 | TF_CODING_ERROR("Destroy Bprim not supported"); 151 | } 152 | 153 | HdInstancer* ExRenderDelegate::CreateInstancer(HdSceneDelegate *delegate, SdfPath const& id) 154 | { 155 | TF_CODING_ERROR("Creating Instancer not supported id=%s", id.GetText()); 156 | return nullptr; 157 | } 158 | 159 | void ExRenderDelegate::DestroyInstancer(HdInstancer *instancer) 160 | { 161 | TF_CODING_ERROR("Destroy instancer not supported"); 162 | } 163 | 164 | HdRenderParam* ExRenderDelegate::GetRenderParam() const 165 | { 166 | return nullptr; 167 | } -------------------------------------------------------------------------------- /Source/ExRenderPass.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace VulkanWrappers; 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | // Resource IDs 23 | // --------------------- 24 | 25 | enum ShaderID 26 | { 27 | MESH_VS, 28 | UNLIT_PS, 29 | }; 30 | 31 | enum ImageID 32 | { 33 | COLOR, 34 | DEPTH 35 | }; 36 | 37 | enum BufferID 38 | { 39 | COLOR_MAP_STAGING, 40 | }; 41 | 42 | // Resources 43 | // --------------------- 44 | 45 | static VkCommandBuffer s_InternalCmd; 46 | static VkViewport s_PreviousViewport; 47 | 48 | static std::unordered_map s_Shaders; 49 | static std::unordered_map s_Images; 50 | static std::unordered_map s_Buffers; 51 | 52 | static unsigned int s_GLBackbufferImage; 53 | static unsigned int s_GLBackbufferObject; 54 | 55 | void CreateViewportSizedResources(Device* device, VkViewport viewport) 56 | { 57 | VkFormat colorFormat; 58 | { 59 | if (device->GetWindow() != nullptr) 60 | colorFormat = device->GetWindow()->GetColorSurfaceFormat(); 61 | else 62 | colorFormat = VK_FORMAT_R8G8B8A8_SRGB; 63 | } 64 | 65 | s_Images[ImageID::COLOR] = Image(viewport.width, viewport.height, 66 | colorFormat, 67 | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 68 | VK_IMAGE_ASPECT_COLOR_BIT); 69 | 70 | s_Buffers[BufferID::COLOR_MAP_STAGING] = Buffer(4 * viewport.width * viewport.height, 71 | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, 72 | VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); 73 | 74 | for (auto& image : s_Images) 75 | device->CreateImages( { &image.second } ); 76 | 77 | for (auto& buffer : s_Buffers) 78 | device->CreateBuffers( { &buffer.second } ); 79 | } 80 | 81 | ExRenderPass::ExRenderPass(HdRenderIndex *index, HdRprimCollection const &collection, ExRenderDelegate* renderDelegate) 82 | : HdRenderPass(index, collection), m_Owner(renderDelegate) 83 | { 84 | auto device = m_Owner->GetGraphicsDevice(); 85 | 86 | // Fetch the base plugin in order to construct asset paths. 87 | auto pluginBase = PlugRegistry::GetInstance().GetPluginWithName("hdExample"); 88 | TF_VERIFY(pluginBase != nullptr); 89 | 90 | /* 91 | s_Shaders = 92 | { 93 | { ShaderID::MESH_VS, Shader(pluginBase->FindPluginResource("shaders/TriangleVert.spv").c_str(), VK_SHADER_STAGE_VERTEX_BIT) }, 94 | { ShaderID::UNLIT_PS, Shader(pluginBase->FindPluginResource("shaders/TriangleFrag.spv").c_str(), VK_SHADER_STAGE_FRAGMENT_BIT) } 95 | }; 96 | 97 | for (auto& shader : s_Shaders) 98 | device->CreateShaders ({ &shader.second }); 99 | 100 | // The internal command should be secondary since there already exist a primary one to be used for application-managed queue submission. 101 | if (m_Owner->RequiresManualQueueSubmit()) 102 | device->CreateCommandBuffer(&s_InternalCmd); 103 | */ 104 | } 105 | 106 | ExRenderPass::~ExRenderPass() 107 | { 108 | auto device = m_Owner->GetGraphicsDevice(); 109 | 110 | vkDeviceWaitIdle(device->GetLogical()); 111 | 112 | for (auto& shader : s_Shaders) 113 | device->ReleaseShaders ({ &shader.second }); 114 | 115 | for (auto& image : s_Images) 116 | device->ReleaseImages ({ &image.second }); 117 | 118 | for (auto& buffer : s_Buffers) 119 | device->ReleaseBuffers ({ &buffer.second }); 120 | } 121 | 122 | static void GetViewportScissor(HdRenderPassStateSharedPtr const& renderPassState, VkRect2D* scissor, VkViewport* viewport) 123 | { 124 | const CameraUtilFraming &framing = renderPassState->GetFraming(); 125 | 126 | GfRect2i dataWindow; 127 | 128 | if (framing.IsValid()) 129 | dataWindow = framing.dataWindow; 130 | else 131 | { 132 | // For applications that use the old viewport API instead of 133 | // the new camera framing API. 134 | const GfVec4f vp = renderPassState->GetViewport(); 135 | dataWindow = GfRect2i(GfVec2i(0), int(vp[2]), int(vp[3])); 136 | } 137 | 138 | if (dataWindow.GetWidth() <= 1 && dataWindow.GetHeight() <= 1) 139 | { 140 | struct { GLint x, y, w, h; } viewport; 141 | glGetIntegerv(GL_VIEWPORT, (GLint*)&viewport); 142 | 143 | // Final attempt if the viewport is still invalid, fetch it from GL (necesarry for Houdini). 144 | dataWindow = GfRect2i(GfVec2i(0), int(viewport.w), int(viewport.h)); 145 | } 146 | 147 | viewport->x = 0.0; 148 | viewport->y = 0.0; 149 | viewport->width = (float)dataWindow.GetWidth(); 150 | viewport->height = (float)dataWindow.GetHeight(); 151 | viewport->minDepth = 0.0; 152 | viewport->maxDepth = 1.0; 153 | 154 | 155 | scissor->offset.x = 0; 156 | scissor->offset.y = 0; 157 | scissor->extent.width = (uint32_t)dataWindow.GetWidth(); 158 | scissor->extent.height = (uint32_t)dataWindow.GetHeight(); 159 | 160 | } 161 | 162 | void ExRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState, TfTokenVector const &renderTags) 163 | { 164 | // Grab a handle to the device. 165 | Device* device = m_Owner->GetGraphicsDevice(); 166 | 167 | // grab a handle to the current frame. 168 | Frame* frame = m_Owner->GetRenderSetting(TfToken("CurrentFrame")).UncheckedGet(); 169 | 170 | VkRect2D currentScissor; 171 | VkViewport currentViewport; 172 | GetViewportScissor(renderPassState, ¤tScissor, ¤tViewport); 173 | 174 | if (s_PreviousViewport.width != currentViewport.width || 175 | s_PreviousViewport.height != currentViewport.height) 176 | { 177 | CreateViewportSizedResources(device, currentViewport); 178 | s_PreviousViewport = currentViewport; 179 | } 180 | 181 | // Create necesarry backbuffers if needed 182 | static bool bCreatedGLObjects = false; 183 | 184 | if (m_Owner->RequiresManualQueueSubmit() && !bCreatedGLObjects) 185 | { 186 | glewInit(); 187 | 188 | GLint currentFramebuffer; 189 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFramebuffer); 190 | 191 | std::vector imageData(4 * currentViewport.width * currentViewport.height); 192 | glGenTextures(1, &s_GLBackbufferImage); 193 | glBindTexture(GL_TEXTURE_2D, s_GLBackbufferImage); 194 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, currentViewport.width, currentViewport.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData.data()); 195 | 196 | glGenFramebuffers(1, &s_GLBackbufferObject); 197 | glBindFramebuffer(GL_READ_FRAMEBUFFER, s_GLBackbufferObject); 198 | glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_GLBackbufferImage, 0); 199 | 200 | glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFramebuffer); 201 | glBindTexture(GL_TEXTURE_2D, currentFramebuffer); 202 | 203 | bCreatedGLObjects = true; 204 | } 205 | 206 | VkCommandBuffer cmd; 207 | { 208 | if (m_Owner->RequiresManualQueueSubmit()) 209 | { 210 | assert(s_InternalCmd != nullptr); 211 | 212 | // Reset the command buffer for this frame. 213 | vkResetCommandBuffer(s_InternalCmd, 0x0); 214 | 215 | // Enable the command buffer into a recording state. 216 | VkCommandBufferBeginInfo commandBegin = {}; 217 | 218 | commandBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 219 | commandBegin.flags = 0; 220 | commandBegin.pInheritanceInfo = nullptr; 221 | 222 | vkBeginCommandBuffer(s_InternalCmd, &commandBegin); 223 | 224 | cmd = s_InternalCmd; 225 | } 226 | else 227 | { 228 | assert(frame != nullptr); 229 | 230 | // A frame command buffer will be provided in a reset + record-ready state. 231 | cmd = frame->commandBuffer; 232 | } 233 | } 234 | 235 | Image::TransferUnknownToWrite(cmd, s_Images[ImageID::COLOR].GetData()->image); 236 | 237 | VkRenderingAttachmentInfoKHR colorAttachment = {}; 238 | colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR; 239 | colorAttachment.imageView = s_Images[ImageID::COLOR].GetData()->view; 240 | colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 241 | colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 242 | colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 243 | colorAttachment.clearValue.color = { 1, 0, 0, 1 }; 244 | 245 | 246 | VkRenderingInfoKHR renderInfo = {}; 247 | renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR; 248 | renderInfo.renderArea = currentScissor; 249 | renderInfo.layerCount = 1; 250 | renderInfo.colorAttachmentCount = 1; 251 | renderInfo.pColorAttachments = &colorAttachment; 252 | renderInfo.pDepthAttachment = nullptr; 253 | renderInfo.pStencilAttachment = nullptr; 254 | 255 | 256 | // Write commands for this frame. 257 | #if __APPLE__ 258 | Device::vkCmdBeginRenderingKHR(cmd, &renderInfo); 259 | #else 260 | vkCmdBeginRendering(cmd, &renderInfo); 261 | #endif 262 | 263 | /* 264 | Shader::Bind(cmd, s_Shaders[ShaderID::MESH_VS]); 265 | Shader::Bind(cmd, s_Shaders[ShaderID::UNLIT_PS]); 266 | 267 | // Configure render state 268 | Device::SetDefaultRenderState(cmd); 269 | 270 | if (m_Owner->RequiresManualQueueSubmit()) 271 | { 272 | VkViewport flippedViewport = currentViewport; 273 | { 274 | flippedViewport.height = -currentViewport.height; 275 | flippedViewport.y = currentViewport.height; 276 | } 277 | 278 | Device::vkCmdSetViewportWithCountEXT(cmd, 1u, &flippedViewport); 279 | Device::vkCmdSetFrontFaceEXT(cmd, VK_FRONT_FACE_COUNTER_CLOCKWISE); 280 | } 281 | else 282 | Device::vkCmdSetViewportWithCountEXT(cmd, 1u, ¤tViewport); 283 | 284 | 285 | Device::vkCmdSetScissorWithCountEXT(cmd, 1u, ¤tScissor); 286 | 287 | vkCmdDraw(cmd, 3u, 1u, 0u, 0u); 288 | */ 289 | 290 | #if __APPLE__ 291 | Device::vkCmdEndRenderingKHR(cmd); 292 | #else 293 | vkCmdEndRendering(cmd); 294 | #endif 295 | 296 | // Conclude internal command buffer recording. 297 | if (m_Owner->RequiresManualQueueSubmit()) 298 | { 299 | // Prepare internal color target for copy. 300 | Image::TransferWriteToSource(cmd, s_Images[ImageID::COLOR].GetData()->image); 301 | 302 | // Transfer the internal color target to staging buffer memory that will be mapped after the command is executed. 303 | Buffer::CopyImage(cmd, &s_Images[ImageID::COLOR], &s_Buffers[BufferID::COLOR_MAP_STAGING]); 304 | 305 | // Conclude internal command rendering. 306 | vkEndCommandBuffer(cmd); 307 | 308 | VkSubmitInfo submitInfo = {}; 309 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 310 | submitInfo.commandBufferCount = 1u; 311 | submitInfo.pCommandBuffers = &cmd; 312 | 313 | 314 | // Submit the the internal command to graphics queue. 315 | vkQueueSubmit(device->GetGraphicsQueue(), 1u, &submitInfo, nullptr); 316 | 317 | // Wait until the work is done. 318 | vkDeviceWaitIdle(device->GetLogical()); 319 | 320 | // Currently Hydra does not really make it easy to share memory on the device-side. 321 | // So we need to have a round trip copy via the CPU to the current GL backbuffer. 322 | 323 | VmaAllocationInfo allocInfo; 324 | vmaGetAllocationInfo(device->GetAllocator(), s_Buffers[BufferID::COLOR_MAP_STAGING].GetData()->allocation, &allocInfo); 325 | 326 | auto mappedData = std::vector(allocInfo.size); 327 | vmaCopyAllocationToMemory(device->GetAllocator(), s_Buffers[BufferID::COLOR_MAP_STAGING].GetData()->allocation, 0u, mappedData.data(), allocInfo.size); 328 | 329 | #if 0 330 | // Copy the mapped data into the internal backbuffer image data. 331 | WritePNG("/Users/johnparsaie/Development/test.png", currentViewport.width, currentViewport.height, 4u, mappedData.data(), 4u); 332 | #endif 333 | 334 | GLint currentFramebuffer; 335 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFramebuffer); 336 | 337 | // Bind our internal FBO for write. 338 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_GLBackbufferObject); 339 | 340 | // Copy the mapped data into the internal backbuffer image data. 341 | glBindTexture(GL_TEXTURE_2D, s_GLBackbufferImage); 342 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, currentViewport.width, currentViewport.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, mappedData.data()); 343 | 344 | // Blit the internal backbuffer into the host one. 345 | glBindFramebuffer(GL_READ_FRAMEBUFFER, s_GLBackbufferObject); 346 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFramebuffer); 347 | 348 | glBlitFramebuffer(0, 0, currentViewport.width, currentViewport.height, 349 | 0, 0, currentViewport.width, currentViewport.height, 350 | GL_COLOR_BUFFER_BIT, GL_NEAREST); 351 | 352 | glBindFramebuffer(GL_READ_FRAMEBUFFER, currentFramebuffer); 353 | } 354 | else 355 | { 356 | // Otherwise we can copy the image memory directly to the back buffer. 357 | 358 | Image::TransferWriteToSource(cmd, s_Images[ImageID::COLOR].GetData()->image); 359 | Image::TransferUnknownToDestination(cmd, frame->backBuffer); 360 | 361 | VkImageCopy copyRegion = {}; 362 | copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 363 | copyRegion.srcSubresource.layerCount = 1; 364 | copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 365 | copyRegion.dstSubresource.layerCount = 1; 366 | copyRegion.extent.width = (uint32_t)currentScissor.extent.width; 367 | copyRegion.extent.height = (uint32_t)currentScissor.extent.height; 368 | copyRegion.extent.depth = 1; 369 | 370 | // Else just copy the color target into the frame-provided backbuffer. 371 | vkCmdCopyImage(cmd, 372 | s_Images[ImageID::COLOR].GetData()->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 373 | frame->backBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 374 | 1u, ©Region); 375 | 376 | // After copying we prepare the backbuffer for swapchain present. 377 | Image::TransferDestinationToPresent(cmd, frame->backBuffer); 378 | } 379 | } -------------------------------------------------------------------------------- /Source/ExRendererPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TF_REGISTRY_FUNCTION(TfType) 5 | { 6 | // Register the plugin with the renderer plugin system. 7 | HdRendererPluginRegistry::Define(); 8 | } 9 | 10 | HdRenderDelegate* RendererPlugin::CreateRenderDelegate() 11 | { 12 | return new ExRenderDelegate(); 13 | } 14 | 15 | HdRenderDelegate* RendererPlugin::CreateRenderDelegate(HdRenderSettingsMap const& settingsMap) 16 | { 17 | return new ExRenderDelegate(settingsMap); 18 | } 19 | 20 | void RendererPlugin::DeleteRenderDelegate(HdRenderDelegate *renderDelegate) 21 | { 22 | delete renderDelegate; 23 | } 24 | 25 | bool RendererPlugin::IsSupported(bool /* gpuEnabled */) const 26 | { 27 | // Nothing more to check for now, we assume if the plugin loads correctly 28 | // it is supported. 29 | return true; 30 | } -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/ExMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH 2 | #define MESH 3 | 4 | #include "PxrUsage.h" 5 | 6 | PXR_NAMESPACE_USING_DIRECTIVE 7 | 8 | /// \class Mesh 9 | /// 10 | /// This class is an example of a Hydra Rprim, or renderable object, and it 11 | /// gets created on a call to HdRenderIndex::InsertRprim() with a type of 12 | /// HdPrimTypeTokens->mesh. 13 | /// 14 | /// The prim object's main function is to bridge the scene description and the 15 | /// renderable representation. The Hydra image generation algorithm will call 16 | /// HdRenderIndex::SyncAll() before any drawing; this, in turn, will call 17 | /// Sync() for each mesh with new data. 18 | /// 19 | /// Sync() is passed a set of dirtyBits, indicating which scene buffers are 20 | /// dirty. It uses these to pull all of the new scene data and constructs 21 | /// updated geometry objects. 22 | /// 23 | /// An rprim's state is lazily populated in Sync(); matching this, Finalize() 24 | /// can do the heavy work of releasing state (such as handles into the top-level 25 | /// scene), so that object population and existence aren't tied to each other. 26 | /// 27 | class ExMesh final : public HdMesh 28 | { 29 | public: 30 | HF_MALLOC_TAG_NEW("new Mesh"); 31 | 32 | ExMesh(SdfPath const& id); 33 | ~ExMesh() override = default; 34 | 35 | /// Inform the scene graph which state needs to be downloaded in the 36 | /// first Sync() call: in this case, topology and points data to build 37 | /// the geometry object in the scene graph. 38 | /// \return The initial dirty state this mesh wants to query. 39 | HdDirtyBits GetInitialDirtyBitsMask() const override; 40 | 41 | /// Pull invalidated scene data and prepare/update the renderable 42 | /// representation. 43 | /// 44 | /// This function is told which scene data to pull through the 45 | /// dirtyBits parameter. The first time it's called, dirtyBits comes 46 | /// from _GetInitialDirtyBits(), which provides initial dirty state, 47 | /// but after that it's driven by invalidation tracking in the scene 48 | /// delegate. 49 | /// 50 | /// The contract for this function is that the prim can only pull on scene 51 | /// delegate buffers that are marked dirty. Scene delegates can and do 52 | /// implement just-in-time data schemes that mean that pulling on clean 53 | /// data will be at best incorrect, and at worst a crash. 54 | /// 55 | /// This function is called in parallel from worker threads, so it needs 56 | /// to be threadsafe; calls into HdSceneDelegate are ok. 57 | /// 58 | /// Reprs are used by hydra for controlling per-item draw settings like 59 | /// flat/smooth shaded, wireframe, refined, etc. 60 | /// \param sceneDelegate The data source for this geometry item. 61 | /// \param renderParam State. 62 | /// \param dirtyBits A specifier for which scene data has changed. 63 | /// \param reprToken A specifier for which representation to draw with. 64 | /// 65 | void Sync( 66 | HdSceneDelegate* sceneDelegate, 67 | HdRenderParam* renderParam, 68 | HdDirtyBits* dirtyBits, 69 | TfToken const &reprToken) override; 70 | 71 | protected: 72 | // Initialize the given representation of this Rprim. 73 | // This is called prior to syncing the prim, the first time the repr 74 | // is used. 75 | // 76 | // reprToken is the name of the repr to initalize. 77 | // 78 | // dirtyBits is an in/out value. It is initialized to the dirty bits 79 | // from the change tracker. InitRepr can then set additional dirty bits 80 | // if additional data is required from the scene delegate when this 81 | // repr is synced. InitRepr occurs before dirty bit propagation. 82 | // 83 | // See HdRprim::InitRepr() 84 | void _InitRepr(TfToken const &reprToken, HdDirtyBits *dirtyBits) override; 85 | 86 | // This callback from Rprim gives the prim an opportunity to set 87 | // additional dirty bits based on those already set. This is done 88 | // before the dirty bits are passed to the scene delegate, so can be 89 | // used to communicate that extra information is needed by the prim to 90 | // process the changes. 91 | // 92 | // The return value is the new set of dirty bits, which replaces the bits 93 | // passed in. 94 | // 95 | // See HdRprim::PropagateRprimDirtyBits() 96 | HdDirtyBits _PropagateDirtyBits(HdDirtyBits bits) const override; 97 | }; 98 | 99 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/ExRenderBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_BUFFER 2 | #define RENDER_BUFFER 3 | 4 | #include "PxrUsage.h" 5 | 6 | PXR_NAMESPACE_USING_DIRECTIVE 7 | 8 | class VkImage; 9 | class VkImageView; 10 | 11 | class ExRenderBuffer final : public HdRenderBuffer 12 | { 13 | public: 14 | 15 | /// Get allocation information from the scene delegate. 16 | /// Note: Embree overrides this only to stop the render thread before 17 | /// potential re-allocation. 18 | /// \param sceneDelegate The scene delegate backing this render buffer. 19 | /// \param renderParam The renderer-global render param. 20 | /// \param dirtyBits The invalidation state for this render buffer. 21 | void Sync(HdSceneDelegate *sceneDelegate, 22 | HdRenderParam *renderParam, 23 | HdDirtyBits *dirtyBits) override; 24 | 25 | /// Deallocate before deletion. 26 | /// \param renderParam The renderer-global render param. 27 | /// Note: Embree overrides this only to stop the render thread before 28 | /// potential deallocation. 29 | void Finalize(HdRenderParam *renderParam) override; 30 | 31 | /// Allocate a new buffer with the given dimensions and format. 32 | /// \param dimensions Width, height, and depth of the desired buffer. 33 | /// (Only depth==1 is supported). 34 | /// \param format The format of the desired buffer, taken from the 35 | /// HdFormat enum. 36 | /// \param multisampled Whether the buffer is multisampled. 37 | /// \return True if the buffer was successfully allocated, 38 | /// false with a warning otherwise. 39 | bool Allocate(GfVec3i const& dimensions, 40 | HdFormat format, 41 | bool multiSampled) override; 42 | 43 | /// Accessor for buffer width. 44 | /// \return The width of the currently allocated buffer. 45 | unsigned int GetWidth() const override; 46 | 47 | /// Accessor for buffer height. 48 | /// \return The height of the currently allocated buffer. 49 | unsigned int GetHeight() const override; 50 | 51 | /// Accessor for buffer depth. 52 | /// \return The depth of the currently allocated buffer. 53 | unsigned int GetDepth() const override; 54 | 55 | /// Accessor for buffer format. 56 | /// \return The format of the currently allocated buffer. 57 | HdFormat GetFormat() const override; 58 | 59 | /// Accessor for the buffer multisample state. 60 | /// \return Whether the buffer is multisampled or not. 61 | bool IsMultiSampled() const override; 62 | 63 | /// Map the buffer for reading/writing. The control flow should be Map(), 64 | /// before any I/O, followed by memory access, followed by Unmap() when 65 | /// done. 66 | /// \return The address of the buffer. 67 | void* Map() override; 68 | 69 | /// Unmap the buffer. 70 | void Unmap() override; 71 | 72 | /// Return whether any clients have this buffer mapped currently. 73 | /// \return True if the buffer is currently mapped by someone. 74 | bool IsMapped() const override; 75 | 76 | /// Is the buffer converged? 77 | /// \return True if the buffer is converged (not currently being 78 | /// rendered to). 79 | bool IsConverged() const override; 80 | 81 | /// Set the convergence. 82 | /// \param cv Whether the buffer should be marked converged or not. 83 | void SetConverged(bool cv); 84 | 85 | /// Resolve the sample buffer into final values. 86 | void Resolve() override; 87 | 88 | private: 89 | 90 | // Release any allocated resources. 91 | void _Deallocate() override; 92 | }; 93 | 94 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/ExRenderDelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERER_DELEGATE 2 | #define RENDERER_DELEGATE 3 | 4 | #include "PxrUsage.h" 5 | 6 | PXR_NAMESPACE_USING_DIRECTIVE 7 | 8 | namespace VulkanWrappers 9 | { 10 | class Device; 11 | } 12 | 13 | class ExRenderDelegate final : public HdRenderDelegate 14 | { 15 | public: 16 | 17 | // Render Delegate Implementation 18 | // ---------------------------------- 19 | 20 | /// Render delegate constructor. 21 | ExRenderDelegate(); 22 | 23 | /// Render delegate constructor. 24 | ExRenderDelegate(HdRenderSettingsMap const& settingsMap); 25 | 26 | /// Render delegate destructor. 27 | virtual ~ExRenderDelegate(); 28 | 29 | void SetDrivers(HdDriverVector const& drivers) override; 30 | 31 | /// Supported types 32 | const TfTokenVector &GetSupportedRprimTypes() const override; 33 | const TfTokenVector &GetSupportedSprimTypes() const override; 34 | const TfTokenVector &GetSupportedBprimTypes() const override; 35 | 36 | // Basic value to return from the RD 37 | HdResourceRegistrySharedPtr GetResourceRegistry() const override; 38 | 39 | // Prims 40 | HdRenderPassSharedPtr CreateRenderPass(HdRenderIndex *index, HdRprimCollection const& collection) override; 41 | 42 | HdInstancer *CreateInstancer(HdSceneDelegate *delegate, SdfPath const& id) override; 43 | void DestroyInstancer(HdInstancer *instancer) override; 44 | 45 | HdRprim *CreateRprim(TfToken const& typeId, SdfPath const& rprimId) override; 46 | void DestroyRprim(HdRprim *rPrim) override; 47 | 48 | HdSprim *CreateSprim(TfToken const& typeId, SdfPath const& sprimId) override; 49 | HdSprim *CreateFallbackSprim(TfToken const& typeId) override; 50 | void DestroySprim(HdSprim *sprim) override; 51 | 52 | HdBprim *CreateBprim(TfToken const& typeId, SdfPath const& bprimId) override; 53 | HdBprim *CreateFallbackBprim(TfToken const& typeId) override; 54 | void DestroyBprim(HdBprim *bprim) override; 55 | 56 | void CommitResources(HdChangeTracker *tracker) override; 57 | 58 | HdRenderParam *GetRenderParam() const override; 59 | 60 | // Utility 61 | // --------------------------- 62 | 63 | inline VulkanWrappers::Device* GetGraphicsDevice() { return m_GraphicsDevice; } 64 | 65 | // If the delegate owns the graphics device, we will need to submit commands ourselves. 66 | inline bool RequiresManualQueueSubmit() { return m_DefaultGraphicsDevice.get() != nullptr; } 67 | 68 | private: 69 | 70 | static const TfTokenVector SUPPORTED_RPRIM_TYPES; 71 | static const TfTokenVector SUPPORTED_SPRIM_TYPES; 72 | static const TfTokenVector SUPPORTED_BPRIM_TYPES; 73 | 74 | void _Initialize(); 75 | 76 | // Default delegate-managed device resource that is created if the application doesn't provide one. 77 | std::unique_ptr m_DefaultGraphicsDevice; 78 | 79 | VulkanWrappers::Device* m_GraphicsDevice; 80 | 81 | HdResourceRegistrySharedPtr _resourceRegistry; 82 | }; 83 | 84 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/ExRenderPass.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_PASS 2 | #define RENDER_PASS 3 | 4 | #include "PxrUsage.h" 5 | 6 | PXR_NAMESPACE_USING_DIRECTIVE 7 | 8 | class ExRenderDelegate; 9 | 10 | /// \class RenderPass 11 | /// 12 | /// HdRenderPass represents a single render iteration, rendering a view of the 13 | /// scene (the HdRprimCollection) for a specific viewer (the camera/viewport 14 | /// parameters in HdRenderPassState) to the current draw target. 15 | /// 16 | class ExRenderPass final : public HdRenderPass 17 | { 18 | public: 19 | /// Renderpass constructor. 20 | /// \param index The render index containing scene data to render. 21 | /// \param collection The initial rprim collection for this renderpass. 22 | ExRenderPass(HdRenderIndex *index, HdRprimCollection const &collection, ExRenderDelegate* renderDelegate); 23 | 24 | /// Renderpass destructor. 25 | virtual ~ExRenderPass(); 26 | 27 | protected: 28 | 29 | /// Draw the scene with the bound renderpass state. 30 | /// \param renderPassState Input parameters (including viewer parameters) 31 | /// for this renderpass. 32 | /// \param renderTags Which rendertags should be drawn this pass. 33 | void _Execute(HdRenderPassStateSharedPtr const& renderPassState, TfTokenVector const &renderTags) override; 34 | 35 | private: 36 | ExRenderDelegate* m_Owner; 37 | }; 38 | 39 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/ExRendererPlugin.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERER_PLUGIN 2 | #define RENDERER_PLUGIN 3 | 4 | #include "PxrUsage.h" 5 | 6 | PXR_NAMESPACE_USING_DIRECTIVE 7 | 8 | class RendererPlugin final : public HdRendererPlugin 9 | { 10 | public: 11 | RendererPlugin() = default; 12 | virtual ~RendererPlugin() = default; 13 | 14 | /// Construct a new render delegate of type HdTinyRenderDelegate. 15 | virtual HdRenderDelegate *CreateRenderDelegate() override; 16 | 17 | /// Construct a new render delegate of type HdTinyRenderDelegate. 18 | virtual HdRenderDelegate *CreateRenderDelegate(HdRenderSettingsMap const& settingsMap) override; 19 | 20 | /// Destroy a render delegate created by this class's CreateRenderDelegate. 21 | virtual void DeleteRenderDelegate(HdRenderDelegate *renderDelegate) override; 22 | 23 | /// Checks to see if the plugin is supported on the running system. 24 | virtual bool IsSupported(bool gpuEnabled = true) const override; 25 | }; 26 | 27 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/PxrUsage.h: -------------------------------------------------------------------------------- 1 | #ifndef PXR_USAGE 2 | #define PXR_USAGE 3 | 4 | #ifdef __APPLE__ 5 | // MacOS fix for bug inside USD. 6 | #define unary_function __unary_function 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | // Imaging (Hydra) 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #endif -------------------------------------------------------------------------------- /Source/Include/ExampleDelegate/StbUsage.h: -------------------------------------------------------------------------------- 1 | #ifndef STB_USAGE 2 | #define STB_USAGE 3 | 4 | void WritePNG(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 5 | 6 | #endif -------------------------------------------------------------------------------- /Source/PxrUsage.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /Source/StbUsage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define STB_IMAGE_WRITE_IMPLEMENTATION 4 | #include 5 | 6 | void WritePNG(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes) 7 | { 8 | stbi_write_png(filename, w, h, comp, data, stride_in_bytes); 9 | } -------------------------------------------------------------------------------- /Source/plugInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugins": [ 3 | { 4 | "Info": { 5 | "Types": { 6 | "RendererPlugin": { 7 | "bases": [ 8 | "HdRendererPlugin" 9 | ], 10 | "displayName": "Manwe Renderer", 11 | "priority": 10 12 | } 13 | } 14 | }, 15 | "LibraryPath": "../libExampleHydraRenderDelegate.dylib", 16 | "Name": "hdExample", 17 | "ResourcePath": "resources", 18 | "Root": "..", 19 | "Type": "library" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /StandaloneTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TEST_NAME StandaloneTest) 2 | project(${TEST_NAME}) 3 | 4 | # Library 5 | # -------------------------------------------------- 6 | 7 | add_executable(${TEST_NAME} "StandaloneTest.cpp") 8 | 9 | # Include 10 | # -------------------------------------------------- 11 | 12 | target_include_directories (${TEST_NAME} PRIVATE ${Vulkan_INCLUDE_DIRS}) 13 | target_include_directories (${TEST_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/External/VulkanWrappers/Include) 14 | target_include_directories (${TEST_NAME} PRIVATE ${PXR_INCLUDE_DIRS}) 15 | 16 | # Link 17 | # -------------------------------------------------- 18 | 19 | target_link_libraries (${TEST_NAME} ${Vulkan_LIBRARIES}) 20 | target_link_libraries (${TEST_NAME} ${PXR_LIBRARIES}) 21 | target_link_libraries (${TEST_NAME} OpenGL::GL) 22 | target_link_libraries (${TEST_NAME} GLEW::GLEW) 23 | target_link_libraries (${TEST_NAME} VulkanWrappers) -------------------------------------------------------------------------------- /StandaloneTest/StandaloneTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define TBB_USE_DEBUG 1 12 | 13 | #ifdef __APPLE__ 14 | // MacOS fix for bug inside USD. 15 | #define unary_function __unary_function 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | // Hydra Core 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // HDX (Hydra Utilities) 29 | #include 30 | #include 31 | 32 | // USD Hydra Scene Delegate Implementation. 33 | #include 34 | 35 | PXR_NAMESPACE_USING_DIRECTIVE 36 | 37 | using namespace VulkanWrappers; 38 | 39 | // Implementation 40 | // --------------------- 41 | 42 | int main(int argc, char **argv, char **envp) 43 | { 44 | // Create window. 45 | Window window("Standalone Vulkan Hydra Executable", 800, 600); 46 | 47 | // Initialize graphics device usage with the window. 48 | Device device(&window); 49 | 50 | // Load Render Plugin 51 | // --------------------- 52 | 53 | // NOTE: For GetRendererPlugin() to successfully find the token, ensure the PXR_PLUGINPATH_NAME env variable is set. 54 | // NOTE: Technically since we are linked with the ExampleDelegate we can just directly instantiate one here but we don't for demo purpose. 55 | HdRendererPlugin *rendererPlugin = HdRendererPluginRegistry::GetInstance().GetRendererPlugin(TfToken("RendererPlugin")); 56 | TF_VERIFY(rendererPlugin != nullptr); 57 | 58 | // Create render delegate instance from the plugin. 59 | // --------------------- 60 | 61 | HdRenderDelegate *renderDelegate = rendererPlugin->CreateRenderDelegate(); 62 | TF_VERIFY(renderDelegate != nullptr); 63 | 64 | // Wrap our vulkan device implementation in an HDDriver 65 | // We could use the OpenUSD Hydra "HGI" here but for learning purposes I would rather use a from-scratch implementation. 66 | // --------------------- 67 | 68 | HdDriver customDriver{TfToken("CustomVulkanDevice"), VtValue(&device)}; 69 | 70 | // Create render index from the delegate. 71 | // --------------------- 72 | 73 | HdRenderIndex *renderIndex = HdRenderIndex::New(renderDelegate, { &customDriver }); 74 | TF_VERIFY(renderIndex != nullptr); 75 | 76 | // Construct a scene delegate from the stock OpenUSD scene delegate implementation. 77 | // --------------------- 78 | 79 | UsdImagingDelegate *sceneDelegate = new UsdImagingDelegate(renderIndex, SdfPath::AbsoluteRootPath()); 80 | TF_VERIFY(sceneDelegate != nullptr); 81 | 82 | // Load a USD Stage. 83 | // --------------------- 84 | 85 | UsdStageRefPtr usdStage = pxr::UsdStage::Open("C:/Users/parsa/OneDrive/Desktop/pig_head.usd"); 86 | TF_VERIFY(usdStage != nullptr); 87 | 88 | // Pipe the USD stage into the scene delegate (will create render primitives in the render delegate). 89 | // --------------------- 90 | 91 | sceneDelegate->Populate(usdStage->GetPseudoRoot()); 92 | 93 | // Create the render tasks. 94 | // --------------------- 95 | 96 | HdxTaskController taskController(renderIndex, SdfPath("/taskController")); 97 | { 98 | auto params = HdxRenderTaskParams(); 99 | { 100 | params.viewport = GfVec4i(0, 0, 800, 600); 101 | } 102 | 103 | // The "Task Controller" will automatically configure an HdxRenderTask 104 | // (which will create and invoke our delegate's renderpass). 105 | taskController.SetRenderParams(params); 106 | taskController.SetRenderViewport({ 0, 0, 800, 600 }); 107 | } 108 | 109 | // Initialize the Hydra engine. 110 | // --------------------- 111 | 112 | HdEngine engine; 113 | 114 | // Render-loop 115 | // --------------------- 116 | 117 | // Handle to current frame to write commands to. 118 | Frame frame; 119 | 120 | while (window.NextFrame(&device, &frame)) 121 | { 122 | // Forward the current backbuffer and commandbuffer to the delegate. 123 | // This feels quite hacky, open to suggestions. 124 | // There might be a simpler way to manage this by writing my own HdTask, but 125 | // it would require sacrificing the simplicity that HdxTaskController offers. 126 | renderDelegate->SetRenderSetting(TfToken("CurrentFrame"), VtValue(&frame)); 127 | 128 | // Invoke Hydra! 129 | auto renderTasks = taskController.GetRenderingTasks(); 130 | engine.Execute(renderIndex, &renderTasks); 131 | 132 | window.SubmitFrame(&device, &frame); 133 | } 134 | 135 | return 0; 136 | } -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "000d1bda1ffa95a73e0b40334fa4103d6f4d3d48", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": 3 | [ 4 | { "name": "glfw3" }, 5 | { "name": "glew" } 6 | ] 7 | } 8 | --------------------------------------------------------------------------------