3616 lines · 155.1 KB · C++
RawDownload
////  Created by Bradley Austin Davis on 2016/08/07//  Adapted for Vulkan in 2022-2025 by dr Karol Suprynowicz.//  Copyright 2013-2018 High Fidelity, Inc.//  Copyright 2023-2025 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0//#include "VKBackend.h" #include <mutex>#include <queue>#include <list>#include <functional>#include <glm/gtc/type_ptr.hpp> #include <QtCore/QProcessEnvironment> // For hash_combine#include <RegisteredMetaTypes.h> #include <gpu/TextureTable.h>#include <vk/Helpers.h>#include <vk/Version.h>#include <vk/Pipelines.h>#include "VKFramebuffer.h"#include "VKBuffer.h"#include "VKQuery.h" #include "VKForward.h"#include "VKShared.h"#include "VKTexture.h"#include "VKPipelineCache.h"#include <render-utils/ShaderConstants.h>#include "shared/FileUtils.h" #define FORCE_STRICT_TEXTURE 1 using namespace gpu;using namespace gpu::vk; // VKTODO: this is ugly solutionCache _cache; size_t VKBackend::UNIFORM_BUFFER_OFFSET_ALIGNMENT{ 4 }; static VKBackend* INSTANCE{ nullptr };static const char* VK_BACKEND_PROPERTY_NAME = "com.highfidelity.vk.backend"; BackendPointer VKBackend::createBackend() {    // FIXME provide a mechanism to override the backend for testing    // Where the gpuContext is initialized and where the TRUE Backend is created and assigned    std::shared_ptr<VKBackend> result = std::make_shared<VKBackend>();    INSTANCE = result.get();    void* voidInstance = &(*result);    qApp->setProperty(VK_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance));    return result;} VKBackend& getBackend() {    if (!INSTANCE) {        INSTANCE = static_cast<VKBackend*>(qApp->property(VK_BACKEND_PROPERTY_NAME).value<void*>());    }    return *INSTANCE;} void VKBackend::init() {} VKBackend::VKBackend() {    if (!_context.instance) {#ifdef QT_NO_DEBUG        _context.setValidationEnabled(false); // VKTODO: find nicer way of toggling this#else        _context.setValidationEnabled(true); // VKTODO: find nicer way of toggling this#endif        _context.createInstance();        _context.createDevice();    }     {        VkPipelineCacheCreateInfo createInfo{};        createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;        std::vector<uint8_t> pipelineCacheData;        if (vks::util::loadPipelineCacheData(pipelineCacheData) && !pipelineCacheData.empty()) {            createInfo.pInitialData = pipelineCacheData.data();            createInfo.initialDataSize = pipelineCacheData.size();        }         vkCreatePipelineCache(_context.device->logicalDevice, &createInfo, nullptr, &_pipelineCache);    }     // Get the graphics queue    qCDebug(gpu_vk_logging) << "VK Version:     " << vks::Version(_context.device->properties.apiVersion).toString().c_str();    qCDebug(gpu_vk_logging) << "VK Driver:      " << vks::Version(_context.device->properties.driverVersion).toString().c_str();    qCDebug(gpu_vk_logging) << "VK Vendor ID:   " << _context.device->properties.vendorID;    qCDebug(gpu_vk_logging) << "VK Device ID:   " << _context.device->properties.deviceID;    qCDebug(gpu_vk_logging) << "VK Device Name: " << _context.device->properties.deviceName;    qCDebug(gpu_vk_logging) << "VK Device Type: " << _context.device->properties.deviceType;     // Add frame data to frames pool    for (int i = 0; i < 3; i++) {        _framePool.push_back(std::make_shared<FrameData>(this));        _framesToReuse.push_back(_framePool.back());    }} VKBackend::~VKBackend() {    Q_ASSERT(isBackendShutdownComplete);    // FIXME queue up all the trash calls    _context.destroyContext();} void VKBackend::shutdown() {    VK_CHECK_RESULT(vkQueueWaitIdle(_context.graphicsQueue));    VK_CHECK_RESULT(vkQueueWaitIdle(_context.transferQueue) );    VK_CHECK_RESULT(vkDeviceWaitIdle(_context.device->logicalDevice));     killTextureManagementStage();     _context.shutdownWindows();     // Release frames so their destructors can do a cleanup    _currentFrame.reset();    _framesToReuse.resize(0);    _framePool.resize(0);     for (auto &module : _cache.moduleMap) {        _context.recycler.trashVkShaderModule(module.second);    }    _cache.moduleMap.clear();     for (auto &renderPass : _cache.pipelineState._renderPassMap) {        _context.recycler.trashVkRenderPass(renderPass.second);    }    _cache.pipelineState._renderPassMap.clear();     for (auto &pipeline : _cache.pipelineMap) {        _context.recycler.trashVkDescriptorSetLayout(pipeline.second.uniformLayout);        _context.recycler.trashVkDescriptorSetLayout(pipeline.second.storageLayout);        _context.recycler.trashVkDescriptorSetLayout(pipeline.second.textureLayout);        _context.recycler.trashVkPipelineLayout(pipeline.second.pipelineLayout);        _context.recycler.trashVkPipeline(pipeline.second.pipeline);    }    _cache.pipelineMap.clear();     beforeShutdownCleanup();     {        size_t pipelineCacheDataSize;        VK_CHECK_RESULT(vkGetPipelineCacheData(_context.device->logicalDevice, _pipelineCache, &pipelineCacheDataSize, nullptr));        std::vector<uint8_t> pipelineCacheData;        pipelineCacheData.resize(pipelineCacheDataSize);        VK_CHECK_RESULT(vkGetPipelineCacheData(_context.device->logicalDevice, _pipelineCache, &pipelineCacheDataSize, nullptr));        if (!pipelineCacheData.empty()) {            vks::util::savePipelineCacheData(pipelineCacheData);        }    }    vkDestroyPipelineCache(_context.device->logicalDevice, _pipelineCache, nullptr);    vmaDestroyAllocator(vks::Allocation::getAllocator());     isBackendShutdownComplete = true;} const std::string& VKBackend::getVersion() const {    static const std::string VERSION{ "VK1.1" };    return VERSION;} bool VKBackend::isTextureManagementSparseEnabled() const {    return _context.device->features.sparseResidencyImage2D == VK_TRUE;} bool VKBackend::supportedTextureFormat(const gpu::Element& format) const {    switch (format.getSemantic()) {        case COMPRESSED_BC1_SRGB:        case COMPRESSED_BC1_SRGBA:        case COMPRESSED_BC3_SRGBA:        case COMPRESSED_BC4_RED:        case COMPRESSED_BC5_XY:        case COMPRESSED_BC6_RGB:        case COMPRESSED_BC7_SRGBA:            return _context.device->features.textureCompressionBC == VK_TRUE;         case COMPRESSED_ETC2_RGB:        case COMPRESSED_ETC2_SRGB:        case COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA:        case COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA:        case COMPRESSED_ETC2_RGBA:        case COMPRESSED_ETC2_SRGBA:        case COMPRESSED_EAC_RED:        case COMPRESSED_EAC_RED_SIGNED:        case COMPRESSED_EAC_XY:        case COMPRESSED_EAC_XY_SIGNED:            return _context.device->features.textureCompressionETC2 == VK_TRUE;             // VKTODO:            //case COMPRESSED_ASTC_RGBA_10x10:            //case COMPRESSED_ASTC_RGBA_10x5:            //case COMPRESSED_ASTC_RGBA_10x6:            //case COMPRESSED_ASTC_RGBA_10x8:            //case COMPRESSED_ASTC_RGBA_12x10:            //case COMPRESSED_ASTC_RGBA_12x12:            //case COMPRESSED_ASTC_RGBA_4x4:            //case COMPRESSED_ASTC_RGBA_5x4:            //case COMPRESSED_ASTC_RGBA_5x5:            //case COMPRESSED_ASTC_RGBA_6x5:            //case COMPRESSED_ASTC_RGBA_6x6:            //case COMPRESSED_ASTC_RGBA_8x5:            //case COMPRESSED_ASTC_RGBA_8x6:            //case COMPRESSED_ASTC_RGBA_8x8:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_10x10:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_10x5:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_10x6:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_10x8:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_12x10:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_12x12:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_4x4:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_5x4:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_5x5:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_6x5:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_6x6:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_8x5:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_8x6:            //case COMPRESSED_ASTC_SRGB8_ALPHA8_8x8:            // return _context.deviceFeatures.textureCompressionASTC_LDR == VK_TRUE;         default:            break;    }    return true;} void VKBackend::executeFrame(const FramePointer& frame) {    using namespace vks::debugutils;     // Initialize parts that cannot be initialized in default constructor.    if (!_isInitialized) {        initBeforeFirstFrame();        _isInitialized = true;    }     perFrameCleanup();    acquireFrameData();     for (const auto& batchPtr : frame->batches) {        const auto& batch = *batchPtr;        render(batch);    }     // Move pointer to current frame to property that will store it while it's being rendered and before it's recycled.    _previouslyRenderedFrame = _currentlyRenderedFrame;    _currentlyRenderedFrame = _currentFrame;    bool reportBufferUpdates = false;    if (_frameCounter % 20 == 0 && reportBufferUpdates) {    qDebug() << "Buffer transfers: transfer pass: " << _currentFrame->_bufferTransferCounterTransferPass             << " render pass: " << _currentFrame->_bufferTransferCounterRenderPass             << " Batches " << frame->batches.size();        qDebug() << "Buffer transfers: uniform: " << _currentFrame->_uniformBufferTransferCounter                 << " vertex: " << _currentFrame->_vertexBufferTransferCounter                 << " index: " << _currentFrame->_indexBufferTransferCounter                 << " resource: " << _currentFrame->_resourceBufferTransferCounter                 << " created: " << _currentFrame->_bufferCreationCounter                 << " resized: " << _currentFrame->_bufferResizeCounter                 << " bytes: " << _currentFrame->_bufferTransferredBytes;    }    releaseFrameData();    _frameCounter++;    _textureManagement._transferEngine->manageMemory();    // VKTODO: add a good way of toggling this    /*if (_frameCounter % 4000 == 0) {        dumpVmaMemoryStats();    }*/} void VKBackend::render(const Batch& batch) {    using namespace vks::debugutils;     const auto& commandBuffer = _currentCommandBuffer;     _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled();    // FIXME move this to between the transfer and draw passes, so that    // framebuffer setup can see the proper stereo state and enable things    // like foveation    // Allow the batch to override the rendering stereo settings    // for things like full framebuffer copy operations (deferred lighting passes)    bool savedStereo = _stereo._enable;    if (!batch.isStereoEnabled()) {        _stereo._enable = false;    }     // Reset jitter    _transform._projectionJitter._isEnabled = false;     // VKTODO: for debugging, don't remove yet.    /*if (batch.getName() == "SubsurfaceScattering::diffuseProfileGPU") {        continue;    }*/     cmdBeginLabel(commandBuffer, "batch:" + batch.getName(), glm::vec4{ 1, 1, 0, 1 });     {        PROFILE_RANGE(gpu_vk_detail, "Transfer");        renderPassTransfer(batch);    }     //VKTODO/*#ifdef GPU_STEREO_DRAWCALL_INSTANCED    if (_stereo.isStereo()) {        glEnable(GL_CLIP_DISTANCE0);    }#endif*/     {        PROFILE_RANGE(gpu_vk_detail, _stereo._enable ? "Render Stereo" : "Render");        renderPassDraw(batch);    }     //VKTODO/*#ifdef GPU_STEREO_DRAWCALL_INSTANCED    if (_stereo.isStereo()) {        glDisable(GL_CLIP_DISTANCE0);    }#endif*/     // VKTODO: This is a workaround to get image on screen for now. Later we need to look on how it's done on OpenGL branch and do it in a similar way.    if (batch.getName() == "Resample::run") {        _outputTexture = syncGPUObject(_cache.pipelineState.framebuffer);    }    if (batch.getName() == "CompositeHUD") {        _outputTexture = syncGPUObject(_cache.pipelineState.framebuffer);    }    /*if (renderpassActive) {        cmdEndLabel(commandBuffer);        renderpassActive = false;    }*/     // Restore the saved stereo state for the next batch    _stereo._enable = savedStereo;     if (batch._mustUpdatePreviousModels) {        // Update object transform history for when the batch will be reexecuted        for (auto& objectTransform : batch._objects) {            objectTransform._previousModel = objectTransform._model;        }        batch._mustUpdatePreviousModels = false;    }     cmdEndLabel(commandBuffer);} void VKBackend::setDrawCommandBuffer(VkCommandBuffer commandBuffer) {    _currentCommandBuffer = commandBuffer;} VkDescriptorImageInfo VKBackend::getDefaultTextureDescriptorInfo() {    return _defaultTextureVk->getDescriptorImageInfo();} void VKBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo, const StereoState& prevStereo, Vec2u framebufferSize) {    // Check all the dirty flags and update the state accordingly    if (_invalidViewport) {        _camera._viewport = glm::vec4(_viewport);    }     if (_invalidProj) {        _camera._projection = _viewProjectionState._projection;    }     if (_invalidView) {        // Apply the correction        if (_viewProjectionState._viewIsCamera && ((_viewCorrectionEnabled || _viewCorrectionEnabledForFramePlayer) && _presentFrame.correction != glm::mat4())) {            Transform::mult(_viewProjectionState._correctedView, _viewProjectionState._view, _presentFrame.correctionInverse);        } else {            _viewProjectionState._correctedView = _viewProjectionState._view;        }         // This is when the _view matrix gets assigned        _viewProjectionState._correctedView.getInverseMatrix(_camera._view);    }     if (_invalidView || _invalidProj || _invalidViewport) {        size_t offset = _cameraUboSize * _cameras.size();        _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));         pushCameraBufferElement(stereo, prevStereo, _cameras);        if (_currentSavedTransformSlot != INVALID_SAVED_CAMERA_SLOT) {            // Save the offset of the saved camera slot in the camera buffer. Can be used to copy            // that data, or (in the future) to reuse the offset.            _savedTransforms[_currentSavedTransformSlot]._cameraOffset = offset;        }    }     // Flags are clean    _invalidView = _invalidProj = _invalidViewport = false;} void VKBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo, VKBackend::UniformStageState &uniform, FrameData &currentFrame) const {    size_t offset = INVALID_OFFSET;    while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) {        offset = (*_camerasItr).second;        _currentCameraOffset = offset;        ++_camerasItr;    }     if (offset != INVALID_OFFSET) {#ifdef GPU_STEREO_CAMERA_BUFFER        bindCurrentCamera(0, uniform, currentFrame);#else        if (!stereo.isStereo()) {            bindCurrentCamera(0);        }#endif    }} void VKBackend::TransformStageState::bindCurrentCamera(int eye, VKBackend::UniformStageState &uniform, FrameData &currentFrame) const {    if (_currentCameraOffset != INVALID_OFFSET) {        static_assert(slot::buffer::Buffer::CameraTransform >= MAX_NUM_UNIFORM_BUFFERS, "TransformCamera may overlap pipeline uniform buffer slots. Invalidate uniform buffer slot cache for safety (call _uniform._buffers[TRANSFORM_CAMERA_SLOT].reset()).");        // VKTODO: add convenience function for this?        auto &buffer = uniform._buffers[slot::buffer::Buffer::CameraTransform];        Q_ASSERT(currentFrame._cameraBuffer);        buffer.buffer = currentFrame._cameraBuffer.get();        buffer.size = sizeof(CameraBufferElement);        buffer.offset = _currentCameraOffset + eye * _cameraUboSize;    }} void VKBackend::TransformStageState::pushCameraBufferElement(const StereoState& stereo, const StereoState& prevStereo, TransformCameras& cameras) const {    const float jitterAmplitude = _projectionJitter._scale;    const Vec2 jitterScale = Vec2(jitterAmplitude * float(_projectionJitter._isEnabled & 1)) / Vec2(_viewport.z, _viewport.w);    const Vec2 jitter = jitterScale * _projectionJitter._offset;     if (stereo.isStereo()) {#ifdef GPU_STEREO_CAMERA_BUFFER        cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, prevStereo, _viewProjectionState._correctedView,                                                                   _viewProjectionState._previousCorrectedView, jitter),                                              _camera.getEyeCamera(1, stereo, prevStereo, _viewProjectionState._correctedView,                                                                   _viewProjectionState._previousCorrectedView, jitter)));#else        cameras.push_back((_camera.getEyeCamera(0, stereo, prevStereo, _viewProjectionState._correctedView,                                                _viewProjectionState._previousCorrectedView, jitter)));        cameras.push_back((_camera.getEyeCamera(1, stereo, prevStereo, _viewProjectionState._correctedView,                                                _viewProjectionState._previousCorrectedView, jitter)));#endif    } else {#ifdef GPU_STEREO_CAMERA_BUFFER        cameras.push_back(CameraBufferElement(            _camera.getMonoCamera(_skybox, _viewProjectionState._correctedView, _viewProjectionState._previousCorrectedView,                                  _viewProjectionState._previousProjection, jitter)));#else        cameras.push_back((_camera.getMonoCamera(_skybox, _viewProjectionState._correctedView,                                                 _viewProjectionState._previousCorrectedView, _viewProjectionState._previousProjection,                                                 jitter)));#endif    }} void VKBackend::preUpdateTransform() {    // VKTODO: use proper viewport size or remove parameter    _transform.preUpdate(_commandIndex, _stereo, _prevStereo, Vec2u(640, 480));} void VKBackend::store_glUniform1i(const Batch& batch, size_t paramOffset) {    _currentFrame->addGlUniform(sizeof(int), reinterpret_cast<const void*>(&batch._params[paramOffset + 0]._int), _commandIndex);} void VKBackend::store_glUniform1f(const Batch& batch, size_t paramOffset) {    _currentFrame->addGlUniform(sizeof(float), reinterpret_cast<const void*>(&batch._params[paramOffset + 0]._float), _commandIndex);} void VKBackend::store_glUniform2f(const Batch& batch, size_t paramOffset) {    std::array<float, 2> array {batch._params[paramOffset + 1]._float,                                batch._params[paramOffset + 0]._float};    _currentFrame->addGlUniform(sizeof(float) * 2, reinterpret_cast<const void*>(array.data()), _commandIndex);} void VKBackend::store_glUniform3f(const Batch& batch, size_t paramOffset) {    std::array<float, 3> array {batch._params[paramOffset + 2]._float,                                batch._params[paramOffset + 1]._float,                                batch._params[paramOffset + 0]._float};    _currentFrame->addGlUniform(sizeof(float) * 3, reinterpret_cast<const void*>(array.data()), _commandIndex); } void VKBackend::store_glUniform4f(const Batch& batch, size_t paramOffset) {    std::array<float, 4> array {batch._params[paramOffset + 3]._float,                                batch._params[paramOffset + 2]._float,                                batch._params[paramOffset + 1]._float,                                batch._params[paramOffset + 0]._float};    _currentFrame->addGlUniform(sizeof(float) * 4, reinterpret_cast<const void*>(array.data()), _commandIndex);} void VKBackend::store_glUniform3fv(const Batch& batch, size_t paramOffset) {    _currentFrame->addGlUniform(sizeof(float) * 3 * batch._params[paramOffset + 1]._uint,                                reinterpret_cast<const void*>(batch.readData(batch._params[paramOffset + 0]._uint)), _commandIndex);} 

Lines 1–500 of 3616.