mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
This is the first step to optimizing buffer updates for applications that frequently invalidate buffers. The goal is to reduce the number of buffer allocations per frame and reduce the cost of invalidation.
247 lines
7.6 KiB
C++
247 lines
7.6 KiB
C++
#include "dxvk_device.h"
|
|
#include "dxvk_instance.h"
|
|
|
|
namespace dxvk {
|
|
|
|
DxvkDevice::DxvkDevice(
|
|
const Rc<DxvkAdapter>& adapter,
|
|
const Rc<vk::DeviceFn>& vkd,
|
|
const Rc<DxvkDeviceExtensions>& extensions,
|
|
const VkPhysicalDeviceFeatures& features)
|
|
: m_adapter (adapter),
|
|
m_vkd (vkd),
|
|
m_extensions (extensions),
|
|
m_features (features),
|
|
m_memory (new DxvkMemoryAllocator(adapter, vkd)),
|
|
m_renderPassPool (new DxvkRenderPassPool (vkd)),
|
|
m_pipelineCache (new DxvkPipelineCache (vkd)),
|
|
m_pipelineManager (new DxvkPipelineManager(this)),
|
|
m_submissionQueue (this) {
|
|
m_options.adjustAppOptions(env::getExeName());
|
|
m_options.adjustDeviceOptions(m_adapter);
|
|
m_options.logOptions();
|
|
|
|
m_vkd->vkGetDeviceQueue(m_vkd->device(),
|
|
m_adapter->graphicsQueueFamily(), 0,
|
|
&m_graphicsQueue);
|
|
m_vkd->vkGetDeviceQueue(m_vkd->device(),
|
|
m_adapter->presentQueueFamily(), 0,
|
|
&m_presentQueue);
|
|
}
|
|
|
|
|
|
DxvkDevice::~DxvkDevice() {
|
|
// Wait for all pending Vulkan commands to be
|
|
// executed before we destroy any resources.
|
|
m_vkd->vkDeviceWaitIdle(m_vkd->device());
|
|
}
|
|
|
|
|
|
Rc<DxvkPhysicalBuffer> DxvkDevice::allocBufferResource(
|
|
const DxvkBufferCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags memoryType) {
|
|
return new DxvkPhysicalBuffer(m_vkd,
|
|
createInfo, 1, *m_memory, memoryType);
|
|
}
|
|
|
|
|
|
Rc<DxvkStagingBuffer> DxvkDevice::allocStagingBuffer(VkDeviceSize size) {
|
|
// In case we need a standard-size staging buffer, try
|
|
// to recycle an old one that has been returned earlier
|
|
if (size <= DefaultStagingBufferSize) {
|
|
const Rc<DxvkStagingBuffer> buffer
|
|
= m_recycledStagingBuffers.retrieveObject();
|
|
|
|
if (buffer != nullptr)
|
|
return buffer;
|
|
}
|
|
|
|
// Staging buffers only need to be able to handle transfer
|
|
// operations, and they need to be in host-visible memory.
|
|
DxvkBufferCreateInfo info;
|
|
info.size = size;
|
|
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
|
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
|
|
| VK_PIPELINE_STAGE_HOST_BIT;
|
|
info.access = VK_ACCESS_TRANSFER_READ_BIT
|
|
| VK_ACCESS_HOST_WRITE_BIT;
|
|
|
|
VkMemoryPropertyFlags memFlags
|
|
= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
|
|
// Don't create buffers that are too small. A staging
|
|
// buffer should be able to serve multiple uploads.
|
|
if (info.size < DefaultStagingBufferSize)
|
|
info.size = DefaultStagingBufferSize;
|
|
|
|
return new DxvkStagingBuffer(this->createBuffer(info, memFlags));
|
|
}
|
|
|
|
|
|
void DxvkDevice::recycleStagingBuffer(const Rc<DxvkStagingBuffer>& buffer) {
|
|
// Drop staging buffers that are bigger than the
|
|
// standard ones to save memory, recycle the rest
|
|
if (buffer->size() == DefaultStagingBufferSize) {
|
|
m_recycledStagingBuffers.returnObject(buffer);
|
|
buffer->reset();
|
|
}
|
|
}
|
|
|
|
|
|
Rc<DxvkCommandList> DxvkDevice::createCommandList() {
|
|
Rc<DxvkCommandList> cmdList = m_recycledCommandLists.retrieveObject();
|
|
|
|
if (cmdList == nullptr) {
|
|
cmdList = new DxvkCommandList(m_vkd,
|
|
this, m_adapter->graphicsQueueFamily());
|
|
}
|
|
|
|
return cmdList;
|
|
}
|
|
|
|
|
|
Rc<DxvkContext> DxvkDevice::createContext() {
|
|
return new DxvkContext(this);
|
|
}
|
|
|
|
|
|
Rc<DxvkFramebuffer> DxvkDevice::createFramebuffer(
|
|
const DxvkRenderTargets& renderTargets) {
|
|
auto format = renderTargets.renderPassFormat();
|
|
auto renderPass = m_renderPassPool->getRenderPass(format);
|
|
return new DxvkFramebuffer(m_vkd, renderPass, renderTargets);
|
|
}
|
|
|
|
|
|
Rc<DxvkBuffer> DxvkDevice::createBuffer(
|
|
const DxvkBufferCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags memoryType) {
|
|
m_statCounters.increment(DxvkStat::ResBufferCreations, 1);
|
|
return new DxvkBuffer(this, createInfo, memoryType);
|
|
}
|
|
|
|
|
|
Rc<DxvkBufferView> DxvkDevice::createBufferView(
|
|
const Rc<DxvkBuffer>& buffer,
|
|
const DxvkBufferViewCreateInfo& createInfo) {
|
|
return new DxvkBufferView(m_vkd, buffer, createInfo);
|
|
}
|
|
|
|
|
|
Rc<DxvkImage> DxvkDevice::createImage(
|
|
const DxvkImageCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags memoryType) {
|
|
m_statCounters.increment(DxvkStat::ResImageCreations, 1);
|
|
return new DxvkImage(m_vkd, createInfo, *m_memory, memoryType);
|
|
}
|
|
|
|
|
|
Rc<DxvkImageView> DxvkDevice::createImageView(
|
|
const Rc<DxvkImage>& image,
|
|
const DxvkImageViewCreateInfo& createInfo) {
|
|
return new DxvkImageView(m_vkd, image, createInfo);
|
|
}
|
|
|
|
|
|
Rc<DxvkSampler> DxvkDevice::createSampler(
|
|
const DxvkSamplerCreateInfo& createInfo) {
|
|
return new DxvkSampler(m_vkd, createInfo);
|
|
}
|
|
|
|
|
|
Rc<DxvkSemaphore> DxvkDevice::createSemaphore() {
|
|
return new DxvkSemaphore(m_vkd);
|
|
}
|
|
|
|
|
|
Rc<DxvkShader> DxvkDevice::createShader(
|
|
VkShaderStageFlagBits stage,
|
|
uint32_t slotCount,
|
|
const DxvkResourceSlot* slotInfos,
|
|
const DxvkInterfaceSlots& iface,
|
|
const SpirvCodeBuffer& code) {
|
|
return new DxvkShader(stage,
|
|
slotCount, slotInfos, iface, code);
|
|
}
|
|
|
|
|
|
Rc<DxvkComputePipeline> DxvkDevice::createComputePipeline(
|
|
const Rc<DxvkShader>& cs) {
|
|
return m_pipelineManager->createComputePipeline(
|
|
m_pipelineCache, cs);
|
|
}
|
|
|
|
|
|
Rc<DxvkGraphicsPipeline> DxvkDevice::createGraphicsPipeline(
|
|
const Rc<DxvkShader>& vs,
|
|
const Rc<DxvkShader>& tcs,
|
|
const Rc<DxvkShader>& tes,
|
|
const Rc<DxvkShader>& gs,
|
|
const Rc<DxvkShader>& fs) {
|
|
return m_pipelineManager->createGraphicsPipeline(
|
|
m_pipelineCache, vs, tcs, tes, gs, fs);
|
|
}
|
|
|
|
|
|
Rc<DxvkSwapchain> DxvkDevice::createSwapchain(
|
|
const Rc<DxvkSurface>& surface,
|
|
const DxvkSwapchainProperties& properties) {
|
|
return new DxvkSwapchain(this, surface, properties);
|
|
}
|
|
|
|
|
|
VkResult DxvkDevice::presentSwapImage(
|
|
const VkPresentInfoKHR& presentInfo) {
|
|
m_statCounters.increment(DxvkStat::DevQueuePresents, 1);
|
|
|
|
std::lock_guard<std::mutex> lock(m_submissionLock);
|
|
return m_vkd->vkQueuePresentKHR(m_presentQueue, &presentInfo);
|
|
}
|
|
|
|
|
|
Rc<DxvkFence> DxvkDevice::submitCommandList(
|
|
const Rc<DxvkCommandList>& commandList,
|
|
const Rc<DxvkSemaphore>& waitSync,
|
|
const Rc<DxvkSemaphore>& wakeSync) {
|
|
Rc<DxvkFence> fence = new DxvkFence(m_vkd);
|
|
|
|
VkSemaphore waitSemaphore = VK_NULL_HANDLE;
|
|
VkSemaphore wakeSemaphore = VK_NULL_HANDLE;
|
|
|
|
if (waitSync != nullptr) {
|
|
waitSemaphore = waitSync->handle();
|
|
commandList->trackResource(waitSync);
|
|
}
|
|
|
|
if (wakeSync != nullptr) {
|
|
wakeSemaphore = wakeSync->handle();
|
|
commandList->trackResource(wakeSync);
|
|
}
|
|
|
|
{ // Queue submissions are not thread safe
|
|
std::lock_guard<std::mutex> lock(m_submissionLock);
|
|
commandList->submit(m_graphicsQueue,
|
|
waitSemaphore, wakeSemaphore, fence->handle());
|
|
}
|
|
|
|
// Add this to the set of running submissions
|
|
m_submissionQueue.submit(fence, commandList);
|
|
m_statCounters.increment(DxvkStat::DevQueueSubmissions, 1);
|
|
return fence;
|
|
}
|
|
|
|
|
|
void DxvkDevice::waitForIdle() {
|
|
m_statCounters.increment(DxvkStat::DevSynchronizations, 1);
|
|
|
|
if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
|
|
throw DxvkError("DxvkDevice::waitForIdle: Operation failed");
|
|
}
|
|
|
|
|
|
void DxvkDevice::recycleCommandList(const Rc<DxvkCommandList>& cmdList) {
|
|
m_recycledCommandLists.returnObject(cmdList);
|
|
}
|
|
|
|
} |