mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
Adding an extension will now require an additional step, but this evolved version of the extension list code is more sane and the structures can be more easily copied around.
287 lines
8.8 KiB
C++
287 lines
8.8 KiB
C++
#include "dxvk_device.h"
|
|
#include "dxvk_instance.h"
|
|
|
|
namespace dxvk {
|
|
|
|
DxvkDevice::DxvkDevice(
|
|
const Rc<DxvkAdapter>& adapter,
|
|
const Rc<vk::DeviceFn>& vkd,
|
|
const DxvkDeviceExtensions& extensions,
|
|
const VkPhysicalDeviceFeatures& features)
|
|
: m_adapter (adapter),
|
|
m_vkd (vkd),
|
|
m_extensions (extensions),
|
|
m_features (features),
|
|
m_properties (adapter->deviceProperties()),
|
|
m_memory (new DxvkMemoryAllocator (adapter, vkd)),
|
|
m_renderPassPool (new DxvkRenderPassPool (vkd)),
|
|
m_pipelineManager (new DxvkPipelineManager (this)),
|
|
m_metaClearObjects (new DxvkMetaClearObjects (vkd)),
|
|
m_metaMipGenObjects (new DxvkMetaMipGenObjects (vkd)),
|
|
m_metaResolveObjects(new DxvkMetaResolveObjects (vkd)),
|
|
m_unboundResources (this),
|
|
m_submissionQueue (this) {
|
|
m_graphicsQueue.queueFamily = m_adapter->graphicsQueueFamily();
|
|
m_presentQueue.queueFamily = m_adapter->presentQueueFamily();
|
|
|
|
m_vkd->vkGetDeviceQueue(m_vkd->device(),
|
|
m_graphicsQueue.queueFamily, 0,
|
|
&m_graphicsQueue.queueHandle);
|
|
|
|
m_vkd->vkGetDeviceQueue(m_vkd->device(),
|
|
m_presentQueue.queueFamily, 0,
|
|
&m_presentQueue.queueHandle);
|
|
}
|
|
|
|
|
|
DxvkDevice::~DxvkDevice() {
|
|
// Wait for all pending Vulkan commands to be
|
|
// executed before we destroy any resources.
|
|
m_vkd->vkDeviceWaitIdle(m_vkd->device());
|
|
}
|
|
|
|
|
|
DxvkDeviceOptions DxvkDevice::options() const {
|
|
DxvkDeviceOptions options;
|
|
options.maxNumDynamicUniformBuffers = m_properties.limits.maxDescriptorSetUniformBuffersDynamic;
|
|
options.maxNumDynamicStorageBuffers = m_properties.limits.maxDescriptorSetStorageBuffersDynamic;
|
|
return options;
|
|
}
|
|
|
|
|
|
Rc<DxvkPhysicalBuffer> DxvkDevice::allocPhysicalBuffer(
|
|
const DxvkBufferCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags memoryType) {
|
|
return new DxvkPhysicalBuffer(m_vkd,
|
|
createInfo, *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,
|
|
m_pipelineManager,
|
|
m_metaClearObjects,
|
|
m_metaMipGenObjects,
|
|
m_metaResolveObjects);
|
|
}
|
|
|
|
|
|
Rc<DxvkFramebuffer> DxvkDevice::createFramebuffer(
|
|
const DxvkRenderTargets& renderTargets) {
|
|
const DxvkFramebufferSize defaultSize = {
|
|
m_properties.limits.maxFramebufferWidth,
|
|
m_properties.limits.maxFramebufferHeight,
|
|
m_properties.limits.maxFramebufferLayers };
|
|
|
|
auto renderPassFormat = DxvkFramebuffer::getRenderPassFormat(renderTargets);
|
|
auto renderPassObject = m_renderPassPool->getRenderPass(renderPassFormat);
|
|
|
|
return new DxvkFramebuffer(m_vkd,
|
|
renderPassObject, renderTargets, defaultSize);
|
|
}
|
|
|
|
|
|
Rc<DxvkBuffer> DxvkDevice::createBuffer(
|
|
const DxvkBufferCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags memoryType) {
|
|
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) {
|
|
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<DxvkQueryPool> DxvkDevice::createQueryPool(
|
|
VkQueryType queryType,
|
|
uint32_t queryCount) {
|
|
return new DxvkQueryPool(m_vkd, queryType, queryCount);
|
|
}
|
|
|
|
|
|
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<DxvkSwapchain> DxvkDevice::createSwapchain(
|
|
const Rc<DxvkSurface>& surface,
|
|
const DxvkSwapchainProperties& properties) {
|
|
return new DxvkSwapchain(this, surface, properties);
|
|
}
|
|
|
|
|
|
DxvkStatCounters DxvkDevice::getStatCounters() {
|
|
DxvkMemoryStats mem = m_memory->getMemoryStats();
|
|
|
|
DxvkStatCounters result;
|
|
result.setCtr(DxvkStatCounter::MemoryAllocated, mem.memoryAllocated);
|
|
result.setCtr(DxvkStatCounter::MemoryUsed, mem.memoryUsed);
|
|
|
|
std::lock_guard<sync::Spinlock> lock(m_statLock);
|
|
result.merge(m_statCounters);
|
|
return result;
|
|
}
|
|
|
|
|
|
void DxvkDevice::initResources() {
|
|
m_unboundResources.clearResources(this);
|
|
}
|
|
|
|
|
|
VkResult DxvkDevice::presentSwapImage(
|
|
const VkPresentInfoKHR& presentInfo) {
|
|
{ // Queue submissions are not thread safe
|
|
std::lock_guard<std::mutex> queueLock(m_submissionLock);
|
|
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
|
|
|
m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
|
|
return m_vkd->vkQueuePresentKHR(m_presentQueue.queueHandle, &presentInfo);
|
|
}
|
|
}
|
|
|
|
|
|
void DxvkDevice::submitCommandList(
|
|
const Rc<DxvkCommandList>& commandList,
|
|
const Rc<DxvkSemaphore>& waitSync,
|
|
const Rc<DxvkSemaphore>& wakeSync) {
|
|
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);
|
|
}
|
|
|
|
VkResult status;
|
|
|
|
{ // Queue submissions are not thread safe
|
|
std::lock_guard<std::mutex> queueLock(m_submissionLock);
|
|
std::lock_guard<sync::Spinlock> statLock(m_statLock);
|
|
|
|
m_statCounters.merge(commandList->statCounters());
|
|
m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
|
|
|
|
status = commandList->submit(
|
|
m_graphicsQueue.queueHandle,
|
|
waitSemaphore, wakeSemaphore);
|
|
}
|
|
|
|
if (status == VK_SUCCESS) {
|
|
// Add this to the set of running submissions
|
|
m_submissionQueue.submit(commandList);
|
|
} else {
|
|
Logger::err(str::format(
|
|
"DxvkDevice: Command buffer submission failed: ",
|
|
status));
|
|
}
|
|
}
|
|
|
|
|
|
void DxvkDevice::waitForIdle() {
|
|
if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
|
|
Logger::err("DxvkDevice: waitForIdle: Operation failed");
|
|
}
|
|
|
|
|
|
void DxvkDevice::recycleCommandList(const Rc<DxvkCommandList>& cmdList) {
|
|
m_recycledCommandLists.returnObject(cmdList);
|
|
}
|
|
|
|
}
|